Expression Language Support — Functional Description


1. Introduction

Support for an Expression Language (EL) is a clear requirement for JSTL1.0. We need to address this requirement even though it is utimately the responsibility of the JSR152 expert group to specify how an EL can be seamlessly integrated in JSP, as well as which expression language(s) is/are to be supported by JSP containers.

In our quest for an interim solution, JSTL is constrained by the fact that this solution must work in the context of JSP1.2, without requiring any change to the spec, and that it should integrate seamlessly with JSP.next (where an EL would be supported natively). It is also important to consider the fact that the specification of an expression language is a rather controversial issue that could trigger long debates among the expert group, preventing us from getting anything out in any timely fashion.

Our strategy therefore consists in allowing the easy integration, selection, and use of a variety of expression languages via an Expression Language Integration API, a set of tags, as well as conventions used throughout JSTL. We won't rule on any specific EL immediately. Rather we will allow various implementations of Expression Languages to all play in our sandbox. The hope is that through experimentation, it will be much easier to evaluate the merits of each approach, paving the way for the selection of a single or limited number of Expression Language(s) for the final draft of JSTL.

2. Integration of an Expression Language in JSTL1.0

An important requirement for support of an Expression Language within JSTL was that a single attribute should support a value specified in any of the following 3 forms:

       a. String literal    att="15"
       b. rtexprvalue       att="<%= foo.getBar() %>"
       c. elexprvalue       att="$foo.bar"

This requirement made a lot of sense for the following  reasons

However, there are many issues in trying to support this given that it must work in the context of JSP1.2, without requiring any changes to the spec. The most important issue we identified is the danger that an rtexprvalue could return a String beginning with '$' (or any other metacharacter used by an EL to identify an elexprvalue).  This wouldn't be a common 'accidental' case, but the fear is that it could become something of a security issue.  If an rtexprvalue's result is based on user input -- e.g.,

        <%= request.getParameter("username") %>

-- and the user 'maliciously' enters text beginning with '$' in order to establish a context for unchecked evaluation of an expression, an application might be coerced into acting in unintentional and unpredicted ways.  To be safe, every rtexprvalue that picks up data from user input would need to be wrapped in a routine that escapes a leading '$' if present.

Because of this security concern, it was decided to use a different mechanism to integrate an EL. It consists in using twin tag libraries, as described in the next section.

2. Twin Tag Libraries to support the Expression Language

Among the mechanisms that are now available in JSP1.2 is the ability to deliver more than one taglib in the same JAR file. JSTL therefore has two flavors of libraries: RT (rtexprvalues) and EL (elexprvalues). They only differ in the way they support expressions for attribute values.

In an RT tag library, attributes that support expressions support them as 'rtexprvalues'; i.e. as expressions specified in the page's scripting language (and evaluated at runtime). This is exactly how things currently work in tag libraries.

In an EL tag library, attributes that support expressions support them as 'elexprvalues'; i.e. as expressions specified in the JSTL Expression Language (and evaluated at runtime as well). 'rtexprvalues' are not allowed when using an EL tag library. An 'elexprvalue' is a String literal in the syntax of the EL. It is the responsibility of the EL to define the metacharacter(s) used to discriminate expressions from String literals (e.g. use $ at the beginning of an expression).

JSTL therefore provides two sets of URIs, one for tags that accept rtexprvalues, and one for tags that accept elexprvalues. Twin libraries therefore support the same tags with the same syntax, except that one takes rtexprvalues, the other takes elexprvalues.

Usage

These twin tag libraries can be accessed in the following way in a JSP page (using the "core" taglib as an example):

    <%@ taglib uri="http://java.sun.com/jstl/ea/core" prefix="c" %>
   
<%@ taglib uri="http://java.sun.com/jstl/ea/core-rt" prefix="c-rt" %>

In our examples, we append the value '-rt' to differentiate the 'rtexprvalue" library from its 'elexprvalue' twin. It is possible to use the two libraries within the same page, although not recommended for clarity and consistency.

Within the same tag, all attribute values specified as expressions will have to be specified either as 'rtexprvalue' or 'elexprvalue' (cannot mix within the same tag), depending on which tag library the tag belongs to.

3. Expression Language Selection

It is possible to define a web-application's default expression language via the context parameter javax.servlet.jsp.jstl.temp.ExpressionEvaluatorClass. The class specified must implement interface ExpressionEvaluator (see below).

It is also possible to override the default expression language setting with the <expressionLanguage> action. For example:

    <jx:expressionLanguage evaluator="evaluator-class">
        <jx:forEach items="{products/@key}">
            ...
        </jx:forEach>
    </jx:expressionLanguage>

The scope of  the expression language specified by the action <expressionLanguage> is limited to its body. <expressionLanguage> actions can be nested, where nested occurrences shadow their ancestors .

It is illegal to use an expression in a context where no expression evaluator is specified (i.e., no web.xml declaration and no enclosing <expressionLanguage>).

The above  rules make it possible to validate the syntax of an elexprvalue at translation time.

If JSTL rules on a single EL, then both the context parameter and the tag will become illegal.

6. Expression Language API

User code is expected to only interact with ExpressionEvaluatorManager which is reponsible to delegate the requested operation to the currently active ExpressionEvaluator. [Even if JSTL eventually selects a single EL, this will still be the way to access the EL]

    public class ExpressionEvaluatorManager {
        /**
         * Invokes the evaluate() method on the
         * "active" ExpressionEvaluator.
         */
        public static Object evaluate(String attributeName,
                                      String expression,
                                      Class expectedType,
                                      Tag tag,
                                      PageContext pageContext)
           throws ExpressionException, JspException;
    }

As a result of evaluating an expression, two types of Exceptions can be thrown by the ExpressionEvaluator.

The distinction between these two types of exceptions is useful for actions such as <expr> that prefers displaying a default value rather than having an error page displayed should an object in the expression be inaccessible (e.g. if address yielded a null pointer in the expression $customer.address.city). The EL therefore decides what's fatal (JspException) and what isn't (ExpressionException).

    public class ExpressionException extends JspException;

The  integration of custom Expression Languages will be possible during the Early Access phase of JSTL to help us experiment with various ELs. An Expression Language implementation simply needs to implement interface ExpressionEvaluator. Check the details of the JSTL implementation to see if there are any constraints on the implementation of an ExpressionEvaluator.
[For example, one could impose that the implementation of  ExpressionEvaluator be thread-safe, and  that there is no guarantee that a single instance will be used for all evaluations (if an implementation of an ExpressionEvaluator wants to store state (such as an expression cache), a static member variable with proper synchronization should then be used).]

    public interface ExpressionEvaluator {
        /**
         * Translation time validation of an expression.
         * This method will return a null String if the expression
         * is valid; otherwise an error message.
         */
        public String validate(String attributeName,
                               String expression);

        /**
         * Evaluates the expression at request time.
         */
        public Object evaluate(String attributeName,
                               String expression,
                               Class expectedType,
                               Tag tag,
                               PageContext pageContext)
           throws ExpressionException, JspException;
    }

7. Custom Tags using the JSTL Expression Language(s)

Custom tag developers can support expression languages in their tags using the same conventions established in this document.

Assuming a custom action named foo; one could have the following implementation classes:

           FooTag
            /  \
           /    \
     FooTagRT  FooTagEL

The only code in FooTagRT and FooTagEL are the setter and getter methods for the attributes that support expressions (they cannot be inherited because the properties do not have the same type; String for EL and the "native" type for RT).

FooTagEL also needs to override doStartTag() to evaluate the elexprvalue first, and then call back into the base class. For example:

  public void setFoo(String fooEL) throws JspException {
      this.fooEL = fooEL;
  }

  public int doStartTag() throws JspException {
      foo = ExpressionEvaluatorManager.evaluate
                   ("foo", fooEL, FooType.class, this, pageContext);
      }
      return super.doStartTag();
  }

It is important to note that the evaluation of the expression must be performed within the doStartTag() method. The lifecycle of a tag is such that the setter method of values that no not change (string literals) may not necessarily be called if the tag is reused by the container. Since an elexprvalue is specified as a string literal, this could cause an elexprvalue to not to be "re-evaluated" if the evaluation were to be performed in the setter method.

Two TLDs must be written. The only differences are:

When JSP.next comes, a unified tag library can be created. All there is to do is merge the base classes with the RT classes, and remove the 'EL' classes and the EL TLD.

8. Validation of elexprvalues at translation time

@@@ FIXME During the Early Access phase where multiple ELs are made available for experimentation, translation time validation of elexprvalues must be performed by a TagLibraryValidator so the proper ExpressionEvaluator can be invoked for the validation (a TEI would not be able to establish the execution context). One such TagLibraryValidator, ExpressionEvaluatorTLV,  is made available to support translation time validation of elexprvalues of JSTL tags, as well as for any other custom tag library.

It supports an initialization parameter (custom-taglibs) to specify the list of custom tag libraries that should be validated for elexprvalues (* to specify all).

For example:

  <taglib>
    ...
    <validator>
      <validator-class>javax.servlet.jsp.jstl.core.ExpressionEvaluatorTLV</validator-class>
      <init-param>
        <param-name>custom-taglibs</param-name>
        <param-value>*</param-value>
      </init-param>
    </validator>
    ...
  </taglib>

If the JSTL ends up specifying a single EL, then translation-time validation could be performed within the TagExtraInfoClass by simply calling ExpressionEvaluatorManager.validate() on each elexprvalue.

9. Relationship with future JSP specs

The expression language integration model for JSTL will work fine with JSP 1.2 containers and above. Nothing in that model gets in the way of the JSP specification should it decide to include native support for expression language(s).

When a JSP spec comes with native support for an EL (JSP.next), the assumption is that EL expressions would be supported natively only for attributes where rtexprvalue=true.

The twin libraries would behave as follows in a JSP.next environment:

The only drawback to the semantics of 'EL is only interpreted in a rtexprvalue attribute' is that somebody can type a="$expr" and get a literal when they really meant "evaluate expr".

Is it important or not? One may argue that most attributes support rtexprvalues, except for the few cases where the value needs to be known at translation time (in which case elexprvalues cannot be used). For example:

This is a JSP.next issue. If this is seen as an important issue, it could be made a translation-time error to use unescaped $ syntax in a non-rtexpr attribute. But this would have the disadvantage of breaking JSPs that use the EL version of the twin tag libraries.

At the same time as JSP.next comes out, the goal would be to have a new version of the tag library that removes the distinction between RT and EL. For example: "http://java.sun.com/jsptl-1.0" (no suffix). The TLD would state a dependency on JSP.next.