Home > Grimoire, JSF > JSF Tricks: Abstracted Actions

JSF Tricks: Abstracted Actions

JavaServer Faces – Aw, crap…

I hate JavaServer Faces. I think JSF is a bloated abstraction-fest and just about any other framework makes it easier to write MVC applications – even Struts. That being said, I’ve had to recently work with the beast, and in doing so I had to figure out how to do some things that wouldn’t even be an issue in Spring MVC, but required a workaround in JSF. One of those things is abstracting out actions from the JSF tags, and what follows is how to do it.

It can’t do what?

Actually, this is less a problem of what JSF can’t do, and more a problem of how it does things. The main issue here is how the action attribute of the commandLink JSF tag is handled. Normally, the commandLink tag’s action attribute’s value is an EL expression which indicates a managed bean and member method. According to the spec, the method has to take no constructors and return a string value, which the underlying JSF framework’s NavigationHandler instance then acts on. To try and put it simply:

  1. You click on a button on a web page that was rendered by a commandLink tag in a jsp.
  2. The commandLink tag takes the expression from its action attribute, which normally looks something like “#{menuitem.invokeAction}”, creates a MethodBinding object from it, and invokes the method binding.
  3. The method “invokeAction” on the object “menuitem” is called, and returns a string.
  4. The returned string, the expression, and the current FacesContext instance are handed to NavigationHandler.handleNavigation().
  5. Any navigation cases with a “from-action” value matching the expression are run.
  6. The HttpServletResponse object is processed and committed.

I am probably missing a few steps somewhere, but for this workaround recipe, that’s all we’re really concerned with.

What problem are we trying to solve again…?

The problem I came up against was when I was creating commandLink tags in a loop in the actual JSP. If you are looping over an iteration of objects, each one outputing values for the commandLink attributes, the action expression is always going to be the same. In my case, I was iterating over a collection of menu item objects. Each menu item is of course going to navigate your users to somewhere different, but because of the loop, the action expression was always the same, as shown here:

<t:htmlTag value="ul">
<t:dataList var="menuitem" value="#{sessionScope[menus]}" rowIndexVar="i">
    <t:htmlTag value="li">
        <t:commandLink action="#{menuitem.invokeAction}">
            <t:outputText value="#{menuitem.label}"/>
        </t:commandLink>
    </t:htmlTag>
</t:dataList>
</t:htmlTag>

I could print out fifty menu items and the action of every one will be “#{menuitem.invokeAction}”. If I had any navigation cases like this:

<t:htmlTag value="ul">
    <t:dataList var="menuitem" value="#{sessionScope[menus]}" rowIndexVar="i">
        <t:htmlTag value="li">
            <t:commandLink action="#{menuitem.invokeAction}">
                <t:outputText value="#{menuitem.label}"/>
            </t:commandLink>
        </t:htmlTag>
    </t:dataList>
</t:htmlTag>

… it wouldn’t work, again because underneath the covers, the commandLink tag takes the action expression (always “#{menuitem.invokeAction}”) and hands it to the NavigationHandler which looks at the “from-action” values and thinks to itself “I hate you”, and keeps you on the same page you were on. Nifty.

Making JSF do it anyway…

Let’s take the workaround one step at a time.

1. Faking The Action

The first problem that needs to be solved is the insufficient (I’m feeling charitable) action API presented to us by JSF. For a system that can rely on the actual expression string that was evaluated – which I think is a crime against the art of programming, JSF sure makes it hard to tell the underlying navigation process that the action attribute value isn’t the expression it is looking for. In order to convince it otherwise, we have to literally hijack the navigation process ourselves. Here’s a small class that does the trick:

public class ProxyMenuItemAction {
  private String elExpression;
  public void setExpression(String expression) {
    this.elExpression = expression;
  }
  public String getExpression() {
    return this.elExpression;
  }
  public String invoke() {
    FacesContext ctx = FacesContext.getCurrentInstance();
    MethodBinding methodBinding = ctx.getApplication().createMethodBinding(this.elExpression, null);
    String result = (String)methodBinding.invoke(ctx, new Class[0]);
    ctx.getApplication().getNavigationHandler().handleNavigation(ctx, this.elExpression, result);
    return result;
  }
}

In keeping with my menu theme, the MenuItem class I have sports a method called “invokeAction”, which has this in it:

public String invokeAction() {
  if(this.action!=null) {
    this.action.invoke();
  }
  return "";
}

The action object on which the invoke method is being called is an instance of the above ProxyMenuItemAction class. The invoke method take whatever expression you set on it, creates the method binding, invokes the binding, and then ties it all up by handing the NavigationHandler instance what it needs to operate properly per any navigation cases in your jsf-config file.

Well, that’s it! You’ve faked JSF out and now your application working like a charm. Just hit that menu link and…

javax.servlet.ServletException: Response already committed
  at javax.faces.webapp._ErrorPageWriter.throwException(_ErrorPageWriter.java:585)
  at javax.faces.webapp._ErrorPageWriter.handleThrowable(_ErrorPageWriter.java:488)
  at javax.faces.webapp._ErrorPageWriter.handleException(_ErrorPageWriter.java:468)
  at javax.faces.webapp.FacesServlet.handleLifecycleException(FacesServlet.java:254)
  at javax.faces.webapp.FacesServlet.service(FacesServlet.java:152)
  at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:225)
  at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:127)
  at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:283)
        ...

2. Son of a @#$!%!

… son of a @#$!%! (is there an echo in here?) What the hell did we miss!? Let’s go over the execution order again. We clicked on the link… commandLink evaluated the “#{menuitem.invokeAction}” to execute the menu item’s invokeAction method which in turn invokes the proxy action’s invoke method which evaluates the real expression creating a method binding which is then invoked (my editor thinks I’ve used the word “invoke” too many times and has opened a browser window featuring Strunk and White’s Element of Style on Amazon.com – oooo, a 50th anniversary edition!) … where was I? Oh yeah… the method binding is invoked which returns a result string which is then sent to the Navigation handler which checks navigation directives and then closes the HttpServletResponse… uh-oh.

To quote two of my favourite tinkerers of all time – “Well there’s your problem right there!”. In calling the NavigationHandler ourselves, we’ve closed the response stream. After our call to the handler, we return the string result value which bubbles up to the commandLink tag, which then again calls the NavigationHandler with that same value. Except the response is now closed, meaning you cannot add anything more to it. This calls for another hack…

3. Overriding the NavigationHandler

Fortunately for us, JSF provides a way to override the NavigationHandler class with one of our own. Here’s our class:

public class MyNavigationHandler extends NavigationHandler {
    private NavigationHandler parentHandler;

    public MyNavigationHandler(NavigationHandler handler) {
        super();
        this.parentHandler = handler;
    }
    @Override
    public void handleNavigation(FacesContext context, String fromAction, String outcome) {
        if(!context.getResponseComplete()) {
            super().handleNavigation(context, fromAction, outcome);
        }
    }
}

We aren’t doing much here except overriding the handleNavigation method. Now when that method is called, it will check for a committed response, and if the response stream is closed then ignore the call outright. If the response stream isn’t closed, we let the method continue normally. That takes care of “Response already committed” exceptions. In order to get JSF to use our class instead of the default, we go to the JSF config file and add the navigation-handler line like so:

<faces-config>
    <application>
<navigation-handler>com.facets.examples.MyNavigationHandler</navigation-handler>
    </application>
    <managed-bean>
    ...

Fin

NOW you’re done! As always, there’s probably a better way of doing this that I just didn’t run across in my hacking. Given that this was done with the MyFaces implementation of the JSF 1.1 specification while as of this writing, the JSF 1.2 spec is pretty robust and the 2.0 spec has been out for five months, there might be a better way in the newer specs that I am not privileged to waste my Spring MVC coding time with. If you know a better way (besides not using JSF), there’s a comment button for a reason!

Categories: Grimoire, JSF Tags: , , , , ,
  1. mnicpt
    June 30th, 2010 at 19:41 | #1

    Need to use rather than .

  2. mnicpt
    June 30th, 2010 at 19:42 | #2

    It processed the tag and didn’t render the full message. What I had mentioned was that you need to us from-outcome rather than from-action.

  1. No trackbacks yet.

*