Chapter 5: Advanced Action Classes

 < Day Day Up > 



In this chapter, we dig further into the Controller components of the Struts framework by covering the built-in Action classes that come with Struts. Our goal is to provide you with a solid understanding of the Struts built-in actions and how they can be used to facilitate the design of your Struts applications.

The Struts Framework provides several built-in actions. A few of these are essential for any Struts application. Others are necessary to add cohesion to what would normally be a granular collection of related actions.

ForwardAction and Beyond

Generally speaking, it is a bad idea to place within your JSP pages any direct links to other JSP pages. For one reason, it is not a good design decision; the struts-config.xml file, which is part of the Controller, should contain the entire flow of your application.

In the Model-View-Controller (MVC) architecture, it is the role of the Controller to select the next View. The Controller consists of Struts configuration, the ActionServlet, and the actions. If you add a link directly to another JSP, you are violating the architectural boundaries of the Model 2 architecture. (Model 2 is the instantiation of the MVC architecture for Web applications.)

At times, however, all you really need is just a plain link; you don't want (or need) an action to execute first. Perhaps there are no objects from the domain that need to be mapped into scope in order for the View to display. Perhaps the page is very simple. In this case, a better approach is to use the ForwardAction.

The ForwardAction acts as a bridge from the current View (JSP) and the pages it links to. It uses the RequestDispatcher to forward to a specified Web resource. It is the glue that allows you to link to an action instead of directly to a JSP.

Later, if you need to, you can change the ActionMapping in the Struts configuration file so that every page that linked to that action will link to the new action. Also, you can change the action to a custom one that you write instead of using the ForwardAction that Struts provides.

To use the ForwardAction, follow these steps:

  1. Using the html:link tag with the action attribute, add a link to the JSP page that points to the action.

  2. Create an action mapping in the Struts configuration file that uses the ForwardAction with the parameter attribute to specify the JSP path.

Let's say you have a JSP page that has a direct link to another JSP page:

 <html:link page="/index.jsp">Home</html:link> 

You have recently converted to the MVC/Model 2 architecture religion and you want to change the html:link tag to link to an action. Because you already have a link, simply change it as follows:

 <html:link action="home">Home</html:link> 

All you do is remove the page attribute and add an action attribute that points to the home action. Now you have to create the home action mapping. (The link in the previous code snippet would be expanded to a URL, like http://localhost:8080/actions/home.do.)

To add an action mapping to the home action that you referenced in your html:link tag, use this code:

            <action                path="/home"                type="org.apache.struts.actions.ForwardAction"                parameter="/index.jsp"                /> 

The ForwardAction uses the parameter attribute so that you can specify the path. Notice the parameter is set to /index.jsp, which was what the page attribute of the html:link tag was originally set to. Thus, the parameter attribute indicates to where you want the ForwardAction to forward.

Linking to JSP Directly: More than Just Bad Design

In addition to being a bad design decision, linking directly to JSP pages may result in errors. The Controller maps in the correct ModuleConfig based on the request. Having the correct module implies that the resource bundles for that module are loaded. If the JSP page uses the resource bundles to display messages (which is required for internationalized applications), then you need to link to the action directly because that is the only way the JSP page is guaranteed to work properly.

Linking to Global Forwards Instead of Actions

From a purely design perspective, there are other alternatives to using the ForwardAction. Rather than linking to an action or a page, you could link to a global forward:

         <html:link forward="home">Home</html:link> 

In order for this to work, you would need to add a home forward to the Struts configuration file as follows:

       <global-forwards >           <forward name="home" path="/index.jsp"/>       </global-forwards> 

I find this approach more aesthetically pleasing (barring any other possible ramifications, as we mentioned earlier). Thus, the main problem with linking forwards is not the design per se but that the functionality is the same as when you link directly to a JSP page. The previous html:link tag would result in the generation of a URL like this:

  • http://localhost:8080/actions/index.jsp

which is identical to the version of the html:link tag that used the page attribute.

You just change the global forward to point to an action mapping as follows:

           <forward name="home" path="/home.do"/> 

This seems to be the best possible practice. It is much more natural to link to forwards than to actions. In addition, this approach gives you the functionality of linking to an action with the intuitiveness of linking to a forward. And just as before, the actions themselves can be simple ForwardAction actions, and later you can change them to full-blown custom actions that talk to the Model. With this approach, you have the best of both worlds.

Using the forward Attribute vs. ForwardAction

For better or for worse, ForwardActions get used quite a bit—so much so that the Struts configuration file includes support for them. Thus, rather than doing this:

            <action                path="/home"                type="org.apache.struts.actions.ForwardAction"                parameter="/index.jsp"                /> 

you can do this:

            <action                path="/home"                forward="/index.jsp"                /> 

These two mappings are functionally equivalent. It is an easy decision to pick the one you should use (the shorter one, of course). The nice thing about this approach is that you have to specify only two attributes, and you don't have to type in the fully qualified ForwardAction path. The forward attribute specifies the Web resource (in this case a JSP) that will be acting as the action.

Thus, the best approach for our example is to add this in the JSP:

 <html:link action="home">Home</html:link> 

Then add this in the global forwards section of the Struts configuration file:

 <forward name="home" path="/home.do"/> 

And this in the action mappings section of the Struts configuration file:

 <action path="/home" forward="/index.jsp" /> 

Concise, straightforward, and well designed. Exactly what we needed!

Don't Bleed Your V into Your C

If you find that you have a lot of links to JSP pages from other JSP pages, you may not understand MVC very well. Using ActionForward may mask the fact that your design is inherently messed up. JSPs linking to other JSPs, whether they use ForwardAction or not, works only for the simplest of Web applications in MVC. If it works for your application, you are probably doing something wrong.

Complex MVC Web applications typically need an action to execute first; it is the job of the action to map Model items into scope so that the View (typically JSP) can display them. If this is not the case for your nontrivial Web application, you are probably putting too much logic in the JSP pages (or in custom tags that JSP pages use)—which can be the source of huge maintenance issues.

The JSP pages and custom tags should contain only display logic, and this logic should be kept to a bare minimum. Thus, the JSP pages and custom tags should not talk directly to the Model—they only display different pieces of the Model. In essence, they should speak only to the Value object and Data Transfer objects. Again, the action talks directly to the Model and delegates the display of the Model objects to the View. The Model in turn implements the persistence and business rules for the application.

Adopting a strict MVC architecture is generally a good idea; it keeps your JSP smaller and more focused. JSP are harder to test than actions, so adopting MVC increases the liquidity and flexibility of your code base.

In short, if you overuse the ForwardAction, you need to evaluate your understanding of the MVC architecture.

Forwards for Legacy Access

A lot of folks wrote Web applications before Struts existed. A lot of other folks have had to integrate with commercial portal implementations and other frameworks that are built on top of servlets (legacy and otherwise). Using the ForwardAction (or the forward attribute) is a good way to encapsulate this legacy integration from your View components (JSP and custom tags). In this manner, the ForwardAction acts as "legacy glue" to other Web resources.

Let's say that you have a legacy Web resource that you want to use with the form validation of Struts. However, your legacy resources are part of an elaborate MVC-based Web application framework that you created before the dawn of the Struts Framework.

This legacy resource does all types of neat things that you have not ported to the Struts Framework yet. For some reason (time and money come to mind), you do not want to rewrite this legacy resource—at least not yet.

For the purposes of simplicity, our sample legacy Web resource will be a servlet. Essentially, you want the servlet's doGet method to be called only if the ActionForm validates. Here is our example servlet:

 public class LegacyServlet extends HttpServlet {      protected void doPost(           HttpServletRequest request,           HttpServletResponse response)           throws ServletException, IOException {                //Get the mapping           ActionMapping mapping =(ActionMapping)                       request.getAttribute(Globals.MAPPING_KEY);             //Get the UserForm           UserForm form = (UserForm)                       request.getAttribute(mapping.getName());               // Do some fly, super tricky, whiz                // bang legacy stuff                //Generate some output           response.getWriter().println("User name "                                + form.getFirstName());      } } 

Notice how the servlet can access the context that the Struts Framework mapped into request scope. Now suppose this servlet was mapped into our Web application like so:

      <servlet>          <servlet-name>legacy</servlet-name>          <servlet-class>legacy.LegacyServlet</servlet-class>      </servlet>      <servlet-mapping>          <servlet-name>legacy</servlet-name>          <url-pattern>/legacy/roar</url-pattern>      </servlet-mapping> 

Thus, posts to /legacy/roar would cause this servlet's doPost method to be called. Now, to map this servlet as an action that acts as a form handler, you need to do this:

           <action               path="/legacy"               forward="/legacy/roar"               input="/form/userForm.jsp"               name="userForm"               parameter="/legacy/roar"               validate="true"               scope="request"               /> 

Of course, the previous code assumes that you have a form bean:

       <form-beans>           <form-bean name="userForm" type="form.UserForm" />       </form-beans> 

Your input JSP (/form/userForm.jsp) would look like this:

 ...      <h1>Legacy: Struts Form for userForm s</h1>      <html:form action="/legacy">           email :                <html:text property="email"/></br>           first name :                <html:text property="firstName"/></br>           last name :                <html:text property="lastName"/></br>           password :                <html:password property="password"/></br>           password check :                 <html:password                           property="passwordCheck"/></br>           userName :                 <html:text property="userName"/></br>                <html:submit/><html:cancel/>      </html:form> 

Notice that the html:form tag points to an action path, just as you expect. Because the RequestDispatcher forward method to the servlet is invoked only if the ForwardAction's execute method is invoked, the doPost method of the legacy servlet is called only if the ForwardsAction's execute method is called. We set the validate attribute to true in the action mapping for this servlet, so the execute method of the ForwardAction is called only if the ActionForm (UserForm) validates (returns no ActionError objects).



 < Day Day Up > 



Professional Jakarta Struts
Professional Jakarta Struts (Programmer to Programmer)
ISBN: 0764544373
EAN: 2147483647
Year: 2003
Pages: 183

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