7 Replies Latest reply on Mar 27, 2012 3:12 AM by zeeman

    Exception handling and redirects

    Bernard Labno Master

      Hi

      Has anyone figured out how to do elegant exception handling?

      Let's say on session/conversation timeout, user should be redirected to login page with message "Your session/conversation has ended, please log in again", on authorization exception user should be redirected to "accessDenied.xhtml" and on other exceptions user should be redirected to "error.xhtml" page with exception stacktrace and some faces message.

      Bear in mind that such exception handling should work both for rest and jsf requests.

       

      It was so easy in seam2 and now (seam3) I can't make it right.

        • 1. Re: Exception handling and redirects
          zeeman Novice

          I seam seam Solder and its exception handling mechanism. I catch all of exceptions I'm interested in like view expired, conversation, etc... for each one depending on my app needs I either redirect to a generic error page or I redirect to an error page that backed by a bean which has a custom error message set on it from the exception handler.

           

          For protected URLs, I use ViewsConfig enum with Seam security and redirect to access denied view.

           

          Of course the actual implementation could be tricky as you need to catch the right exceptions and put the right config. If you follow the examples from Seam and seam security and research this forum you'll have all the info you need.

          • 2. Re: Exception handling and redirects
            Richard Romanowski Newbie

            I have issue with my error page redirection, it works... sometimes.

             

            My code:

             

            public void handleSomeException(@Handles CaughtException<SomeException> event, HttpServletResponse response) {

                 ...

                  event.handled();

                  ...

                  response.sendRedirect("/myApp/errorPage");

             

            produces exception:

            java.lang.IllegalStateException: Attempted to inject an HttpServletResponse before it has been initialized.

             

            It happen in some cases only, I guess it depends on the state of response.

             

            Also I have trouble getting externalContext from FacesContext and have to hard-code myApp name.

             

            Any Ideas?

            • 3. Re: Exception handling and redirects
              zeeman Novice

              You want to use navigatino handler so your faces-config rules work and this works better as you don't need to worry about http response.

               

              Here is what I use for view expired error:

               


              public void onNonexistentConversation(@Handles final CaughtException<NonexistentConversationException> evt) {


              final NonexistentConversationException exception = evt.getException();


              logger.error(exception);


              evt.handled();


              // conversationContext.activate(null); // Workaround WELD-855 - Create a new transient conversation.


              FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "No conversation!", exception.getMessage());


              final FacesContext facesContext = FacesContext.getCurrentInstance();


              facesContext.addMessage(null, msg);


              facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, "", "error");


              facesContext.renderResponse();

              }
              • 4. Re: Exception handling and redirects
                Richard Romanowski Newbie

                Zeeman,

                 

                Thank you for the suggestion.

                 

                I tried the code with the old  catch-basic-servlet example. It doesn’t work because this line

                final FacesContext facesContext = FacesContext.getCurrentInstance();

                produces null.

                 

                Then I tried with my application producing JPA NonUniqueResultException  and JSF ELException.

                The code is called but redirection doesn’t happen. 

                 

                I use Seam 3.1 final, AS 7.1. 

                I really thought that redirection to error page is a common issue and love the simplicity of Seam 2.

                • 5. Re: Exception handling and redirects
                  zeeman Novice

                  If final FacesContext facesContext = FacesContext.getCurrentInstance(); is producing null that means you're not in a Faces request. Are you in a servlet or a filter when your exception occurs? If not, then you seem to have some config issues in your app.

                  I think catch-basic-servlet example is only a servlet so Faces context is not active. Try with booking example.

                   

                  If you have your navigation in faces-config  you send a redirect there like pages.xml in Seam 2:

                   

                  For example, something I use:


                  <navigation-rule>


                  <from-view-id>/home.xhtml</from-view-id>


                  <navigation-case>



                  <from-outcome>success</from-outcome>



                  <if>#{true}</if>



                  <to-view-id>/home.xhtml</to-view-id>



                  <redirect />


                  </navigation-case>

                  </navigation-rule>

                   

                  If you want to redirect

                  • 6. Re: Exception handling and redirects
                    Richard Romanowski Newbie

                    Thanks Zeeman,

                     

                    I tried your code with the booking example, and the “sometimes” factor is still an issue.

                     

                     

                    I added 2 oversimplified pages to the booking example

                     

                    wrongOnCall.xhtml:

                    template="/WEB-INF/layout/template.xhtml">

                        <ui:define name="content">

                            <h:form>

                            <h:commandButton value="call wrong" action="#{wrong.wrongCall}" />

                            </h:form>

                        </ui:define>

                     

                     

                    wrongOnLoad.xhtml

                    template="/WEB-INF/layout/template.xhtml">

                        <ui:define name="content">

                            <h:outputText value="#{wrong.user.email}" />

                        </ui:define>

                    </ui:composition>

                     

                    my bean:

                    @Model

                    public class Wrong {

                        @PersistenceContext

                        private EntityManager em;

                     

                        public User getUser() {

                            TypedQuery<User> query = em.createQuery("FROM User ", User.class);

                            User user = query.getSingleResult();

                            return user;

                        }

                     

                        public void wrongCall(){

                            TypedQuery<User> query = em.createQuery("FROM User ", User.class);

                            User user = query.getSingleResult();

                        }

                    }

                     

                     

                    and error handler:

                    public void onNonUniqueResultException(@Handles final CaughtException<NonUniqueResultException> evt) {

                     

                            final NonUniqueResultException exception = evt.getException();

                            evt.handled();

                            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Some text", exception.getMessage());

                            final FacesContext facesContext = FacesContext.getCurrentInstance();

                            facesContext.addMessage(null, msg);

                            facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, "", "error");

                            facesContext.renderResponse();

                        }

                     

                     

                    Both pages cost  NonUniqueResultException.

                    In both cases the  onNonUniqueResultException IS executed.

                    Error page happen only for wrongOnCall.xhtml. (The URL stays the same, but this is not showstopper)

                     

                    Thus, when exception happen during page load redirection doesn’t kick in.

                     

                    At the same time part of the page without exception is rendered just fine. In my case I got page with menu bar only which is not very pretty.

                    • 7. Re: Exception handling and redirects
                      zeeman Novice

                      I think you have a specific use case that you need to handle. If you have logic that executes on page load and throws an exception, you need to handle that correctly.

                      You need to redirect if you have not started writing back to the client, if you have, how is the browser going to redirect? It's not magic.

                       

                      Two suggestions; either move your logic from being excuted on page load, or set a flag in flash scope if an error occurs once a page loads, check for that flag, then redirect if it's true.

                       

                      To fix the issue with the url staying the same, add </redirect> to your error nav case.

                       

                      I think my answers get what you want here.