ICEfaces
  1. ICEfaces
  2. ICE-9224

sessionExpiredRedirectURI does not redirect with CDI/Weld

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Won't Fix
    • Affects Version/s: 3.3
    • Fix Version/s: EE-3.3.0.GA, 4.0.BETA, 4.0
    • Component/s: Bridge, Framework
    • Labels:
      None
    • Environment:
      JBoss 6, CDI, Weld
    • Assignee Priority:
      P1
    • Salesforce Case Reference:
    • Workaround Exists:
      Yes
    • Workaround Description:
      Hide
      As noted in the case, it's possible to handle the CDI exception from the client using either the JSF or ICE API for error handling.

      It's also possible to make your own ExceptionHandler that can either remove the exception and let the SessionExpiredException propagate to the client so that the redirect behaves normally or handle the error in some other way.
      Show
      As noted in the case, it's possible to handle the CDI exception from the client using either the JSF or ICE API for error handling. It's also possible to make your own ExceptionHandler that can either remove the exception and let the SessionExpiredException propagate to the client so that the redirect behaves normally or handle the error in some other way.

      Description

      After a CDI Conversation is started the sessionExpireRedirectURI does not redirect the user after the session has expired. If a CDI conversation is not started then the parameter works correctly.

        Activity

        Hide
        Arran Mccullough added a comment -

        Attached test case that shows the issue. It is setup to run on Jboss EAP 6.1.0 and is compiled using JDK7.

        Steps:

        • Open 'http://localhost:8080/Concept2' URL
        • In the server logs it should show that the session was created.
        • Click the start button.
        • Wait 1 min for the session to timeout.
        • Click the noop button, the Session Expired popup is shown and the user is not redirected to the nada.xhtml page.
        • If the start button is not clicked then after the session expires the use is redirected correctly after clicking the noop button.
        Show
        Arran Mccullough added a comment - Attached test case that shows the issue. It is setup to run on Jboss EAP 6.1.0 and is compiled using JDK7. Steps: Open 'http://localhost:8080/Concept2' URL In the server logs it should show that the session was created. Click the start button. Wait 1 min for the session to timeout. Click the noop button, the Session Expired popup is shown and the user is not redirected to the nada.xhtml page. If the start button is not clicked then after the session expires the use is redirected correctly after clicking the noop button.
        Hide
        Deryk Sinotte added a comment -

        Got it set up properly and confirmed the behaviour noted in the test case. Normally when the session has expired, the next interaction with the app would return the following (as shown from the Firefox console log):

        [10:24:31.272] [window] received error message [code: 200]: 
        <?xml version='1.0' encoding='UTF-8'?>
        <partial-response><error><error-name>class org.icefaces.application.SessionExpiredException...</partial-response>

        However, it appears that the when a conversation is started, the next interaction after the session expired throws a different exception:

        [10:26:21.813] [window] received error message [code: 200]: 
        <?xml version='1.0' encoding='UTF-8'?>
        <partial-response><error><error-name>class org.jboss.weld.context.NonexistentConversationException</error-name><error-message><![CDATA[WELD-000321 No conversation found to restore for id 1]]>...</partial-response>

        I suspect that the check and failure to find the conversation id is done before the session is accessed. Since the bridge is configured to redirect on a SessionExpiredException, it's not working.

        I'm digging in to why this exception is thrown in the first place to see the best way to possibly get it to work. One thing to try might be to use the container's exception handling:

        <error-page>
            <exception-type>org.jboss.weld.context.NonexistentConversationException</exception-type>
            <location>/faces/error.xhtml</location>
        </error-page>
        Show
        Deryk Sinotte added a comment - Got it set up properly and confirmed the behaviour noted in the test case. Normally when the session has expired, the next interaction with the app would return the following (as shown from the Firefox console log): [10:24:31.272] [window] received error message [code: 200]: <?xml version='1.0' encoding='UTF-8'?> <partial-response><error><error-name>class org.icefaces.application.SessionExpiredException...</partial-response> However, it appears that the when a conversation is started, the next interaction after the session expired throws a different exception: [10:26:21.813] [window] received error message [code: 200]: <?xml version='1.0' encoding='UTF-8'?> <partial-response><error><error-name>class org.jboss.weld.context.NonexistentConversationException</error-name><error-message><![CDATA[WELD-000321 No conversation found to restore for id 1]]>...</partial-response> I suspect that the check and failure to find the conversation id is done before the session is accessed. Since the bridge is configured to redirect on a SessionExpiredException, it's not working. I'm digging in to why this exception is thrown in the first place to see the best way to possibly get it to work. One thing to try might be to use the container's exception handling: <error-page> <exception-type> org.jboss.weld.context.NonexistentConversationException </exception-type> <location> /faces/error.xhtml </location> </error-page>
        Hide
        Deryk Sinotte added a comment -

        Tried a couple of different variations of this myself but none seemed to work.

        <error-page>  
            <exception-type>org.jboss.weld.context.NonexistentConversationException</exception-type>  
            <location>error.html</location>  
        </error-page>  
        
        <error-page>  
            <exception-type>org.jboss.weld.context.NonexistentConversationException</exception-type>  
            <location>/faces/nada.xhtml?nocid=true</location>  
        </error-page>

        The exception is likely be handled in a way that the servlet container never sees.

        Show
        Deryk Sinotte added a comment - Tried a couple of different variations of this myself but none seemed to work. <error-page> <exception-type> org.jboss.weld.context.NonexistentConversationException </exception-type> <location> error.html </location> </error-page> <error-page> <exception-type> org.jboss.weld.context.NonexistentConversationException </exception-type> <location> /faces/nada.xhtml?nocid=true </location> </error-page> The exception is likely be handled in a way that the servlet container never sees.
        Hide
        Deryk Sinotte added a comment - - edited

        This doesn't look to be an ICEfaces issue per se. If I remove the ICEfaces libraries and tweak the pages and web.xml so that it's a plain JSF app, the error-page directives noted before will work. However, if I then add Ajax to the buttons:

        <h:commandButton value="start" action="#{javax.enterprise.context.conversation.begin()}">
            <f:ajax execute="@all" render="@all"/>
        </h:commandButton>
        <h:commandButton value="noop" action="#{backingBean.noop()}">
            <f:ajax execute="@all" render="@all"/>
        </h:commandButton>
        

        Then the behaviour is similar to ICEfaces in that a the error-page redirects are not honoured. If in Development mode:

        <context-param>
            <param-name>javax.faces.PROJECT_STAGE</param-name>
            <param-value>Development</param-value>
        </context-param>

        JSF will pop up an alert that a server error occurred but in Production mode it just sits there.

        So to summarize, the problems are:

        1. The ICEfaces bridge is looking for a SessionExpiredException returned as the Ajax response in order to trigger the redirect. Since the NonexistentConversationException occurs first, it's returned as the Ajax response and the redirect never happens.
        2. It's a JSF behaviour to ignore error-page directs for Ajax responses and is not constrained to ICEfaces.

        The best/easiest way to handle this might be to use the jsf API on the client (jsf.ajax.addOnError(callback)) and do the redirect in that code. I'll look next at doing something up as an example.

        Show
        Deryk Sinotte added a comment - - edited This doesn't look to be an ICEfaces issue per se. If I remove the ICEfaces libraries and tweak the pages and web.xml so that it's a plain JSF app, the error-page directives noted before will work. However, if I then add Ajax to the buttons: <h:commandButton value= "start" action= "#{javax.enterprise.context.conversation.begin()}" > <f:ajax execute= "@all" render= "@all" /> </h:commandButton> <h:commandButton value= "noop" action= "#{backingBean.noop()}" > <f:ajax execute= "@all" render= "@all" /> </h:commandButton> Then the behaviour is similar to ICEfaces in that a the error-page redirects are not honoured. If in Development mode: <context-param> <param-name> javax.faces.PROJECT_STAGE </param-name> <param-value> Development </param-value> </context-param> JSF will pop up an alert that a server error occurred but in Production mode it just sits there. So to summarize, the problems are: The ICEfaces bridge is looking for a SessionExpiredException returned as the Ajax response in order to trigger the redirect. Since the NonexistentConversationException occurs first, it's returned as the Ajax response and the redirect never happens. It's a JSF behaviour to ignore error-page directs for Ajax responses and is not constrained to ICEfaces. The best/easiest way to handle this might be to use the jsf API on the client (jsf.ajax.addOnError(callback)) and do the redirect in that code. I'll look next at doing something up as an example.
        Hide
        Deryk Sinotte added a comment -

        As noted, this isn't a problem with the conversation id not getting propagated. The real issue is that the org.jboss.weld.context.NonexistentConversationException occurs ahead of the SessionExpiredException and interferes with the ability for ICEfaces to run the logic for redirecting to the sessionExpiredRedirectURI.

        There are at least two application-centric ways to work around this:

        1. Add a client-side handler to listen for the specific error and redirect from there. The script, using ICEfaces JavaScript API (http://www.icesoft.org/wiki/display/ICE/JavaScript+Client+API) would look something like the following:

        var handleNoConversationICE = function handleNoConversationICE(status,responseText,responseXML) {
            if (responseText) {
                if (responseText.indexOf("NonexistentConversationException") != -1 ) {
                    //This is the point where we decide how to handle the exception.  Assuming
                    //that the conversation id is gone because the session has expired, there are
                    //a few options on how to handle it.
                    console.log("detected NonexistentConversationException");
                    window.location.href = "./faces/nada.xhtml";
                }
            }
        };
        
        ice.onServerError(handleNoConversationICE);
        

        There is also use the standard JSF API - jsf.ajax.addOnError(handleNoConversationJSF) - to register a callback and do something similar.

        2) The JSF way to deal with exceptions in Ajax is to create an ExceptionHandler. There are 3 things you need to do:

        • Create your own class that extends ExceptionHandler
        • Create an ExceptionHandlerFactory that returns your ExceptionHandler wrapped around the parent ExceptionHandler (to maintain the chain of ExceptionHandlers).
        • Register your factory in the faces-config file.
        Show
        Deryk Sinotte added a comment - As noted, this isn't a problem with the conversation id not getting propagated. The real issue is that the org.jboss.weld.context.NonexistentConversationException occurs ahead of the SessionExpiredException and interferes with the ability for ICEfaces to run the logic for redirecting to the sessionExpiredRedirectURI. There are at least two application-centric ways to work around this: 1. Add a client-side handler to listen for the specific error and redirect from there. The script, using ICEfaces JavaScript API ( http://www.icesoft.org/wiki/display/ICE/JavaScript+Client+API ) would look something like the following: var handleNoConversationICE = function handleNoConversationICE(status,responseText,responseXML) { if (responseText) { if (responseText.indexOf("NonexistentConversationException") != -1 ) { //This is the point where we decide how to handle the exception. Assuming //that the conversation id is gone because the session has expired, there are //a few options on how to handle it. console.log("detected NonexistentConversationException"); window.location.href = "./faces/nada.xhtml"; } } }; ice.onServerError(handleNoConversationICE); There is also use the standard JSF API - jsf.ajax.addOnError(handleNoConversationJSF) - to register a callback and do something similar. 2) The JSF way to deal with exceptions in Ajax is to create an ExceptionHandler. There are 3 things you need to do: Create your own class that extends ExceptionHandler Create an ExceptionHandlerFactory that returns your ExceptionHandler wrapped around the parent ExceptionHandler (to maintain the chain of ExceptionHandlers). Register your factory in the faces-config file.
        Hide
        Deryk Sinotte added a comment -

        Attaching a modified version of the test case that shows the ExceptionHandler strategy. Source has been included but the libs have been removed to reduce the size.

        Show
        Deryk Sinotte added a comment - Attaching a modified version of the test case that shows the ExceptionHandler strategy. Source has been included but the libs have been removed to reduce the size.
        Hide
        Deryk Sinotte added a comment -

        Since this is not currently seen as a generic solution, we're going to leave it as something for application developers to use. Another case (ICE-9234) has been opened to potentially add a feature for more generic handling for exceptions from Ajax responses.

        Show
        Deryk Sinotte added a comment - Since this is not currently seen as a generic solution, we're going to leave it as something for application developers to use. Another case ( ICE-9234 ) has been opened to potentially add a feature for more generic handling for exceptions from Ajax responses.
        Hide
        Nicklas Karlsson added a comment -

        (Case owner commenting)

        I've used option 2 before but I considered it more of handling a side effect (NonexistingConversationException) than the actual session expiring, I'll give option 1 a spin. What should the web.xml parameters for disabling default popups be for option 1 and 2.

        What I find slightly confusion is that Weld activates the non-transient conversation in a PhaseListener but even if I activate the Filter I have in the original WAR, I can detect the stale session and redirect before the exception is thrown but redirect is still ignored.

        Show
        Nicklas Karlsson added a comment - (Case owner commenting) I've used option 2 before but I considered it more of handling a side effect (NonexistingConversationException) than the actual session expiring, I'll give option 1 a spin. What should the web.xml parameters for disabling default popups be for option 1 and 2. What I find slightly confusion is that Weld activates the non-transient conversation in a PhaseListener but even if I activate the Filter I have in the original WAR, I can detect the stale session and redirect before the exception is thrown but redirect is still ignored.
        Hide
        Arran Mccullough added a comment -

        The following context parameter can be used to disable the default error popups:

        <context-param>
        <param-name>org.icefaces.disableDefaultErrorPopups</param-name>
        <param-value>true</param-value>
        </context-param>

        http://www.icesoft.org/wiki/display/ICE/disableDefaultErrorPopups

        Show
        Arran Mccullough added a comment - The following context parameter can be used to disable the default error popups: <context-param> <param-name>org.icefaces.disableDefaultErrorPopups</param-name> <param-value>true</param-value> </context-param> http://www.icesoft.org/wiki/display/ICE/disableDefaultErrorPopups
        Hide
        Deryk Sinotte added a comment -

        The ICEfaces client-side bridge is looking for a very specific Ajax response to do the redirect. It must be an error containing org.icefaces.application.SessionExpiredException. If anything else arrives as the Ajax response, the redirect does not get triggered. That's why I've created the new case as a potential improvement to allow more flexible error handling on the client.

        I'm not a Weld/CDI expert so I'm not sure about all the conditions that cause NonexistingConversationException to occur so it's best at this point to handle it in the application. I still think option 2 is a legitimate solution as well. It's the mechanism that JSF provides for handling errors triggered via Ajax requests.

        Show
        Deryk Sinotte added a comment - The ICEfaces client-side bridge is looking for a very specific Ajax response to do the redirect. It must be an error containing org.icefaces.application.SessionExpiredException. If anything else arrives as the Ajax response, the redirect does not get triggered. That's why I've created the new case as a potential improvement to allow more flexible error handling on the client. I'm not a Weld/CDI expert so I'm not sure about all the conditions that cause NonexistingConversationException to occur so it's best at this point to handle it in the application. I still think option 2 is a legitimate solution as well. It's the mechanism that JSF provides for handling errors triggered via Ajax requests.

          People

          • Assignee:
            Deryk Sinotte
            Reporter:
            Arran Mccullough
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: