Wednesday, September 8, 2010
Struts request lifecycle
Struts request lifecycle
In this section you will learn about the Struts controller classes – ActionServlet, RequestProcessor, ActionForm, Action,ActionMapping and ActionForward – all residing in org.apache.struts.action package and struts-config.xml – the Struts Configuration file. Instead of the traditional "Here is the class – go use it" approach, you will study the function of each component in the context of HTTP request handling lifecycle in Struts.
ActionServlet
The central component of the Struts Controller is the ActionServlet. It is a concrete class and extends the javax.servlet.HttpServlet. It performs two important things.
1. On startup, its reads the Struts Configuration file and loads it into memory in the init() method.
2. In the doGet() and doPost() methods, it intercepts HTTP request and handles it appropriately.
The name of the Struts Config file is not cast in stone. It is a convention followed since the early days of Struts to call this file as struts-config.xml and place it under the WEB-INF directory of the web application. In fact you can name the file anyway you like and place it anywhere in WEB-INF or its subdirectories. The name of the Struts Config file can be configured in web.xml. The web.xml entry for configuring the ActionServlet and Struts Config file is as follows.
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/config/myconfig.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
In the above snippet, the Struts Config file is present in the WEB-INF/config directory and is named myconfig.xml. The ActionServlet takes the Struts Config file name as an init-param. At startup, in the init() method, the ActionServlet reads the Struts Config file and creates appropriate Struts configuration objects (data structures) into memory. For now, assume that the Struts Config file is loaded into a set of objects in memory, much like a properties file loaded into a java.util.Properties class.
Like any other servlet, ActionServlet invokes the init() method when it receives the first HTTP request from the caller. Loading Struts Config file into configuration objects is a time consuming task. If the Struts configuration objects were to be created on the first call from the caller, it will adversely affect performance by delaying the response for the first user. The alternative is to specify load-on-startup in web.xml as shown above. By specifying load-onstartup to be 1, you are telling the servlet container to call the init() method immediately on startup of the servlet container.
The second task that the ActionServlet performs is to intercept HTTP requests based on the URL pattern and handles them appropriately. The URL pattern can be either path or suffix. This is specified using the servlet-mapping in web.xml. An example of suffix mapping is as follows.
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
If the user types http://localhost:8080/xyz/submitCustomerForm.do in the browser URL bar, the URL will be intercepted and processed by the ActionServlet since the URL has a pattern *.do, with a suffix of "do". Once the ActionServlet intercepts the HTTP request, it doesn't do much. It delegates the request handling to another class called RequestProcessor by invoking its process()method. Most of the Struts Controller functionality is embedded in the process() method of RequestProcessor class. Mastery over this flowchart will determine how fast you will debug problems in real life Struts applications. Let us understand the request handling in the process() method step by step with an example covered in the next several sub sections.
RequestProcessor and ActionMapping
The RequestProcessor does the following in its process() method:
Step 1: The RequestProcessor first retrieves appropriate XML block for the URL from struts-config.xml. This XML block is referred to as ActionMapping in Struts terminology. In fact there is a class called ActionMapping in org.apache.struts.action package. ActionMapping is the class that does what its name says – it holds the mapping between a URL and Action. A sample ActionMapping from the Struts configuration file looks as follows.
A sample ActionMapping from struts-config.xml
<action path="/submitDetailForm"
type="mybank.example.CustomerAction"
name="CustomerForm"
scope="request"
validate="true"
input="CustomerDetailForm.jsp">
<forward name="success"
path="ThankYou.jsp"
redirect="true"/>
<forward name="failure" path="Failure.jsp" />
</action>
Step 2: The RequestProcessor looks up the configuration file for the URL pattern /submitDetailForm. (i.e. URL path without the suffix do) and finds the XML block (ActionMapping) shown above. The type attribute tells Struts which Action class has to be instantiated. The XML block also contains several other attributes. Together these constitute the JavaBeans properties of the ActionMapping instance for the path /submitDetailForm. The above ActionMapping tells Struts to map the URL request with the path /submitDetailForm to the class mybank.example.CustomerAction. The Action class is explained in the steps ahead. For now think of the Action as your own class containing the business logic and invoked by Struts. This also tells us one more important thing. Since each HTTP request is distinguished from the other only by the path, there should be one and only one ActionMapping for every path attribute. Otherwise Struts overwrites the former ActionMapping with the latter.
ActionForm
Another attribute in the ActionMapping that you should know right away is name. It is the logical name of the ActionForm to be populated by the RequestProcessor. After selecting the ActionMapping, the RequestProcessor instantiates the ActionForm. However it has to know the fully qualified class name of the ActionForm to do so. This is where the name attribute of ActionMapping comes in handy. The name attribute is the logical name of the ActionForm. Somewhere else in struts-config.xml, you will find a declaration like this:
<form-bean name="CustomerForm"
type="mybank.example.CustomerForm"/>
This form-bean declaration associates a logical name CustomerForm with the actual class mybank.example.CustomerForm.
Step 3: The RequestProcessor instantiates the CustomerForm and puts it in appropriate scope – either session or request. The RequestProcessor determines the appropriate scope by looking at the scope attribute in the same ActionMapping.
Step 4: Next, RequestProcessor iterates through the HTTP request parameters and populates the CustomerForm properties of the same name as the HTTP request parameters using Java Introspection. (Java Introspection is a special form of Reflection using the JavaBeans properties. Instead of directly using the reflection to set the field values, it uses the setter method to set the field value and getter method to retrieve the field value.)
Step 5: Next, the RequestProcessor checks for the validate attribute in the ActionMapping. If the validate is set to true, the RequestProcessor invokes the validate() method on the CustomerForm instance. This is the method where you can put all the html form data validations. For now, let us pretend that there were no errors in the validate() method and continue. We will come back later and revisit the scenario when there are errors in the validate() method.
Action
Step 6: The RequestProcessor instantiates the Action class specified in the ActionMapping (CustomerAction) and invokes the execute() method on the CustomerAction instance. The signature of the execute method is as follows.
public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response) throws Exception
Apart from the HttpServletRequest and HttpServletResponse, the ActionForm is also available in the Action instance. This is what the ActionForm was meant for; as a convenient container to hold and transfer data from the http request parameter to other components of the controller, instead of having to look for them every time in the http request.
The execute() method itself should not contain the core business logic irrespective of whether or not you use EJBs or any fancy middle tier. The first and foremost reason for this is that business logic classes should not have any dependencies on the Servlet packages. By putting the business logic in the Action class, you are letting the javax.servlet.* classes proliferate into your business logic. This limits the reuse of the business logic, say for a pure Java client. The second reason is that if you ever decide to replace the Struts framework with some other presentation framework (although we know this will not happen), you don't have to go through the pain of modifying the business logic. The execute() method should preferably contain only the presentation logic and be the starting point in the web tier to invoke the business logic. The business logic can be present either in protocol independent Java classes or the Session EJBs.
The RequestProcessor creates an instance of the Action (CustomerAction), if one does not exist already. There is only one instance of Action class in the application. Because of this you must ensure that the Action class and its attributes if any are thread-safe. General rules that apply to Servlets hold good. The Action class should not have any writable attributes that can be changed by the users in the execute() method.
Subscribe to:
Post Comments (Atom)
Thanks Boss, It's very simple and understandable
ReplyDeleteThank u very much .It's short & sweet
ReplyDeletebest explanation :) simple & easy to understand
ReplyDeleteBest Example and easy Explanation. Please cover the following things also.
ReplyDelete1) What happens when the error occurs during validate() method call?
2) How do struts invoke the Message Resource Properties?
Superb!
ReplyDeleteExcellent !
ReplyDelete