Struts MVC Semantics


Building upon the knowledge of how Struts offers various infrastructure services, this section will discuss the design patterns and implementation details of the key components of the framework. The semantics of key Struts components will assist in recapping this chapter, and at the same time offer an "under the hood" view that will be helpful in extending the framework, should such a need arise. The Struts framework uses the Service to Worker design pattern [Core].

click to expand

Note

Struts is constantly evolving, as such, it is very likely that the semantics captured here may change to some degree as the 1.1 beta undergoes bug fixes and optimizations.

The Controller Object

The controller semantics are realized by the ActionServlet class. It provides a central place for handling all client requests. This promotes a cleaner division of labor for the controller layer that typically deals with view and navigation management, leaving the model access and manipulation to request handlers (Command objects [Gof] ) that are typically request specific. All incoming requests are mapped to the central controller in the deployment descriptor as follows:

 <servlet>     <servlet-name>action</servlet-name>     <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet> <servlet-mapping>     <servlet-name>action</servlet-name>     <url-pattern>*.do</url-pattern> </servlet-mapping> 

The logical mapping of resources depicted in the preceding permits modification of resource mappings within the configuration file without the need to change any application code; this mapping scheme is also referred to as Multiplexed Resource Mapping. The controller provides a centralized access point for all presentation-tier requests. The controller delegates each incoming request to the RequestProcessor, which in turn dispatches the request to the associated form bean for form validation, and to a request handler for accessing the model. The combination of controller and RequestProcessor forms the core controller process. The abstraction provided by the controller alleviates a developer from creating common application services such as managing views, sessions, and form data; a developer leverages standardized mechanisms such as error and exception handling, navigation, internalization, data validation, data conversion, and so on.

Controller Object Semantics

The controller servlet (ActionServlet) essentially initializes the resources required for controlling the behavior of the framework; all request processing function is delegated by the controller to the RequestProcessor. The following is a listing of the init() method–related key controller operations in the order of execution sequence:

  1. Get the initialization parameters declared in the deployment descriptor (refer to the ActionServlet API for a complete list of available initialization parameters and their usage).

  2. Parse the web.xml deployment descriptor to retrieve the <url-pattern> element's body; this will assist the RequestProcessor in understanding how to extract the path information from the request URI and strip the .do extension. The URL mapping is saved in the ServletContext using Action.SERVLET_KEY.

  3. Parse the struts-config.xml using the Digester instance and the ConfigRuleSet (discussed earlier in this section), and create the configuration object hierarchy rooted in the ModuleConfig object. The ModuleConfig is saved in the context using Globals.MODULE_KEY.

  4. Create a MessageResources object for each MessageResourcesConfig object and save it in the ServletContext using the key supplied for each message resource, or the default key Action.MESSAGES_KEY (a.k.a. Globals.MESSAGES_KEY).

  5. Create a DataSource object for each DataSourceConfig object and save it in the ServletContext using the key supplied for each data source, or the default key Action.DATA_SOURCE_KEY (a.k.a. Globals.DATA_SOURCE_KEY).

  6. Create a PlugIn[] object for all PlugInConfig objects and save the array in the ServletContext using the key Action.PLUG_INS_KEY. Initialize each PlugIn object with the properties available in the corresponding PlugInConfig object. For each PlugIn object created, call its init() method.

  7. Freeze the configuration from further modification. This logic prevents changes to the configuration objects once the servlet begins accepting client requests.

In the process() method, the ActionServlet will create a RequestProcessor if it has not been created already, and delegate the request processing to the RequestProcessor by calling the process() method of the RequestProcessor. In the following section, we will continue the discussion on the RequestProcessor.process() method.

The Dispatcher Object

The RequestProcessor functions as a dispatcher and handles client requests by instantiating (or reusing) a request handler, and a corresponding form bean. The errors created, or exceptions thrown by the form beans and the request handlers, (and processed by the RequestProcessor) which influences the view management function of the RequestProcessor. Form beans assist RequestProcessor in storing the form data and/or staging intermediate model data required by the view. The RequestProcessor uses the <action> declarations, as shown next, for instantiating request specific request handlers.

 <form-bean name="PortalAllianceRegistrationForm"        type="packageName.PortalAllianceRegistrationForm"/> <action-mappings>     <action path="/PortalAllianceRegistration"             type="com.gc.prez.admin.PortalAllianceRegistrationAction"             name="PortalAllianceRegistrationForm"             scope="session" input="ShowPage"             validate="false">        <forward name="ShowPage" path="/2_1_PortalAllianceRegistration.jsp"/>        <forward name="EnterPortalID" path="/2_3A_EnterPortalID.jsp"/>        <forward name="success" path="/2_SiteAdministratorServicesMainPage.jsp"/>     </action> </action-mappings> 

The path specified in the request URI is used for locating the corresponding <action> element (which is the corresponding ActionMapping object) whose type property specifies the class for instantiating request handler objects.

Dispatcher Object Semantics

The following is a listing of process method–related key dispatcher operations in the order of execution sequence:

  1. From the servlet path, get the path information (after stripping the .do extension). This path information will be used to find the matching ActionMapping object. (A client request encapsulates the desired action in the request URI as servlet path.)

  2. If ControllerConfig specifies locale="true", get the locale from the request and store it in the user's session using Action.LOCALE_KEY. If the locale is already existing, no action is taken.

  3. If ControllerConfig provides a content type, set the content type for responses.

  4. If ControllerConfig specifies nocache="true"", set no-cache HTTP headers on each response.

  5. Call the processPreprocess method. This method is provided for doing any custom processing prior to form processing. A return value of true indicates success, otherwise the process method is terminated with a return.

  6. Get the ActionMapping object from ModuleConfig for the given path; if a match is not found, an ActionMapping object associated with the property unknown="true" is used. The resulting ActionMapping is saved in the request scope using Action.MAPPING_KEY. If no mapping is found, the response.sendError method is called for sending an error message to the client, and the process method is terminated with a return.

  7. Perform Java Authentication and Authorization Service (JAAS)–based authentication using the request.isUserInRole method for verifying privilege to perform the current action; the roles are specified as a comma-delimited string in the roles property of the ActionMapping. If no roles are provided in the action mapping object, or the user is in the appropriate role, then the processing will continue, otherwise the response.sendError is called for sending an error message to the client, and the process method is terminated with a return.

  8. Try finding the ActionForm associated with ActionMapping in the specified scope. If found, use this ActionForm, else, create a new ActionForm object or DynaActionForm object using the type property of the corresponding ActionFormBean; save the form in the specified scope using the name property from the ActionFormBean. Call the reset() method of the ActionForm object or DynaActionForm object to initialize the form; populate the form object with the parameters in the request object.

  9. The form object's validation method is called if the validate property of the ActionMapping object is set to true. If the validation is successful, then we proceed to the next step. If the validation returns a non-null or non-empty ActionErrors object, an ActionForward object with the name property that is the same as the input property of ActionMapping object is chosen as the candidate ActionForward object. The ActionForward object provides the URL (path property of ActionForward) of the next view. This is usually the same view whose processing generated the ActionErrors. The ActionErrors object is saved in the request object using the key Action.ERROR_KEY.

  10. Check for presence of the forward property in the ActionMapping object. This property is mutually exclusive with the type and include property. If found, the URI specified by the forward property is used for forwarding the current request instead of using a request handler object to handle this request. After the forward is done, the process() method is terminated with a return.

  11. Check for presence of the include property in the ActionMapping object. This property is mutually exclusive with the type and forward properties. If found, the URI specified by the include property is used in RequestDispatcher.include() for processing the current request instead of using a request handler object to process this request. After the include is done, the process method is terminated with a return. If an include property was not specified then we precede to the next step.

  12. Find an instance of the request handler from the request handler cache using its fully qualified class name specified by the type property of the ActionMapping object. If an instance is found, use this instance for the next step, else create a new instance of the class specified by the type property and save it in the cache.

  13. Call the execute method of the request handler. The request handler will return an ActionForward object depending on the outcome of its processing.

  14. The path property in the ActionForward object is used for forwarding the current request to the next view.

The Request Handler

A subclass of an Action class is used as an adaptor between incoming requests and the model. A request is intercepted initially by the RequestProcessor, which in turn instantiates a corresponding request handler. This Action class–derived object, also called the request handler, is created specific to every request as explained in the preceding section. The request handler implements the Command pattern [Gof]. A client request encapsulates the desired action in the request URI as servlet path, the path information is subsequently extracted by the dispatcher (RequestProcessor) for creating an instance of the corresponding request handler. The command pattern decouples the UI from request handlers.

Note

User-specific state information must not be stored in request handlers because they are used for servicing requests from all users.

Request Handler Semantics

For distributed applications, an action class houses the control logic required for interacting with business logic in EJB components and will typically use a Business Delegate [Core] object for this purpose. Business delegate shields the request handlers from having to deal with the complexity of accessing distributed components. The business delegate design pattern promotes loose coupling between the request handlers and the server-side components since the logic for accessing server-side components is embedded in the business delegate. A request handler is written by a developer working in the presentation tier; a business delegate is usually written by a developer responsible for creating the business tier services. For smaller nondistributed applications, the action class may contain business logic. When distributed processing is not required, and business logic is embedded in request handlers, a Data Access Object [Core] can be used to abstract the underlying data access implementation; this provides a loose coupling between the request handlers and the data access layer, thus protecting the presentation tier from implementation changes in the integration tier. The base Action class of request handlers provides several convenience methods; please refer to the API documentation at http://jakarta.apache.org/struts/api/index.html.




Practical J2ee Application Architecture
Practical J2EE Application Architecture
ISBN: 0072227117
EAN: 2147483647
Year: 2003
Pages: 111
Authors: Nadir Gulzar

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net