Communities
|
Social Applications
Networks
Support
|
|
C-Level Executives
Other Roles
|
|
Support
Education
Partner
Other Tasks
|
| By Ryan Lubke,
Jennifer Ball, and Pierre Delisle, August 2005 |
| |
The unified expression language started out as the JavaServer Pages Standard Tag Library (JSTL), version 1.0 expression language, originally called SPEL (Simplest Possible Expression Language). True to its name, this expression language made life easier for page authors by providing a simple, easy-to-use way to access external data objects. For example, the expression
${customer.name} is used to look up the
customer JavaBeans component and call the
getName method to retrieve the value of the
name property. This value is then rendered into the page.
By using this expression language, page authors could drastically reduce the amount of scripting in their pages, resulting in greater productivity, easier maintenance, and a flatter learning curve in terms of page development. Over the years, the expression language has evolved to include more advanced functionality, while still maintaining its simplicity.
During the development of JSP 2.0, JavaServer Faces technology was released. The primary feature of this technology is its flexible and extensible UI component model, which includes: a set of classes that define UI components; a mechanism for handling events generated by the components; and a process for converting and validating component data. It also includes a set of component tags that are used to map directly to individual, stateful, server-side UI component objects. Therefore, this technology needed an expression language that could be used to wire events generated by the components to server-side code, bind the components to server-side data objects, and register data validators and converters with the components.
While the expression language included in JSP 2.0 technology offers many advantages to the web application developer, it clearly didn't satisfy all the needs of those developing with JavaServer Faces technology. From the viewpoint of these developers, the primary limitation of the JSP EL is that its expressions are evaluated immediately, meaning that the JSP container immediately parses and resolves the expression when it processes the page and returns a response.
Immediate evaluation is sufficient when a JavaServer Faces page is rendered for the first time. However, after the user enters values into the UI components and submits the page again, those values are converted, validated, and propagated to server-side data objects, and component events are processed. In order to convert data to the proper type, validate it, store it in an external object, and process events in an orderly fashion, the JavaServer Faces life cycle is split into separate phases to handle each of these tasks. Therefore, it must be possible to evaluate expressions at different phases of the life cycle rather than immediately, as is done in JSP technology.
Another problem with the immediate evaluation of expressions is that it is done in read-only mode. JavaServer Faces components need to be able to get data from server-side objects during the rendering phase and set the data in the server-side objects during postback.
Finally, JavaServer Faces components need a way to invoke methods on server-side objects during various stages of the life cycle in order to validate data and handle component events. JSP functions are not sufficient because they can only be used to call static methods defined in a TLD file; they cannot be used to dynamically invoke public methods on objects.
For all these reasons, a more powerful expression language was created that included the following new features:
The new expression language worked well for the purposes of JavaServer Faces component tags. When using component tags with JSTL tags, however, page authors would find that the expression language used by the JSTL tags would conflict with that used by the component tags. Consider the following example, which shows a
forEach tag containing an
inputText tag, which represents a text field component:
<c:forEach var="book" items="${BooksBean.books}">
...
<h:inputText id="quantity" value="#{book.quantity}" ... />
...
</c:forEach>
itemsvalueinputTextThe new unified EL essentially represents a union of the JSP and JavaServer Faces expression languages and largely benefits JavaServer Faces technology. Other Java-based web component technologies benefit too because the unified EL is more pluggable and flexible as a result of unifying the expression languages. In addition to the expression language features that were already available in the JSP EL, the unified EL has the following features:
<fmt:formatNumber value="${cart.total}"/>
<h:inputText id="quantity" value="#{book.quantity}" />
#{book.quantity}quantitybook
${book.quantity}
#{book.quantity}
quantity
...
<h:inputText id="quantity"
value="#{book.quantity}"
validator="#{book.validateQuantity}"/>
...
<h:commandButton id="update"action="#{book.submit}" />
...
validatorinputTextvalidateQuantitybookinputTextvalidatorbook.submitactioncommandButtonsubmitcommandButton
<c:forEach var="book" items="#{BooksBean.books}">
...
<h:inputText id="quantity" value="#{book.quantity}" ... />
...
</c:forEach>
itemsforEachbooksforEachforEach
${Color.LightGrey.hex}
<style type="text/css">
...
.oddRow {background-color: ${Color.LightGrey.hex};}
...
</style>
webtier-sample.jsp
Figure 1: Table of Books Displayed on webtier-sample.jsp Page
When the expression
"${Color.LightGrey.hex}" is encountered, the following happens:
faces-config.xmlIf you are using the resolver with a JSP application that does not use any JavaServer Faces tags, you use a context listener to register the resolver. If your application contains a mixture of JSP tags and JavaServer Faces tags, both of which might use expressions that your resolver will handle, you should register the resolver using the application configuration file. Because the JavaServer Faces technology-specific resolvers are added to the JSP container, the JSP tags have access to the resolver registered by way of the configuration file. Let's go over using the context listener first.
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext context = servletContextEvent.getServletContext();
JspApplicationContext jspContext =
JspFactory.getDefaultFactory().getJspApplicationContext(context);
jspContext.addELResolver(new ColorELResolver());
servletContextEvent.
getServletContext().log("ColorELResolver registered");
}
CompositeELResolverImplicitObjectELResolver
<el-resolver>
webtier-sample.ColorELResolver
</el-resolver>
...
<style type="text/css">
...
.oddRow {
background-color: ${Color.LightGrey.hex};
}
...
</style>
...
<table>
...
<c:forEach items="#{BooksBean.books}" var="book"
varStatus="stat">
<tr class="${(stat.index % 2) == 0 ? "evenRow" : "oddRow"}">
<td>
<h:outputText id="title" value="#{book.title}"/>
</td>
...
</c:forEach>
</table>
...
For those of you who already have JSP and JavaServer Faces applications that use the previous versions of the EL, you can be assured that your applications are completely backward compatible, except for a couple rare corner cases. Aside from these cases, you don't need to make any changes to run your applications with the latest versions.
If you plan to modify your current applications to use the new EL, however, there are some things you need to do, which this section explains. These changes primarily affect those developing JavaServer Faces applications. The good news is, though, that the page author and application developer don't need to do anything differently in JavaServer Faces 1.2 applications vs. version 1.1 or earlier applications. The component writer and the application architect do have some changes to make, which include:
If your custom tags are only JSP custom tags, however, there really isn't anything further you need to do unless you want to modify your tag attribute definitions to accept deferred expressions. If you want a tag attribute to accept a deferred expression instead of a dynamic one (one that uses
${} syntax), simply replace the
rtexprvalue element with either the
deferred-value or
deferred-method element as explained in this section. If you want the tag attribute to accept both deferred and dynamic expressions, keep the
rtexprvalue element and add either the
deferred-value or
deferred-method element. Most likely, you won't find a use for deferred expressions in a straight JSP application; these kinds of expressions mainly benefit JavaServer Faces technology because of its multi-phase life cycle.
For JavaServer Faces custom tags, you must replace the
rtexprvalue element with either
deferred-value or
deferred-method, depending on whether the corresponding property accepts value expressions or method expressions.
The following TLD defines a custom component tag based on version 1.1:
<taglib>
<!-- ============== Tag Library Description Elements ============= -->
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>DemoTaglib</short-name>
<tag>
<name>simple</name>
<tag-class>example.SimpleComponentTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>someProperty</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>validator</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</tag>
</taglib>
The following TLD defines the same custom component tag for JavaServer Faces technology 1.2 and JSP 2.1:
<taglib><!--============== Tag Library Description Elements ============= -->
<tlib-version>1.1</tlib-version>
<jsp-version>2.1</jsp-version>
<short-name>DemoTaglib</short-name>
<tag>
<name>simple</name>
<tag-class>example.SimpleComponentTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>someProperty</name>
<required>true</required>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>validator</name>
<required>false</required>
<deferred-method>
<method-signature>
void validate(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
</method-signature>
</deferred-method>
</attribute>
</tag>
</taglib>
jsp-versionValueExpressionMethodExpression
public class SimpleComponentTag extends UIComponentTag {
String someProperty;
String validator;
public void setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
public void setValidator(String validator) {
this.validator = validator;
}
...
protected void setProperties(UIComponent component) {
super.setProperties(component);
if (someProperty != null) {
if (isValueReference(someProperty)) {
ValueBinding vb = FacesContext.getCurrentInstance().
getApplication().createValueBinding(someProperty);
component.setValueBinding("someProperty", vb);
} else {
component.setProperty(Boolean.valueOf(someProperty));
}
}
if(validator != null) {
if (isValueReference(validator)) {
Class args[] = { FacesContext.class, UIComponent.class,
Object.class };
MethodBinding vb = FacesContext.getCurrentInstance().
getApplication().createMethodBinding(validator, args);
input.setValidator(vb);
} else {
throw new FacesException("Invalid Expression");
}
}
}
}
public class SimpleComponentTag extends UIComponentELTag {
ValueExpression someProperty;
MethodExpression validator;
public void setSomeProperty(ValueExpression someProperty) {
this.someProperty = someProperty;
}
public void setValidator(MethodExpression validator) {
this.validator = validator;
}
...
protected void setProperties(UIComponent component) {
super.setProperties(component);
if (someProperty != null) {
if (!someProperty.isLiteralText()) {
component.setValueExpression("someProperty", someProperty);
} else {
component.setSomeProperty(Boolean.valueOf(someProperty.getExpressionString()));
}
}
if(validator != null) {
component.addValidator(new MethodExpressionValidator(validator));
}
}
}
Notice that the new
setProperties method does not have to do the work of getting the expression from the
Application instance and converting a String to a value binding or method binding. Instead, the JSP container manages the
ValueExpression and
MethodExpression objects and passes them to the tag handler. Also,
setProperties does not have to check if the property is enabled to accept a value expression or method expression because this information is already in the TLD, which is shown in the previous section.
public boolean someProperty() {
if (this.someProperty) {
return (this.someProperty);
}
ValueBinding vb = getValueBinding("someProperty");
if (vb != null) {
Boolean value = (Boolean) vb.getValue(getFacesContext());
if (value != null) {
return (value.booleanValue());
} else {
return false;
}
} else {
return (this.someProperty);
}
}
public boolean someProperty() {
if (this.someProperty) {
return (this.someProperty);
}
ValueExpression ve = getValueExpression("someProperty");
if (ve != null) {
Boolean value = (Boolean)
ve.getValue(getFacesContext().getELContext());
if (value !- null) {
return (value.booleanValue());
} else {
return false;
}
} else {
return (this.someProperty);
}
}
some text \#{ some more text
<my:tag someAttribute="sometext\#{moretext" />
<jsp-property-group>
<deferred-syntax-allowed-as-literal>true</deferred-syntax-allowed-as-literal>
</jsp-property-group>
<%@page ... deferredSyntaxAllowedAsLiteral="true" %>
So, you've seen what the new unified expression language has to offer: a pluggable, extensible resolver machinery and a way to set data and invoke methods from the page in addition to all the powerful features the JSP expression language already offered you. And the best part is that it is available now for you to use. All you need to do is go to
https://glassfish.dev.java.net/ to get started. If you need help, check out these forums:

