A recent RFE[1] in our issue tracking system, plus discussions with several
folks at the recently completed ApacheCon US conference in San Diego, has
motivated me to attempt a distillation of where I think Shale's support for
"remoting" should go. For comparision (since there isn't any
feature-related documentation to speak of), I'll start with a brief
description of the current functionality, and then talk about what seems
like a good idea for the future.
CURRENT REMOTING FUNCTIONALITY:
Shale's application level functionality is supplied by a Servlet filter (
org.apache.shale.faces.ShaleApplicationFilter). One of the features of this
filter is that it executes a standard Commons Chain[2] chain of commands for
each request. In turn, the default chain that is executed includes a
command (org.apache.shale.remote.RemoteCommand) that is invoked if the
request URL matches an extension mapping (default is "*.remote"). In turn,
this command performs the following tasks:
* Creates a ShaleWebContext instance (extension of Commons Chain
ServletWebContext implementation)
to represent the "context" passed to each command.
* Extracts the context relative path such as "/foo/bar.remote" from the
servlet request path properties.
* Looks for a corresponding Commons Chain command in a catalog named
"remote". If there is such a
command, invoke it (passing in the constructed ShaleWebContext). This
command is responsible for
constructing a response, which can be done by the usual
RequestDispatcher.forward() call -- normally
to a JSP page that might use JSTL and EL expressions -- or programmatic
construction via an
instance of org.apache.shale.remote.ResponseWrapper that acts much like a
standard JSF ResponseWriter.
* Several default command implementations are provided, including one that
invokes an arbitrary JSF
value binding expression and stores the result as a ShaleWebContext
attribute.
While this functionality does support achieving the original goal of this
portion of Shale (providing back-end support for AJAX enabled JSF
components), it has several issues, including:
* Bound to Servlet API objects, so not directly portable to a portlet
environment.
* As described in [1], requires quite a lot of work (plus an understanding
of
Commons Chain itself) simply to map an incoming URL to a piece of business
logic that produces the response.
* Does not directly support serving of static resources (images, JavaScript
files,
stylesheets) without mapping each individual resource as an individual
command.
PROPOSED REMOTING FUNCTIONALITY - GOALS:
The remoting support in Shale should be focused on providing
client-technology-agnostic mapping of incoming HTTP requests to server side
dynamic logic for AJAX-enabled widgets (including AJAX-enabled JavaServer
Faces components), plus an easy way to serve static resources (from either
the web application itself or from class loader resources) that the client
side widgets might require. This support must operate in either a servlet
or portlet environment, and require minimal (ideally none in the simplest
case) configuration at the application level.
PROPOSED REMOTING FUNCTIONALITY - IMPLEMENTATION STRATEGY:
Shale will provide a PhaseListener implementation that receives notification
after the Restore View phase of the request processing lifecycle has been
completed. At this point, a "view identifier" (a context-relative path to
the resource to be processed) will have been identified. This view
identiifer will be compared to one or more preregistered mapping patterns --
if the view identifier matches, this request will be processed by a
corresponding processing logic module, instead of following the usual JSF
request processing lifecycle.
Registering processing logic modules will be easy and extensible. To
minimize the effort required to assemble an application, however, the
following standard processing logic modules will be provided:
* Serve a static resource corresponding to this view identifier, from the
web application's
static resources (i.e. starting from the document root). To be consistent
with a servlet
container's standard behavior, no such resources will be served directly
from the "/WEB-INF"
or "/META-INF" paths.
* Serve a static resource corresponding to this view identifer, from the
class loader resources
of this web application. For example, this facilitates component
libraries that wish to make
JavaScript files available to the client pages that utilize those
components, without requiring
*any* specific configuration in web.xml, or manual assembly of resources
into a reserved
directory of the web application.
* Map the view identifier (such as "/foo/bar.remote") to a method binding
expression (such as
"#{foo.bar}"), identifying a public method that will be invoked to create
the response for this
HTTP request. The bean containing this method can be a managed bean, and
thus created
on demand if needed. The method itself can call
FacesContext.getCurrentInstance() to retrieve
the per-request state information for this request (since we are, by
defnition, performing a JSF
request at this point), and can use any desired JSF facilities (including
the standard ResponseWriter)
to prepare the actual response.
* (Optional) alternative dynamic execution mapping to execute a Commons
Chain command
(essentially equivalent to the current functionality, and/or a tie-in to
Struts Action Framework 1.3.x
type processing logic).
* (Optional) alternative dynamic execution mapping to execute an XWork
action (essentially
equivalent to what WebWork currently does for mapping incoming URLs to
business logic).
In addition, the configuration information mapping incoming URL patterns to
corresponding processing logic modules will be made available in a simple
JavaBean stored in application scope, so that dynamic code (such as the
renderer for a JSF component wanting to utilize these facilities) will know
what kind of context relative URL is required).
Given these capabilities, and assuming some default mappings, one could have
all of the following facilities with *zero* application level configuration,
other than including the appropriate Shale library into /WEB-INF/lib:
* For a context relative URL of the form "/web/foo/bar.js", serve the static
resource at "/foo/bar.js"
with appropriate content type settings.
* For a context relative URL of the form "/classpath/foo/bar.css", serve the
resource "/foo/bar.css"
from the webapp classpath (typically, a resource file embedded in the JAR
file containing the
component whose renderer requested this resource), with appropriate
content type settings.
* For a context relative URL of the form "/execute/foo/bar", execute the
bar() method on the managed
bean named "foo". This method will take total responsibility for
producing the output, so it can do
whatever it needs.
SUMMARY:
Thoughts?
Craig
[1] http://issues.apache.org/bugzilla/show_bug.cgi?id=38050
[2] http://jakarta.apache.org/commons/chain/