Developing a Web Application
with Spring Framework and OpenRules

Part 3 – Adding Business Logic in Java

This is the third part of a step-by-step account of how to develop web applications using Spring framework and OpenRules. The first part described how to setup a Tomcat-based development environment and create a trivial web application. The second part added major Spring modules and a view that displays current date and time. In this part we will add business object model and business logic that will allow us to greet a customer based on his/her personal data and time of the day. For example, at 2:20PM for a customer "Robinson" who is a married woman of 28, we want our application to generate a greeting like "Good Afternoon, Mrs. Robinson!". We will use Java classes and JSTL-based GUI to represent business and presentation logic. We want to implement this application in such a way that it can be easily extended to cover a much more complicated business logic with multiple business objects.

 

Step 16 – Defining Business Object Model

So far our application is not very useful. Now we would like to add a little bit of business logic and related business objects to generate customer specific greetings. Our application will deal with an object Customer, for which we should be able to specify name, gender, marital status, age, and probably other characteristics.  In order to separate the web dependent logic from the business logic we will create two separate packages for the Java source – 'web' and 'hello'. Let's create a Java package 'hello' (a folder under src), in which we will put a new Java class Customer and all other classes that will describe a business object model of our application. We also make our classes Serializable, not necessary for our application, but could come in handy later on if we have to pass this class between different application layers. The Customer class is implemented as a JavaBean:
 

hello.spring/src/hello/Customer.java
package hello;

import java.io.Serializable;

public class Customer implements Serializable
{
	protected String name;
	protected String maritalStatus;
	protected String gender;
	protected int age;
		
	public Customer() {
		name = "Robinson";
		maritalStatus = "Married";
		gender = "Female";
		age = 25;
	}

	public String getGender() {
		return gender;
	}
	public String getMaritalStatus() {
		return maritalStatus;
	}
	public String getName() {
		return name;
	}
	public void setGender(String string) {
		gender = string;
	}
	public void setMaritalStatus(String string) {
		maritalStatus = string;
	}
	public void setName(String string) {
		name = string;
	}

	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

The Customer class allows us to create a customer and set/get its attributes when we need to do it. Now we will define an object GreetingEngine that should implement an interface to our web-based greeting service. The main function of this service is to generate a personalized greeting for any Customer based on time of the day and customer's attributes. Let's add the proper interface to our 'hello' package:

 

hello.spring/src/hello/IGreetingEngine.java
package hello;

public interface IGreetingEngine {

	public String generateGreeting(Customer customer);
}

We can have different implementations of this interface including Java, XML, or rules. These two classes present our simple business model.

Step 17 – Implementing Business Logic in Java

While we can have different implementations of GreetingEngine interface, now we will proceed with a basic Java implementation. Even using Java, it makes sense to encapsulate business logic of greeting generation in a separate underlying "engine" that will be called by our GreetingEngine. Later on (Part 4) we will move business logic from this JavaEngine to OpenRules. Let's postpone an implementation of JavaEngine for a while, and try to implement our GreetingEngine first. Here is a possible implementation:
 

hello.spring/src/hello/GreetingEngine.java
package hello;

import java.io.Serializable;

public class GreetingEngine implements Serializable, IGreetingEngine {

	private JavaEngine engine;

	public GreetingEngine() {
		engine = new JavaEngine();
	}

	public String generateGreeting(Customer customer) {
		EngineResponse response = new EngineResponse();

		engine.run(customer, response); 

		String result = response.get("greeting") + ", " +
				response.get("salutation") + " " +
				customer.getName() + "!";
		System.out.println("Generated Greeting: " + result);
		return result;
	}
	public static void main(String[] args) {
		GreetingEngine engine = new GreetingEngine();

		Customer customer = new Customer();
		customer.setName("Robinson");
		customer.setGender("Female");
		customer.setMaritalStatus("Married");

		engine.generateGreeting(customer);
	}
}

In this implementation, we create an instance of JavaEngine inside a constructor for GreetingEngine. We run this engine inside method 'generateGreeting' by passing to it two parameters:

  1. customer - to analyze customer's attributes

  2. response - an object of a new class EngineResponse, into which JavaEngine is supposed to place generated greeting (like "Good Morning") and salutation (like "Mrs.").

So, we will also need a simple Java object for EngineResponse that can be implemented as a simple HashMap:

HelloSpring/src/bus/EngineResponse.java
package hello;
import java.util.HashMap;

public class EngineResponse extends HashMap {
}

And finally, we are ready to concentrate on a JavaEngine implementation. By now we already know that this Java class should have a method 'run' with two parameters of types Customer and EngineResponse. We also can create two separate methods to define Greeting and Salutation. Here is a possible implementation:
 

hello.spring/src/hello/JavaEngine.java
package hello;

import java.util.Calendar;
import java.io.Serializable;

public class JavaEngine implements Serializable {

	public void run(Customer customer, EngineResponse response) {
		defineGreeting(customer,response);
		defineSalutation(customer,response);
	}
	
	public void defineGreeting(Customer customer, EngineResponse response) {
		int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);

		String greeting = "?";
		if (hour > 0 && hour <= 12)
			greeting = "Good Morning";
		else
		if (hour > 12 && hour <= 18)
			greeting = "Good Afternoon";
		else
		if (hour > 18 && hour <= 21)
			greeting = "Good Evening";
		else
		if (hour > 21 && hour <= 24)
			greeting = "Good Night";
		response.put("greeting",greeting);
	}

	public void defineSalutation(Customer customer, EngineResponse response) {
		String salutation;
		if (customer.getGender().equals("Male"))
		{
			if (customer.getAge() < 4)
				salutation = "Little";
			else
				salutation = "Mr.";
		}
		else
		{
			if (customer.getMaritalStatus().equals("Married")) 
				salutation = "Mrs.";
			else
				salutation = "Ms.";
		}
		response.put("salutation",salutation);
	}
}

While this implementation is not end-user friendly and difficult to extend, it should serve our purposes. You may already test it by running GreetingEngine as a regular Java application. Later on we will replace this Java code with business rules.

Step 18 - Updating the Controller

First, let's create a Java package 'web' (a folder under src) and move into it the file HelloController.java. Next, we will modify the HelloController to hold a reference to instances of new classes Customer and GreetingEngine.
 

hello.spring/src/web/HelloController.java
package web;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Map; import java.util.HashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import bus.Customer; import bus.GreetingEngine;
public class HelloController implements Controller {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    private Customer customer;
    private GreetingEngine greetingEngine;

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String now = (new java.util.Date()).toString();
        logger.info("returning hello view with " + now);

        Map myModel = new HashMap();
        myModel.put("now", now);
        myModel.put("customer", getCustomer());
        myModel.put("greetingEngine", getGreetingEngine());

        logger.info("running greetingEngine for customer " +getCustomer().getName());
        getGreetingEngine().generateGreeting(getCustomer());

        return new ModelAndView("hello", "model", myModel);
    }

    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    public GreetingEngine getGreetingEngine() {
        return greetingEngine;
    }
    public void setGreetingEngine(GreetingEngine engine) {
        this.greetingEngine = engine;
    }
}

Now the ModelAndView keeps references to the date and time, customer, and a greeting engine. While handling the request, the controller will execute method "generateGreeting" to produce the correct ModelAndView.

Step 19 – Modify the view to display business data and add support for message bundle

We will modify hello.jsp to display a customer data add a generated greeting for this customer. We will also replace the title, heading and greeting text with a JSTL <fmt:message> tag that pulls the text to display from a provided 'message' source – we will show this source in a later step.
 

hello.spring/war/WEB-INF/jsp/hello.jsp
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
<head><title><fmt:message key="title"/></title></head>

<body>
  <h1> <fmt:message key="heading"/> </h1>


  <fmt:message key="greeting"/> <c:out value="${model.now}"/>

  <p>
  <b>Customer Information:</b> <br>
  <table border="1">
	<tr>
	  <td>Name</td>
	  <td><c:out value="${model.customer.name}" /></td>
	</tr>
	<tr>
	  <td>Age</td>
	  <td><c:out value="${model.customer.age}" /> </td>
	</tr>
	<tr>
	  <td>Marital Status</td>
	  <td><c:out value="${model.customer.maritalStatus}" /> </td>
	</tr>
	<tr>
	  <td>Gender</td>
	  <td><c:out value="${model.customer.gender}" /> </td>
	</tr>
  </table>

  <p>
   <b>Generated Greeting: </b><br>
   <c:out value="${model.greetingEngine.result}"/>
  </p>

</body>

</html>


Step 20 – Add some test data to automatically populate some business objects

We are not going to add any code to load the business objects from a database (at least for now). Instead, we can “wire up” a couple of Customer instances using Spring's bean and application context support. We will simply put the data we need as a couple of bean entries in hello.spring-servlet.xml. We will also add the messageSource entry that will pull in the messages resource bundle ('messages.properties') that we will create in the next step.
 

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

<!--
- Application context definition for "springapp" DispatcherServlet.
-->


<beans>
<bean id="helloController" class="web.HelloController"> <property name="customer"> <ref bean="customer"/> </property> <property name="greetingEngine"> <ref bean="greetingEngine"/> </property> </bean> <bean id="customer" class="hello.Customer"> <property name="name"><value>Robinson</value></property> <property name="gender"><value>Male</value></property> <property name="maritalStatus"><value>Single</value></property> <property name="age"><value>3</value></property> </bean> <bean id="greetingEngine" class="hello.GreetingEngine"> <property name="result"><value>?</value></property> </bean> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename"><value>messages</value></property> </bean> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/hello.htm">helloController</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property> <property name="prefix"><value>/WEB-INF/jsp/</value></property> <property name="suffix"><value>.jsp</value></property> </bean> </beans>

 

Step 21 – Add the message bundle and a 'clean' target to build.xml

Create a 'messages.properties' file in the war/WEB-INF/src directory. This properties bundle so far has three entries matching the keys specified in the <fmt:message> tags that we added to the hello.jsp.

hello.spring/war/WEB-INF/src/messages.properties
title=hello.spring
heading=hello.spring application
greeting=It is now

Since we moved some source files around, it makes sense to add a 'clean' and an 'undeploy' target to the build scripts. Add the following entries to the build.xml file.

    <target name="clean" description="Clean output directories">
        <delete>
            <fileset dir="${build.dir}">
                <include name="**/*.class"/>
            </fileset>
        </delete>
    </target>

    <target name="undeploy" description="Un-Deploy application">
        <delete>
            <fileset dir="${deploy.path}/${name}">
                <include name="**/*.*"/>
            </fileset>
        </delete>
    </target>

Now stop the Tomcat server, run the clean, undeploy and deploy targets. This should remove all old class files, re-build the application and deploy it. Start up Tomcat again and you should see the following:



Up  Part 2 – Adding Major Spring Components   Part 4 – Adding Unit Tests and a Form to the Application