Sunday, July 18, 2010

How a Request is Processed in Struts

ActionServlet is the servlet class in Struts which is configured in web.xml and read by the web application server. This is the entry point into struts framework and is responsible for handling all of the requests. Whenever it receives a request, it first tries to find a sub-application for the current request. Once a sub-application is found, it creates a RequestProcessor object for that sub-application and calls its process() method by passing it HttpServletRequest and HttpServletResponse objects.

The RequestProcessor.process() method is where most of the request processing takes place. The process() method is implemented using the Template Method design pattern, in which there is a separate method for performing each step of request processing, and all of those methods are called in sequence from the process() method. The RequestProcessor class in the Struts distribution provides a default implementation for each of the request-processing steps. That means you can override only the methods that interest you, and use default implementations for rest of the methods. For example, by default Struts calls request.isUserInRole() to find out if the user has one of the roles required to execute the current ActionMapping, but if you want to query a database for this, then then all you have to do is override the processRoles() method and return true or false, based whether the user has the required role or not.

Lets have a look at the default implementation of process() method first.

public void process(HttpServletRequest request, HttpServletResponse response)  throws IOException, ServletException {
        // Wrap multipart requests with a special wrapper
        request = processMultipart(request);
        // Identify the path component we will
        // use to select a mapping
        String path = processPath(request, response);
        if (path == null) {
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("Processing a '" + request.getMethod() +
                      "' for path '" + path + "'");
        }
        // Select a Locale for the current user if requested
        processLocale(request, response);
        // Set the content type and no-caching headers
        // if requested
        processContent(request, response);
        processNoCache(request, response);
        // General purpose preprocessing hook
        if (!processPreprocess(request, response)) {
            return;
       }
        // Identify the mapping for this request
        ActionMapping mapping =
            processMapping(request, response, path);
        if (mapping == null) {
            return;
        }
        // Check for any role required to perform this action
        if (!processRoles(request, response, mapping)) {
            return;
        }
        // Process any ActionForm bean related to this request
        ActionForm form =
            processActionForm(request, response, mapping);
        processPopulate(request, response, form, mapping);
        if (!processValidate(request, response, form, mapping)) {
            return;
        }
        // Process a forward or include specified by this mapping
        if (!processForward(request, response, mapping)) {
            return;
        }
        if (!processInclude(request, response, mapping)) {
            return;
        }
        // Create or acquire the Action instance to
        // process this request
        Action action =
            processActionCreate(request, response, mapping);
        if (action == null) {
            return;
        }
        // Call the Action instance itself
        ActionForward forward =
            processActionPerform(request, response,
                                action, form, mapping);
        // Process the returned ActionForward instance
        processForwardConfig(request, response, forward);
}



1. processMultipart(): In this method, Struts will read the request to find out if its contentType is multipart/form-data. If so, it will parse it and wrap it in a wrapper implementing HttpServletRequest. When you are creating an HTML FORM for posting data, the contentType of the request is application/x-www-form-urlencoded by default. But if your form is using FILE-type input to allow the user to upload files, then you have to change the contentType of the form to multipart/form-data. But by doing that, you can no longer read form values submitted by user via the getParameter() method of HttpServletRequest; you have to read the request as an InputStream and parse it to get the values.

2. processPath(): In this method, Struts will read request URI to determine the path element that should be used for getting the ActionMapping element.

3. processLocale(): In this method, Struts will get the Locale for the current request and, if configured, it will save it in HttpSession as the value of the org.apache.struts.action.LOCALE attribute. HttpSession would be created as a side effect of this method. If you don't want that to happen, then you can set the locale property to false in ControllerConfig by adding these lines to your struts-config.xml file:

      <\controller>
          <\set-property  property="locale" value="false"/>
     

4. processContent(): Sets the contentType for the response by calling response.setContentType(). This method first tries to get the contentType as configured in struts-config.xml. It will use text/html by default. To override that, use the following:

      <\controller>
          <\set-property  property="contentType" value="text/plain"/>
     

5. processNoCache(): Struts will set the following three headers for every response, if configured for no-cache:


      requested in struts config.xml
      response.setHeader("Pragma", "No-cache");
      response.setHeader("Cache-Control", "no-cache");
      response.setDateHeader("Expires", 1);

      If you want to set the no-cache header, add these lines to struts-config.xml:

      <\controller>
          <\set-property  property="noCache" value="true"/>
     

6. processPreprocess(): This is a general purpose, pre-processing hook that can be overridden by subclasses. Its implementation in RequestProcessor does nothing and always returns true. Returning false from this method will abort request processing.

7. processMapping(): This will use path information to get an ActionMapping object. The ActionMapping object represents the element in your struts-config.xml file.

      <\action path="/newcontact" type="com.sample.NewContactAction"
              name="newContactForm" scope="request">
          <\forward name="sucess" path="/sucessPage.do"/>
          <\forward name="failure" path="/failurePage.do"/>
     


The ActionMapping element contains information like the name of the Action class and ActionForm used in processing this request. It also has information about ActionForwards configured for the current ActionMapping.

8. processRoles(): Struts web application security just provides an authorization scheme. What that means is once user is logged into the container, Struts' processRoles() method can check if he has one of the required roles for executing a given ActionMapping by calling request.isUserInRole().

          <\action path="/addUser" roles="administrator"/>

Say you have AddUserAction and you want only the administrator to be able to add a new user. What you can do is to add a role attribute with the value administrator in your AddUserAction action element. So before executing AddUserAction, it will always make sure that the user has the administrator role.

9. processActionForm(): Every ActionMapping has a ActionForm class associated with it. When Struts is processing an ActionMapping, it will find the name of the associated ActionForm class from the value of the name attribute in the element.

      <\form-bean name="newContactForm"
                 type="org.apache.struts.action.DynaActionForm">
              <\form-property name="firstName"
                                type="java.lang.String"/>
              <\form-property name="lastName"
                                type="java.lang.String"/>
     


In our example, it will first check to see if an object of the org.apache.struts.action.DynaActionForm class is present in request scope. If so, it will use it; otherwise, it will create a new object and set it in the request scope.

10. processPopulate(): In this method, Struts will populate the ActionForm class instance variables with values of matching request parameters.

11. processValidate(): Struts will call the validate() method of your ActionForm class. If you return ActionErrors from the validate() method, it will redirect the user to the page indicated by the input attribute of the <\action>element.

12. processForward() and processInclude(): In these functions, Struts will check the value of the forward or include attributes of the <\action> element and, if found, put the forward or include request in the configured page.

          <\action forward="/Login.jsp" path="/loginInput"/>
          <\action include="/Login.jsp" path="/loginInput"/>

You can guess difference in these functions from their names. processForward() ends up calling RequestDispatcher.forward(), and processInclude() calls RequestDispatcher.include(). If you configure both forward and include attributes, it will always call forward, as it is processed first.

13. processActionCreate(): This function gets the name of the Action class from the type attribute of the <\action> element and create and return instances of it. In our case it will create an instance of the com.sample.NewContactAction class.

14. processActionPerform(): This function calls the execute() method of your Action class, which is where you should write your business logic.

15. processForwardConfig(): The execute()method of your Action class will return an object of type ActionForward, indicating which page should be displayed to the user. So Struts will create RequestDispatcher for that page and call the RequestDispatcher.forward() method.

1 comment:

  1. Thank you so much... this post help me alot in understanding the flow... Please provide some more post to clear the fundamentals and crack interviews...

    ReplyDelete