Modeling Notes
This is a scatchpad page that tries to describe the Struts workflow in generic form, and then reconstruct a generic configuration file to match the description. This is a brainstorming document only.
On startup, controller parses configuration and deploys the decorator objects, including the handlers, as its ControllerConfig object.
The configuration elements are decorators that are used to create and manage other objects. At runtime, the target objects may be passed a reference to its decorator. The object can then alter its behavior according to the properties of its decorator. If the target object is a singleton, the decorator lets us reuse the object without changing its member properties.
A reference to the servlet's ControllerConfig is placed in application scope, using upper-case versions of the servlet-name and config-name properties as the attribute name {$SERVLET-NAME.$CONFIG-NAME}. If an attribute by this name already exists, a servlet exception must be thrown. At runtime, container passes request to controller (servlet). Controller passes both get and post requests to its processor object.
Processor matches URI with handler's matchPath property. (This is the handler decorator, not its target object.) If no path matches, processor checks for the default-handler before surrendering request back to container. Processor checks for secure attribute and the security role and throws an exception if user is not authorized to access this handler.
Processor obtains a copy of the handler (decorator) and passes it the details of this request event. A reference to the handler is placed in the request (under the default token: HANDLER). If so configured by the controller, the processor also obtains a reference to the standard locale object in the user's session.
If a handler already exists in the request, it is set to to the new handler's previous property. The new object then replaces the old in the request.
{A reference collection of the handlers (decorators) is deployed at startup. The processor refers to this collection when creating a new copy of the handler for a request. When a handler (later) refers to other handler, a reference from the collection may be returned to avoid unnecessary object creates. The handlers have a locked property which indicate whether they are global singletons (locked) or local to this request (not locked).}
By default, the controller will maintain a standard locale object in the user's session (LOCALE). The processor may be configured to place additional helper beans in every request. The helper beans must have a zero-arguments constructor and a handler property.
If the handler specifies an input decorator, the processor obtains the input-object and, if appropriate, calls the input bean's reset, populate, and validate methods, passing each method a reference to the handler for this request. The handler encapsulates details such as a reference to the pending request (which includes the handler instance.) To obtain the input-object, processor first checks to see if there is already an object under the name and scope specified by the input decorator. Otherwise, a new input-object is instantiated.
If the input-object's locked property is true, processor will not call reset or populate (but may still call validate). If the handler's validateInput property is false, validate on the input bean is not called.
The validate method can examine the input-object's properties (populated from the request). If messages are generated, the method can pass a queue of errors to the handler. If validate returns false, the processor uses the specified input-location to complete this cycle. If the input-location is non-null, the processor uses its path (or handler-name) to return a URI to the controller. If null is returned, the response is considered complete.
Processor obtains the handler-object and any helper bean. When a handler-object is specified, the processor calls the object's dispatch method, passing it the handler for this request. The dispatch method returns a location or null. If the handler (decorator) specifies a forward-path property or redirect-path property instead of a handler-object, a servlet-forward or servlet-redirect is performed instead of calling the handler-object. One of these three must be specified.
When a non-null location is return, the processor uses its match-path to return a URI to the controller. If null is returned, the response is considered complete.
Other components in the application (servlet or presentation-layer) may access the handler from the request (and any helper beans) to obtain whatever implementation details may be needed. The controller configuration may also be accessed by other components.
On the presentation layer, handlers may be used to create hyperlinks. The link-param property is provided to help create hyperlinks that use a query string. The hyperlink may be to a handler or any other resource. If the hyperlink is to a resource handled by another component or service (JSPs, HTMLs, VMs,), the handler-object property may be omitted (null). In this case, the match-path will not correspond to the pattern registered for our controller servlet. (But it other cases it may.)
A handler is recommended to create links to local resources, but locations may also be used to create hyperlinks, expecially when the location is outside the application context.
The controller may be passed a module name and template by the web deployment descriptor (web.xml). By default, the module name and template will be applied when matching URIs or rendering the match-path as a hyperlink. When modules are used, non-virtual resources (JSPs, HTMLs) must be placed in a subdirectory corresponding to the module name and template. For a reference outside the module, but within the same application, set the <context> property on the handler to "application". For a reference in the same domain, but in another application, set the <content> property to "server" or "domain". For a reference to another domain, set the <context> property to "absolute". Including the scheme ("http://") in the property value forces the context to be absolute.
Note that all handlers, whether to internal or external resources, share a common name space. All components refer to the handler by its logical name. The match-path is used when the URI first enters the controller, but does not need to be referenced by other components
These are some scratchpad elements to illustrate the workflow. Note that it tries to distinguish between the "decorator" objects (like Struts FormBeans) and the "worker" objects (like Struts ActionForms).
<controller-config> <name/> <module-name/> <module-template/> <default-handler/> <decorators>
<inputclassname> <name/> <input-object-classname/> <dyna-property/>+ <init-param/>+ <scope/> </input>
<!-- input-object interface boolean isLocked(); void setLocked(boolean locked); void reset(Handler handler); void populate(Handler handler); boolean validate(Handler handler); -->
<helperclassname> <name/> <helper-object-classname/> <dyna-property///>+ <init-param/>+ <scope/> </helper>
<locationclassname> <name/> <path/> </location>
or
<locationclassname> <name/> <handler-name/> </location>
<handlerclassname> <name/> <handler-object-classname/> <dyna-property/>+ <init-param>+ <scope/> <match-path/> <scheme/> <secure/> <contextmodule]/> <roles/> <validate-input/> <input-location-name/> <error-location-name/> <helper-name/> <exception-location-name/>+ <exit-location-name/>+ </handler>
<!-- handler interface handler-object-instance previous-handler locked locale void setMessages(Messages messages, String bundleName); Messages getMessages(referenceName); -->
<!-- handler-object interface Location dispatch(Handler handler); ControllerConfig getConfig(); ServletRequest getRequest(); -->
<!-- http-handler-object interface HttpLocation HttpDispatch(HttpHandler HttpHandler); HttpServletRequest getHttpRequest(); HttpServletResponse getHttpResponse(); String getHref(); --> {alternate forms: servlet forward, servlet redirect, hyperlink}
<handler> <name/> <match-path/> <forward-path/> </handler>
<handler> <name/> <match-path/> <redirect-path/> </handler> <handler> <name/> <match-path> <link-param/>+ </handler>
<eventclassname> <name/> <classname/> <init-param/>+ <handler-name/> </event>
<processor/>
<session/>
<exception/>
<properties/>
<resourceBundle/> {first = default bundle}
<resource/> <!-- including DataSources -->
</decorators> </controller-config>