Primefaces Push导致IllegalStateException

时间:2016-11-01 23:06:39

标签: jsf primefaces push

目前正在研究primefaces推送功能。实际上,推送功能正常工作。

这是我的推送流程: 我有两个应用程序共享相同的数据库,管理仪表板(primefaces + spring)和webapi(spring mvc)。当通过webapi将数据插入数据库时​​,web api将调用将触发所有登录用户的推送通知的管理URL。及其工作正常,如预期的那样。

我的问题是,当有推送通知,然后用户同时注销时(至少通知咆哮没有消失),它将抛出java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed,有时Cannot create a session after the response has been committed

我尝试将Login Controller更改为ViewScope,并将此context-param放到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] threw exception [java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed] with root cause
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:482)
    at com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:678)
    at org.omnifaces.util.FacesLocal.redirect(FacesLocal.java:882)
    at org.omnifaces.util.Faces.redirect(Faces.java:1170)
    at com.sepakbole.web.controller.LoginController.doLogout(LoginController.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.el.parser.AstValue.invoke(AstValue.java:279)
    at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:273)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
    at javax.faces.component.UICommand.broadcast(UICommand.java:315)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:658)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.sepakbole.web.filter.AuthorizationFilter.doFilter(AuthorizationFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at 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:     import org.primefaces.push.annotation.OnMessage;     import org.primefaces.push.annotation.PushEndpoint;     import org.primefaces.push.annotation.Singleton;     import 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>

触发通知,我称之为网址。其中data参数是动态消息,将显示为growl。

http://localhost:5050/WebAdmin/primepushpage/receive-message.jsf?data=Testing123

这是我的receive-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 http://stackoverflow.com/q/4194207/157882
                    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上添加了push servlet映射,我的pom.xml也已经有了大气运行时依赖。我使用Tomcat 7作为容器。

1 个答案:

答案 0 :(得分:3)

我有同样的问题。在通过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