Leverage Spring Web development with Offspring

A simple Java-based Spring extension allows easy Web application development

Offspring, a Spring-based Web framework extension, accelerates and simplifies the design and implementation of Web applications. Re-using aspect-oriented modules separates the responsibilities of the software components and hides the Spring specifics from your development process. This article demonstrates the extreme flexibility that results from combining Offspring's generic framework with specialized modules that handle program functions.

The Spring Framework is such a brilliant phenomenon of modern Java development that praising it has become common place. However, to start working on Spring projects, one has to invest significant time to obtain the necessary expertise. This article briefly introduces Spring essentials and describes Offspring, a fully functional solution that can help you build Web applications with a minimum learning curve and without compromising Spring ideology.

Spring bare essentials

To follow the course of this article, we must brush up on the two major notions employed by Spring—the Inversion of Control (IoC) paradigm and aspect-oriented programming (AOP). Also of significance is the Spring Web layer. By no means, do the following sections represent a substitute for serious reading on the Spring Framework; but they will get us started building our framework.


Let's assume we are assigned to design the class CoffeeShop, which obviously serves the instance of the CaffeinatedDrink to the customer:

 public class CoffeeShop  implements Shop{
   CaffeinatedDrink _Drink;
   public void setDrink(CaffeinatedDrink drink){
   void serveDrink(){
            " served. The drink temperature is "+_Drink.getTemperature()+" degrees F.");
   public static void main(String[] args) {
      CoffeeShop shop=new CoffeeShop();
      CaffeinatedDrink drink=Coffeemaker.getInstance().makeCoffee();

Let's also assume another team designed the Coffeemaker singleton (shop has only one coffeemaker), the CaffeinatedDrink interface, and the Coffee concrete class. We should be proud of ourselves and our object-oriented skills: we managed to decouple CoffeeShop and CaffeinatedDrink by hiding implementation details behind the interface, and we followed the Big Rule—program to interfaces, not implementations.

It's too soon to gloat! As a test case, we run the CoffeShop.main() method and receive the message Coffee served. The drink temperature is 160 degrees F. The coffee is too cold to justify the price. A war of words and corporate emails erupt. We argue that either the Coffeemaker is broken or the Coffee class implementation is faulty. Other teams insist nothing is wrong with their implementation; CoffeeShop is too slow to serve the hot drinks, and so on and so forth.

The core of the problem is that we did not test the CoffeShop class's pure functionality. Instead, we tested the functional relationship between CoffeeShop and the concrete class Coffee. Let's test our perfect CoffeeShop by serving boiled water instead of coffee. As a result, we eliminate the complexity of the Coffee instance from the equation:

 public class BoiledWater implements CaffeinatedDrink {
   int _Temperature=210; //  Drink temperature 
   String _Name="Boiled water"; // Drink name
   public String getName() {
      return _Name;
   public int getTemperature() {
      return _Temperature;

Now the coffeemaker should brew the water:

 public class Coffeemaker {
   static Coffeemaker _Coffemaker=null;
   public synchronized static Coffeemaker getInstance() {
         _Coffemaker=new Coffeemaker();
      return _Coffemaker;
   private Coffeemaker() {
   public CaffeinatedDrink makeCoffee(){
      //Return new Coffee(); 
      return new BoiledWater(); //Just water, please

We run again CoffeShop.main() and receive the message Boiled water served. The drink temperature is 210 degrees F. We served boiled water, as expected, proving that our store is fine, but at a substantial price of modifying and recompiling Coffeemaker twice; we now must change it back to brew the coffee instead of water. This price is substantiated by the fact that even if CoffeeShop does not deal with the drink's specific implementation, somebody still must create the concrete instance of Coffee. Whether that instance is created by a singleton, factory, or class loader, this dirty job is inevitable.

IoC in Spring wonderfully handles the whole situation. When we deploy Spring in our project, the slightly modified CoffeShop.main() method looks like this:


public static void main(String[] args) { /* Instantiate the Spring context */

ApplicationContext context = new ClassPathXmlApplicationContext("coffeeshop.xml"); CoffeeShop shop=new CoffeeShop(); /* Spring container has instantiated the drink */ CaffeinatedDrink drink = (CaffeinatedDrink)context.getBean("drink"); shop.setDrink(drink); shop.serveDrink(); }

We have just instantiated the Spring context from the application-context file coffeeshop.xml, asked the Spring context to instantiate the drink object, called the shop's setter method, and served the drink. File coffeeshop.xml contains the definition of the bean drink:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
   <bean id="drink" class="com.offspring.examples.coffeeshop.Coffee" singleton="false" >

Now to switch from coffee to boiled water, we just have to change the bean definition in the XML file: <bean id="drink" class="com.offspring.examples.coffeeshop.BoiledWater"></bean> and nothing else. We can even do better. By modifying coffeeshop.xml's content, we can instruct the Spring container to instantiate not only the drink object, but also CoffeeShop itself, and even call the setDrink() method with an appropriate drink argument:

 <bean id="shop" class="com.offspring.examples.coffeeshop.CoffeeShop">
   <property name="drink"><ref bean="drink"/></property>

As a result, CoffeeShop.main() is simplified significantly:


public static void main(String[] args) { /* Instantiate Spring context */ ApplicationContext context =

new ClassPathXmlApplicationContext("coffeeshop.xml"); /* Spring container instantiated shop object and called setDrink method */ CoffeeShop shop=(CoffeeShop)context.getBean("shop"); shop.serveDrink(); }

A fundamental event has just quietly occurred: Inversion of Control, or Dependency Injection. CoffeeShop did not look up the dependent drink object, but the Spring container has provided (actually, injected) the dependent drink object into the CoffeeShop in reverse.


So the problem is fixed: the coffee is hot and the price is justified. Let the client pay. Traditionally, to do that, we must make a call to the billing system. Something like CashRegister.getInstance().chargeItem(drink.getName()) from the method serveDrink(). But this call is bad for many reasons:

  • Our team features the finest experts on how to serve coffee, but nobody is familiar with the billing issues
  • Code tuning and debugging become troubled again, since we introduce new functional dependency (see the boiled water example)
  • If our company decided to replace the billing subsystem, we would have to modify and recompile every billable event or build some adapters from the old APIs to the new ones

In terms of AOP, we have introduced a cross-cutting concern. Cross-cutting concerns (also called orthogonal services) are billing, notifications, logging—everything that is not a part of our core process or logic (serving coffee) and is cut across our system's multiple components.

Let's build a Spring AOP solution to overcome our shop's little problems. We do not modify the CoffeeShop class! Instead, Spring builds us its dynamic proxy. The whole application no longer deals with our CoffeeShop directly; it deals with its proxy. The CoffeeShop class has become a target class. All public methods of the target class's interface are dynamically available on the proxy with identical signatures. All calls to the proxy are directed transparently to the target class, unless we want to intercept them and apply our cross-cutting functionality (or, in AOP terms, advice).

Thus, the setDrink() invocation goes to the proxy and from the proxy, transparently to the target class, CoffeeShop. However, we intercept the execution flow after the proxy calls the target class's serveDrink() method (we can do it before, after, or even instead). There, by applying billing advice, we can get our precious per cup. We have accomplished our sinister plan mostly by modifying the coffeeshop.xml file:


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>

<bean id="drink" class="com.offspring.examples.coffeeshop.Coffee" singleton="false"> </bean> <bean id="shopTarget" class="com.offspring.examples.coffeeshop.CoffeeShop"> <property name="drink"><ref bean="drink"/></property> </bean>

<bean id="shop" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.offspring.examples.coffeeshop.Shop</value> </property> <property name="interceptorNames"> <list> <value>billingAdvice</value> </list> </property> <property name="target"> <ref bean="shopTarget"/> </property> </bean>

<bean id="billingAdvice" class="com.offspring.examples.coffeeshop.BillingAdvice"></bean>


Class BillingAdvice is our cash register:

 public class BillingAdvice implements AfterReturningAdvice {
   public void afterReturning(Object returnValue, Method method,
         Object[] args, Object target) throws Throwable {
         Shop shop = (Shop)target;
         System.out.println("2 dollar charge applied for the "+shop.getDrink().getName());

Congratulations! The execution of our application produced the desirable message: Coffee served. The drink temperature is 160 degrees F. 2 dollar charge applied for the Coffee.

What have we have achieved so far? A lot. Our implementation is a truly object-oriented feat. Without modifying and recompiling our application, just by editing coffeeshop.xml, we can serve any possible drink, even boiled water, and can change/plug/unplug the cross-component services (cash register). We have eliminated the cross-cutting concerns and focused without distraction on our major passion—serving coffee. Most importantly, we followed the Second Big Rule—keep your classes open for functional extension, but closed for content modification.

The Spring Web layer

Also important to our discussion is the Spring Web layer, which provides the rich hierarchy for the controllers to handle user requests.

The AbstractCommandController is the most common choice for accommodating the Web application's real-life requirements, where we must process commands with parameters. Let's build the notorious "Hello World" application: The jw-0313-offspring.war file is a simple illustration of this standard Spring implementation and is available for download from Resources. I deployed it in the Tomcat 5 environment and typed in my browser the URI http://localhost:8080/offspring/helloworld?name=Edward (alternatively, http://www.whowillbethere.com/helloworld?name=Edward). The highly anticipated result is a page with the sophisticated message: "Hello World! Regards, Edward."

The core of the solution is the HelloWorldController class. It extends AbstractCommandController:


public class HelloWorldController extends AbstractCommandController{ public HelloWorldController(){ setCommandClass(HelloWorldCommand.class); } protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception { Map model=new HashMap(); HelloWorldCommand helloCommand =(HelloWorldCommand)command; String name=helloCommand.getName(); model.put("name",name); return new ModelAndView("helloworld",model); /* Alternative solution for the single value pair */ //Return new ModelAndView("helloworld","name",name);

} }

The line setCommandClass(HelloWorldCommand.class) tells the controller to bind URI parameters (we have only one, name) to the HelloWorldCommand class's command object. It's just a simple bean for holding request parameters:

 public class HelloWorldCommand {
   String _Name;
   public String getName() {
      return _Name;
   public void setName(String name) {
      _Name = name;

As a result, when Spring invokes the handle() method, the value Edward is already assigned to class member _Name. The handle() method contains our implementation's specifics. The most important line is return new ModelAndView("helloworld",model);. It returns an instance of the object ModelAndView. Now Spring knows that view "helloworld" should display the model's content. The model is represented by the Map object, which has an entry with key name and value Edward. The view "helloworld" maps to the JavaServer Pages helloworld.jsp:

<h1>Hello World!</h1>
<h2>Regards, ${name}</h2>

When this page is rendered, the reference ${name} will be substituted by the value Edward.

The whole application is glued together by the XML application context in the helloworld.xml file:


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/helloworld">helloWorldController</prop> </props> </property> </bean> <bean id="viewResolver"

class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> <bean id="helloWorldController" class="com.offspring.examples.helloworld.HelloWorldController"> </bean> </beans>

The urlMapping maps the "/helloworld" path to the proper controller defined by the bean helloWorldController, and viewResolver maps the view to the JSP page {viewname}.jsp.

Another important configuration piece is the web.xml file, which is available from Resources.


We have just scratched the surface of the rich Spring Framework's functionality; but we have enough to get the basic idea and even to build our first application.

The big question haunting any good programmer is: How can we do better with less effort? Let's make a giant leap toward reality and assume we must implement 50 different greeting tasks with different "hello" business logics, such as "Hello Europe," "Hello Mars," or "Hello Milky Way." If we decide to follow Spring fundamentals, we must create 50 different classes (all greeting logic differs in each case). Each class extends AbstractCommandController and overrides the handle() method to implement specific greeting business logic. Our real and potential problems are:

  • Ten programmers working on the same project will create 10 different interfaces (each solution is superior, according to its author). This heterogeneous environment is bug-prone and difficult to maintain.
  • To implement the global changes affecting the whole application, we must modify 50 tasks and/or apply AOP against 10 different interfaces.
  • Applying AOP against 10 different interfaces to cut cross-reference concerns will create complex XML definitions—usually, a messy mix of references to the different tasks inside the same XML fragments.
  • The final blow comes when management finds out that JavaServer Faces (JSF) or another technology provides better looking greeting gadgets. Now we have to rewrite all 50 tasks almost from scratch because they no longer extend the Spring controller class.

Offspring offers a generic controller to address those issues.

Offspring generic controller

A generic controller simplifies the development process and resolves the problems described above. It extends AbstractCommandController and becomes the main gateway for any user- or scheduler-generated requests. It centralizes the request flow and achieves decoupling from the native Spring Web layer. A generic controller is truly generic because it lacks knowledge of the specific tasks, interfaces, managers, and so on. It deals only with the common Action interface by calling its methods. This way, the class that implements Action becomes a business flow entry for the specific task. The whole schema perfectly complies with the Second Big Rule: the generic controller is closed for modifications, but opened for functional extensions by plugging in new actions. Let's look closer at the Action interface:


public interface Action { //Execution flow public boolean initAction() throws Exception; public void endAction() throws Exception; public boolean performAction() throws Exception; public void failedAction() throws Exception; public void failedValidation() throws Exception; public void succeedAction() throws Exception; //Web layer support public String getView(); public void setView(String view); public String getModelName(); public Object getModel(); public void setCommand(Object command); public Object getCommand(); public BindException getErrors();

public void setErrors(BindException errors);

public Map getActionContext(); }

The Action interface consists of two parts—execution flow and Web layer support. Execution flow reflects the task's lifecycle: initiation, success/failure, and action demise. Web layer support is responsible for the interaction with the generic controller. It should be growing clear what the generic controller is supposed to do: it calls the execution flow methods to govern the action object's lifecycle.

We can do even better. We can create an extra level of abstraction between controller and action—the chain of executors. When the controller needs to perform an action, it invokes the performAction() method of the first executor in the chain. Each subsequent executor is called by the previous one, until the last executor in the chain invokes the corresponding method of the action object. This falling-dominos structure allows us to plug into the controller the various executors with value-adding functionality such as logging, transaction management, notifications, billing, or whatever the creative product-management imagination may conceive of in the future. Not surprisingly, all executors should implement the Executor interface:

 public interface Executor {
   public boolean initAction(Action action,Iterator it) throws Exception;
   public void endAction(Action action,Iterator it) throws Exception;
   public boolean performAction(Action action,Iterator it) throws Exception;
   public void failedAction(Action action,Iterator it) throws Exception;
   public void failedValidation(Action action,Iterator it) throws Exception;
   public void succeedAction(Action action,Iterator it) throws Exception;

The BaseExecutor class implements default extendable functionality of chaining and action invocation:


public class BaseExecutor implements Executor{ public boolean initAction(Action action, Iterator it) throws Exception{

if(it.hasNext()){ return ((Executor)it.next()).initAction(action,it); }else{ return action.initAction(); } } public void endAction(Action action,Iterator it) throws Exception {

if(it.hasNext()){ ((Executor)it.next()).endAction(action,it);

}else{ action.endAction(); } } public boolean performAction(Action action, Iterator it) throws Exception{ if(it.hasNext()){ return ((Executor)it.next()).performAction(action,it); }else{ return action.performAction();

} } public void failedAction(Action action, Iterator it ) throws Exception { if(it.hasNext()){ ((Executor)it.next()).failedAction(action,it); }else{ action.failedAction(); } } public void failedValidation(Action action, Iterator it) throws Exception{ if(it.hasNext()){ ((Executor)it.next()).failedValidation(action,it);

}else{ action.failedValidation(); }

} public void succeedAction(Action action, Iterator it) throws Exception { if(it.hasNext()){ ((Executor)it.next()).succeedAction(action,it); }else{ action.succeedAction(); } } }

Later, we will see how to apply AOP and extend this class to modify task behavior. Figure 1's sequence diagram illustrates the chain-of-executors concept:

Figure 1. Chain-of-executors sequence diagram. Click on thumbnail to view full-sized image.

The generic controller's source code is available for download in the package com.offspring.frame. The business logic is in the overridden handle method:


public class GenericController extends AbstractCommandController { String _ActionName = null; ArrayList _Executors; protected String _ViewSuccess; protected String _ViewFailure; protected Map _ViewSuccessMap; protected Map _ViewFailureMap; public GenericController() { _Executors=new ArrayList(); _Executors.add(new BaseExecutor()); } public GenericController(Object command, String actionName) { /* Command is a simple bean to hold request parameter. Each value pair in URI query string has the corresponding setter and getter methods. Spring Framework will assign command object values by calling corresponding setter method. Command object is defined in the XML file and injected by the the Spring container. */ /* Action name is the name of Action bean defined in XML descriptor. It extends Action interface and implements business logic of specific task. */ this(); setCommandClass(command.getClass()); _ActionName = actionName; } public GenericController(String actionName) { this(); setCommandClass(BaseCommand.class); _ActionName = actionName; }

public void setExecutor(Executor executor){ _Executors.add(executor); } public void setExecutors(ArrayList executors){ _Executors.addAll(executors); } public void setViewSuccess(String viewSuccess) { _ViewSuccess = viewSuccess; } public String getViewSuccess() { return _ViewSuccess; } public void setViewFailure(String viewFailure) { _ViewFailure = viewFailure; } public String getViewFailure() { return _ViewFailure; } public void setViewSuccessMap(Map viewSuccessMap) { _ViewSuccessMap = viewSuccessMap; } public String getViewSuccess(String mode){ if(_ViewSuccessMap==null) return null; if(mode==null) mode=""; return (String)_ViewSuccessMap.get(mode); } public void setViewFailureMap(Map viewFailureMap) { _ViewFailureMap = viewFailureMap; } public String getViewFailure(String mode){ if(_ViewFailureMap==null) return null; if(mode==null) mode=""; return (String)_ViewFailureMap.get(mode); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception { Util.LogTraffic(request,response); Log log=LogFactory.getLog(this.getClass()); ApplicationContext context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); /* Spring container instantiates Action object. It should not be defined as a singleton. This way, we can carry request-specific values as the class members. */ Action action = (Action) context.getBean(_ActionName); if(command instanceof HttpCommand){ HttpCommand httpCmd = (HttpCommand)command; httpCmd.setRequest(request); httpCmd.setResponse(response); httpCmd.setServletContext(getServletContext()); } action.setCommand(command); action.setErrors(errors); boolean isSuccess=true; Executor firstExecutor =(Executor) _Executors.get(0);

/*Start the chain of executors. Last executor in the chain will call action corresponded method. */ if (!firstExecutor.initAction(action,_Executors.iterator() )){ firstExecutor.failedAction(action,_Executors.iterator()); isSuccess=false; }else if (errors.hasErrors()) { log.debug(errors); firstExecutor.failedValidation(action,_Executors.iterator()); isSuccess=false; } else if (!firstExecutor.performAction(action,_Executors.iterator())) { firstExecutor.failedAction(action,_Executors.iterator()); isSuccess=false; } else { firstExecutor.succeedAction(action,_Executors.iterator());

} firstExecutor.endAction(action,_Executors.iterator()); String view=action.getView(); String mode=((BaseCommand)command).getMode(); /* Depending on the mode parameter and success or failure of execution, we will extract the name of the corresponded view and render it. Views are mapped in _ViewSuccessMap and _ViewFailureMap and defined in the controller XML definition. */ if((view==null) && isSuccess){ view = getViewSuccess(); if(view==null){ view=getViewSuccess(mode); } } if((view==null) && !isSuccess){ view = getViewFailure(); if(view==null){ view=getViewFailure(mode); } } Map modelMap = errors.getModel(); Object model = action.getModel(); if(model instanceof Map){ modelMap.putAll((Map)model); }else{ String modelName = action.getModelName(); if (modelName != null) { modelMap.put(modelName, model); } } if(view==null){ throw new Exception("Generic Controller failed to resolve view. IsSuccess="+ isSuccess +" mode="+mode +" command"+command.getClass().getName()+ " action="+action.getClass().getName()); } boolean isRedirect=false; //If view name starts with "redirect:" prefix, we will redirect through the user browser. if(view.startsWith("redirect:")){ isRedirect=true; } if(!isRedirect){

//Automatically put command object in the model. modelMap.put("command",command); view = Util.PopulateModel(view,modelMap); }else{ view = Util.getRedirectView(view,(Map)model); } return new ModelAndView(view, modelMap); } }

The generic controller also provides simple dynamic mapping of the mode parameter and success/failure execution results to the different views and redirections. The subsequent examples demonstrate how to employ this mechanism through the controller XML definition. The concept can be visualized through Figure 2's class diagram:

Figure 2. Offspring class diagram. Click on thumbnail to view full-sized image.

The Offspring class diagram deals with two helper classes: BaseAction and BaseCommand. We can save a lot of typing by extending them. The BaseAction and BaseCommand classes are mostly made up of setters and getters and are available for download from Resources. Class BaseAction implements the Action interface. Each time we have to create a new task, we just create a new specific action and extend BaseAction:

 public abstract class BaseAction implements Action {
   protected Object _Command;
   BindException _Errors;
   protected String _ModelName;
   protected Object _Model;
   protected Map _Results;
   protected String _View;
   public BaseAction(){
   public boolean initAction() throws Exception {
      return true;
   public void endAction()throws Exception {
   public void succeedAction()throws Exception {
   public void failedAction()throws Exception {
   public void failedValidation() throws Exception {
   public void setCommand(Object command){
   public Object getCommand(){
      return _Command;
   public String getModelName() {
      return _ModelName;
   public void setModelName(String modelName) {
   public Object getModel() {
      return _Model;
   public void setModel(Object model){
   public BindException getErrors() {
      return _Errors;
   public void setErrors(BindException errors) {
      _Errors = errors;
   public Map getActionContext(){
      return _Results;
   public String getView() {
      return _View;
   public void setView(String view) {
      _View = view;

Offspring example

Let's unleash the power of the Offspring technology to build our Greeting Enterprise Solution. As is often the case, the loud commercial name is over compensation for the primitive two-page Web application.

The first page is a form based on helloOffspring.jsp (complete files are available for download from Resources):

 <FORM ACTION="hellooffspring" METHOD="get">
<input TYPE="hidden" NAME="mode" value="greeting" />
<h1>Hello world entry     form</h1>
<h3>Thank you for your interest in the greeting process.<br>
Please sign up.   
   <td>Name:</td><td><input TYPE="text" name="name" /></td>
<spring:bind path="command.name">
<c:if test="${status.errorMessage!=''}">
   <td></td><td><font size="-1" color="red">${status.errorMessage}</font></td>
<br><INPUT TYPE="submit" value="Meet the world" /></td>

The user is supposed to enter his/her name and press the Meet the World button. The mode parameter is a key to dispatch execution flow to the right code and view. Its value is assigned to the string greeting.

The fragment above illustrates another important Spring concept—validation. We employed the Spring tag library (tag <spring:bind>) to validate the parameter name. The class HelloOffspringValidator is plugged into the controller through the application context XML definition (we will see later how) and makes sure that the user entered his/her name:

 public class HelloOffspringValidator implements Validator {
   public boolean supports(Class clazz) {
      return clazz.equals(HelloOffspringCommand.class);
   public void validate(Object form, Errors errors) {
      HelloOffspringCommand command = (HelloOffspringCommand) form;
      String mode=command.getMode();
      if(mode!=null && mode.equals("greeting")){
         ValidationUtils.rejectIfEmpty(errors, "name", "required.name", "Name is required");

It's important to validate the form only if the user is in the "greeting" mode. The mode parameter is assigned in the JSP page and carried by the Spring Web framework as a property of the Command object.

The second page of our application is the actual greeting itself, helloOffspringGreeting.jsp:

<h1>Hello World!</h1>
<h2>Regards, ${command.name}</h2>
${command.name}, You have met the world ${command.visits} time(s).
<!-- alternatively with HelloOffspringAction.java modfication:
${name}, You have met the world ${visits} time(s).  -->

It resembles the helloworld.jsp page, but with extra flavor: it displays the number of visits corresponding to the username stored in the session.

Action is better than words

As we know already, Action is a class where we make the transition from generic behavior to the specific business logic implementation. It can be complex and deal with all possible services, managers, remote invocations, subsystems, and APIs. Our Greeting Enterprise Solution is much simpler. The HelloOffspringAction has only one class:


public class HelloOffspringAction extends BaseAction { public boolean performAction() throws Exception { boolean answer=false; HelloOffspringCommand command =(HelloOffspringCommand)getCommand(); String mode=command.getMode(); if(mode==null){ mode="form"; } if(mode.equals("greeting")){ answer=performGreeting(); } else if(mode.equals("form")){ answer=true; } return answer; } private boolean performGreeting(){ HelloOffspringCommand command =(HelloOffspringCommand)getCommand(); HttpSession session= command.getRequest().getSession(); String name=command.getName(); Integer visits =logGuest(session,name); command.setVisits(visits.toString()); //Generic controller will automatically treat command instance as a model. Map model=new HashMap(); model.put("name",name); model.put("visits",visits.toString()); setModel(model); //To render values in the JSP page, we would use notation: //${name}, ${visits} return true; } private Integer logGuest(HttpSession session, String name){ Map namesLog=(HashMap) session.getAttribute("guests"); if(namesLog==null){ namesLog=new HashMap(); } Integer visits= (Integer)namesLog.get(name); boolean isFirstVisit=false; if(visits==null){ isFirstVisit=true; visits= new Integer(0); } this.getActionContext().put("isFirstVisit",new Boolean(isFirstVisit)); int value=visits.intValue();

visits=new Integer(++value);

namesLog.put(name,visits); session.setAttribute("guests",namesLog); return visits; } }

The method logGuest() maintains the number of visits per name in the Session object. The performAction() method populates the command object with the number of visits. The command object is automatically associated with the model by GenericController. Alternatively, we could set our model manually (see code comments).

Glue it all together with XML

Since we are dealing with a Spring framework, without proper bean definitions in the helloworld.xml file, nothing would work. The most valuable additions to this file are action, command, and controller beans:

 <bean id="helloOffspringController" class="com.offspring.frame.GenericController">
        <constructor-arg><ref bean="helloOffspringCommand"/></constructor-arg>
   <property name="validator">
      <bean class="com.offspring.examples.helloworld.HelloOffspringValidator" />
   <property name="viewSuccessMap">
         <prop key="form">helloOffspringForm</prop>
           <prop key="greeting">helloOffspringGreeting</prop>
   <property name="viewFailureMap">
         <prop key="greeting">helloOffspringForm</prop>
   <bean id="helloOffspringCommand"
        class="com.offspring.examples.helloworld.HelloOffspringCommand" />
   <bean id="helloOffspringAction" class="com.offspring.examples.helloworld.HelloOffspringAction"
         singleton="false" />
1 2 Page 1