Patterns and Struts

Java has been around for awhile now, which means that there is endless codes examples and design patterns for all specific types of applications, you should take full advantage of everyone else's work, even if you have to modify it abit for your own environment. In the real world many companies use the same hardware design, the architecture is configured into layers (tiers) of functionality and adding more computers to a tier is known as horizontal scaling and is considered to be the best ways to increase throughput. I have touched on this subject in my Tomcat clustering section.

Most people now days have been using Java EE containers to solve most problems you will encounter, they have found that reoccurring themes in the nature of the problem they are dealing with and they come up with reusable solutions to these problems. These design patterns have been used, tested and refined by other developers so you do not have to reinvent the wheel, so a software design pattern is a repeatable solution for a commonly-occuring software problem.

There are three important non-functional requirements you are likely to face

Performance Patterns can help in producing faster response times and increase throughput
Modularity You can use different parts of your application on different servers at the same time, so it has to modular
Flexibility, Maintainability, Extensibility Your application needs to be flexible, so that parts of code can be swapped out easily with big code changes, it also has to be easily maintainable in case of bugs, upgrades be able to handle different vendors and lastly it must be extensible to allow for new features that a client may ask for.

There are many design principle definitions that can be used but mostly mean the same thing

code to interfaces An interface is a contract between two objects, it also has another benefit and that is polymorphism.
separation of concerns & cohesion Cohesion means the degree to which a class is designed for one, cohesive task or purpose
hide complexity Try to hide complex code for example if you need to perform a lookup its best to hide the complexity of that operation in a single component and allow other components that need access to the lookup service to use that specialized component. Most of the new Java EE containers perform lots of services to reduce complex coding see my JBoss sections.
Loose coupling By coding interfaces you can reduce the number of things that one class needs to know about another class to communicate with it. The less two classes know about each other the more loosely coupled they are to each other. A very common approach is when class A wants to use methods in class B is to create an interface between the two. Once class B implements this interface, class A can use class B via the interface.
Remote proxy A remote proxy is an object local to the client object that pretends to be a remote object. The client object communicates with the proxy and the proxy handles all the networking complexities of communicating with the actual service object, as far as it is concerned its talking to a local object.
Increase declarative control Containers use deployment descriptors which is a XML files that can be maintained and updated by non-programmers, this means the more abstract and generic our code becomes.

JNDI and RMI

Java and Java EE provide two mechanisms that handle objects across the network, locating remotes objects and handling all the low level network I/O communications between local and remote objects.

JNDI Java Naming Directory Interface, gives a network a centralized location to find things, you register your objects to a JNDI server which allows other programs to find them by performing a JNDI lookup. I have also discussed JNDI in my Tomcat section.
RMI Remote Method Invocation, is a mechanism that greatly simplifies the process of getting objects to communicate across a network. Discussed in detail below.

What you want is an object in one JVM to cause a method invocation on a remote object (different JVM) but you want to pretend that you are invoking a method on a local object. This is what RMI gives you, the ability to pretend that you are making a regular old local method call.

To invoke an object method remotely using RMI you must

Using RMI you will create a proxy and you will register your object with some sort of registry. Any client who wants to call your methods will do a lookup on the registry and get a copy of the remote proxy. Then the client will make calls on the remote proxy pretending its the real thing. The remote proxy (stub) handles all the communication details like sockets, I/O streams, TCP/IP, serializing and deserializing method arguments and return values, handling exceptions, etc. The proxy on the server side is called the skeleton which performs the similar cores that the client. This lookup process can be performed a Service Locator which has the necessary code to perform lookups, this Service locator can then be used by other objects thus reducing duplicate code.

Remember remote object invocation uses passed-by-value semantics. The object is turned into a byte stream known as marshalling, then unmarshalling the object at the other turns the byte stream back into a object, you use the java.io.Serializable interface to do this. As a performance boast try to make the object as small as possible.

The JNDI registry is like a tree, an example of one is below, it can store JBDC data sources, JMS queues, JMS connection factories, JPA entity managers, JPA entity manager factories, etc

What the above allows you to do is have multiple Java EE servers that can access each others objects, thus performing horizontal scaling.

Now I am not going into too much detail has things have advanced with EJB 3 and the new Containers like JBoss 5, I suggest that you take a look that this new technologies, I simply discussed them as there is still plenty of old code out there using this method.

Struts

I am only going to give a very basic outline of Struts 1, I hope to have a new topic on Stuts 2 soon and when I do I will place a link at the bottom of this section.

There are a number of key components in Struts 1

Action Servlet you need one of these per application and its supplied by Struts
Form Beans you will write one of these for each HTML form your app needs to process, they are Java beans and once the Struts Action Servlet has called the setters on the form bean (to populate the bean with form parameters) it will call the beans validate() method. This is a great place to put data conversion and error handling logic.
Action Objects An action maps to a single activity in a use-case. It has a call-back-like method called execute() which is a great place to get the validated form params and call model components, its kinda like a servlet lite
struts-config.xml

This is the Struts-specific deployment descriptor, in it you will map

  • Request URLs to actions
  • Actions to Form beans
  • Actions to views

What this means is that the controller of an MVC application does less work, Officially Struts is a framework.

To install Struts you need to follow the below steps

  1. Navigate to the official Struts web site Apache Struts
  2. Download either version 1.3.x or the new version 2.x
  3. choose the JAR you wish to use
  4. Unzip the file
  5. Copy the JAR files in the lib directory to your own applications WEB-INF/lib directory
  6. Make sure that the Struts core file is in your classpath with your compile your form beans and action objects.

To give you an idea on how to use Struts I am going to incorporate into my MVC tutorial application.

  1. Having received a request, the ActionServlet locates the correct form bean using the struts-config.xml file. The ActionServlet invokes the forms beans validation logic. If the form bean finds any errors, it populates an ActionErrors object.
  2. Using the struts-config.xml file, the ActionServlet locates and invokes the Action object, which invokes the model and returns an ActionForward object to the ActionServlet.
  3. Having previously extracted the necessary mappings from the struts-config.xml, the ActionServlet uses the ActionForward object to dispatch to the correct view component.

Now for the code

Form Bean (classes/com/example/web)

package com.example.web;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionErrors;

import javax.servlet.http.HttpServletRequest;

public class CoffeeSelectForm extends ActionForm {

  private String type;
  
  public void setType(String type) {
    this.type = type;
  }

  public String getType() {
    return type;
  }

  private static final String VALID_TYPES = "milky,froffy,icey,strong";

  public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {

    if ( VALID_TYPES.indexOf(type) == -1 ) {
      errors.add("type, new ActionMessage("error.typeFeild.notValid"));
    }
    return errors;
  }
}

Action Object (classes/com/example/web)

package com.example.web;

import com.example.model.*;
import java.util.*;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionErrors;

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

public class CoffeeSelectAction extends Action {

  public ActionForward execute(Actionapping mapping, ActionForm form,
                               HttpServletRequest request,
                               HttpServletResponse response) {
    
    CoffeeSelectForm myForm = (CoffeeSelectForm) form;
    
    CoffeeExpert ce = new CoffeeExpert();
    List result = ce.getTypes(myForm.getType());

    request.setAttribute("styles", result);
    return mapping.findForward("show_results");
  }
}

struts-config.xml (WEB-INF) <?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
    "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>
  <form-beans>
    <form-bean name="selectCoffeeForm" type="com.example.web.CoffeeSelectForm" />
  </form-bean>

  <action-mappings>
    <action path="/SelectCoffee"
            type="com.example.web.CoffeeSelectAction"
            name="selectCoffeeForm" scope="request"
            validate="true" input="/form.jsp">

      <forward name="show_results" path="/result.jsp" />
    </action>
  </action-mappings>

  <message-resources parameter="ApplicationResources" />
</struts-config>
web.xml (WEB-INF) <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         Version="2.4">

  <servlet>
    <servlet-name>FrontController</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

    <init-param>
      <param-name>config</para-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>

  
  <servlet-mapping>
    <servlet-name>FrontController</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>