【问题标题】:In jsf 1.2, defining a locale from bean causes IllegalStateException in getOutputStream在 jsf 1.2 中,从 bean 定义语言环境会导致 getOutputStream 中出现 IllegalStateException
【发布时间】:2011-04-04 09:49:57
【问题描述】:

我正在尝试了解这是如何发生的以及如何解决它。 我有一个 jsf 请求 bean,其 jsp 页面如下所示:(总结)

<f:view locale="#{drcBean.userLocale}">
</f:view>

支持 bean 代码:

public Locale getUserLocale() {
    return new Locale("en");
}

最后,当会话开始时,这个方法被调用(向客户端发送一个文件)

private void sendFile()
{
        byte[] config = ...;
        String clientFileName = "iphone.mobileconfig";

        // Prepare.
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

        // File file = new File(getFilePath(), getFileName());

        BufferedInputStream input = null;
        BufferedOutputStream output = null;

        try {
            // Open file.
            ByteArrayInputStream bais = new ByteArrayInputStream(config);
            input = new BufferedInputStream(bais);

            // Init servlet response.
            response.reset();
            response.setContentType("application/x-apple-aspen-config");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + clientFileName
                    + "\"");
            response.setContentLength((int) config.length);

            // if (logger.isTraceEnabled()) {
            // logger.trace("Writing XML Script:" + new String(scriptDataByteArray));
            // }


            output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);

            // Write file contents to response.
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int length;
            while ((length = input.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }

            // Finalize task.
            output.flush();
        } catch

我得到一个IllegalStateException 异常调用response.getOutputStream() 我不明白的是,如果未定义 locale="#{drcBean.userLocale}",则不会发生这种情况。 (意思是我省略了视图的locale标签,问题就消失了)

  • 另一个证据是,我最终收到的文件是提到的 JSP 页面,在我看来,这意味着以某种方式发送了一个新页面并中止了文件发送。但这与locale 有什么关系?

  • 另外,如果我使用 &lt;f:view locale="en"&gt; 而不是使用支持 bean 作为值,它可以正常工作。

【问题讨论】:

  • IllegalStateException 的消息是什么?
  • java.lang.IllegalStateException at org.apache.myfaces.application.jsp.ViewResponseWrapper.getOutputStream(ViewResponseWrapper.java:115)
  • 听起来像 JSF impl bug(但我从未见过这个)。您使用的是哪个 impl 和版本?尝试升级。 Mojarra 1.2 目前是相当成熟的 1.2_15。
  • 我的脸 1.2.7。我认为问题源于在语言环境中使用 EL 表达式会导致它向响应中写入一些内容,然后您就无法再发送文件了..
  • 通常情况下,如果在调用 getOutputStream() 之前已经向响应发送了一些内容,那么您会收到 IllegalArgumentException,但会显示一条消息“已为此响应调用 getOutputStream”,但是您根本没有收到任何消息?

标签: jsp jsf servlets


【解决方案1】:

如果您尝试将文件下载给用户,为什么要为语言环境而烦恼?

无论如何,我不知道您为什么会收到 IllegalArgumentException。当您尝试使用已写入的 OutputStream 时,通常会发生这种情况。

如果我使用下面的jsp:

<%@ page import="uk.co.farwell.MyBean" %>

<f:view locale="en">
<%
MyBean bean = (DossierBean) pageContext.findAttribute ("bean");

bean.sendFile(response);
%>
</f:view>

在 bean 中使用以下代码:

public void sendFile(HttpServletResponse response) throws Exception {
    byte[] config = "foobar".getBytes();
    String clientFileName = "iphone.mobileconfig";

    BufferedInputStream input = null;
    BufferedOutputStream output = null;

    ByteArrayInputStream bais = new ByteArrayInputStream(config);
    input = new BufferedInputStream(bais);

    response.reset();
    response.setContentType("application/x-apple-aspen-config");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + clientFileName + "\"");
    response.setContentLength((int) config.length);

    output = new BufferedOutputStream(response.getOutputStream(), 1024);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = input.read(buffer)) > 0) {
        output.write(buffer, 0, length);
    }

    output.flush();
}

我得到一个 IllegalArgumentException。如果这是您遇到的错误,请尝试直接使用 Servlet。

public final class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        byte[] config = "foobar".getBytes();

        resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");

        resp.setContentType("application/rtf");
        resp.setContentLength(config.length());
        resp.getOutputStream().write(config.getBytes());
        resp.getOutputStream().flush();
        resp.getOutputStream().close();
    }
}

或类似的。这比使用jsp下载文件更可靠。

【讨论】:

  • 非常感谢您的回答,但我想我已经找到了解决这个问题的真正答案。详细如下
【解决方案2】:

真正解决这个问题的方法是在JSP页面加载之前发送文件。 为此,我注册了一个 phaselistener 并在 JSP 页面之前发送了文件。

public class DrcPhaseListener implements PhaseListener {

    @Override
    public void afterPhase(PhaseEvent pe) { 
              ..... send file here ....
        }

问题是,正如我所怀疑的,JSP 页面已经开始向响应发送日期,当我尝试从 bean 发送文件时为时已晚。 上面的修复将强制在页面尚未开始加载的阶段发送文件,因此没有任何内容写入响应。

【讨论】:

  • 这味道很重。你究竟是如何请求文件的?通过提交表单并在 bean 的操作方法中调用它,对吗?这个 hack 表明您在附加到 JSP 的 getter 方法中执行此操作,这反过来又是错误的方法。
  • @BalusC 实际上,bean 属于请求范围,每个请求的“状态”由 URL 参数确定。文件传输仅由 URL 参数的值决定,因此无需加载任何 JSP 页面。甚至提交按钮也会创建一个带有新 URL 参数的新请求。更有意义?
  • 因此,通过提交表单?好的,我发布了一个答案。
【解决方案3】:

您需要在操作方法的末尾调用FacesContext#responseComplete() 以防止JSF 导航到页面(默认为提交表单的页面)。

public void download() {
    sendFile();
    FacesContext.getCurrentInstance().responseComplete();
}

【讨论】:

  • 问题是页面在调用 sendFile() 之前已经开始向响应发送数据,所以调用responseComplete() 为时已晚
  • 为什么会这样呢?这只有在您使用一些 getter 方法而不是命令按钮/链接操作调用它时才能解释。这几乎可以肯定是 MyFaces 中的一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-16
  • 2013-01-06
  • 1970-01-01
  • 2013-03-15
  • 2016-01-20
  • 2011-05-27
  • 2012-08-18
相关资源
最近更新 更多