使用Trinidad导出CSV/PDF文件时,会遇到getOutputStream() has already been called的异常,其原因是JSF在导出文件的时候会调用response.getWriter()方法,而在此之前backingbean中调用了response.getOutputStream()方法。两者会有冲突。看一下org.apache.catalina.connector.Response.java的源代码就知道,如果调用了getOutputStream() 方法,则usingOutputStream=true,再调用getWriter()方法就会抛异常,代码片断如下:
609 if (usingOutputStream)
610 throw new IllegalStateException
611 (sm.getString("coyoteResponse.getWriter.ise"));
出现这种异常的场景如下:
使用Trinidad中的控件导出CSV文件
==========.xhtml=======================================
...
<tr:commandButton icon="../images/csv.jpg" shortDesc="Export to CSV">
<tr:fileDownloadActionListener
contentType="text/csv; charset=utf-8" filename="content.csv"
method="#{backingBean.exportCsv}" />
</tr:commandButton>
...
==========================
=========backingBean===================================
public final void sendContent(final FacesContext context,
final OutputStream out,
final UIXTable table)
throws IOException {
OutputStreamWriter outw = new OutputStreamWriter(out, "UTF-8");
Object rowKey = table.getRowKey();
for (int i = 0; i < table.getRowCount(); i++) {
table.setRowIndex(i);
if (!table.isRowAvailable()) {
break;
}
exportRow(table, outw);
}
outw.flush();
table.setRowKey(rowKey);
}
====================================
after exporting csv file, there is an error on console like this:
=============================================================
SEVERE: Servlet.service() for servlet Spring Dispatcher Servlet threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at com.sun.facelets.FaceletViewHandler.createResponseWriter(FaceletViewHandler.java:400)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:557)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:189)
at org.apache.myfaces.trinidadinternal.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:176)
at org.springframework.faces.webflow.JsfView.render(JsfView.java:94)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:245)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:204)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:262)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:163)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:173)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
......
在导出CSV的时候,可以用一个替代方案,就是用Writer代替OuputStream,这样就不会有异常了。
但如果我们使用iText导出PDF文件,就必须使用OutputStream,所以也会有上述异常出现。
对于这种情况有一个折中的解决方案, that is, call servlet in webflow, then export pdf in servlet.
the sevlet is independant on Webflow and JSF, so the getWriter() mothod won't be called by the container.
the solution is like below
========================================
listView.xhtml
========================================
<tr:commandLink action="exportPdf">
<tr:image source="../images/pdf.jpg" shortDesc="Export to PDF"/>
</tr:commandLink>
===============================================
webflow.xml
===============================================
<view-state id="listView">
...
<transition on="exportPdf" to="pdfView" >
<evaluate expression="backingBean.saveTable()" />
</transition>
</view-state>
...
<view-state id="pdfView" view="externalRedirect:contextRelative:/exportPdfAction" >
<transition to="txnHistoryListView" />
</view-state>
================================================
in the saveTable() method of backingbean, we can put tabel value and related value into HTTPSession like this.
===========================================
backingbean.java
===========================================
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.setAttribute("key", tableValue, RequestAttributes.SCOPE_SESSION);
=================================================
"externalRedirect:contextRelative:/exportPdfAction" configured in webflow.xml will navigator to
the servlet configured in web.xml
in servlet, we can get data from session and invoke method to export pdf file.
you should pay attention to one thing, that is, you can't pass UIXTable object into the session.
Because out of JSF lifecycle, we can't get label value from the UIXTable object.
I guess this is related to the FacesContext issue.
so you should wrap the data in UIXTable into your own object and save it into session.
原帖地址:
http://jsfgroup.group.iteye.com/group/topic/9959?page=2
609 if (usingOutputStream)
610 throw new IllegalStateException
611 (sm.getString("coyoteResponse.getWriter.ise"));
出现这种异常的场景如下:
使用Trinidad中的控件导出CSV文件
==========.xhtml=======================================
...
<tr:commandButton icon="../images/csv.jpg" shortDesc="Export to CSV">
<tr:fileDownloadActionListener
contentType="text/csv; charset=utf-8" filename="content.csv"
method="#{backingBean.exportCsv}" />
</tr:commandButton>
...
==========================
=========backingBean===================================
public final void sendContent(final FacesContext context,
final OutputStream out,
final UIXTable table)
throws IOException {
OutputStreamWriter outw = new OutputStreamWriter(out, "UTF-8");
Object rowKey = table.getRowKey();
for (int i = 0; i < table.getRowCount(); i++) {
table.setRowIndex(i);
if (!table.isRowAvailable()) {
break;
}
exportRow(table, outw);
}
outw.flush();
table.setRowKey(rowKey);
}
====================================
after exporting csv file, there is an error on console like this:
=============================================================
SEVERE: Servlet.service() for servlet Spring Dispatcher Servlet threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
at com.sun.facelets.FaceletViewHandler.createResponseWriter(FaceletViewHandler.java:400)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:557)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:189)
at org.apache.myfaces.trinidadinternal.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:176)
at org.springframework.faces.webflow.JsfView.render(JsfView.java:94)
at org.springframework.webflow.engine.ViewState.render(ViewState.java:245)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:204)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:262)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:163)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:173)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
......
在导出CSV的时候,可以用一个替代方案,就是用Writer代替OuputStream,这样就不会有异常了。
但如果我们使用iText导出PDF文件,就必须使用OutputStream,所以也会有上述异常出现。
对于这种情况有一个折中的解决方案, that is, call servlet in webflow, then export pdf in servlet.
the sevlet is independant on Webflow and JSF, so the getWriter() mothod won't be called by the container.
the solution is like below
========================================
listView.xhtml
========================================
<tr:commandLink action="exportPdf">
<tr:image source="../images/pdf.jpg" shortDesc="Export to PDF"/>
</tr:commandLink>
===============================================
webflow.xml
===============================================
<view-state id="listView">
...
<transition on="exportPdf" to="pdfView" >
<evaluate expression="backingBean.saveTable()" />
</transition>
</view-state>
...
<view-state id="pdfView" view="externalRedirect:contextRelative:/exportPdfAction" >
<transition to="txnHistoryListView" />
</view-state>
================================================
in the saveTable() method of backingbean, we can put tabel value and related value into HTTPSession like this.
===========================================
backingbean.java
===========================================
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.setAttribute("key", tableValue, RequestAttributes.SCOPE_SESSION);
=================================================
"externalRedirect:contextRelative:/exportPdfAction" configured in webflow.xml will navigator to
the servlet configured in web.xml
in servlet, we can get data from session and invoke method to export pdf file.
you should pay attention to one thing, that is, you can't pass UIXTable object into the session.
Because out of JSF lifecycle, we can't get label value from the UIXTable object.
I guess this is related to the FacesContext issue.
so you should wrap the data in UIXTable into your own object and save it into session.
原帖地址:
http://jsfgroup.group.iteye.com/group/topic/9959?page=2