Primefaces Push 导致 IllegalStateException
Primefaces Push causes IllegalStateException
目前我正在研究 primefaces 推送功能。实际上,推送功能工作正常。
这是我的推送流程:
我有两个共享同一个数据库的应用程序,admin dashboard(primefaces + spring) 和 webapi (spring mvc)。当有数据通过 webapi 插入数据库时,web api 将调用 admin url 将触发为所有登录用户推送通知。正如预期的那样,它工作正常。
我的问题是,当有推送通知时,然后用户同时注销(至少通知咆哮没有消失)它会抛出 java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
并且有时会抛出 Cannot create a session after the response has been committed
。
我已经尝试将登录控制器更改为 ViewScope,并将此上下文参数放入 Web xml,但仍然无法正常工作。
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
请帮忙,
这是完整的堆栈跟踪:
SEVERE:Servlet.service() for servlet [facesServlet] in context with path [/WebAdmin] throw exception [java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed] 根本原因
java.lang.IllegalStateException: 响应提交后无法调用 sendRedirect()
在 org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:482)
在 com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:678)
在 org.omnifaces.util.FacesLocal.redirect(FacesLocal.java:882)
在 org.omnifaces.util.Faces.redirect(Faces.java:1170)
在 com.sepakbole.web.controller.LoginController.doLogout(LoginController.java:84)
在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)
在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
在 java.lang.reflect.Method.invoke(Method.java:606)
在 org.apache.el.parser.AstValue.invoke(AstValue.java:279)
在 org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:273)
在 com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
在 javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
在 com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
在 javax.faces.component.UICommand.broadcast(UICommand.java:315)
在 javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
在 javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
在 com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
在 com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
在 com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
在 javax.faces.webapp.FacesServlet.service(FacesServlet.java:658)
在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
在 org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
在 com.sepakbole.web.filter.AuthorizationFilter.doFilter(AuthorizationFilter.java:51)
在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
在 org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
在 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
在 org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
在 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082)
在 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623)
在 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
在 org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
在 java.lang.Thread.run(Thread.java:745)
更新:
这是我的控制器:
import java.io.Serializable;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import org.primefaces.push.EventBus;
import org.primefaces.push.EventBusFactory;
@ManagedBean
@ApplicationScoped
public class PushMessageController implements Serializable {
private static final long serialVersionUID = 1L;
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public void execute(){
EventBus eventBus = EventBusFactory.getDefault().eventBus();
eventBus.publish("/receive-message", data);
}
}
下面是 PushEndPoint:
导入 org.primefaces.push.annotation.OnMessage;
导入 org.primefaces.push.annotation.PushEndpoint;
导入 org.primefaces.push.annotation.Singleton;
导入 org.primefaces.push.impl.JSONEncoder;
@PushEndpoint("/receive-message")
@Singleton
public class MessageResource {
@OnMessage(encoders = {JSONEncoder.class})
public String onMessage(String data) {
return data;
}
}
这是放在我的模板上的:
<p:socket channel="/receive-message" onMessage="handleMessage"></p:socket>
<p:growl widgetVar="growl-msg" globalOnly="true" id="growl-msg" life="2000" />
<script type="text/javascript">
function handleMessage(facesmessage) {
PF('growl-msg').renderMessage({"summary":"New Message",
"detail":facesmessage,
"severity":"info"})
}
</script>
触发通知,我称之为url。其中数据参数是将显示为咆哮的动态消息。
http://localhost:5050/WebAdmin/primepushpage/receive-message.jsf?data=Testing123
这是我收到的-message.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="../pages/template.xhtml">
<ui:define name="content">
<f:metadata>
<f:viewParam name="data" value="#{pushMessageController.data}" />
</f:metadata>
<h:form>
<p:remoteCommand name="remoteCommand" actionListener="#{pushMessageController.execute}" autoRun="true" />
</h:form>
</ui:define>
</ui:composition>
这是我的授权过滤器上的 doFilter 方法:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURL = request.getContextPath() + "/pages/index.jsf";
boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));
boolean pushRequest = request.getRequestURI().contains("primepush");
if (loggedIn || loginRequest || resourceRequest || pushRequest) {
if (!resourceRequest) { // Prevent browser from caching restricted resources. See also
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response); // So, just continue request.
}
else if (ajaxRequest) {
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
}
else {
if(!response.isCommitted()){
response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
return;
}
}
}
并且我在 web.xml 上添加了推送 servlet 映射,而且我的 pom.xml 已经具有大气运行时依赖性。我使用 Tomcat 7 作为容器。
我遇到了同样的问题。通过 ajax 刷新页面并且用户手动刷新页面后,我从 ocpsoft 重写了以下警告:
响应已经提交,不允许进一步的写操作。这可能会导致基础应用程序触发 IllegalStateException。
之后页面处于无效状态,需要第二次刷新。我花了很多时间试图解决这个问题,最后我尝试使用不同版本的大气运行时(起初我使用的是最新的 2.4.9)并发现它在 2.4.0 到 2.4 版本中都能正常工作。 2.
Primefaces 手册说您可以使用 2.3.RC6 和最新版本,但是对于低于 2.4.0 的版本,我在应用程序启动期间遇到了讨厌的异常。
我使用 Primefaces 6.0,应用程序部署在 Tomcat 8.0.39
目前我正在研究 primefaces 推送功能。实际上,推送功能工作正常。
这是我的推送流程: 我有两个共享同一个数据库的应用程序,admin dashboard(primefaces + spring) 和 webapi (spring mvc)。当有数据通过 webapi 插入数据库时,web api 将调用 admin url 将触发为所有登录用户推送通知。正如预期的那样,它工作正常。
我的问题是,当有推送通知时,然后用户同时注销(至少通知咆哮没有消失)它会抛出 java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
并且有时会抛出 Cannot create a session after the response has been committed
。
我已经尝试将登录控制器更改为 ViewScope,并将此上下文参数放入 Web xml,但仍然无法正常工作。
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
请帮忙,
这是完整的堆栈跟踪:
SEVERE:Servlet.service() for servlet [facesServlet] in context with path [/WebAdmin] throw exception [java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed] 根本原因 java.lang.IllegalStateException: 响应提交后无法调用 sendRedirect() 在 org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:482) 在 com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:678) 在 org.omnifaces.util.FacesLocal.redirect(FacesLocal.java:882) 在 org.omnifaces.util.Faces.redirect(Faces.java:1170) 在 com.sepakbole.web.controller.LoginController.doLogout(LoginController.java:84) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:606) 在 org.apache.el.parser.AstValue.invoke(AstValue.java:279) 在 org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:273) 在 com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105) 在 javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87) 在 com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) 在 javax.faces.component.UICommand.broadcast(UICommand.java:315) 在 javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790) 在 javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282) 在 com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81) 在 com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) 在 com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198) 在 javax.faces.webapp.FacesServlet.service(FacesServlet.java:658) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 com.sepakbole.web.filter.AuthorizationFilter.doFilter(AuthorizationFilter.java:51) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) 在 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) 在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 在 org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442) 在 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082) 在 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623) 在 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 在 org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 在 java.lang.Thread.run(Thread.java:745)
更新: 这是我的控制器:
import java.io.Serializable;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import org.primefaces.push.EventBus;
import org.primefaces.push.EventBusFactory;
@ManagedBean
@ApplicationScoped
public class PushMessageController implements Serializable {
private static final long serialVersionUID = 1L;
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public void execute(){
EventBus eventBus = EventBusFactory.getDefault().eventBus();
eventBus.publish("/receive-message", data);
}
}
下面是 PushEndPoint: 导入 org.primefaces.push.annotation.OnMessage; 导入 org.primefaces.push.annotation.PushEndpoint; 导入 org.primefaces.push.annotation.Singleton; 导入 org.primefaces.push.impl.JSONEncoder;
@PushEndpoint("/receive-message")
@Singleton
public class MessageResource {
@OnMessage(encoders = {JSONEncoder.class})
public String onMessage(String data) {
return data;
}
}
这是放在我的模板上的:
<p:socket channel="/receive-message" onMessage="handleMessage"></p:socket>
<p:growl widgetVar="growl-msg" globalOnly="true" id="growl-msg" life="2000" />
<script type="text/javascript">
function handleMessage(facesmessage) {
PF('growl-msg').renderMessage({"summary":"New Message",
"detail":facesmessage,
"severity":"info"})
}
</script>
触发通知,我称之为url。其中数据参数是将显示为咆哮的动态消息。
http://localhost:5050/WebAdmin/primepushpage/receive-message.jsf?data=Testing123
这是我收到的-message.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="../pages/template.xhtml">
<ui:define name="content">
<f:metadata>
<f:viewParam name="data" value="#{pushMessageController.data}" />
</f:metadata>
<h:form>
<p:remoteCommand name="remoteCommand" actionListener="#{pushMessageController.execute}" autoRun="true" />
</h:form>
</ui:define>
</ui:composition>
这是我的授权过滤器上的 doFilter 方法:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURL = request.getContextPath() + "/pages/index.jsf";
boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));
boolean pushRequest = request.getRequestURI().contains("primepush");
if (loggedIn || loginRequest || resourceRequest || pushRequest) {
if (!resourceRequest) { // Prevent browser from caching restricted resources. See also
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response); // So, just continue request.
}
else if (ajaxRequest) {
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
}
else {
if(!response.isCommitted()){
response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
return;
}
}
}
并且我在 web.xml 上添加了推送 servlet 映射,而且我的 pom.xml 已经具有大气运行时依赖性。我使用 Tomcat 7 作为容器。
我遇到了同样的问题。通过 ajax 刷新页面并且用户手动刷新页面后,我从 ocpsoft 重写了以下警告:
响应已经提交,不允许进一步的写操作。这可能会导致基础应用程序触发 IllegalStateException。
之后页面处于无效状态,需要第二次刷新。我花了很多时间试图解决这个问题,最后我尝试使用不同版本的大气运行时(起初我使用的是最新的 2.4.9)并发现它在 2.4.0 到 2.4 版本中都能正常工作。 2.
Primefaces 手册说您可以使用 2.3.RC6 和最新版本,但是对于低于 2.4.0 的版本,我在应用程序启动期间遇到了讨厌的异常。
我使用 Primefaces 6.0,应用程序部署在 Tomcat 8.0.39