关闭

Unified Expression Language

1518人阅读 评论(0) 收藏 举报

By Ryan Lubke,
Jennifer Ball,
and Pierre Delisle, August 2005
 
Those of you who have been developing with JavaServer Pages (JSP) technology have probably used its simple expression language to access data from your JSP pages. Since the introduction of this expression language, other Java-based web technologies have been released and have become quite popular with the Java web application developer community. 

One of these is the UI component framework, JavaServer Faces technology. To support its powerful features, this framework needed its own expression language. However, this expression language presented some problems when used with some JSP tags. Therefore, the specification writers and expert groups of the Java webtier technologies collaborated on a new, unified expression language to help align these technologies by adopting the features offered by the JavaServer Faces expression language. These features include the ability to use expressions to set the value of external object properties as well as get data from them and to invoke methods. At the same time, the webtier team made the language pluggable and extensible so that advanced developers can add custom resolvers able to evaluate expressions not already supported by the expression language.

This article uses the demo, webtier-sample, to illustrate the capabilities of the unified expression language. We recommend that you download the demo and take a look at the code. You will need to register at java.net to download the example, but registration is free of charge. To download the example, go to https://javaserverfaces.dev.java.net/ , follow the instructions for downloading and unpacking the nightly bundle, and find the example in the samples/webtier-sample directory. This example runs on Sun's Java System Application Server PE 9.0, code-named glassfish. You can download glassfish from https://glassfish.dev.java.net/.

Contents

Expression Language Beginnings
A Separate EL for JavaServer Faces Technology
Conflicting Expression Language Syntax
Features of the Unified EL
Migrating to the Unified EL
Now, It's Your Turn

Expression Language Beginnings


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.

Because of the popularity and success of the expression language in the community, the JSP 2.0 expert group included it in its specification while making it more flexible. With the integration of the expression language into JSP 2.0, not only could page authors use the expressions inside of standard tag attributes (as they could before), but they could also use them in template text as well as with custom tags. In addition, they could use functions created by a tag author to invoke static methods not supported by the current expression language.

A Separate EL for JavaServer Faces Technology


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:

  • Deferred expressions, which can be evaluated at different stages of the page life cycle
  • Expressions that can set data of external objects as well as get that data
  • Method expressions, which can invoke methods that perform event handling, validation, and other functions for the JavaServer Faces UI components

Conflicting Expression Language Syntax


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>
Notice that the items attribute uses JSP EL syntax, whereas the value attribute of the inputText component tag uses the JavaServer Faces EL syntax. This causes two problems:
  • As the iteration is processed, the JavaServer Faces implementation tries to create a component object with the same quantity ID every time the inputText component tag is encountered during the iteration. This fails because duplicate component IDs are not allowed.
  • Even if duplicate IDs were allowed, this iteration still has problems because of the fact that the expression referenced by items is evaluated immediately. This results in the book variable being available only when the component is rendered during the render response phase, not during the other phases of the JavaServer Faces life cycle. Therefore, the values entered by the user into the components would not be updated to the external data object.
Because of these incompatibilities, unifying these expression languages was essential to the success of Java-based web component technologies.

Features of the Unified EL


The 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:

  • Deferred evaluation of expressions
  • Support for expressions that can set values and expressions that can invoke methods
  • Support for using JSTL iteration tags with deferred expressions
  • A pluggable API for resolving expressions

Immediate vs. Deferred Evaluation of Expressions

Immediate evaluation means that the JSP engine evaluates expressions immediately when a page is rendered. These expressions take the form ${expr}, and can only be used within template text or as the value of a JSP tag attribute that can accept run-time expressions.

The following example shows a tag whose value attribute references an expression that gets the total price from the the session-scoped bean named cart:
	<fmt:formatNumber value="${cart.total}"/> 

The JSP engine evaluates the expression, ${cart.total}, converts it, and passes the returned value to the tag handler.

Expressions that are evaluated immediately are always read-only value expressions. The expression shown above can only get the total price from the cart bean; it cannot set the total price.

Deferred evaluation means that the technology using the unified EL takes over the responsibility of evaluating the expression from the JSP engine and evaluates the expression at the appropriate time during the page life cycle. These expressions take the form #{expr} and can only be used by tag attributes that accept deferred value expressions. In the case of a JavaServer Faces application, the controller can evaluate the expression at different phases of the life cycle depending on how the expression is being used in the page.

The following example shows a JavaServer Faces inputText tag, which represents a text field component into which a user enters a value. The inputText tag's value attribute references an expression that points to the quantity property of the book bean.
	<h:inputText id="quantity" value="#{book.quantity}" />
For an initial request of the page containing this tag, the JavaServer Faces implementation evaluates the #{book.quantity} expression during the render response phase of the life cycle. During this phase, the expression merely accesses the value of quantity from the book bean, as is done in immediate evaluation.

For a postback, the implementation evaluates the expression during apply request values, process validations, and update model phases, during which the value is retrieved from the request, validated, and propagated to the book bean.

Value and Method Expressions

The unified EL supports two kinds of expressions: value expressions and method expressions. Value expressions reference data, which might be in the form of a bean property or other data structure or a literal value. Method expressions refer to public methods, which are invoked when the expression is processed.

Value expressions can be further categorized into rvalue and lvalue expressions. Rvalue expressions are those that can read data, but cannot write it. Lvalue expressions can both read and write data. Expressions that use immediate evaluation syntax are always rvalue expressions. Value expressions that use deferred evaluation syntax can act as both rvalue and lvalue expressions. Consider these two value expressions:
	${book.quantity}
#{book.quantity}
The first one uses immediate evaluation syntax, whereas the second one uses deferred evaluation syntax. The first expression accesses the quantity property, gets its value, and the value is added to the response and rendered on the page. The same thing happens with the second expression if it is evaluated during an initial request. In this case, both expressions are rvalue expressions.

During a postback, however, the second expression can be used to set the value of the quantity property with user input. In this case, the expression is an lvalue expression.

One of the motivating factors for creating the JavaServer Faces EL was the need for support of lvalue expressions because of the multi-phase life cycle. Now, the unified EL also supports these expressions. Lvalue expressions use deferred evaluation syntax because they can be evaluated at different stages a page's life cycle, depending on where the expression is used, as was shown in the previous section.

Another motivation for creating the JavaServer Faces EL was support of method expressions. These expressions are now supported by the unified EL, which means that they are not exclusively for use JavaServer Faces applications. But let's look at an example that uses JavaServer Faces components to see how method expressions can be utilized in that technology.

A JavaServer Faces component tag uses method expressions to invoke methods that perform some processing for the component that the tag is representing on the page. For the standard components, these methods are necessary for handling events that the components generate and validating component data. The following example includes two component tags: one that represents a text field component and another one that represents a button component.
   ...
<h:inputText id="quantity"
value="#{book.quantity}"
validator="#{book.validateQuantity}"/>
...
<h:commandButton id="update"action="#{book.submit}" />
...
The validator attribute of the inputText component tag references a method, called validateQuantity, in the bean, called book. The TLD that defines the inputText tag specifies what signature the method referred to by the validator attribute must have. The same is true of the book.submit method referenced by the action attribute of the commandButton tag. The TLD specifies that the submit method must return a string that specifies with page to navigate to next after the button represented by the commandButton tag is clicked.

The validateQuantity method is invoked during the process validation phase of the life cycle, whereas the submit method is invoked during the invoke application phase of the life cycle. Because a method can be invoked during different phases of the life cycle, method expressions must always use the deferred evaluation syntax.

The good news for the page author and the application developer is that they can continue to use expressions, JavaBeans components, and other objects the way they always have. The component writer and application architect do have to do things a little differently now in order to take advantage of the new unified EL, but the new way of doing things is actually quite simpler. The section on migrating to the new unified EL at the end of this document gives you an idea of how custom components, tag handlers, and TLDs are created now.

Compatibility of Deferred Expressions in Iteration Tags

As described in Conflicting Expression Language Syntax, using tags with deferred expressions inside of a JSTL iteration tag was not possible prior to the unified EL. To get this use case to work in the unified EL, the page author must use a deferred expression for the items attribute of the forEach tag, as shown in this piece of the forEach tag from webtier-sample.jsp:
   <c:forEach var="book" items="#{BooksBean.books}">
...
<h:inputText id="quantity" value="#{book.quantity}" ... />
...
</c:forEach>
By using a deferred evaluation expression in the items attribute of the forEach tag, the page author can be sure that the iteration variable books will be processed as an expression that refers to the proper book item within the book collection that the forEach tag is iterating over.

What this means for the JavaServer Faces input components contained in the forEach tag is that whatever values the user enters into these components will be updated to the proper object properties at the appropriate stage of the JavaServer Faces life cycle.

See the webtiersample.jsp page for the complete forEach tag example, which contains expressions using both immediate and deferred evaluation syntax.

Pluggable Resolver Mechanism

So, now that you know all about using expressions, you might wonder how they are resolved. In other words, how does the JSP engine or the JavaServer Faces implementation know that the expression ${customer.name} points to a name property of a JavaBeans component called customer?

The unified EL specification defines a generic ELResolver class and a set of ELResolver implementations that define how to resolve various kinds of expressions. The set of standard resolvers include those that resolve expressions that point to arrays, JavaBeans components, lists, maps, and resource bundles.

Web application developers can also provide their own custom ELResolver implementations to resolve expressions that can't be handled by the standard resolvers and plug these resolvers into their applications. Examples of pluggable resolvers include those that handle:
  • Connections to resources such as JNDI and JDBC resources
  • Implicit objects
  • Methods that don't follow JavaBeans conventions
Writing a custom resolver and including it in your application can be quite simple. In general, it involves:
  • Writing an implementation of ELResolver
  • Registering the ELResolver implementation with the application
  • Using expressions in the page

Writing an ELResolver Implementation

To find out how to implement ELResolver, we'll take a look at the custom ColorELResolver class, which resolves expressions that reference a custom implicit object called ColorImplicitObject.  This implicit object can do a lot of things with colors, such as take the following expression and return the hexadecimal code that corresponds to the color LightGrey, as defined in an external text file:

   ${Color.LightGrey.hex}

The standard ELResolver class defines five methods, all of which are abstract, meaning that ColorELResolver and any custom EL resolver must implement all of these methods, which are:
  • getValue(ELContext context, Object base, Object property)
  • getType(ELContext context, Object base, Object property)
  • isReadOnly(ELContext context, Object base, Object property)
  • setValue(ELContext context, Object base, Object property, Object value)
  • getCommonPropertyType(ELContext context, Object base)
  • getFeatureDescriptors(ELContext context, Object base)
The purpose of the getValue method is to resolve the specified property object on the specified base object and return the result of the resolution. For example in the expression #{books.quantity}, books is the base object (a bean in this case) and quantity is a property of the books bean. The returned value is the value of the quantity property.

In the webtiersample.jsp page, the stylesheet classes use expressions that reference ColorImplicitObject, as shown here:
   <style type="text/css">
...
.oddRow {background-color: ${Color.LightGrey.hex};}
...
</style>

Within a forEach tag that iterates over the list of books and renders them into a table, the class attribute of the HTML tr tag uses an rvalue expression to calculate whether the current row is an odd-numbered row or an even-numbered row:

    <tr class="${(stat.index % 2) == 0 ? "evenRow" : "oddRow"}">

The oddRow and evenRow variables in the expression refer to the stylesheet classes that the page defines. As the expression shows, if the row is odd-numbered, its cells will have the LightGrey color obtained from ColorImplicitObject.
The following figure shows the table displayed in the webtier-sample.jsp page.


table of books displayed on webtier-sample.jsp page
Figure 1: Table of Books Displayed on webtier-sample.jsp Page


When the expression "${Color.LightGrey.hex}" is encountered, the following happens:

  1. The getValue method of ColorELResolver is called to resolve the base object, Color, to the ColorImplicitObject instance.
  2. The getValue method is called again to resolve the second part of the expression, LightGrey. It resolves this part of the expression by invoking the fromName method of ColorImplicitObject with the argument LightGrey in order to locate the color LightGrey in a text file that lists all the available colors.
  3. The fromName method creates a map of the names of the colors and associated ColorRGB objects, which encapsulate the red, green, and blue values of each color.
  4. From the map it creates, fromName finds the LightGrey color and returns its ColorRGB object.
  5. Finally, the standard BeanELResolver implementation (which is part of the unified EL API)  is called to resolve the last part of the expression, hex, causing the getHex method of ColorRGB to be called. This method returns the hexadecimal value of LightGrey.
After getValue resolves each part of the expression, it sets the propertyResolved flag of the ELContext to true so that no other resolvers are consulted to resolve that part of the expression.

Please refer to the ColorELResolver.java file to see how the getValue method is implemented.

The getType method must return the expected type of the object to which the expression resolves.  This method is primarily used by tools to discover the acceptable type of the object in situations such as when the tool must set the type of the object using the setValue method.

If this resolver handles lvalue expressions, then it must implement the isReadOnly method to return false; otherwise it must return true from this method.

The setValue method, as its name suggests, sets the value of the object referenced by the expression. If the expression points to a property on a bean, in general, it finds the base object and its property, gets the method that sets the value for this property, and invokes this method with the specified value.

The ColorELResolver class does not support lvalue expressions.  Therefore,  its setValue method throws PropertyNotWritableException if it is invoked with a base of ColorImplicitObject or ColorRGB or with a property of Color.

In terms of an lvalue expression used by a JavaServer Faces component tag, the setValue method is called during the postback of the page. Assuming the user entered a value into a text field (for example) and then submitted the page, control is transferred to the apply request values phase, during which the setSubmittedValue method of the component object associated with the text field is called. After the user input is validated during the process validations phase, the update model values phase begins. At this point, the component object supplies a reference to the ValueExpression object that represents the lvalue expression.  The ValueExpression object was created during the the initial request for the page.  The JavaServer Faces implementation calls the ValueExpression object's setValue method, which will in turn call setValue on the appropriate ELResolver instances until the expression is resolved.

The getCommonPropertyType and getFeatureDescriptors methods are intended to assist tools by providing general information about the objects or properties to which the expressions refer.  This information is provided using JavaBeans component introspection APIs.

The getFeatureDescriptors method can return null, which indicates that the resolver does not handle the given base object or that the results are too complex to be handled with this method.  In the latter case, getCommonPropertyType is used instead.

The getFeatureDescriptors can also return an iterator over a collection of FeatureDescriptor objects, each of which contains information about one of the base class methods that are relevant to the resolver. As a resolver writer, you are free to add any descriptor information you think would be useful to tools. The getFeatureDescriptors method of the ColorResolver class returns an iterator over a collection of FeatureDescriptor objects containing the names of the available colors.

At the very least, you should set the following two values in each FeatureDescriptor object:
  • The ELResolver.RESOLVABLE_AT_DESIGN_TIME flag must be set to true or false
  • The ELResolver.TYPE flag must be set to the return type of the method in question
The RESOLVABLE_AT_DESIGN_TIME flag, as its name suggests, indicates whether it is safe to resolve this property at design time. This is important because there are instances in which it would not be advisable to attempt to resolve the property at design time, such as when the resolver needs access to a resource that is only available at runtime.

The getCommonPropertyType method returns the most general type that the resolver will accept for a property that it resolves. For example, a resolver that resolves bean properties might return Object.class because this type would be the most general type of a bean property. If you don't know what the most general type is, you can return null from this method.

Registering the Resolver

After creating your resolver, you need to register it with your application. If you are going to be using the resolver in a JavaServer Faces application, you use its application configuration file (usually called faces-config.xml) to register the resolver. 

If 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.

Registering the Resolver Using a Context Listener
To register your resolver with your JSP application, you need to implement the contextInitialized method of ServletContextListener such that the EL resolver is added to the JspApplicationContext before any requests are received from the client. The following contextInitialized method registers the ColorELResolver class:

   public void contextInitialized(ServletContextEvent servletContextEvent) { 
ServletContext context = servletContextEvent.getServletContext();
        JspApplicationContext jspContext = 
JspFactory.getDefaultFactory().getJspApplicationContext(context);
jspContext.addELResolver(new ColorELResolver());
servletContextEvent.
getServletContext().log("ColorELResolver registered");
}
This resolver will be added to the chain of EL resolvers represented by a CompositeELResolver implementation in the order in which you register them.  This chain of  EL resolver instances is consulted to resolve each expression.  Custom EL resolvers are added to the chain after the standard ImplicitObjectELResolver instance included with the JSP container and after any resolvers used specifically by the JavaServer Faces component tags.

Registering the Resolver Using a JavaServer Faces Application Configuration File
To register an EL resolver with a JavaServer Faces application, you merely need to specify its fully-qualified class name with an el-resolver element in the application's configuration file, as shown here:

   
   <el-resolver>
        webtier-sample.ColorELResolver
   </el-resolver>

This resolver is added to the chain of resolvers represented by Faces EL Resolver for JSP, which is an implementation of CompositeELResolver and resolves expressions used by JavaServer Faces tags contained in JSP pages.  It is added to the chain of resolvers mentioned in the previous section directly after the ImplicitObjectELResolver included with the JSP container.  If you are registering more than one EL resolver, you need to add them to the configuration file in the order you want them added to the chain of resolvers, which determines the order in which they are resolved.   Please refer to Figure 2-3 of the JSP 2.1 Specification to see a picture of the chain of resolvers available to a JSP container.

Using the Expression in the Page

Using the EL resolver is as simple as adding an expression that the resolver can handle to your page, as shown in webtiersample.jsp, which uses ColorELResolver to set the colors of the table cells:

   ...
<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>
...

Migrating to the Unified EL


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:

  • Updating the TLD defining the custom component tags with the new version number and the new deferred-value or deferred-method elements
  • Editing custom component tag handlers to use the new APIs
  • Editing custom component classes to use the new APIs
 
This section also explains how to modify an application that is not backward compatible so that it will not generate an error.

Making Changes to a Custom Tag TLD

If you create custom tags, you will need to make some changes to your TLD files in order to migrate your custom tags to use the unified EL expressions.  The most important one is to change the jsp-version element of your TLD files to 2.1.  In fact, if your custom tag is a JavaServer Faces custom tag, this change will require making all of the other changes outlined in this section and the rest of the Migrating to the Unified EL section of this document.  If your custom tag is a JSP tag, you also need to make any necessary changes outlined in the section on backwards compatibility.

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>
Note that the jsp-version element is now 2.1. This indicates to the JSP 2.1 container whether or it should try to create a ValueExpression or MethodExpression instance and pass it to the tag handler or pass an expression string as a literal value to the tag handler. If the version number is 2.1, it does the former.

The other change is that rtexprvalue has been replaced with the deferred-value element for the someProperty property and with deferred-method for the validator property. The deferred-value element tells the JSP container that the property accepts deferred value expressions. The deferred-method element tells the JSP container that the validator property accepts method expressions.  Therefore, the container creates a ValueExpression object for someProperty and a MethodExpression object for the validator property and passes these objects to the tag handler. The type subelement of deferred-value indicates the type to which the expression must evaluate. The method-signature subelement of deferred-method specifies the signature that the corresponding method must have.

Making Changes to the Custom Component Tag Handlers

If you develop custom JavaServer Faces components and you've made the changes to your TLDs as outlined in the previous section, you also need to migrate your tag handler classes, including performing the following tasks:
  • Make the class extend UIComponentELTag instead of UIComponentTag
  • Change ValueBinding to ValueExpression and MethodBinding to MethodExpression
  • Simplify the setProperties method by removing the condition that checks if the expressions is value-binding enabled.  By default, all JavaServer Faces tag attributes can take either value or method expressions.
Here are the relevant pieces of a tag handler class using the version 1.1 APIs:
   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");
          }
        }
      }
   }
And here is the same tag handler class converted to use the 1.2 APIs:

   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.

Making Changes to the Custom Component Classes

JavaServer Faces 1.1 and earlier versions used the ValueBinding and MethodBinding classes to represent value expressions and method expressions, respectively. These classes are still available for those who want to continue using them.

If you want your components to use the unified EL, however, you need to change all instances of ValueBinding to ValueExpression and all instances of MethodBinding to MethodExpression. The ValueExpression and MethodExpression APIs are now part of the unified EL specification. The following component method shows the 1.1 version of a value-binding enabled component property.

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);
}
}
The following code shows this method converted to use the new APIs.

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);
}
}

Backwards Compatibility Issue for JSP Applications

Prior to JSP 2.1, the #{} syntax was not reserved.  Therefore, there might exist JSP pages based on earlier versions of JSP technology that use the #{ characters where they are not allowed according to the JSP 2.1 specification.  These pages, when used in a JSP 2.1 application, will generate a translation error.  The following are the two cases in which the use of #{ will produce an error when the page is translated by a JSP 2.1 container:
  • If #{ is used in template text as a String literal
  • If #{ is used as a String literal for a tag attribute value when the tag's TLD specifies a jsp-version of 2.1 or greater
To correct these problems, you need to take one of the following actions:
  • Escape the #{ characters as follows:

    some text /#{ some more text

    and
    <my:tag someAttribute="sometext/#{moretext" />

  • Configure your application to allow the #{} characters as a String literal by adding the deferred-syntax-allowed-as-literal subelement to the jsp-property-group element and setting it to true:

    <jsp-property-group>
      <deferred-syntax-allowed-as-literal>true</deferred-syntax-allowed-as-literal>
    </jsp-property-group>


  • Configure the page to accept the #{ characters as String literals with the deferredSyntaxAllowedAsLiteral attribute of  the page directive:

    <%@page ... deferredSyntaxAllowedAsLiteral="true" %>

Now It's Your Turn


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:

We'll be seeing you on java.net!
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:327813次
    • 积分:4042
    • 等级:
    • 排名:第8031名
    • 原创:66篇
    • 转载:126篇
    • 译文:0篇
    • 评论:5条
    阅读排行
    最新评论