Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EL Context Framework Design

Revision as of 16:23, 21 September 2006 by Unnamed Poltroon (Talk) (Supporting multiple EL Contexts)

Motivation

In EL expressions, user and system-defined symbols can be used be used to store and retrieve values as well as point to methods that can be called. For example, the following expression references a property on some object:

#{object.property}

In JSP 2.1/Faces 1.2, the job resolving 'object' and 'property' to values that can be used in evaluating the expression is delegated to an API defined in the Unified EL specification. The root of the API is an class called 'ELContext'. Each implementer of Unified EL can create and populate one or more ELContext object for use in evaluating expressions.

This document lays out the design of a 'design-time framework' to allow tooling that supports technologies that use Unified EL. The emphasis is on supporting JSF and JSP technologies. An effort is also made to remain as backward-compatible with previous tooling as possible without shackling new design.

Restatement of Requirements

ELToolingStakeholderUser.usecase.png

There are three major user-stakeholders in this framework. The first is Developer (specialized as JSFDeveloper for JSF tooling). Developer needs to be able to support EL at design-time any place they may use it at runtime. This user also needs a way to install additional support for dependent behaviour for things like third-party libraries.

The second stakeholder is LibraryDeveloper. LibraryDeveloper classifies any users who wishes to have a library that uses Unified EL to be supported by the tooling. In JSP an example of LibraryDeveloper is anyone creating JSP tag libraries; in JSF a specialized example is the JSFComponentLibraryDeveloper. This user needs to be able to add additional support for their library to default framework behaviour.

The third user-stakeholder is ToolsDeveloper. ToolsDeveloper uses the tooling to create specialized tooling that needs to support EL technologies. This user needs to be able to modify any default behaviour to suit specialization to the standard technologies being supporting by the tooling.

Within this context, framework design goals will emphasize the following requirements:

  • The framework must provide a way for Developer to have support EL expression resolution wherever it is useful and that LibraryDeveloper and ToolsDeveloper can easily provide it.
  • The framework must be flexible enough to allow each web application (as defined at design time by a dynamic web project rooted in an IProject) to have entirely different symbol resolution in different application contexts. This supports the needs of ToolsDeveloper. For example, Faces 1.2 supports two different ELResolvers depending on where an expression is being resolved.
  • The framework must allow third parties to modify the way symbols are resolved in each of the possible application contexts. This supports the needs LibraryDeveloper.
  • The framework must provide a simple but comprehensive way for JSFDeveloper to manage these new behaviors on each web application within the constraints of providing correct design time behavior. This includes managing third-party contributions and the ability to enable or disable functionality in ways that match what could happen at runtime.

Why EL symbols are resolved at design-time

The actual value of an EL symbol is normally not computable at design-time because it is often dependent on runtime state, user input and so on. However, with many of the more widely used EL symbol objects like POJO beans and resource bundles, enough information can be predicted about runtime semantics to provide useful tooling support to tooling users. In some cases like resource bundles, it is sometimes even possible to evaluate the actual value of a symbol and aid the user further.

Major users design-time resolution information include:

  • Validation: type information can be derived and checked against what is expected.
  • Content-assist: an object's possible methods and properties can be determined and presented to the user as code assist tips.
  • Refactoring: When underlying objects change, EL that was valid may become invalid. The user can be presented with automatic refactoring options when underlying dependencies change.


How EL symbols are resolved at design-time

In order to predict the runtime type and other semantics of an EL symbol, the design-time framework needs to parallel as closely as possible the way it is evaluated at runtime. In that respect, the EL Context Framework mirrors the runtime Unified EL API by creating "design-time versions" of the API. Class and interface naming generally follows the convention of taking the runtime API name and prepending "DT" to it.

The "root" of the API class hierarchy is the ELContext object. The ELContext contains resolver objects that can map EL symbols into meaningful runtime objects and values. The design time framework therefore defines a "DTELContext" object that similar contains similar design-time resolvers. The most important classes are listed below:

File:ELContext class diagram.png

All of the classes in this diagram except for DTELResolverDescriptor are direct mirrors of runtime equivilents. DTELResolverDescriptor provides meta-data about a DTELResolver to help ToolsDeveloper add configuration support and will be discussed later.

DTELResolver is used to resolve model objects, properties and method names as the following example:

#{bean.property}

In the first example, the symbol 'bean' would be passed to the DTELResolver to determine if it is a valid model object. If it is, the DTELResolver would then be asked if 'property' is a valid property of that object. The result of each request to DTELResolver is symbol information returned conforming the following ISymbol API:


EL Symbol framework.png

A 'symbol' is modelled broadly as simply a named object. The descendents correspond to the main types of named objects: model objects (i.e. managed beans, resource bundles), properties and methods. From there objects are further specialized for specific uses such as IBeanInstanceSymbol that is used to model instances of managed beans. The model is intended to further extended by LibraryDeveloper and ToolsDeveloper as needed.

FunctionMapper allows a JSP-style function to be resolved to an executable Method object at runtime. In previous versions of EL, this was generally done through the tag library function mechanism. Unified EL generalizes this so that each implementer can determine how such functions are resolved. For example, the expression:

#{ns:f(1)}

decomposes into a namespace, "ns" and a function name "f()". It the responsibility of the runtime FunctionMapper to map these two pieces of information to a callable method. Similarly, the design-time framework defines DTFunctionMapper to resolve design-time information for such a method. TODO: an open problem is still determine whether a the result of querying DTFunctionMapper should map to a new type of ISymbol, or whether it could map directly to something more tangible like a Method (Java introspection framework) or a JDT IMethod object.

VariableMapper is used at runtime to allow an entire EL expression to become a named, first-class object in the symbol namespace. At design-time, DTVariableMapper is used to store and retrieve the design-time equivalent. For example, consider this piece of JSP code:

<c:forEach var="book" items="\#\{BooksBean.books\}">
    <h:inputText id="quantity" value="\#\{book.quantity\}" ... />
</c:forEach>
In this case, the "value" attribute of the JSF tag, "h:inputText" uses the "book" symbol defined in the JSTL tag "c:forEach". The value of the "book" variable in this case is the expression
#{BooksBean.books[i]}
where i is the element of the BookBean.books collection selected at each iteration of the loop. So DTVariableMapper must also support a way to communicate symbol information between different EL Contexts -- in this case, between JSF and non-JSF tag contexts.

TODO: do we use the symbols framework for variable mapper?


Supporting multiple EL Contexts

Each runtime implementer of EL can define one or more ELContext objects for use in different application contexts to help resolve expressions. Both JSP and JSF define their own ELContexts.

The framework supports this by defining two new extension points: model context specifiers and DTELContext contributors. Model context specifiers define a specific context-space in which a single DTELContext may wish to operate. DTELContext contributors are simply a way to contribute new DTELContext objects to the list available to Developer to use. Developer can configure which DTELContext contribution is used in which model context by using a new project property page:

ELContextSelectionDialog.png

One row in the dialog is constructed per registered Model context specifier. The combo box for each specifier is populated with each DTELContext contribution that indicates that it supports that model context. The current selection in each combo box indicates the DTELContext that will be used when EL expressions are resolved for that model context.

Note that there are three main states for the DTELContext combo box corresponding to the three possible options the EL framework has for resolution in each context:

1) an DTELContext is available, supported and selected for a model context specifier. The first three rows show this state. This is a normal, non-error case.

2) a DTELContext is available and supported, but Developer has indicated that no DTELContext should be selected for a model context. This is shown in row four of the dialog. The user may choose to do this if all DTELContexts seem to be behaving incorrectly or are not providing desired behaviour. This is an exceptional, non-error case. Note that a message is placed in the combo box to indicate the exceptional case.

3) a model context specifier has been registered, but no DTELContext's exist that support that model context. This is an exceptional, non-error case since it would be unusual to have model context without a DTELContext, but it is a supported behaviour. Not that in this case, the combo box is disabled and a message is set to indicate an exceptional case to the user. TODO: Should we add a warning marker?

The following diagram illustrates the main entities involved in defining Model context specifiers and selecting related DTELContexts.

ContextSelection.png

The diagram can be seen as having two columns of entities: the left column shows the main management classes and the right column shows the important classes that are managed. The DTELContextSelection encapsulates the matching of a model context specifier and a DTELContext. The DTELSelector provides an interface for clients to request a DTELContext in a particular model context. DTModelContextManager manages the collection of all registered model context selection extensions. DTModelContextSpecifier encapsulate a single model context specifier extension.

A DTModelContextSpecifier consists one or more description elements that define the specifics of the context. The IModelContext kind of specifier describes static context in a particular content model -- for example, a line and column number in a JSP document. The StaticContextDescriptor can be used to collect otherwise disparate information about a static context: for example, it may be used to define a context as being within one or more Content-Types or one or more DOM elements within a particular XML DOM tree. The DTModelContextSpecifier is intended to allow a contributor to generalize context in very specific application locations. For example, in JSF an important context for EL is within the attributes of JSF tags. To specify this, we must be able to say "only JSP content types" and "only within DOM attributes of elements that map to JSF tag libraries".

A DTELContext is generally used by a symbol resolver during EL resolution. The following sequence diagram shows how a DTELContext is acquired:

ELContextAcquisition.png

In this case, an entity called ISymbolContextResolver (this is a JSF interface used to resolve symbols in a specific IModelContext), has a model context specification (modelContext) for which it wishes to acquire the DTELContext. It passes this request to the DTELContextSelector. DTELContextSelector checks if it has a DTELContextSelection matching this specifier. If it does (the normative case), it retrieves it and returns its DTELContext object. Note that the DTELContextSelection here is determined from what is configured in "Configure Active EL Contexts" dialog. TODO: is there a need for a 'default' selection in cases where the user does not configure it? Can this mechanism be set programmatically by ToolsDeveloper to override these defaults with their own?

Finally, to elucidate how the various entities inter-relate, below is a sequence diagram showing how the "Configure Active EL Contexts" dialog would be constructed:

ELContextUIConstruction.png


Configurable DTELResolvers

Back to the top