19.4. The Struts ControllerIn a moment we will discuss the Struts approach to the MVC controller and then provide detail on the building-block classes, org.apache.struts.action.Action and org.apache.struts.action.ActionForm. But first we should take a step back and look at the nail for which Struts is attempting to be the hammer. In many web applications, the basic HTTP interaction is not especially complicated. An HTTP request includes at least the name of a resource and zero or more parameters. Based on the name of the resource and the parameters, control must be handed off to specialized code that performs an action. The action determines what response is appropriate; this HTTP response is then encoded and returned to the requester, typically with HTML as the message body. So far, so good. But in ad hoc web programming, it is all too easy to handle each of these tasks in an idiosyncratic way. For example, a developer might write a servlet to handle a request and might create his own methods and conventions for validating parameters (ensuring that certain parameters are syntactically valid phone numbers, perhaps). Meanwhile, another coder, working primarily on GET requests, writes her own method for validating parameters. Clearly, there are organizational and maintenance issues here with the existence of two modules doing similar tasks, maintained by two different people. But the larger issue is that there are well-known patterns for handling such minutiaeand if handled once, and well, those patterns become amenable to all of the good features we associate with object-oriented programming: encapsulation, separation of responsibilities, inheritance where appropriate, and the rest. In fact, validation is small potatoes compared to the main course of organizing the management of actions in a systematic way. When it comes to the more elaborate requirement of making it easy to rearrange the relationships between requests, actions, and responses, we need a framework such as Struts to provide an object representing each piece in request/response processing. Struts provides an object encapsulating each of the features in our earlier summary of request processing. It's worth noting at this point a significant difference between Struts and JSF. In Struts, the developer is exposed more directly to the underlying request processing, whereas in JSF, much more of the detail of request processing is handled by the framework. This distinction also affects our discussion of these two frameworks. For Struts, you will find that we frequently talk about classes and methods, whereas in JSF, we talk about XML declarations that define the "wiring" of the relationships between beans. Let's look at request processing again, but from the point of view of Struts. The starting point is the connection between the servlet container and Struts. Struts typically monopolizes all requests in your web application with its own implementation of javax.servlet.Servlet (org.apache.struts.action.ActionServlet). Control is passed to this Servlet by the container. It is this ActionServlet that chooses which class should handle any particular request. The choice is based on settings in an XML file so that you can change the control flow without recompiling. An Action class handles the request and extends org.apache.struts.action.Action. This Action class has an execute method that you will almost always want to override. It is passed the request and response instances that the ActionServlet receives as well as references to the form that contains the parameter data and a "mapping" that defines the control flow. An ActionForm represents the parameter data. Once again, you extend a Struts class: org.apache.struts.action.ActionForm. Now for some more detail on each of these fundamental classes. 19.4.1. The ActionServletThe fundamental feature of Struts is a servlet, org.apache.struts.action.ActionServlet, which delegates incoming HTTP GET and POST requests to a request processor (org.apache.struts.action.RequestProcessor) and from there to special-purpose handlersinstances of the action classes. Specify the Struts ActionServlet and a mapping for it in the web.xml file for the application: <servlet> <servlet-name>Struts Action Servlet</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Struts Action Servlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> The servlet-name value can be anything you like, although it should be descriptive. You should also specify a parameter named config whose value defines the location of a Struts-specific XML configuration file. The default value for the location of this configuration file is /WEB-INF/struts-config.xml (for more details, see "The Struts Configuration File" later in this chapter). It is conventional to specify this file even when you are using the default location so that it is obvious in your web.xml where the Struts-specific configuration lives. Struts must perform its configuration work before any requests are served, so the <load-on-startup> element is essential to ensure that Struts initializes first (note: if you have a singleton class that should be initialized when Struts starts, see "Struts Plug-ins" later in this chapter). All requests to URL patterns matching *.do will be handled by the Struts ActionServlet. Mapping Struts actions to the .do extension is conventional. Some Struts developers map a path to the Struts ActionServlet, which can be handy if you want to provide J2EE security for the path that is mapped to Struts actions (see Chapter 10) or put a servlet filter in front of your Struts actions (see Chapter 3). Note that if you're using Struts modules, you must use extension mapping. When ActionServlet is initialized by the servlet container, it performs a number of tasks. It initializes Struts "modules," it sets up Struts-managed DataSources, it initializes Struts plug-ins, and it reads in and processes the Struts configuration file. Once initialized, the ActionServlet will satisfy requests that are passed to it by the container. When doGet( ) or doPost( ) is called, ActionServlet immediately calls a protected method that then delegates to an instance of a RequestProcessor . 19.4.2. The RequestProcessorIf you've downloaded the source to Struts, open the source for org.apache.struts.action.RequestProcessor and follow along. Understanding how this class uses resources is the key to becoming an adept Struts developer. By the way, here as elsewhere with Struts, the source reveals a number of tidbits that can be quite helpful; as a tiny but useful example, you will see where Struts provides debug messages to the console. When an HTTP GET or POST request is passed to the process method of RequestProcessor, the following tasks are performed in this order:
The key steps in this process are the ones that select the ActionMapping based on the path, populate and validate the ActionForm, acquire the Action, process the Action, and then return the ActionForward. |