Thoughts on…

Java Middleware & Systems Management

Posts Tagged ‘webdev

Web Development Tips – automate the little things

with 3 comments

I recall a colleague of mine mentioning several weeks ago that it’s annoying to have to log into RHQ every time you redeploy UI code that causes portal-war’s web context to reload. I completely agreed at the time, but it wasn’t until today that I finally got annoyed enough to look for a workaround myself. Here’s the solution I ended up with:

1) Use FireFox
2) Download and install GreaseMonkey
3) Install the AutoLogin script
4) Log into “http://localhost:7080/Login.do” and make sure to tell FF to remember your password
5) Test that auto-login is working properly by logging out of the application…you should be forwarded to the login page, which FF will automatically fill in with your saved credentials, and the grease monkey script will perform the login for you

This should also work when you get logged out due to session expiry. The expiry handler will redirect you back to /Login.do, which will now automatically log you back in and – on a best effort basis – redirect you back to the last “valid” page you were on. RHQ has a mechanism for recording the last couple of pages you visited (see WebUserTrackingFilter) and will try them in most-recently-visited order until it finds a page that doesn’t blow up with JSF’s “classic” ViewExpiredException. I discuss the details of how this mechanism works in my other post.

Note: if you ever want to log into localhost with a different user, all you have to do is click the GreaseMonkey icon (on the far right-hand side of the status bar at the bottom of your browser) and you’ll temporarily disable the AutoLogin script from executing.

How would you solve this? How have you solved this? I’m eager to read your post backs.

Written by josephmarques

February 5, 2010 at 4:19 pm

Posted in webdev

Tagged with

Custom JSF/Facelet Exception Handling

with 13 comments

Have you ever used a JSF-based application, navigated to some page, only to see a nasty error?

This is what you get out-of-box with facelets. Most of the time it happens when the facelet tries to resolve some EL expression, needs to create some JSF managed bean, but one or more required URL parameters are either missing or have invalid values.

In a development environment, it makes sense to show this page because the various pieces of contextual information (full stack trace + JSF component tree + variables in scope) provide plenty of clues with which to diagnose the issue. However, when you ship a product to a customer or push your changes to a production environment, it would be nice to change the behavior and provide a pleasant error page to the user.

Fortunately, the facelets framework makes overriding this default behavior incredibly simple. The basic premise is to redirect to a custom error page so you can provide a layout that hides the unappealing stack trace, but which still provides a link to view those details (primarily so your customers can report the bugs back to you).

Note: the following code examples will be pulled directly from the RHQ / Jopr code base.

The first step is to add a custom view handler to your web application. Open up the faces-config.xml file and add a custom view handler:

<faces-config ...>
   <application>
      <view-handler>org.rhq.enterprise.gui.common.framework.FaceletRedirectionViewHandler</view-handler>
      ...
   </application>
</faces-config>

Then override the default mechanism for dealing with errors that the facelet framework encounters:

public class FaceletRedirectionViewHandler extends FaceletViewHandler {
    ...
    @Override
    protected void handleRenderException(FacesContext context, Exception ex) throws IOException, ELException,
        FacesException {
        try {
            if (context.getViewRoot().getViewId().equals("/rhq/common/error.xhtml")) {
                /*
                 * This is to protect from infinite redirects if the error
                 * page itself is updated in the future and has an error
                 */
                log.error("Redirected back to ourselves, there must be a problem with the error.xhtml page", ex);
                return;
            }

            getSessionMap().put("GLOBAL_RENDER_ERROR", ex);
            getHttpResponseObject().sendRedirect("/rhq/common/error.xhtml");
        } catch (IOException ioe) {
            log.fatal("Could not process redirect to handle application error", ioe);
        }
    }
}

View full source here

The basic strategy is capture the exception and redirect to your new error page. However, what if there is a compile error on the error page itself? Your new error handling code would capture that error and try to handle it, which would redirect back to the custom error page which still has that error on it! This leads to an infinite redirect, which all modern browsers can and will detect, but it doesn’t provide much useful information as to what error occurred on the page.

You might be asking yourself, “How likely is it that the error page will have an error?” Well, this could happen either as you’re writing the page for the first time, or are updating the page in the future to add other features. Fortunately, there is an easy way of protecting against this. In your error handling code, simply test which JSF viewId you’re coming from and, if it’s your new error page, then revert back to the default handler by calling the superclass method being overridden. Don’t forget to explicitly break/return out of the custom handler, otherwise you’ll still see the infinite recursion.

The next and final step is to write the logic that pulls the exception back out of the session map on the other side of the redirect:

public class GenericErrorUIBean {
    String summary; // the name of the exception class, usually self-descriptive
    String details; // a little more information about the named exception
    List<Tuple> trace; // fine-grained trace details

    public GenericErrorUIBean() {
        trace = new ArrayList<Tuple>();
        Throwable ex = (Exception) getSessionMap().remove("GLOBAL_RENDER_ERROR");

        String message = ex.getLocalizedMessage();
        String stack = StringUtil.getFirstStackTrace(ex);
        trace.add(new Tuple(message, stack));
        while (ex.getCause() != null) {
            ex = ex.getCause();
            message = ex.getLocalizedMessage();
            stack = StringUtil.getFirstStackTrace(ex);
            trace.add(new Tuple(message, stack));
        }

        summary = ex.getClass().getSimpleName();
        details = ex.getMessage();
    }

    private static String getFirstStackTrace(Throwable t) {
        if (t == null) {
            return null;
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        return sw.toString();
    }
}

View full source here

Depending on what you want your error page to look like, you can use a variety of methods for chopping up the stack trace into more manageable and/or user-friendly bits. In this case, the solution I used for the RHQ platform loops over the full trace one stack frame at a time. At each frame, we record the name of the exception-class and map that to the exception-stack at that frame. In this fashion, we can generate of list of tuples which can be iterated over in our error.xhtml facelet with either the ui:repeat or a4j:repeat tag.

The end result is a page that looks at professional by hiding the ugly errors and showing the root cause in small, easy to understand language.
Note, however, that the page can still be used as a debugging tool. See the “view the stack trace” link at the bottom? Clicking it opens up a modal dialog which display the full stack trace (which is useful for reporting bugs through customer support [for downloaded products], or emailing the webmaster [for hosted applications]).

For brevity, the source code of the error.xhtml facelet has been left out of this blog entry, but you can view the full source here

This is just one of the many solutions employed by the RHQ platform to provide a great web driven interface that centralizes the monitoring and management of your enterprise systems. To find out more, please visit one of the links below:

RHQ (base management platform) – http://www.rhq-project.org/
Jopr (Jboss specific extensions to RHQ) – http://www.jboss.org/jopr/

Written by josephmarques

April 27, 2009 at 7:29 am

Posted in webdev

Tagged with

JSF Odyssey – ViewExpiredException

with 9 comments

For any developer that has used JSF for even a week, you know exactly what the title of this entry is referring to.

A quick google search for “ViewExpiredException” finds users complaining about this on threads from all different sorts of vendors: Oracle, Apache, IceFaces, MyFaces, JBoss. And that’s just the first 10 entries! But the fault does not lie with any of these vendors. The dreaded ViewExpiredException is most likely the result of the fact that you’re interacting with (or developing) a user-authenticated, web-based application written in JSF. If you’re logged in and your HTTP session expires, JSF will quite annoyingly throw ViewExpiredException during the RESTORE_VIEW phase of the JSF life cycle – it’s a feature!

During my wanderings I’ve seen people try to solve this issue in a variety of ways. Some try to use global Exception handling in the web.xml file for their WAR. Others try to use a pure JSF-based solution whereby a PhaseListener can attempt to perform a response redirect during the RESTORE_VIEW phase. Even JBoss Seam tries to solve this in their own way using their exception handlers in the components.xml file.

However, none of these solutions are complete in my opinion. All of them seem to ignore one completely obvious question – how do I redirect the user back to the LAST page they were on before the session timed out? Without this piece, the usability of the site goes way down and, as most of these solutions will proffer, the user has to start at the site’s main entry point again.

Restoring the last visited JSF view, however, is easier said than done. The HTTP POST-based nature of the framework makes it very difficult to capture the needed state / context prior to the timeout. It doesn’t really matter if you record the last accessed path if you don’t have the appropriate URL parameters to pass to it after the user logs back in. So, the only proper fix for this is to extend JSF in some way to provide RESTful site interactions.

Luckily, there are frameworks which (to different degrees) provide RESTful concepts, such as JBoss Seam with bookmarkable URLs or RestFaces with the enablement of HTTP GET in your JSF environment. Unfortunately, the RHQ Project needed one of these solutions in early ’06, and those frameworks either didn’t exist or were still being incubated at the time. So, necessity being the father of invention, we wrote our own mechanism. It can be summarized as follows:

1) Redirects across all form submission boundaries
2) Custom JSF navigation handler that performs EL resolution of “enhanced” viewIds

With it, a standard JSF navigation rule then looks like:

<navigation-rule>
   <from-view-id>/some/path/from/page.xhtml</from-view-id>
   <navigation-case>
      <from-outcome>outcome1</from-outcome>
      <to-view-id>/some/path/to/page.xhtml?
         p1=#{param.p1}&p2=#{param.p2}</to-view-id>
      <redirect/>
   </navigation-case>
</navigation-rule>

Granted, the syntax is not all that inviting, but it gets the job done. Nearly every single page flow in RHQ starts and ends with a URL specifying representational state explicitly in its parameter map. And from a developer’s perspective, the slightly modified navigation rule is likely the only modification that has to be made. Most of the time the visible form elements (on the form leading up to the enhanced navigation rule) will be a superset of the state that will be resolved, in which case you’re done. However, if you need to propagate additional state, you can always include an unlimited number of hidden form inputs to submit your extra context information.

The bulk of the logic for how this all works is hidden behind various extensions that were made to the JSF framework. Since we’re crossing redirect boundaries, any errors that your managed bean would have placed in the FacesContext would normally be lost. But this was taken into consideration and is handled in a 3-step process by a custom phase listener:

* Step 1: capture any messages put in the GLOBAL faces messages context, and save them to the session at the end of biz tier processing
* Step 2: before the processing continues on the other side of the redirect boundary, copy the save elements back into the GLOBAL faces context
* Step 3: clear out the area of the session used for this message propagation

For those that understand better by seeing the code:

public class FacesMessagePropogationPhaseListener 
implements PhaseListener {
    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }
    public void beforePhase(PhaseEvent event) {
        if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
            List savedMessages = 
               removeGlobalFacesMessagesFromSession();
            // step2: retrieve 'em
            putGlobalFacesMessagesInFacesContext(savedMessages); 
        }
    }
    public void afterPhase(PhaseEvent event) {
        if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
            // step1: save 'em
            putGlobalFacesMessagesInSession(); 
        } else if (phaseId == PhaseId.RENDER_RESPONSE) {
            // step3: delete 'em
            removeGlobalFacesMessagesFromSession(); 
        }
    }
}

The only thing left to do is to override the standard navigation handler with one performs variable resolution:

public class FaceletRedirectionViewHandler 
extends FaceletViewHandler {
    @Override
    // Evaluate any EL variables contained in the view id.
    public String getActionURL(FacesContext facesContext, 
    String viewId) {
        FacesContext facesContext = 
            FacesContext.getCurrentInstance();
        ELContext elContext = facesContext.getELContext();
        ExpressionFactory factory = 
            facesContext.getApplication().getExpressionFactory();
        ValueExpression valueExpression = 
            factory.createValueExpression(elContext, 
                viewId, String.class);
        String resolvedActionURL = 
            (String) valueExpression.getValue(elContext);
        return resolvedActionURL;
    }
}

What was this blog about again? Oh, yes, that’s right – ViewExpiredException. Although this mechanism concerning URL resolution might have seemed like a tangent to the problem at hand, it was necessary groundwork that had to be laid, which now gives us the tools to undo the damage caused by a lifecycle “feature” of our component framework. As you navigate around the site now, your URL history will look something like:

/some/path.xhtml?p1=value1
/some/other/path.xhtml?p2=value2&p3=value3
...
/still/some/other/path.xhtml?p4=value4

All you have to do is record the last URL the user was on; if at any point the ViewExpiredException occurs, simply redirect back to that last URL. The RHQ platform uses standard error handling in the web.xml to forward to a JSP page that then has the logic to call and redirect the response writer to this saved value. If you rely on the browser’s history as the storage mechanism, then recalling that last URL only takes one line of javascript:

history.go(-1)

Note: you may have to use either -1 or -2 as the argument to the ‘go’ method, depending on whether the user was attempting to access a secured page (which might have redirected to some authenticating servlet before redirecting you back to the requested URL where the ViewExpiredException was thrown from)

Using the browser’s history also has the advantage of operating quite naturally in a multi-window (or multi-tab) environment. If you want your users to be able to navigate around your application using multiple windows, the browser history will provide natural isolation of these individual browsing histories. This way, regardless of which tab the user is on when they experience the ViewExpiredException, they are redirected back to the last page they are on *within that window* (as opposed to the last accessed page in any of that user’s open windows).

If it wasn’t already apparent, the key factor driving this solution is the user experience. Anyone can get away with “forgetting” where a user was and simply redirecting to an entry point or login page upon receiving the ViewExpiredException. Though that’s easy to implement, it’s not exactly the most appealing functionality from the end user’s perspective. This solution tries to make your application as attractive as possible my minimizing the distractions. Even though it requires a little bit more work on the part of the developer, it goes a long way towards providing an intuitive experience to your users.

On the one hand, it’s a shame that the JSF spec didn’t have standard mechanisms for handling this extremely common use case. On the other hand, if it was the spec writers intention to keep us developers in business, they’ve done an excellent job. Time will tell whether JSF 2 will obviate the need for this custom solution.

Written by josephmarques

February 5, 2009 at 1:29 pm

Posted in webdev

Tagged with

Follow

Get every new post delivered to your Inbox.