Implementing Model-View-Controller Patterns Using Struts2

ybVvCvsc

The Apache Struts 2 framework allows developers to build large, scalable, reliable, and secure enterprise applications, centered around the Web.  In this post, we’ll discuss a few important aspects of the framework, particularly it’s MVC structure, and create a few examples.

Model-View-Controller

Model-View-Controller (MVC) is a popular software design paradigm or pattern for enterprise applications.  The basic goal of this pattern is to provide a clear separation between:

  • The data manipulated by the application (Model)
  • The graphical or other user interfaces presented (View)
  • The business logic that ties it all together (Controller)

MVC

By establishing this separation, your code can be much cleaner, without mixing several technologies in one component.  This also allows different teams, each with specific skills, to focus on the areas of the project for which they are most effective.

Struts Actions, vs. JEE Servlets

When we looked at Java Servlets in our Resin post, we saw that servlets are Java classes that inherit from javax.servlet and implements any business logic required.

ServletSource

Servlets however are also responsible for generating the HTML to be presented to the requesting browser for whatever URI was mapped to it.  The PrintWriter object is used for this.

Struts’ Actions provide the same opportunity to execute arbitrarily complex business rules.  However the framework uses Java Server Pages (JSPs) and related technologies to handle the presentation duties rather than the actions themselves.  Actions also provide mechanisms to pass intermediate data between themselves and JSPs.

As for models, both JEE and Struts expect you to build separate Data Transfer Objects (DTOs) using tools like Hibernate to manage the data layer.  More on that later.

Setting Up Struts 2

As always, we’ll do our development in Eclipse, but now that we familiar with Maven, we’ll use it to setup the project and download the necessary dependencies.  Launch Eclipse and go to File -> New -> Other… -> Maven -> Maven Project.

The first step is to select the appropriate archetype for our project.  We could start with maven-archetype-webapp, or even maven-archetype-quickstart, from the central repository, and build it up for Struts, but it would be easier if we could use an archetype already set up for it.  Unfortunately, filtering the archetypes for ‘struts’ doesn’t yield anything useful.  However Apache maintains its own maven repository with specific archetypes for its products.

Back to Eclipse, go to Window -> Preferences -> Maven -> Archetypes, then click on Add Remote Catalog…, and create a new entry for https://repository.apache.org/content/repositories/releases.

ArchetypeCatalog

Now when you create a new maven project, you can filter for ‘struts’ archetypes and find struts2-archetype-blank.  That’s the simplest Struts 2 archetype available, but still illustrates much of the features of the framework. v2.3.24 was the latest stable version at the time of this writing.

Setting up the New Maven Project, enter the package for your project as the Group ID and the name of the project as the Artifact ID.  When you click Finish, Maven will download the required JARs and setup the project.

A Sample Project

Begin by setting up a new maven project using thing struts2-archetype-blank archetype.  We could put our enter website in one project, so we’ll use a group ID of ‘net.proloquor’, and artifact ID of ‘Web’.

Project Structure

Viewing the project through the Navigator window, you should see a layout like this:

WebProject

There’s a lot going on here.  The src folder, a staple for all maven projects, contain a main subfolder for the source code and a test subfolder for junit unit tests.  Specifically main contains a java subfolder for the actual source code, resources for configuration files (in particular struts.xml), and webapp to stage the deployment.    src/main/java/net/proloquor/Web/example contains some example actions that come with the archetype you can review and run.

The Action

An action is a Plain Old Java Object (POJO) that implements the controller of a Struts web application.  You create one in Eclipse simply by clicking File->New->Class.  Since it’s not associated with the examples that came with the archetype, put it in the net.proloquor.Web package, and call it Member.  It needs no parent class, but inheriting from com.opensymphony.xwork2.ActionSupport provides some benefits we’ll see in a bit.

Our net.proloquor.Web.Member action is exceedingly simple.  It contains a single property called name, along with name’s getter and setter using standard naming conventions.  It also contains one method called execute().

/* Member.java */
package net.proloquor.Web;

public class Member extends ActionSupport {
   private String name;
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Member() {
   }

   public String execute() {
      return SUCCESS;
   }
}

The execute() method is called every time the associated URL is requested.  It performs whatever business logic is necessary, then returns a String indicating the status of processing.  The constant SUCCESS maps to “success” by the ActionSupport parent.  This value will help determine which JSP to present upon completion.

The JSPs

Our app requires two JSP’s.  Register.jsp prompts the user to enter the name of a Member.  Create it with File->New->Other->Web->JSP File, and make sure it lands in /src/main/webapp.

<-- Register.jsp -->
<%@ page language="java" contentType="text/html%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html>
      <title>Welcome Member</title>
   </head>

   <body>
      <s:form action="Member">
         <s:textfield name="Name" label="name" />
         <s:submit />
      </s:form>
   </body>
</html>

We’ll take about Struts tags in later posts.  For now, notice that the action in our form equates to the action class Member we just created, and the textfield equates to its name property.  When this form is submitted, the Member action’s execute() method will be called, with the name property set to whatever the use typed in.

We want the system to present the HelloMember.jsp page when Member’s execute() method returns SUCCESS.

<-- HelloMember.jsp -->
<%@ page language="java" contentType="text/html%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html>
      <title>Hello Member</title>
   </head>

   <body>
      <p>Hello <s:property value="name" /></p>
   </body>
</html>

HelloMember.jsp can retrieve the value of name through the struts’ property tag and present it accordingly.

Tying It Altogether

The struts.xml file, located in src/main/resources, provides the mapping between actions, their results, and the JSP files used for presentation.  The archetype already comes with a file that manages the accompanying example actions, so we can add what we need for the Members action to it.  The relevant parts to add are:

<package name="MemberPackage" namespace="/" 
extends="struts-default">
   <action name="Register">
      <result>
         /WEB-INF/Register.jsp
      </result>
   </action>
 
   <action name="Member" class="net.proloquor.Web.Member" 
   method="execute">
      <result name="success">
         /WEB-INF/HelloMember.jsp
      </result>
   </action>
</package>

Our MemberPackage contains two actions.  When the “Register” action is invoked, the Register.jsp page is presented.  It’s form invokes the “Member” action, which presents the HelloMember.jsp page if SUCCESS (“success”) is returned.  Additional results and landing JSPs could be added.

One More Thing

Although we’re working with a framework other than standard JEE, web servers are still expecting to process servlets inside the submitted .war file. That is, the server will be expecting a web.xml file from which to take its direction.  While this cannot be avoided, we can add filters to web.xml so that all further action is handled by Struts. The necessary additions to web.xml are:

<filter>
   <filter-name>struts2</filter-name>
   <filter-class>
      org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
   </filter-class>
</filter>

<filter-mapping>
   <filter-name>struts2</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

These elements define the Struts object to process the requests and which URLs should be handled by it (all of them).  Fortunately, the archetype we used already set this up, so there’s nothing further we have to do.

Deploying and Running Your Project

Because this is a maven project, running your project works a little differently in the Eclipse environment.  Although we didn’t add any unit tests to our Member actions, we can run tests provided for the examples.  Right-click on the project in the Navigator or Package window, then select Run As->Maven build….  For goals, enter ‘test’, and click Run.   You will see the result of the unit tests in the console window.

Next, you probably want to run the entire application on your local Resin server.  Because you installed the Resin adapter into your Eclipse environment, you can use it to run you apps locally.  Simply right-click on the project, the select Run As->Run on Server.  Make sure your project, Web, is in the Configured column, and the server will start with your app.  You’ll even get a web browser in Eclipse to go to the app’s home page.

RegisterMember

Finally, you’ll want to deploy your app to a production server.  Maven helps here again.  Right-click on your project and select Run As->Maven build…, but this time use ‘package’ as the goal.  Maven will generate a .war file with your apps and all dependencies, stored as \Web\target\Web-0.0.1-SNAPSHOT.war or similarly.  Upload that file to your production Resin server and Resin will unpack it and begin servicing URLs.

What’s Next?

We’ve covered a lot of ground again today.  In future posts we’ll delve deeper into some of Struts’ features, and work in the tools needed for the Model portion of MVC.

Leave a Reply

Your email address will not be published. Required fields are marked *