(dirty little) Trick for calling methods with multiple input parameters from JSF EL Expressions

I am not sure yet whether I have been really clever or am suffering from a twisted mind. In this article I present a grantedly farfetched approach for invoking methods from EL expressions in JSF (and JSP) applications – nothing spectacular so far – including calling methods that take one or even multiple input parameters.

Calling a method for retrieving its return value – other than normal bean getters – is not supported in JSF EL Expressions, outside of specific method-bound properties such as action. A trick for calling a method that does take at most one input parameters from an EL expression is using an object that implements the Map interface. Through a notation such as #{bean[object]}, we can invoke the get method on the Map implementing bean and pass as key parameter the object. The get method can use the key to decide which method to invoke or as the input parameter for the one method it knows to invoke. See: How to call methods from EL expressions- pre JSP 2.0 trick for JSPs with JSTL (June 2005) for a description of that ‘trick’.

I have extended that trick to allow for passing of multiple parameters. ....

Note: the trick described here is somewhat contrived. As far as I am concerned, that may be because I am overlooking some better approach and functionality in JSF or simply because I am looking for generic, declarative rather than specific and programming based solutions for my challenges. It does not reflect on limitations in JSF or EL as a whole, as I am a very satisfied JSF developer.

The actual call to the multi-parameter method is preceded by multiple calls to managed beans that act as placeholders. These placeholders are made available to the bean on which the multi-parameter method is invoked. In its very simplest form, we can invoke a multi-parameter method from a JSF EL expression in the following way:

#{((store1[row.Value1]) and (store2[row.Value2])?beanWrapper.method:'')} 

In this example, store1 and store2 are both managed beans that implement the Map interface. Their get method always returns true. The special element in this get method is that it stores the key value passed to the get method, and publishes it through a special getter: getStoreValue. The beanWrapper-method can retrieve the values for its parameters from the various store beans that have been injected into it, using the getStoreValue() method.  Then it can make the actual call, passing these parameters.

Multiple Parameter method calling in action 

I will demonstrate this approach in a simple example. Note: for this particular example, I would have easily extended the Book bean itself to implement a getSummary() method. I only use this simple example to demonstrate how the mechanism works. I have used it for complex applications, that are not as easily presented in a blog article.

The case is the Library system, that presents the books in our collection. Our model consists of the Book and the LibraryManager class, the latter returning a collection of the former. We want to present a list of our books using a readonly JSF datatable component. However, instead of the individual properties title, year of publication and author, we want to show a summary of those three values. For some reason we (pretend we) cannot extend the Book class to implement a getSummary method. We are forced to use the method bookSummary on a BookUtils class; this method takes three parameters: String, int and String. I will demonstrate the multiparameter-method-call trick for this bookSummary method (and again: this can be done in simpler ways, I just want to demonstrate the mechanism).

The JSF snippet that writes the book summary in the datatable is this one:

<h:column>
<f:facet name="header">
<h:outputText value="Book Summary"/>
</f:facet>
<h:outputText value="#{((store1[book.title]) and (store2[book.publicationYear])
and (store3[book.author]))?booksUtil.bookSummary:null}"/>
</h:column>
 

It refers to three managed beans store1, store2 and store3. These are the ObjectPlaceHolders that will record the value send to them as Map Key (in this case the title, publicationYear and author of the book). It then calls the bookSummary method on a managed bean called booksUtil. That is an instance of the BooksUtilWrapper class. Its getBookSummary method is shown here:

    public String getBookSummary() {
return BooksUtil.bookSummary((String)param1.getStoreValue()
, ((Integer)param2.getStoreValue()).intValue()
, (String)param3.getStoreValue() );
}
 

It calls the static bookSummary method on the BooksUtil class, passing the three parameters it has collected from the JSF page through the three injected param members.

The rendered page in the browser displays the bookSummary:

(dirty little) Trick for calling methods with multiple input parameters from JSF EL Expressions dirtytrick1 

Of some interest now are the faces-config.xml file with the managed bean definitions:

<faces-config xmlns="http://java.sun.com/JSF/Configuration">
<managed-bean>
<managed-bean-name>libraryManager</managed-bean-name>
<managed-bean-class>nl.amis.model.library.LibraryManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>store1</managed-bean-name>
<managed-bean-class>nl.amis.jsf.util.ObjectPlaceHolder</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>store2</managed-bean-name>
<managed-bean-class>nl.amis.jsf.util.ObjectPlaceHolder</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>store3</managed-bean-name>
<managed-bean-class>nl.amis.jsf.util.ObjectPlaceHolder</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>booksUtil</managed-bean-name>
<managed-bean-class>nl.amis.model.library.BooksUtilWrapper</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>param1</property-name>
<value>#{store1}</value>
</managed-property>
<managed-property>
<property-name>param2</property-name>
<value>#{store2}</value>
</managed-property>
<managed-property>
<property-name>param3</property-name>
<value>#{store3}</value>
</managed-property>

</managed-bean>
 

And the ObjectPlaceHolder class:

package nl.amis.jsf.util;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class ObjectPlaceHolder implements Map {

private Object storeValue;
public ObjectPlaceHolder() {
}

public Object get(Object key) {
this.storeValue = key;
return Boolean.TRUE;
}


pu blic void setStoreValue(Object storeValue) {
this.storeValue = storeValue;
}

public Object getStoreValue() {
return storeValue;
}
... and the trivial default implementations of the other methods in the Map interface

 

The BooksUtilWrapper class that takes the three params (inject ObjectPlaceHolders) and turns the call to getBookSummary() into a multiparameter call to BooksUtil.bookSummary:

package nl.amis.model.library;

import nl.amis.jsf.util.ObjectPlaceHolder;


public class BooksUtilWrapper {

ObjectPlaceHolder param1;
ObjectPlaceHolder param2;
ObjectPlaceHolder param3;

public BooksUtilWrapper() {
}

public String getBookSummary() {
return BooksUtil.bookSummary((String)param1.getStoreValue()
, ((Integer)param2.getStoreValue()).intValue()
, (String)param3.getStoreValue() );
}

public void setParam1(ObjectPlaceHolder param1) {
this.param1 = param1;
}

public ObjectPlaceHolder getParam1() {
return param1;
}

public void setParam2(ObjectPlaceHolder param2) {
this.param2 = param2;
}

public ObjectPlaceHolder getParam2() {
return param2;
}

public void setParam3(ObjectPlaceHolder param3) {
this.param3 = param3;
}

public ObjectPlaceHolder getParam3() {
return param3;
}
}
 

In a subsequent article I will extend this trick a little to support collecting an array of values while itering over a collection. This can be used for example to calculate aggregate values over all rows in a table, like column totals.

Afterthoughts

After having completed this article and reading Jan’s comment, I realized that my example is really lame. I sort of overlooked the fact that inside any method invoked from EL expressions in the JSF page I can use EL expressions to get access to managed beans as well as objects created in iterations inside the page. That means that my contrived way of making the three parameters available to the booksUtil bean through the three ObjectPlaceHolders is totally ridiculous, as the bookSummary method can reach out to get at the book variable:

 

< >  public String getBookSummary() {
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding vb = context.getApplication().createValueBinding("#{book}");
Book book = (Book)vb.getValue(context); >
< > return BooksUtil.bookSummary( book.getTitle(), book.getPublicationYear(), book.getAuthor());>
< > }
>

That means that the applicability of my ‘trick’ is reduced to a small niche of situations, that may not even exist.

Resources

The JDeveloper 10.1.3 Application with the source code for this article: multiparametermethodcalldemo.zip

4 Comments

  1. Dan January 31, 2007
  2. Jan Vervecken January 28, 2007
  3. Lucas Jellema January 27, 2007
  4. Jan Vervecken January 27, 2007