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.
Design Time View Handlers
Note: this document is provisional and subject to change.
Contents
Overview
The JSF specification allows users to configure custom view handlers for their web applications. The custom view handler can change the following aspects of an application:
- The construction and initialization of the view root.
- The construction and initialization of the component tree.
- Adaptation of the component tree to one or more view definition formats (i.e. JSP, XHTML, XML).
- Rendering of the component tree to the target (i.e. typically rendering to HTML).
- Store and retrieve view state between requests.
- Calculate locale and character encoding information for the target.
- Map viewId's to url's (action URL) that when invoked on a request, will restore the corresponding view.
The wide range of capabilities means that assumptions made by the JSF design time can be seriously invalidated by the introduction of a custom view handler. This document covers the significant design time implications of custom view handlers and outlines the requirements for a Design Time View Handler extension mechanism to allow adopters to handle them.
View Root Construction
The view is the basic addressable unit for creating and rendering responses to servlet page requests. A view consists of a tree of UIComponent objects that represent the logical structure of a JSF user interface. Each component tree is rooted at a UIViewRoot, which is a special UI component that is defined by the JSF API framework. It is the responsibility of the view handler to create, and optionally populate, the UIViewRoot upon request for a particular view. (2.4.2.1).
The createView method on the ViewHandler interface is used at runtime to delegate construction of the UIViewRoot to the view handler. The design time view handler needs a parallel method:
abstract DTUIViewRoot createView(DTFacesContext facesContext, String viewId);
Component Tree Construction
The view handler may optionally populate the component tree for a view in createView (2.4.2.1). It must also re-initialize the component tree for a view on a postback through the restoreView API interface (2.2.1). Although the component tree may be modified at any time prior to (or even during) rendering, the view handler is the primary actor in tree creation. Therefore, the design time must provide a mechanism through which the DTUIRoot can be populated with component information:
abstract populateView(DTUIViewRoot viewRoot, DTFacesContext context, String viewId)
Note a key difference between the design time and run time here. The tree creation functions of createView and restoreView are folded into this method since the request vs. postback lifecycle stages that they demark at runtime, hold no meaning at design time (since the design time is simulating and not actually emulating the application).
View Definition Adaptation
The view handler, as part of its responsibility to create and restore view, normally acts as the adapter to view definition formats. It may do this in one of several ways. In the default case, JSF uses the servlet container's resource handling mechanism to build (and render) its component tree using the RequestDispatcher.forward method. The most common configuration of JSF forward requests to the JSP dispatch handler and uses JSP tags as the view definition format.
Custom view handlers may choose to also dispatch view construction and rendering and rely on the web application to be configured correctly to handle the supported definition format. Or (as in the case of Facelets) the view handler may choose to define its own definition format. Several design time issues arise as a result.
The design time view handler must provide support for the editing of its view definition format. This in itself raises several issues since the two most common view handlers (JSP and Facelets) use meta-data formats which are not specific to JSF (JSP and XHTML respectively). Therefore, in many cases the design time view handler need only provide an adaptation between the UI definition artifacts in the meta-data (typically these are XML tags) and the UIComponent tree will be built from them at runtime.
The design time view handler can be queried for the meta-data definition types it supports via a metadata factory:
JSFViewMetadataAdapterFactory getViewMetadataAdapter(DTFacesContext context)
The JSFViewDefinitionAdapterFactory can be provided view ids in the current project's DTFacesContext and can return a JSFViewDefinitionAdapter for that view:
JSFViewDefinitionAdapter createJSFViewDefinitionAdapter(DTFacesContext context, String viewId)
JSF View Definition Adapter
Because the view meta-data is generally the artifact that the end user wants to use to construct their UI (i.e. the JSP or XHTML pages), the adapter must support common design time view and editing functions. The JSFViewDefinitionAdapter is a value adapter so that it can adapt generically between metadata artifacts and component artifacts (here component artifacts include non-component classes that are often included in view definitions such as validators and converters). For example, given a design time component artifact called ComponentTypeInfo that describes a UIComponent and the WTP-defined TLDElementDeclaration used to describe the corresponding JSP tag, the adapter could be queried:
ComponentTypeInfo componentType = viewAdapter.getAdapter(someTldDecl, ComponentTypeInfo.class);
or conversely, to map back to a tag definition from a component type:
TLDElementDeclaration tagDecl = viewAdapter.getAdapter(componentType, TLDElementDeclaration.class);
This basic adaptability supports view construction at design time since a client can traverse a meta-data document and adapt, for example, TLD-defined tags into components to derive the design-time component tree. Indeed, a design time view handler would likely use its adapter to perform its populateView contract.
To fulfill editing of meta-data, the adapter requires the ability to enumerate the available meta-data artifacts for the framework to support features like content assist. TODO: how to generically provide the available features? Use the JSF meta-data framework's domain/entity key system?
Meta-data based composition
Since components can be added to the view at any time (2.4.2.3), view definition artifacts can be used to create new components by composing existing components (a real life example is seen with the ui:composition tag in Facelets). TODO:
Sub-view inclusion
TODO:
Expression Language Discovery
A JSF view meta-data language may define ways to construct EL ValueExpression (ValueBinding) and MethodExpression (MethodBinding) expressions and embed them in the view definition for different purposes. In the default case, the well-defined JSP construct "#{}" is used to embed such expressions in attributes and (in JSF 1.2) regular template text. The meta-data adapter must identify EL expression types in a view definition. We use the IModelContext to describe the position in a view definition model:
String getELExpression(IModelContext context)
View Rendering Control
TODO:
Calculate locale
The locale is used by the view to customize its resources. The most common usage is to use it to localize loadBundle text. The locale is determined by calling the calculateLocale method on the ViewHandler at runtime. The Design Time View Handler has a parallel interface:
String calculateLocale(DTFacesContext context)
Mapping viewId's to url's
The getActionURL on the ViewHandler is used to map a view id to an action url that when invoked on the web application will trigger the request lifecycle for that view (7.5.1). At design time, similar mappings are required to determine:
- what url would be invoked to retrieve a particular source (view definition) file (i.e. a JSP).
- the location in the workspace of view definition source files.
- the location in both the runtime and workspace of files being included by subviews and other templating methods.
Runtime URL
The runtime URL for a view is needed because the WTP server launch profiles need to know what URL to point a browser to when a view definition file (i.e. a JSP) is launched. The J2EE server framework provides JSF with a mechanism to do this through the FileURL interface. To support this interface generically, the design view handler implements a mapping method:
IPath getActionURL(DTFacesContext context, IResource resource, IPath requestPath);
Note that implicit in this method is the fact that the DTFacesContext contains a DTExternalContext through which servlet mapping data may be derived.
Workspace URL
The design time view handler maps viewId's to workspace files, allowing definition files to be loaded by viewers and editors:
IResource getActionDefinition(DTFacesContext context, String viewId);
Inclusion URL
Finally, the design time view handler provides a way to map to a workspace resource relative to another. This is necessary for scenarios such as the f:subview include where a URI relative to the current view is referenced for inclusion:
IPath getRelativeActionPath(DTFacesContext context, String relativeToViewId, String uri);
Impact on current JSF Implementation
Tag Converter Selection
The design time view handler controls the way that the component is rendered. While a change in the Tag Converter framework is probably not required, the design time view handler will need to be queried for what Tag Converter Factory to use as well as (when eventually supported) the conversion decorator. These are aspects that are impacted by the ViewHandler renderView.
Server Launch Profiles
The JSFFileURL needs to be refactored to call getActionURL on the design time view handler.
EL content assist
The EL content assist processor depends on the JSP region parser to detect EL expressions. However, in arbitrary JSF view definition source files, there is no support for EL region parsing. Therefore, calls to the region parser must be changed to call getELExpression on the design time view handler.
EL validation
The EL validation uses the JSP region parser to detect EL expressions to validate. Just as with the content assist, this must be changed to use getELExpression.
Tag Meta-data
Tag meta-data should be acquired through the JSFViewMetadataAdapter rather than directly from the meta-data store to abstract it from the view definition format. Tag meta-data in general may need to move more toward component meta-data to abstract it from the view and rendering technologies in use.
JSPModelProcessor
The JSPModelProcessor is presently used to discover EL variables. This processor needs to be changed to use the component tree generated by the design time view handler to eliminate redundant view source parsing and abstract it from the view definition in use.
Design Time Application Manager
The design time application manager will be modified to provide support for assigning custom design time view handlers.
Key external dependencies
CMDocument Namespace Extension
The most common way to define JSF view definition is using some form of XML document. JSPX and XHTML are two formats already in use by the default view handler and facelets respectively. Any future custom view handler technologies will most likely use XML for their view definition format since it has the same tree-based hierarchical definition format as the component tree. Each new component library is generally registered using a unique namespace, with elements in that namespace representing components and component-like artifacts (i.e. validators, converters).
Functionality to support this editing strategy is already present for JSP files in the JSP editor, which allows namespace declaration of JSP tag libraries to add new element namespaces. However, for other XML formats including XHTML, the SSE framework doesn't support the ability to introduce arbitrary element namespaces. For example, the following XHTML file:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <f:loadBundle var="x" basename="com.blah.bundle"/> </body> </html>
has no support for the f:loadBundle tag and there is no apparent way to supply it.
The proposed solution is to modify the XMLAssociationProvider to allow an extension point (probably based on CMDocumentFactory) to be queried when a namespace or element name request is not found in the DTD or XSD definition for the target document.
Flexible Region Parsing
The JSP editor region parser has extended behaviour to detect EL regions. The design time view handler is responsible for detecting EL in a view definition file, but in XML documents it would be convenient for it to call the underlying region parser to avoid redundant document parsing. This is already true in JSP documents where the parser does detect EL expressions.
The proposed solution is to provide an extension to the region parser that can register for existing XML region types and further parse them further. So, for example, if the following appeared in an arbitrary (non-JSP) document:
<h:outputText value="#{bean.property}"/>
The XML_ATTRIBUTE region that is returned for "#{bean.property}" could be passed to an EL region parser to detect that what is inside the double quotes is an EL region. The design time view handler could then be populated with the available EL regions for use by getELExpression.
Meta-data Extensions to CMNode
Ideally, tag meta-data would be accessible in XML source documents through the CMNode. For example, the possible values for an attribute would example through the CMAttribute type for that node.