WanTii, Inc. Tech Blog

Discussion of Java on the Web Server…

Getting Started with Struts2 (Part 3)

July 3rd, 2007 by Wes Wannemacher

So I have talked and talked in the previous entries without showing any code examples. Finally, it is time to start looking at code. I apologize for waiting this long, but in my defense, the Struts 2.x framework is complex and without thorough coverage, I would not be doing it justice.

Example #1 – Hit Counter

Let’s get our feet wet by comparing framework code with standard JSP/Servlet code. It was a popular practice in the early days of the web to display what often looked like an odometer to tell the user how many other people had viewed the page that was currently loaded. A good Hit Counter requires many considerations, including most importantly storage and concurrency. For this example, the Hit Counter will exist in the server memory only. The goal of this Hit Counter is that it be functional, but not necessarily production worthy.

In the JSP and Servlet platform there are many options on how to approach this project. To maximize reusability, I will create a custom tag that displays and updates the count for the page that is calling the tag. Each time the tag is used to display the count, the count will be incremented. Given the reusable nature of a custom tag, it is a requirement that this tag can be used by more than one page on the site, so the count variable should be an object that will hold an individual count for each page that calls the tag. For this project, there are also potential concurrency issues. On real world projects, the biggest concurrency issues are usually data consistency and possible deadlocks. In this case, I can solve these issues by making sure to only use thread-safe objects to store the counts and also retrieve then manipulate in a thread safe manner.

To get more specific, I will create a custom tag that instantiates a static java.util.Hashtable in a static initializer block. If you are unfamiliar with custom tags, go right now and do a google search for “custom tag” and take a minute to learn how to create and use custom tags. When the tag is called, it will retrieve the current count for the page that is calling the tag, then the count will be incremented. The retrieval and increment will happen in a synchronized method to maintain integrity. The count will then be written to the page.

HitCounter.java ->

package com.wantii.examples.tags;
import java.io.IOException;
import java.util.Hashtable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class HitCounter extends TagSupport {        
  private static final Hashtable counts ;
  static {
    counts = new Hashtable();
  }

  private static synchronized int getCount(String countKey) {
    Integer thisCount = counts.get(countKey);
    if (thisCount == null) {
      thisCount = new Integer(0);
    }
    int count = thisCount.intValue();
    Integer newCount = new Integer(count +1);
    counts.put(countKey, newCount);
    return count;
  }

  @Override
  public int doStartTag() throws JspException {       
    HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
    String callingPage = req.getServletPath();
    int currentCount = HitCounter.getCount(callingPage);
    JspWriter out = pageContext.getOut() ;
    try {
      out.println(currentCount);
    }
    catch (IOException e){
      throw new JspException("IOException caught in writing - " + e.getMessage());
    }
    return SKIP_BODY;
  }
}

As you can see, the code for the tag is simple (as custom tags go). To make this tag usable, you will have to have a TLD file. Here is a sample TLD -

wantiitags.tld ->

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
    <tlibversion>1.0</tlibversion>
    <jspversion>1.2</jspversion>
    <shortname>WanTiiTags</shortname>
    <uri></uri>
    <info>Example Tags</info>
   
    <tag>
        <name>HitCounter</name>
        <tagclass>com.wantii.examples.tags.HitCounter</tagclass>
        <bodycontent>empty</bodycontent>
        <info>Will display the number of times a page has been accessed since the app server was launched.</info>
    </tag>
</taglib>

Here is an example JSP that uses this tag -

HitCounter.jsp ->

<%@ taglib uri="/WEB-INF/wantiitags.tld" prefix="wt" %>
<html>
<head>
<title>A Simple Counted Page</title>
</head>
<body>
    The number of times this page has been accessed is <wt:HitCounter/>.
</body>
</html>

Before I delve into the details of this example, I would like to cover the Struts2 example. Since I am going to solve this problem with Struts2, I could try to retro-fit the code into the Struts2 environment. Unfortunately, retro-fitting a previous solution will only create extra work. For a simple project such as this, it is best to re-think the solution from the ground up.

Applying the Model 2 approach, I am going to start by creating a business object. When creating business objects, there is some temptation to spend too much time coming up with the most elegant solutions and highly reusable objects that are beautifully well-designed. However, I have found that the best solution is usually the simplest solution. For this business object, I will create a ‘Counter’ that will provide a count of the times that a count has been requested for a given key.

Counter.java ->

package com.wantii.biz;
import java.util.Hashtable;
public class Counter {
        private static Hashtable counts ;
        static {
                counts = new Hashtable();
        }
        private static synchronized int getCount(String countKey) {
                Integer thisCount = (Integer)counts.get(countKey);
                if (thisCount == null) {
                        thisCount = new Integer(0);
                }
                int count = thisCount.intValue();
                Integer newCount = new Integer(count +1);
                counts.put(countKey, newCount);
                return count;
        }
        
        private String countKey ;
        public Counter(String countKey) {
                this.countKey = countKey;
        }
        
        public int getCount() {
                return Counter.getCount(countKey);
        }
}

The Counter class above will act as the “Model.” It represents the business case. Although the custom tag I wrote above can be considered reusable, someone could make a case that this class is even more “reusable” because it has no dependencies (other than basic classes like Hashtable and Integer and primitives). Another key feature of this business object is that it has a “getter.” Although this is not a traditional JavaBean, the ‘getCount’ method will be found through introspection and treated in a special way when it comes to the Struts2 coding later.

Now that I have a business object that acts as my “Model,” I have to create the “View” and “Controller.” To proceed, I will need an Action class and a JSP. A Struts2 Action class is a Java object that implements the Action interface. To implement the Action interface, I only need to create one method called “execute” which takes no parameters and returns a String. Although the ‘public String execute()’ method is the only requirement, there are many more features that can be exploited while coding action classes. You can use a default implementation called ‘ActionSupport.’ Every action I have written to date has extended ActionSupport. ActionSupport’s implementation of the ‘execute’ method simply returns the String “success.” This will be significant when I configure the action.

Another nifty feature of Action classes in Struts2 is that all “getter” methods are exposed to the JSP files that are rendered. For the sample action that I will create, I will use this functionality to create an Action that tracks it’s own hit count.

CountableAction.java ->

package com.wantii.struts;
import com.opensymphony.xwork2.ActionSupport;
import com.wantii.biz.Counter;
public class CountableAction extends ActionSupport {
        public int getCount() {
                Counter count = new Counter(this.getClass().getName());
                return count.getCount();
        }
}

To use this action we must configure this class in the struts configuration file.

struts.xml ->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <package name="counter" namespace="/counter" extends="struts-default">
        <action name="Counted" class="com.wantii.struts.CountableAction">
            <result>/WEB-INF/counter/ViewCounter.jsp</result>
        </action>
    </package>
</struts>

Within this web-app, there is one package and actions are configured within this package. Packages allow actions to be configured in logical groups. This can be helpful if you want a particular configuration to be applied to only one group of actions. The action we wrote (CountableAction.java) is referenced as the “Counted” action. This configuration points the default result to “/WEB-INF/counter/ViewCounter.jsp.” This tells Struts that when the execute method of com.wantii.struts.CountableAction returns “success” to display the /WEB-INF/counter/ViewCounter.jsp. This will happen every time because the implementation of the execute method in ActionSupport returns “success.” To complete this example, here is the JSP that will display the hit count.

ViewCounter.jsp ->

<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>A Simple Counted Page</title>
</head>
<body>
    The number of times this page has been accessed is <s:property value="count" />.
</body>
</html>

The ViewCounter.jsp file is very similar to our HitCounter.jsp file from earlier. In fact, you may be asking yourself at this point just what was gained. Once this example was worked out, it would appear that nearly the same amount of effort and code went in to each project. The easiest advantage to point out is that the Struts2 examples have no external dependencies. Without external dependencies, jUnit tests are much easier to write. Here are unit tests for both Counter.java and CountableAction.java.

CounterTest.java ->

package test.wantii.biz;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.wantii.biz.Counter;
public class CounterTest {
        @Test
        public final void testGetCount() {
                Counter counter = new Counter("UnitTestCount");
                int first = counter.getCount();
                Counter anotherCounter = new Counter("UnitTestCount");
                int next = anotherCounter.getCount();
                int difference =  next - first;
                assertTrue(difference ==1);
        }
}

CountableActionTest.java ->

package test.wantii.struts;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.wantii.struts.CountableAction;
public class CountableActionTest {
        @Test
        public final void testGetCount() {
                CountableAction first = new CountableAction();
                CountableAction next = new CountableAction();
                int firstCount = first.getCount();
                int nextCount = next.getCount();
                int difference = nextCount - firstCount ;
                assertTrue(difference ==1);
                try {
                        assertTrue(first.execute().equals("success"));
                        assertTrue(next.execute().equals("success"));
                }
                catch(Exception e) {
                        fail(e.getMessage());
                }
        }
}

There are ways to create unit tests for the first example. To write a unit test for the custom tag written in the Servlet/JSP example, you would need to add quite a bit of housekeeping code that simply creates all of the necessary objects to fill all of the dependencies required. Unit tests are not the only advantage of the Struts example, but the unit tests represent the advantages you gain. Having simple objects and unit tests allow me to develop with a “design by contract” type of paradigm. I can continue to make changes or enhance the objects and the unit tests will keep me from introducing bugs. This is not a full proof method for keeping bugs out of the system, but it is a highly effective method. Another advantage of using simple objects is that I can reuse my components on platforms other than Struts2 and JSP.

There are also a few disadvantages that I should point out. First off, using Struts2 is not particularly intuitive at first. There are many topics that I glossed over that would require explanation before working on your first Struts2 project (struts.xml and <s:property /> tag for example). This is easily overcome though once you start. About two weeks into my first Struts2 project, I felt that I was up to speed. The next disadvantage is that the custom tag in the Servlet/JSP example is usable in any JSP within the web-app, whereas the Counted action is only one page. This disadvantage is also easily dealt with in a number of different ways. One possible solution is to have other actions extend CountableAction. Since Java does not support multiple inheritance, this may not be a great solution. A better solution is to use the Struts built-in AJAX support to call the Counted action from the pages that need a count.

Example #2 – Send a Message Page

Now that we have one Struts2 action under our belts, it is time to get a little more complex. Our next example is one that even the most basic websites have, a “Send Us A Message” page. These pages started to become common for plenty of obvious reasons. The purpose of a page like this is to provide a web visitor with a method for contacting the site owners without displaying email addresses for spammers to harvest with web crawlers. Not only does a page like this protect site owners from potential spam, but it also allows the incoming messages to be recorded. On many sites, the messages are pushed right into a CRM system as a lead.

As with the last example, I will start off by making a solution based on Servlets and JSP alone. For this project, I am going to take what the web visitor types and email it. Even if you have not written much Servlet and JSP code, you probably know that having Java code in your JSP is considered bad design. In fact, it is considered a “best practice” to write your form in a JSP and have the form post to a Servlet which performs the heavy-lifting then passes control on to another JSP. As an added bonus, control can be passed back to the original form if there is an error and have the original form notify the user of the error and make suggestions to fix the condition.

You may be tempted to start out by creating a JSP, but I am going to start with the servlet. By tackling the servlet first, I will know all of the error conditions that may send the user back to the JSP. When I work on the JSP, all of the extra code for handling the errors will be a bit more intuitive.

SendMessage.java ->

package com.wantii.examples.servlets;
import java.io.IOException;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SendMessage extends HttpServlet {
        String emailTo = null ;
        String mailServer = null ;
        InternetAddress emailToAddr = null ;
        
        public void init() throws ServletException {
                super.init();
                ServletConfig config = getServletConfig();
                mailServer = config.getInitParameter("mailServer");
                emailTo = config.getInitParameter("emailTo");
                InternetAddress[] addrArray = null ;
                try {
                        addrArray = InternetAddress.parse(emailTo, false);
                }
                catch (AddressException a) {
                        throw new RuntimeException("bad email address, check the servlet init params in your web.xml");
                }
                emailToAddr = addrArray[0];
        }
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                /* if data is GETted, then just process it anyways. */
                doPost(request, response);
        }       
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                /* Let's gather up all the parameters sent from the browser */
                String msgReplyTo = request.getParameter("senderEmail");
                String msgContents = request.getParameter("messageContent");
                String subject = request.getParameter("subject");
                /* make sure there is an email address to reply to later */
                if(msgReplyTo == null || msgReplyTo.equals(""))
                {
                        response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/contactUs.jsp?msgFailed=1"));
                        return;
                }
                
                /* check the email address for validity */
                InternetAddress[] addrArray = null ;
                try {
                        addrArray = InternetAddress.parse(msgReplyTo, false);
                }
                catch (AddressException a) {
                        response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/contactUs.jsp?msgFailed=2"));
                        return;
                }
                InternetAddress replyToAddr = addrArray[0];
                /* Now, we'll create the email message */
                try {
                        Properties props = new Properties();
                        props.put("mail.smtp.host", mailServer);
                        Session mailSession = Session.getDefaultInstance(props, null);
                        Message msg = new MimeMessage(mailSession);
                        msg.setFrom(replyToAddr);
                        msg.setReplyTo(addrArray);
                        InternetAddress sendTo[] = {new InternetAddress()};
                        sendTo[0] = emailToAddr;
                        msg.setRecipients(Message.RecipientType.TO, sendTo);
                        msg.setSubject("Web Message - " + subject);
                        msg.setText(msgContents);
                        Transport.send(msg);
                }
                catch (MessagingException me){
                        response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/contactUs.jsp?msgFailed=3"));
                        return;
                }
                
                /* Lastly, we'll send the user onto the next page, if they haven't already been redirected */
                response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/messageSent.jsp"));
        }                  
}

The SendMessage servlet is almost a production-ready servlet (you may have noticed that an exception can be thrown in the init method, but the error is not properly handled). One thing an astute reader may notice about this code is that I actually commented it this time. I have nothing against comments, but the previous examples were brief and I felt that comments would have only insulted you since the explanations followed the code. The comments here are only meant to guide the reader through the code since this one is a bit longer than the previous example. As you can see, we pull a few configurable parameters out of the Servlet’s configuration in the web.xml. Since we only need a few strings in this case (the server to use as a relay and the recipient of the emails) using servlet init-params will suffice. Servlets are only instantiated once. The init method will only be run once, but is guaranteed to run before either the doPost or doGet methods. To maintain thread-safety, never modify anything that has been set in the init method. The error handling in the init method is not particularly helpful. The way this servlet is written, the application server’s console would have to be monitored for error messages. In a production environment, better precautions should be taken.

Because of the nature of this project, it would be better to have an HTML form that POSTs to this servlet rather than GETs. Rather than making that a limitation, I have a doGet method that simply forwards the processing to the doPost method. This does not change the browser’s request to a POST, but only runs the same logic regardless of the browser’s request. The heavy-lifting is done in the doPost method. If you are unfamiliar with the SUN JavaMail API, some of this code may not make sense. Fear not, this is not an article on using the JavaMail API, so look over it briefly and know that it sends an email. When reading the doPost method, there are four things that happen.

  1. the parameters are read
  2. the parameters read previously are validated
  3. the business logic happens
  4. the user is redirected to a JSP that indicates success

If one of these steps fails in a way that is recoverable, the browser is redirected back to the contactUs.jsp page and an error is displayed.

In designing the JSP page, I have to now account for the current error handling. To handle errors, I am employing a simple technique, I send a parameter called ‘msgFailed’ with a number from one to three which would indicate to the JSP which error has occurred. I also have to consider that the Servlet expects to receive parameters called “senderEmail,” “messageContent” and “subject.”

contactUs.jsp ->

<%
    String msgFailed = request.getParameter("msgFailed");
    int msgFailedNo = 0;
    if (msgFailed != null && ! msgFailed.equals("") ) {
        msgFailedNo = Integer.parseInt(msgFailed);
    }
%><html>
<head>
<title>Send Us A Message</title>
</head>
<body>
<h3>Send Us A Message</h3>
<% if(msgFailedNo != 0) { %>
<span style="color:red">
<%    if (msgFailedNo == 1) { %>
You must specify an email address.       
<%    }
    else if (msgFailedNo == 2) {%>
You must specify an appropriate email address.
<%    }
    else if (msgFailedNo == 3) {%>
An error occurred while attempting to send your message. Please try again later.
<%    } %>
</span>
<% } %>
<form method="post" action="<%= request.getContextPath() %>/servlets/SendMessage">
<table>
    <tr>
        <td align="right">Your Email Address:</td>
        <td align="left"><input name="senderEmail" /></td>
    </tr>
    <tr>
        <td align="right">Subject:</td>
        <td align="left"><input name="subject" /></td>
    </tr>
    <tr>
        <td align="right">Your Message:</td>
        <td align="left">
            <textarea name="messageContent" cols="45" rows="15">Type your message here.</textarea>
        </td>
    </tr>
    <tr>
        <td align="right"><input type="reset" /></td>
        <td align="left"><input type="submit" /></td>
    </tr>
</table>
</form>
</body>
</html>

If you read this JSP from top to bottom, you will see that the first thing I do is check for the error condition. I have made it a habit of doing these sorts of checks first in my JSP pages because there may come a time when one of these error conditions would cause me to want to redirect from this page to another. To send a redirect through HTTP, rather than JavaScript, you have to do it before you send any output. Most JSP compilers are very simple, they will take the open and close JSP tags (<% and %>) and and makes everything between them into a String within an out.println() call. What many people forget is that whitespace is significant. In our JSP, the first thing to be out.println’d from the server is the <html> tag. Since the page starts with <% …, a response.sendRedirect could be called and will work properly. If you have whitespace that is not located within scriptlet code and you try to call response.sendRedirect, then the server may throw an IllegalStateException.

As a matter of opinion, the scriptlet code in this JSP is very poor style. I could create a custom tag to perform the error handling, but the custom tag would like look worse. When looking at a page like this one, it inspires a programmer to want to write an elegant solution for Servlets to talk to JSPs. Although Struts 2 provides what I believe to be a great solution, there are other possible solutions which I am not exploring in this article. One place to look would be the JSTL. The JSTL provides tags that can perform logic tests which would clean up this JSP quite a bit, and there are many other features of the JSTL that could be useful on any project like looping tags, date and currency formatting tags, etc.

I am not going to paste the contents of messageSent.jsp. This page contains only HTML code but is a JSP in case we add some programmatic elements such as custom tags in the future.

Before I cover a Struts 2 solution, I would like to say that since this is a larger example, right away the disadvantages of using only JSP and Servlet technology start to surface. I do not want to give the impression that JSP and Servlets are bad technology, but just that there are inherent disadvantages that can be overcome using a framework (namely the Struts 2 framework). Many of the problems I mentioned above are compounded even further on other platforms (such as perl CGI code). As with any platform, as your codebase grows, organization and good design become important. One of the teams I worked on in the past used JSP and Servlet technology quite extensively. We were not using any frameworks. Each time enhancements were requested, the project would usually be nearly re-written from scratch. Very little of the code was considered reusable because even if changes were not necessary, an engineer would have to spend time reading code just to verify that the changes were not necessary. This had less to do with the lack of framework and more to do with a lack of unit testing and no modern design patterns being incorporated. Although this article (and other future articles) may read as though there is a heavy Struts 2 bias, the point that I would like to make is that disciplining yourself to using a design pattern and sticking to it will make your life easier as your projects grow.

For the Struts 2 solution I will start with the business objects. In this case, I need an object to represent the message submitted from the website. I will call this class WebSiteMessage. The business object will follow JavaBeans conventions much better than our first Struts example. I will make getters and setters for all of the members and I will make a ’send’ method that sends the message.

WebSiteMessage.java ->

package com.wantii.biz;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import static com.wantii.biz.Config.config;
public class WebSiteMessage {
        private String msgReplyTo ;
        private String msgContents ;
        private String subject ;
        
        public void send() throws RuntimeException {
                try {
                        SimpleEmail email = new SimpleEmail();
                        email.setHostName( config.getString("email_server") );
                        email.addTo( config.getString("email_recipient") );
                        email.setFrom(this.msgReplyTo, "WebSiteMessage");
                        email.setSubject( "Web Message - " + this.subject );
                        email.setMsg( this.msgContents );
                        email.send();
                }
                catch(EmailException ee){
                        throw new RuntimeException("EmailException thrown - " + ee.getMessage());
                }
        }
        
        public String getMsgContents() {
                return msgContents;
        }
        public void setMsgContents(String msgContents) {
                this.msgContents = msgContents;
        }
        public String getMsgReplyTo() {
                return msgReplyTo;
        }
        public void setMsgReplyTo(String msgReplyTo) {
                this.msgReplyTo = msgReplyTo;
        }
        public String getSubject() {
                return subject;
        }
        public void setSubject(String subject) {
                this.subject = subject;
        }       
}

You will notice a few important items. First, this time around, I decided to use the commons-email package from the Apache Jakarta project. Overall, the code is only shortened slightly, but this is a bit easier to read. There is also a Config object referenced. I am not going to insert the code, it really is a wrapper around the commons-configuration package from the Apache Jakarta project. Struts does not have a mechanism for providing general purpose configuration elements. In the first half of this example, I used the servlet’s init-params. For a large scale project, neither method would be preferred. On a large scale project, I would suggest investing time learning a Dependency Injection framework like Spring which will be covered in a future article. At first, it may not seem that Spring can solve simple configuration issues, but like Struts, if you invest some time learning the Spring framework as it is intended to be used, then you will find that your code will become easier to maintain and many mundane tasks can be handled within the framework.

To create a unit test for this JavaBean is trivial. Although the object now has external dependencies, they are invisible to the calling code. The action for this project will be more complex than the last one I wrote. In the last action, I exploited a getter method from a result JSP. In this action, I am going to use setters to receive the form fields from the input form. I will also write an execute method since the ActionSupport implementation will not suffice. I am also going to use Struts’ validation facilities. As you will see the input validation will not happen within the action code. First I will create the action class, then I will cover the validation technique.

SendMessageAction.java ->

package com.wantii.struts;
import com.opensymphony.xwork2.ActionSupport;
import com.wantii.biz.WebSiteMessage;
public class SendMessageAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
        private String msgReplyTo ;
        private String msgContents ;
        private String subject ;
        
        public String execute() {
                WebSiteMessage msg = new WebSiteMessage();
                msg.setMsgContents(msgContents);
                msg.setMsgReplyTo(msgReplyTo);
                msg.setSubject(subject);
                msg.send();
                return SUCCESS;
        }
        
        public String getMsgContents() {
                return msgContents;
        }
        public void setMsgContents(String msgContents) {
                this.msgContents = msgContents;
        }
        public String getMsgReplyTo() {
                return msgReplyTo;
        }
        public void setMsgReplyTo(String msgReplyTo) {
                this.msgReplyTo = msgReplyTo;
        }
        public String getSubject() {
                return subject;
        }
        public void setSubject(String subject) {
                this.subject = subject;
        }
}

There is something we need to cover before we move forward, ActionSupport has a method called “input” that returns the String “input.” I will use this knowledge to configure the action in a way such that when the user first views the page, the action will understand that validation need not be performed. In the first half of this example, I validated the input by hard-coding a few checks directly in the servlet. If I decide to validate further, that would require more code, a recompile and re-installation of the web-app. The Struts 2 validation facilities are highly reusable and very flexible. To have your input data validated, you create an XML file with the validation rules that exists in the same directory as the Action class. Struts will look for a set of XML files and the existence of these XML files triggers the validation (as long as the validation interceptor is in the stack running against the request).

SendMessageAction-validation.xml ->

<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
    <field name="msgReplyTo">
        <field-validator type="requiredstring">
<message>You must specify an email address.</message>
        </field-validator>
        <field-validator type="email">
<message>The email address you specified does not appear valid.</message>
        </field-validator>
    </field>
    <field name="msgContents">
        <field-validator type="requiredstring">
<message>Please type a message.</message>
        </field-validator>
    </field>
</validators>

The format of the validation file is fairly intuitive. For each field that requires some form of validation, there is a <field> entry. For each check to be made against the given field, there is a <field-validator> entry. There are many different validator types that can be used. If the check fails, the message specified within the field-validator entry will be displayed to the user. Once you introduce validation, your action’s execute method will not be reached until all of the checks pass. This is significant because unless you take a special precaution, users will see the error messages before they have had the chance to fill out the form. The easy way around this is to tell the action to run the “input” method. When the input method returns the String “input,” Struts will follow the “input” result which automatically bypasses validation. When the form is filled out and submitted, the validation will be checked and if passed, the execute method will be run.

Now that I have an action and I have the validation rules setup for the action’s input, I can create a JSP with the input form. In the first example, I used the <s:property /> tag. For this JSP, I will introduce the Struts2 form tags. The Struts2 form tags are much more than HTML form tags. There is quite a bit of functionality built into the Struts2 form tags that handle displaying the validation errors and even controlling layout.

SendMessage-input.jsp ->

<%@ taglib uri="/struts-tags" prefix="s"
%><html>
<head>
<title>Send Us A Message</title>
<script language="JavaScript">
<!--
var taSelected = 0;
//-->
</script>
<s:head theme="ajax" />
</head>
<body>
<h3>Send Us A Message</h3>
<s:form action="SendMessage" namespace="/contact" validate="true">
<s:textfield name="msgReplyTo" label="Your Email" required="true" />
<s:textfield name="subject" label="Subject" />
<s:textarea name="msgContents" rows="15" cols="45"
    required="true" label="Message" value="Type your message here."
    onfocus="if (taSelected == 0){taSelected = 1; this.select();}" />
<s:submit />
<s:reset />
</s:form>
</body>
</html>

Although ContactUs.jsp put all of the form elements within an HTML table, you can see that SendMessage-input.jsp does not contain any tags for the form’s layout on the page. Struts2 form tags will lay themselves out cleanly within a containing HTML block. This behavior is somewhat troublesome if you do not like the way that Struts does it. Fortunately, this behavior is very extensible. The look and feel of Struts widgets is guided by the “theme” that is applied to the Struts elements. In this case, I specified an “ajax” theme in the <head> section of the document. In this case using the AJAX theme will generate a clean layout for the generated form, and it will generate JavaScript to perform the validation that was configured in SendMessageAction-validation.xml. There is also a small amount of JavaScript inserted manually to select the text in the <textarea> when the <textarea> receives focus for the first time. The JavaScript is inserted to demonstrate how easy it is to integrate your own JavaScript despite all of the JavaScript generated by the Struts elements. Even if a theme is chosen that does not make use of JavaScript such as the “simple” theme, the JavaScript you add will execute and work as normal.

Let us compare the view generated by each technique. Here we can see the output of the contactUs.jsp page-
screenshot of contactUs.jsp

Here is the same page if the validation fails -
screenshot of contactUs.jsp with errors

Now, let us take a look at the Struts2 pages. Here is the input page -
screenshot of SendMessage_input.action

Let us also take a look at how the page looks when validation fails.
screenshot of SendMessage_input.action with errors

What you can not see in the screenshot was the smoothness with which the error was displayed. In the first half of this example, I used some validation at the server so that for the error message to reach the user, the page would have to be submitted then reloaded with the error. In the second half of this example, the error message is inserted into the page without reloading the entire page.

There are many advantages to discuss just at this point. Obviously, each of the Struts components can be unit tested easily because they have few dependencies. Also, you can tell right away that the validation facilities in Struts are very useful and what you have not seen at this point is just how extensible they can be. I will discuss validation further in a future article. Also, you can probably already sense just how easy it would be to make changes to your business logic while minimizing changes to your UI. Another big advantage at this point is that the Java code is contained within only two simple elements. If you take a second look back through the existing code, there is no direct dependence on any part of the Servlet API. I have also moved input validation out of the code and treat it as a configuration which will allow me to concentrate on the business logic. Another advantage of Struts2’s validation facility is that values are retained with no extra effort. So if a user is working on a form and fails validation, none of what was input will be lost.

I did not cover the messageSent.jsp file because there was nothing worth discussing, but in SendMessage-success.jsp, I have access to all of the action class’s getter methods. So, I can easily echo back to the user what was sent.

SendMessage-success.jsp ->

<%@ taglib uri="/struts-tags" prefix="s"
%><html>
<head>
<title>Message Sent Successfully</title>
</head>
<body>
<h3>Your Message Has Been Sent!</h3>
<table>
    <tr>
        <td align="right">Email Address:</td>
        <td align="left"><s:property value="msgReplyTo" /></td>
    </tr>
    <tr>
        <td align="right">Subject:</td>
        <td align="left"><s:property value="subject" /></td>
    </tr>
    <tr>
        <td align="right">Message:</td>
        <td align="left"><s:property value="msgContents" /></td>
    </tr>
</table>
</body>
</html>

Although this could be done by setting request scoped variables, the Struts2 technique requires little effort. These values are not the only values available through Struts2 tags. There is a component referred to in the documentation as the “Value Stack.” Since the action is added to the value stack, all of the values available through the action’s getter methods are available in the JSP. There are other values pushed onto the value stack that I can access using a mechanism called “OGNL.”

Lastly, I need to cover the relevant section of the struts.xml file so that the action works properly. Here is an updated copy of the struts.xml -

struts.xml ->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <package name="counter" namespace="/counter" extends="struts-default">
        <action name="Counted" class="com.wantii.struts.CountableAction">
            <result>/WEB-INF/counter/ViewCounter.jsp</result>
        </action>
    </package>
    <package name="contact" namespace="/contact" extends="struts-default">
        <action name="SendMessage_*"
          class="com.wantii.struts.SendMessageAction" method="{1}">
            <result name="input">/WEB-INF/contact/SendMessage-input.jsp</result>
            <result>/WEB-INF/contact/SendMessage-success.jsp</result>
        </action>
    </package>
</struts>

This action is configured slightly differently than the first. I am using a wildcard this time around that allows me to specify which of the action’s methods to call by specifying it in the URL. This way, by specifying SendMessage_input.action, the input method is run which bypasses validation and presents the input form. The HTML form on the input page will post to SendMessage.action, which will default to running the execute method. Struts knows to treat the “_*” in a special way, so it is not necessary to post to SendMessage_execute.action. When the input form posts to the action, if validation fails, the input page is redisplayed with appropriate error messages.

Hopefully the two examples give you a feel for the ways that Struts2 can help you. Before I am finished, I will create a few more articles that give more detailed information on writing actions or using the Struts2 tags. At this point, you should start to feel that you understand more about the architecture and have a better general idea how writing code for the Struts2 framework differs from traditional JSP/Servlet programming.



This entry was posted on Tuesday, July 3rd, 2007 at 2:35 pm and is filed under Java, Struts 2. You can follow any responses to this entry through the RSS 2.0 feed.

Spread the Word

del.icio.us Digg Furl Reddit StumbleUpon Technorati


You can leave a response, or trackback from your own site.



17 responses about “Getting Started with Struts2 (Part 3)”

  1. Albert G said:

    How I add this article to Digg?

  2. wesw said:

    There is a link in the “Spread the Word” section to add this page as a Digg bookmark.

    -Wes

  3. Brian said:

    Hi, thanks for the post.

    How do I configure struts2, so that the URL: http://www.mysite.com/chg/1 calls the chg action with a parameter of 1

    Or even to get rid of the .action of a url?

  4. shahram said:

    on your first example:
    i am not sure if i understand the disadvatages of Struts2? you can have custom tags in both Struts1 and Struts2. even though, people often don’t use custom tags and instead use form beans. You can re-use actions in both also probably better in Struts2.

  5. Reddy said:

    Hi,

    I have a question related to struts 2.

    In struts 1.x Action forms have get set methods which will be automatically populated based on the input fields.

    In struts 2 Action forms are replaced by Action and pojos. That means in an action class do I need to write get set methods for all the input fields ??? will they be populated automatically ??? doesn’t this make action classes clumsy and difficult to maintain ??

  6. VamshiK said:

    Hi REddy, you are right. In struts2 we need to write all setters & getters for the form inputs.

    It may look clumsy but we are only adding setters & getters not doing anything more in the action.

    In that way we can avoid creating one more object for the ActionForm to get/read the form values.

  7. Midnight said:

    Hi Reddy,

    Actually, you can implement a class called ModelDriven along with the Preparable class to have Struts 2 populate a class which holds your data. That is much easier to maintain.

  8. Trevis said:

    wesw, thanks for these tutorials. It’s making coming up to speed with Struts2 go very quickly.

    As for the issue you guys were discussing:

    You don’t need to write those getters and setters. In the message example above he does it that way and i’m really not sure why. Here’s what i did:

    public class SendMessageAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    private static Logger log = Logger.getLogger(SendMessageAction.class.getName());
    private WebSiteMessage msg;
    private static final String EXCEPTION = “exception”;

    public String execute() {
    try {
    msg.send();
    } catch (BizException e) {
    log.severe(e.getMessage());
    return EXCEPTION;
    }
    return SUCCESS;
    }

    public WebSiteMessage getMsg() {
    return msg;
    }

    public void setMsg(WebSiteMessage msg) {
    this.msg = msg;
    }
    }

    As you’ll notice instead of duplicating the WebSiteMessage attributes in the action and going through the tedium of copying them, i just put the WebSiteMessage object as an attribute (with Bean accessors) and in my JSP i just reference it like this:

    The same goes for the validator:

  9. Trevis said:

    Repost because the mark up was removed:

    You don’t need to write those getters and setters. In the message example above he does it that way and i’m really not sure why. Here’s what i did:

    public class SendMessageAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    private static Logger log = Logger.getLogger(SendMessageAction.class.getName());
    private WebSiteMessage msg;
    private static final String EXCEPTION = “exception”;

    public String execute() {
    try {
    msg.send();
    } catch (BizException e) {
    log.severe(e.getMessage());
    return EXCEPTION;
    }
    return SUCCESS;
    }

    public WebSiteMessage getMsg() {
    return msg;
    }

    public void setMsg(WebSiteMessage msg) {
    this.msg = msg;
    }
    }

    As you’ll notice instead of duplicating the WebSiteMessage attributes in the action and going through the tedium of copying them, i just put the WebSiteMessage object as an attribute (with Bean accessors) and in my JSP i just reference it like this:

    <s:textfield name=”msg.msgReplyTo” label=”Your Email” required=”true” />

    The same goes for the validator:
    <validators>
    <field name=”msg.msgReplyTo”>

  10. hardik said:

    hi
    i could not get
    import org.apache.commons.mail.EmailException;
    import org.apache.commons.mail.SimpleEmail;

  11. shravan said:

    hi
    i m using struts 2 for my project.
    can you tell me how can i customize struts tags.

    like while creating forms struts creates its own table. and fir each field i add in form it creates a new row.

    but if i want to add the field to next column in the table rather then next row how can i do it.

    need some urgent help

    thanks

  12. rakesh said:

    Excellent article(s)!!! for a starter in struts2… your approach to explaining things was awesome!…u rock wesw

  13. Pankaj Sharma said:

    hi
    I was trying to run the example given above. But I found a static import in WebSiteMessage.java class. Can you please tell me where to get the jar file for this import and for what purpose you use it there.

  14. wesw said:

    Pankaj Sharma,

    This static import does not come from a jar file. If you take a closer look at the code, you will see that it is meant to demonstrate a helper utility to provide configuration information. To run the code in the example, simply try hardcoding values that will work in your setup. For a full reference to the commons-email library, check here –

    http://commons.apache.org/email/

    -Wes

  15. Frank said:

    I think in the Custom Tag example is an (Integer) cast missing on the second line of the getCount method (HitCounter.java, first script).

    Regards,

    Frank

  16. irha said:

    I am just starting with struts and web development in general (I should probably start learning with something else, but I don’t have a choice right now). My understanding is that there is some web.xml configuration required for struts2, but I didn’t see anything mentioned here, what am I missing?

  17. irha said:

    Regarding the above comment, is it because you start with a blank template which comes with a web.xml?

Leave a Reply