logo
header art

Building an Enterprise-Ready Web Application with Struts

June 23, 2005

Actions

Since we were just talking about the Action that forwarded to ShowRecipe.jsp, let's discuss Actions in more detail.

In struts-config.xml, we defined two <action> elements: ShowRecipe and FindRecipe. ShowRecipe is an instance of org.apache.struts.actions.ForwardAction, so we don't have to write any code for it; it just redirects to the page specified by the parameter attribute, which is FindRecipe.jsp in this case. In contrast, ShowRecipe provides custom functionality, so we need to write our own class extending org.apache.struts.action.Action instead of using a pre-defined Action like FindRecipe did. We call that class com.skillfusion.examples.struts.actions.ShowRecipeAction.

001 /*
002  * ShowRecipeAction.java created on Apr 27, 2005 at 10:02:21 AM
003  *
004  * strutsExample is a simple web application that allows a user to search and
005  * view a set of recipes.
006  * Copyright (C) 2005  SkillFusion Software
007  *
008  * This program is free software; you can redistribute it and/or modify
009  * it under the terms of the GNU General Public License as published by
010  * the Free Software Foundation; either version 2 of the License, or
011  * (at your option) any later version.
012  *
013  * This program is distributed in the hope that it will be useful,
014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016  * GNU General Public License for more details.
017  *
018  * You should have received a copy of the GNU General Public License
019  * along with this program; if not, write to the Free Software
020  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
021  */
022 package com.skillfusion.examples.struts.actions;
023 
024 import javax.servlet.http.HttpServletRequest;
025 import javax.servlet.http.HttpServletResponse;
026 
027 import org.apache.struts.action.Action;
028 import org.apache.struts.action.ActionForm;
029 import org.apache.struts.action.ActionForward;
030 import org.apache.struts.action.ActionMapping;
031 import org.apache.struts.action.ActionMessage;
032 import org.apache.struts.action.ActionMessages;
033 
034 import com.skillfusion.examples.struts.bizobjs.Recipe;
035 import com.skillfusion.examples.struts.forms.ShowRecipeForm;
036 import com.skillfusion.examples.struts.serviceobjimpls.RecipeFactoryImpl;
037 
038 /**
039  * Adapter to parse out the name from the request, request a <code>Recipe</code>
040  * of that name from the service layer, and forward the results to a
041  <tt>JSP</tt>.
042  *
043  @author hank
044  */
045 public class ShowRecipeAction extends Action {
046 
047     /**
048      * Pulls the name of a <code>Recipe</code> out of the request, gets it
049      * using the appropriate service object, and forwards to the show recipe JSP
050      * page. If no matching <code>Recipe</code> could be found, it adds an
051      * error message to the response.
052      *
053      @param mapping
054      *            The <code>ActionMapping</code> used to select this instance
055      @param form
056      *            The optional <code>ActionForm</code> bean for this request
057      *            (if any)
058      @param request
059      *            The HTTP request we are processing
060      @param response
061      *            The HTTP response we are creating
062      @return <code>ActionForward</code> instance describing where and how
063      *         control should be forwarded, or <tt>null</tt> if the response
064      *         has already been completed
065      @throws Exception
066      *             if the application business logic throws an exception
067      @see org.apache.struts.action.Action#perform(
068      *      org.apache.struts.action.ActionMapping,
069      *      org.apache.struts.action.ActionForm,
070      *      javax.servlet.http.HttpServletRequest,
071      *      javax.servlet.http.HttpServletResponse)
072      */
073     public final ActionForward execute(final ActionMapping mapping,
074             final ActionForm form, final HttpServletRequest request,
075             final HttpServletResponse responsethrows Exception {
076 
077         // Cast the form bean to its actual type
078         ShowRecipeForm f = (ShowRecipeFormform;
079 
080         // Pull the name off the form
081         String name = f.getName();
082 
083         // Try to pull a recipe matching this name
084         RecipeFactory recipeFactory = RecipeFactoryImpl.getInstance();
085         Recipe recipe = recipeFactory.getRecipeByName(name);
086         if (null == recipe) {
087             // Add an error message - use ActionMessage and its ilk as it is now
088             // favored over ActionError
089             ActionMessages messages = new ActionMessages();
090             messages.add("name"new ActionMessage(
091                     "error.nonexistent.name"));
092             saveMessages(request, messages);
093             // Forward control to the specified failure target
094             return (mapping.findForward("failure"));
095         }
096         
097         // Add the Recipe to the session
098         request.setAttribute("recipe", recipe);
099 
100         // Forward control to the specified success target
101         return (mapping.findForward("success"));
102     }
103 }

org.apache.struts.actions.Action and the classes that extend it are loosely built on the Adapter design pattern. For more information on Adapter, check out the definitive book on design patterns, Design Patterns: Elements of Reusable Object-Oriented Software. Generally speaking, objects built on the Adapter pattern convert the interface of a class into a different interface expected by another class. If that sounds complicated, don't worry; it's a lot easier than it sounds. In the specific case of Struts Actions, they take an incoming request and transform the information it contains into the sort of information expected by the beans containing the logic the user wants to execute.

ShowRecipeAction is fairly simple. On line 78, it casts the generic ActionForm it receives down to the more specific ShowRecipeForm, then pulls the value of the name property off it on line 81. On line 86, it uses that value to request a Recipe object from the RecipeFactory. If the RecipeFactory can't find the requested Recipe, the Action adds an ActionMessage to a collection of such messages named error on the HttpServletRequest and forward to the failure target we defined in struts-config.xml. If the Action does find the requested Recipe, it sets it as an attribute on the HttpServletRequest and then forwards to the success target we defined in struts-config.xml.