Abstract
This article will provide a simple introduction to some of the very basic Struts 1.x (i.e. xml config file driven) implementation features that I have found key to understanding how to use Struts, as well as how to achieve JSP screen display using Struts taglibs and JSTL (JavaServer Pages Standard Tag Library).
Summary
To my mind, the key component to understand Struts is the struts-config.xml. It's the traffic cop that directs business logic flow from both jsp and servlet/Action class.
I'll discuss the two types of form bean type objects that Struts provides to transport information from the jsp into the Action class, and I'll discuss the two types of return process from an Action class back out to either a jsp for display or for further 'forward' processing in another Action class (called 'chaining').
I'll also bring out some issues of error processing and displaying messages onto a jsp. And that discussion would not be complete without talking about some of the conditional logic and iteration capability that Struts in combination with JSTL provide.
Some Background
It's now 2011 and I've been developing business software under an M-V-C Model 2 framework since 2000. I've been using Struts since around 2005. The operative word there is 'using'. Unlike programming with the raw '<%' and '%>', I found Struts helpful for doing a lot of that heavy lifting for me. It was like magic.
The problem was that I always entered a company with a pre-built infrastructure that I wrote my code on top of; I never really looked under the hood to see what all those tags meant that I was using. I just used them, things worked, and all was good.
I was faced with what all of us developers are faced with: we come into an environment, pick up what's going on as fast as we can, and start churning code out. The other day, however, I ran into an error that I just could not figure out no matter how many debug statements I threw into my code. In desperation, I turned to my friend, Wizard Rajan Vora, for help.
After he explained to me what was causing the issue, I could see that I had got myself into the mess because I didn't really understand how Struts was marshalling my data and moving it around. That is, I really didn't understand what was going on inside the magic box of Struts.
Now, I probably ran into this same situation before. But because I didn't understand the inner workings of struts configuration, I solved the problem but didn't understand the solution. And thus, this attempt to put together a very simple Struts app that deals just with the types of really, really basic things you need to understand so that you can use this stuff intelligently.
Is what I'm going to document here The Word? No. I hope there aren't too many errors. All this is, is a representation of how I see how Struts wires stuff together; the highlights so that I use the tool to accomplish the basic tasks that I have to do on a daily basis.
Struts is really complex. Really complex. You can do a lot of really cool stuff with it. I don't do cool stuff. I write business applications. That's the cool stuff. Struts is just the tool I use to do it. Tomorrow, I may be back in raw jsp mode again. For this reason, I don't want to get dependant upon Struts. Struts is a tool of which I need certain parts of to do my job. But I don't need (or want) to know all of it. I'm not that smart, anyway. So, this article is sort of a 'Best of Struts Stuff' approach more to remind myself later, really, than about teaching Struts.
So, you have to temper my comments as to where in your career you are. I say what I say because I'm a senior level business applications software developer. I'm not a 'systems' engineer, or an architect, or principal engineer. I don't like to develop infrastructure or tools. I write complex web business apps using tools someone else far more competent than me has already put into place. But no one understands business development like I do; that's what I excell at. I'm not a race car auto mechanic. I just drive the car in the race. 'nuff said....
Anyway, if the above sounds interesting to you then I hope what I've written will help. But please don't write me if all you want to do is to prove how much smarter you are than I am that have a better way. What's here is simple, it worked for me, and I hope it works for you. That's as far as you should go with it. Besides, if you are smarter than me, then why bother wasting your time reading this? Please....
Basics
I assume this is not the first web application that you are looking at and that you understand Struts in general, can set up Tomcat, use an ant file, know the difference between a servlet and a Struts Action class, have worked with Java Interfaces and abstract classes, and grasp the use of web.xml and struts-config.xml. If not, then this 'simple' example won't be so simple and you may want to pass on this article.
Using struts tags in a JSP does require identifying some struts taglib functions at the top of the page. Some of the ones that you will most likely need are:
The main components of a Struts application are, in no particular order of importance:
Action class
Form bean
web.xml
struts-config.xml
A .jsp file
The trick in using Struts is to find those key useful elements in these components that you need to know in order to understand enough to get your job done, and enough so that you at least know what to look for when you need a certain piece of functionality.
Digging Into The Core
The core of Struts is struts-config.xml. Whereas web.xml just defines the components of your web application, struts-config.xml controls how the information flows through your application, the traffic cop.
The struts-config.xml file is really the focal point of understanding Struts. Hitting a link on a .jsp or doing a forward reference from an Action class triggers commands in stuts-config.xml that redirect application flow. I'm going to address both of these, but before that you need to understand how information gets from the jsp to the Action class.
Form Beans The struts-config.xml file also defines the two different types of form objects you can use in a Struts application:
An xml bean structure in the <form-beans> section that is extracted from the HttpServletRequest as a DynaActionForm,
Or, an actual JavaBean class that extends ActionForm in an <action-mappings> entry.
You may not know what a 'form bean' is. Well, you know that you are trying to get information from the jsp to the Action class. These elements need to be turned into properties on a JavaBean object for manipulation. In a non-Struts environment, these propertis on the jsp would be picked up in the Servlet class via something like:
String title = (String) request.getParameter("title");
And from there, you'd make some assignment like 'bean.setTitle(title);' to pass that around the application. Depending on how many properties on the page you are dealing with, that's a lot of typing that can go wrong and a lot of clutter in the code. Struts does this for you in two different ways, and it's one of it's great strengths.
The xml DynaActionForm The struts-config.xml file defines an xml bean structure in the <form-beans> section that is extracted from the HttpServletRequest as a DynaActionForm like this:
You can then submit the form and pick the results up in your Action class this way:
Of note is that you may now be tempted to pass this DynaActionForm around your application. For example, why not just pass it to your data access layer object to make a call into the database? Well, that binds your entire app to Struts. If you change to Velocity, or back to raw parameter grabbing, then the whole app is affected.
To keep the Struts layer separate, always pull the data out of the DynaActionForm and stuff it into your own JavaBean. Wow! Then what's the point of Struts? Why bother?
Well, if this was Struts only feature, then, yeah, why bother. You have to look at all of the basic features in aggregate to finally say that not only does Struts give you some economy of usage, but that it also creates a more logical framework that allows you as a developer to build web applications using M-V-C Model 2 'best practices'.
The ActionForm The struts-config.xml file also defines an actual JavaBean class that extends ActionForm in an <action-mappings> entry like this:
The name attribute defines a 'bookBeanForm' which translates to a BookBeanForm.java object that you have created and which extends ActionForm. But how does Struts know where to find this BookBeanForm? You have to add the following entry into your struts-config.xml <form-beans> section as follows:
Note that I put BookBeanForm into the action directory and not into a pojo beans directory. The action directory is only for classes that extend Struts objects. Since BookBeanForm.java extends ActionForm, it must go into action so that it does not get mixed into the other code. This is just naming convention. Struts extended Action class objects end in 'Action' and Struts extended ActionForm objects end in 'Form'. Duh....
Again, ActionForm ties you to Struts. So, be careful about passing this around the app. You'll have to strip the data out and build your own JavaBean.
The value these form beans provide is implementing Struts greatest heavy lifting capability from that of pure manipulation of the HttpServletRequest object, i.e. stripping out the name/value pairs and stuffing them into your data objects.
Which Form Bean To Use So, when would you use a DynaActionForm over that of an ActionForm? It doesn't matter. The gage I use is that pages which are returning just a few properties, like a search form, I put into the struts-config.xml as an xml DynaActionForm. A page that is being used maybe to return a big row of data that might have been edited is thus better facilitated by an ActionForm extended JavaBean. But that's just me.
The struts-config.xml file so far Here is our complete struts-config.xml file so far so that I can refer to it later and add entries as we go:
Note that I am writing examples that only highlight stuff that is relevant to what I'm talking about at any given time. Thus, this (and any other example that I give of) 'complete' file listing is only as complete as I need it for my instructional purposes but may not 'work' in that form or really be totally complete. Buyer beware....
Control Flow From the JSP
Okay, from everyone's view: web application analysis and development begins with a submit from a .jsp file. Here's a simple one that we'll disect:
The 'form' Structure The first thing to look at is the jsp's form structure:
<html:form action="/books/lookup.do"...
Note that the action attribute '/books/lookup.do' is found in in the struts-config.xml.
Let's disect this.
/books/lookup From the jsp, struts-config.xml looks for the 'stuff' in front of a '.do'. In this case, the page is telling Struts to find the action mapping for '/books/lookup' and do whatever that xml referencing directs it to do.
Note that this reference could be '/books/bob' or '/books/thisistag1' or anything. It must, however, start with a '/books' because, remember, this tag is going to be appended onto the end of an HttpServletRequest which needs the context '/books' of the application.
type The type attribute identifies the Action class where the jsp's action parameter will find a method of that name on it. This means that you better have an Action class called BooksAction in the indicated path with a 'findTitle()' method on it or things will blow up.
name The name attribute identifies the DynaActionForm or ActionForm that the Action class will use to get the properties passed from the jsp to the Action class. Other entries in struts-config.xml will define what and where this form bean is.
scope The scope property could be any: request, session, or application. It all depends on what persistence you are assigning to the bean in the name attribute.
Be careful putting anything into the session or application scope. Stuff has a habit of not getting flushed out correctly and can hang around plugging the application up with unnecessary garbage. Normally, all you want to do is take the submit parameters in the request and use them for that one query. Rarely do you need to persist this information.
Generally, information that you need to persist through, say in a session, would be a user logon bean. But those are created in the receiving Java code and put into session there.
validate Validate can be set to true or false. If you set it to true, it will look on the form bean for a method called 'validate()', formed as follows:
If it doesn't find it, things will just go on. If it does find the method, and we're thus talking about an ActionForm extended object, you can put some validation code in there.
For example, we see that the jsp is passing a title parameter in the request. Let's say that instead of using the searchCriteriaForm xml DynaActionForm in the xml that we instead use the name reference to bookBeanForm, i.e. if we wanted the searchCriteriaForm, then the reference in the action mapping for name would be: "searchCriteriaForm".
So, if something goes wrong in the validate() method in an ActionForm extended object, the Action class just returns to the jsp and the tag '<html:errors/>' is all you need to display the error.
Note that the validate() method returns type ActionErrors. If you do your own validation (i.e. in the Action class you call some method to check a received property from the jsp submit), your method should return type ActionMessages. That's something I'll chat about a little later.
parameter The parameter attribute simply says to look at the HttpServletRequest for a parameter called 'action' as the method on the Action class to call.
Using 'parameter="action"' could just as easily be cast as 'parameter="bob"' and it would do the same thing; you'd just have to se the jsp to have the hidden input field name to 'bob'. We just use 'action' as a common term as a coding convention; everywhere I've ever worked has expressed it as 'action'.
input The input parameter tells you where to go when the method called on the Action class finishes and the return is of type ActionForward, usually expressed as: 'return mapping.getInputForward();'.
In our case in struts-config.xml, we are telling flow control to go to the catalog.jsp found at '/books/pages'.
Control Flow From the ActionClass
Okay, after looking at flow control instructions coming from the jsp into struts-config.xml, let's take a look at things where the Action class is making the request. This is done via two methodologies: getInputForward() and findForward(forwardReference).
Return via mapping.getInputForward() We've just seen how the getInputForward() return works. It's simply found in the input attribute of the action path tag in the action mappings section.
Return via mapping.findForward(forwardReference) Now, what if under certain circumstances in the findBooks() method on BooksAction.java I want control to be passed to another method in another Action class, say to the method bannedTitle() in the BannedBooksAction and from there go to the bannedlist.jsp? Let's look at the following addition to this action mapping entry in struts-config.xml where the BooksAction.java method returns via:
return mapping.findForward("sendAlert");
The corresponding new 'sendAlert' entry would be created in a '<forward name="' attribute within the current '<action path="' tag as follows:
And then you have to make a corresponding new action mapping entry for '/books/alerts/banned' like this:
Displaying Messages On The JSP
In Object Oriented lingo, a Struts Action class is a 'controller' class. It is the controller class that thus manages displaying messages to the user from the rest of the code.
If an Exception happens three layers down in some DAO object, that object determines what the message should be, bundles it into some sort of AppException that extends Exception, and throws it all the way up the calling stack to the originating method in the controller.
It's the controller's job to then extract the message, put it into some sort of form that the jsp will understand, and then return (i.e. pass control to some designated jsp via the getInputForward() or findForward(ref) methodologies).
Struts has a marvelously simple method for doing this, but like all things in Struts this involves a lot of xml and property file work in the background. We're going to investigate this process from the origination of the Exception and follow that all the way out to display on the jsp.
Catching The Exception And Forming The Message Let's say we're in the findBooks() method in BooksAction and this results in a calling sequence through a couple of layers of business objects and finally landing in BooksDAO.findBooks(Map data) where something baaaaad happens.
Let's say a call is made out to the database and some sort of SQLException occurs. We're going to want to inform the user of this by displaying the message 'A database error occurred while executing your request.' back on the catalog.jsp. Using Struts, we'd do this in the following manner:
Note that we're just following the track for the SQLException for the 'syserrors' reference; I'll mention what's going on with the 'usererrors' reference later.
Recatching the Exception in the Action class We're going to let this error blow all the way out to the initiating caller in the Action class and catch it there.
Just as an aside here, because the parameters for the ActionMessages object are being pulled out of the custom AppException object, it may not be as simple to just have the second parameter where you create the new ActionMessage object with the single 'ae.getPropertyRef()'. The implication here is that the getPropertyRef() method is something like what we added in the method that threw the exception: 'db.error.sys'. This references just that one tag in the properties file.
What if we instead want to use the 'gen.error.data' tag that uses the {0}, {1}, etc. user supplied text? We'd then have to create our message using something like the following form:
Note the addition of the 'ae.getVariableText()' etc. additional parameters to the ActionMessage constructor; that's what I want to draw your attention to. A little more on this later when I'm actually talking about these message properties files. To keep in mind here, you may have to create a custom method in an ancestor application class that extends Struts Action that would have a method like 'postErrors(ae, request) that would decipher what you want to do and from there use the 'saveErrors(request, errors) call.
ActionMessages and ActionMessage are Struts classes, and saveErrors() is a method in an ancestor Struts Action class. This is why I form my own home grown AppException down in the bowels of the application so that I don't pollute my application with Struts objects. Only the controller layer should reference Struts. So, in this example, I just pull the info out of my AppException that the Struts ActionMessages object will need.
How you choose to manage all of this is up to you. But that's a complication beyond the scope of just identifying what an ActionMessages object is vs. the ActionErrors object mentioned before, when I was discussing the Struts ancestor abstract validate() method on the Struts ActionForm class.
ActionMessages: first parameter for display section The first parameter represented via 'ae.getDisplayArea()' will use the String 'syserrors' as its value. What Struts will do is look for the following code on the jsp and display the message there:
Whereas in another part of the jsp you might have the following code to display 'user' type messages:
Another way the page display can be handled from the request is:
In this form, you are formatting the messages in a bulleted list. Note that the property attribute of your 'html:messages' tag must match the first parameter of the ActionMessages.add method. There's other options around all of this, but this will get you started, a basis of terminology upon which to read more about.
And not to state the obvious, but ActionMessages processing is not just for 'errors'. They hold any type of message you want to display to the screen.
It's interesting to note that as these messages are created in the Action class, they are really stored in a List. Thus, if you go through your code accumulating user errors as you go through their form input, Struts will display them all under the appropriate tag on the jsp via the 'html:messages' tag.
ActionMessages: second parameter for property file reference The second parameter in the ActionMessages creation is represented via 'ae.getPropertyRef()'. The reference that I passed for this when I caught the SQLException was the String 'db.error.sys'.
In your struts-config.xml file, you may have seen an entry like this:
What this entry indicates is a resource 'bundle' file called 'struts' has a reference to an 'application.properties' file that you need to place at the following path location:
\war\books\WEB-INF\classes\resources
Note that you could reference this file in struts-config.xml as simply:
parameter="application"
But, then the file would be found in:
\war\books\WEB-INF\classes
Not really the best place for it.... Just create a 'resources' directory under 'classes'.
You could also make this reference any properties file in your structure via:
Regardless, this is just a regular ol' java.utils.Properties property file that will contain the following entries:
I'll show an example that uses all three tags, though in real life you probably wouldn't do it this way. Say that you pass a UserBean to the page and want logic on the page to show different messages depending on what that user's paid up status is.
JSP Iteration
Struts combines with JSTL to offer two different methods of iteration that I've found simple to implement that I'll mention for purposes of this example: c:forEach and logic:iterate. We'll take a look first at simple iteration through an ArrayList, and then I'll show how to iterate through the keys of a Map object.
Let's say that from my BookAction.java Struts extended Action class, I obtain an ArrayList of BookBean.java objects, and put this list into the HttpServletRequest as a 'listOfBooks' entry.
BookBean.java
Notice that to display the content of the element 'listOfBooks' on the jsp, we'll need two loops. The first loop will spin through grabbing each individule BookBean.java object which enables us to get the title property. The second loop will spin through the embedded ArrayList of author names, i.e. property authors, and print them out.
The example shows how to pull an ArrayList out of the request scope and iterate through it. I'd just like to throw in here how to pull a Map out of the scope and iterate through the keys as I found it hard to initially figure out how to do this.
In this example each key is a city name and the value element is a single BookBean.java object, which, in turn, has an ArrayList of authors. The code will print out a different welcome message depending on the city, and then print out the list of authors.
Remember, this is just an example to show the two simplest ways I use for iteration. As you become more experienced, you'll find specific uses for each methodology as there is a lot more depth to these than I'm showing here.
Wrapping Up
Having gone through all of that, I have to state that it's really far, far more complex and confusing than this short intro.
This just scrapes the surface, but at least covers basic generic terminology and the basic process involved to get you started.
I think the biggest take-away that you should have from this is the use of struts-config.xml. That's the real dog that wags all the other tails of a Stuts app. The more comfortable you are with struts-config.xml, the more of Struts' other features you will become aware of - whether you use them or not.