Struts Miscellaneous
— print (last updated: Nov 8, 2009) print

Select font size:
To run this sample project Eclipse, download and install the WAR file: StrutsMiscell.war. In NetBeans, download the StrutsMiscell.zip archive. These JAR files are needed for NetBeans:
Struts Essentials:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
commons-logging-1.1.1.jar
freemarker-2.3.15.jar
ognl-2.7.3.jar
struts2-core-2.1.8.jar
xwork-core-2.1.6.jar
Tiles Plugin:
commons-beanutils-1.8.1.jar
commons-digester-2.0.jar
struts2-tiles-plugin-2.1.8.jar
tiles-api-2.1.4.jar
tiles-core-2.1.4.jar
tiles-jsp-2.1.4.jar
tiles-servlet-2.1.4.jar
JSTL:
jstl.jar
standard.jar

Layout

We've expanded the attribute features provided to the layout by adding lists, specified by this construction:
<put-list-attribute name="NAME">
  <add-attribute type="..." value="..." />
</put-list-attribute>
This list can then be accessed as a bean in the JSP file via:
<tiles:useAttribute id="BEAN" name="NAME" classname="java.util.List" />
Afterwards, the BEAN is available for usage as a list.

For some reason that does not seem obvious, a single definition cannot use both put-attribute and put-list-attribute tags; multiples of either one or the other, only. In order to circumvent this problem, each tiles definition that requires both lists and regular attributes is represented by two levels of extension. There are two occurrences of this construction in our tiles definition file:
<definition name="preBase" template="/layout.jsp">
    ... put-attribute tags ...
</definition>

<definition name="baseLayout" extends="preBase">
    ... put-list-attribute tags ...
</definition>

and
<definition name="ajaxBase" extends="baseLayout">
    ... put-attribute tags ...
</definition>

<definition name="ajax" extends="ajaxBase">
    ... put-list-attribute tags
</definition>

Attributes within the tags

You'll notice three attributes in the put-attribute, put-list-attribute and add-attribute tags:
name, type, value
The name, of course, is what identifies it for the JSP file usage. The value attribute is used to provide the content for a single put-attribute or for the add-attribute as part of a list. How the content is obtained by the value depends on the type, which goes like this: There is apparently a default assumption about the what the type is if it is not explicitly mentioned: if the value starts with a "/", the type is assumed to be "template"; otherwise it is assumed to be "string".

tiles.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE tiles-definitions PUBLIC ...> <tiles-definitions> <definition name="preBase" template="/layout.jsp"> <put-attribute name="nav" value="/nav.jsp" /> </definition> <definition name="baseLayout" extends="preBase"> <put-list-attribute name="cssfiles"> <add-attribute type="string" value="css/common.css" /> </put-list-attribute> <put-list-attribute name="jsfiles" /> </definition> <definition name="welcome" extends="baseLayout"> <put-attribute name="title" value="Welcome" /> <put-attribute name="content" type="string" value="Welcome to this example." /> </definition> <definition name="sess" extends="baseLayout"> <put-attribute name="title" value="Session Example" /> <put-attribute name="content" value="/sess.jsp" /> </definition> <definition name="login" extends="baseLayout"> <put-attribute name="title" value="Login validation" /> <put-attribute name="content" value="/login.jsp" /> </definition> <definition name="failed" extends="baseLayout"> <put-attribute name="title" value="Failed" /> <put-attribute name="content" value="/info.jsp?info=Failed" /> </definition> <definition name="ajaxBase" extends="baseLayout"> <put-attribute name="title" value="Ajax example" /> <put-attribute name="content" value="/ajaxlogin.jsp" /> </definition> <definition name="ajax" extends="ajaxBase"> <put-list-attribute name="jsfiles"> <add-attribute type="string" value="js/jquery.js" /> <add-attribute type="string" value="js/errors.js" /> <add-attribute type="string" value="js/jquery.form.js" /> <add-attribute type="string" value="js/login.js" /> </put-list-attribute> </definition> </tiles-definitions>

layout.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title><tiles:insertAttribute name="title" /></title> <tiles:useAttribute id="cs" name="cssfiles" classname="java.util.List" /> <tiles:useAttribute id="js" name="jsfiles" classname="java.util.List" /> <c:forEach var="item" items="${cs}"> <link rel="stylesheet" href="${item}" type="text/css" /> </c:forEach> <c:forEach var="item" items="${js}"> <script src="${item}" type="text/javascript" ></script> </c:forEach> </head> <body> <div id="nav"><tiles:insertAttribute name="nav" /></div> <h1><tiles:insertAttribute name="title" /></h1> <div id="content"> <tiles:insertAttribute name="content" /> </div> </body> </html>

Query parameters to JSP scripts

Query parameters can be passed to a JSP script within the value attributes (of template type), e.g.:
<put-attribute name="content" value="/info.jsp?info=Failed" />
This allows us to use this generic script to generate simple informational output:

info.jsp
${param.info}

nav.jsp variation

The generation of navigational links has been written somewhat differently.

util.Nav
package util; import java.util.*; public class Nav { private Map<String, String> links = new LinkedHashMap<String, String>(); public Nav() { links.put("welcome", "Home"); links.put("sess_input", "Session Example"); links.put("login_input", "Login Example"); links.put("ajax_input", "Ajax Example"); } public Map<String, String> getLinks() { return links; } }

nav.jsp
<%@taglib uri="/struts-tags" prefix="s"%> <s:bean name="util.Nav" var="nav" /> <table class="nav"> <tr> <s:iterator var="entry" value="#nav.links"> <td><a href="<s:url action="%{#entry.key}" />">${entry.value}</a></td> </s:iterator> </tr> </table>
Instead of pushing the nav bean on the value stack and accessing its links property we use the notation "#" to access the bean directly:
<s:iterator var="entry" value="#nav.links">

The struts config file

The main new concept introduced here is the wildcard action name which allows a given action definition tag to handle multiple actions. For example:
<action name="sess_*" method="{1}" class="action.Sess">
   <result name="success" type="tiles">sess</result>
</action>
means that, say, the action sess_input will call the input method in the action.Sess class.

struts.xml
<!DOCTYPE struts PUBLIC ...> <struts> <constant name="struts.action.extension" value="htm" /> <package name="default" extends="struts-default"> <result-types> <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" /> </result-types> <action name="welcome" class="action.Welcome"> <result name="success" type="tiles">welcome</result> </action> <action name="sess_*" method="{1}" class="action.Sess"> <result name="success" type="tiles">sess</result> </action> <action name="login_*" method="{1}" class="action.Login"> <result name="initial" type="tiles">login</result> <result name="input" type="tiles">failed</result> <result name="success" type="tiles">login</result> </action> <action name="ajax_*" method="{1}" class="action.Login"> <result name="initial" type="tiles">ajax</result> <result name="success">/info.jsp?info=success</result> <result name="input">/info.jsp?info=failed</result> </action> </package> </struts>

Session usage

The Sess Bean and its invoking script illustrate how to access and maintain session information in the action handlers. The key ingredient is the class:
import com.opensymphony.xwork2.ActionContext;
which gives access to the session through the call:
ActionContext.getContext().getSession()
Using this we can control the session contents using the methods:
get("attr")      // retrieve session object for this attribute
put("attr",obj)  // store session object for attribute
This example compares info and sessInfo properties as set by the sess.jsp script. The former is a usual request-stable property and the latter, a session-stable property. The relevant portion of struts.xml is:
<action name="sess_*" method="{1}" class="action.Sess">
  <result name="success" type="tiles">sess</result>
</action>
The initial action used by the navigational link is "sess_input" and the action files are:

sess.jsp
<%@taglib uri="/struts-tags" prefix="s" %> <s:form action="sess_next"> <s:textfield name="info" label="Info" /> <s:textfield name="sessInfo" label="SessInfo" /> <s:submit /> <s:submit action="sess_clear" value="Clear" /> </s:form>

action.Sess
package action; import com.opensymphony.xwork2.ActionContext; import util.Data; public class Sess { public String input() { return "success"; } public String next() { return "success"; } public String clear() { ActionContext.getContext().getSession().remove("data"); return "success"; } private Data data; public Sess() { data = (Data) ActionContext.getContext().getSession().get("data"); if (data == null) { data = new Data(); ActionContext.getContext().getSession().put("data",data); } } public String getSessInfo() { return data.value; } public void setSessInfo(String info) { data.value = info; } private String info; public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }

Login validation

The Login Bean and its usage initially appears relatively simple. The relevant portion of struts.xml is:
<action name="login_*" method="{1}" class="action.Login">
   <result name="initial" type="tiles">login</result>
   <result name="input"   type="tiles">failed</result>
   <result name="success" type="tiles">login</result>
</action>
The initial action used by the navigational link is "login_input" and the action files are:

login.jsp
<%@ taglib uri="/struts-tags" prefix="s" %> <s:form action="login_execute"> <s:textfield name="username" label="User Name" /> <s:submit /> </s:form>

action.Login
package action; import com.opensymphony.xwork2.ActionSupport; public class Login extends ActionSupport { private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String input() { return "initial"; } @Override public String execute() { return "success"; } }
The new feature is the automatic validation effected by this XML file:

action/Login-validation.xml
<?xml version="1.0" encoding="UTF-8"?> <!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="username"> <field-validator type="requiredstring"> <message>Username is required</message> </field-validator> </field> </validators>
Both the name and the positioning of this file are important. The name is of the format:
ACTION_BEAN-validation.xml
I believe the "-validation.xml" part is case insensitive. This is placed the same directory as the ACTION_BEAN itself. This validation example ensures that the username field, when trimmed of whitespace, is non-empty; i.e., there must be at least one non-whitespace character.

It's essential to understand the behavior of this validation. Note our first usage of the ActionSupport class:
public class Login extends ActionSupport 
We could have done this in all previous classes, but we wanted to illustrate that, for the most part, Action objects are simple pojos (plain old java objects). In this case it is necessary. Here is how it works:
  1. The special input method is recognized by struts as the one method which does not do validation. This is needed because our first activation of an action, leading to an input form cannot have the correct form values.
  2. Upon submission, if the validation is successful, the desired result string (in this case "success" from the execute method) is returned.
  3. If the validation fails the execute output is ignored and the "input" string is returned (this seems like a poor default choice). Our struts action recognizes the input result as failure.
The choice of non-validating method (input) is configurable, and I currently do not know if the failure output string ("input") is configurable or not. There is an extensive list of XML validators which can be used, allowing you to effectively not worry about doing validation whatsoever within the action class.

Reentrant version with automatic error generation

Struts has the ability to "put the errors into the form," in particular, using the error messages associated with the validation file. This is a very simple modification:
  1. Modify the layout.jsp file adding these two lines (indicated by bolding):
    ...
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <html>
    <head>
    ...
    <s:head />
    </head>
    <body>
    ...
    
  2. Modify struts.xml in one place, for the input result, making the tiles login the consistent target throughout:
    <action name="login_*" method="{1}" class="action.Login">
      <result name="initial" type="tiles">login</result>
      <result name="input"  type="tiles">login</result>
      <result name="success" type="tiles">login</result>
    </action>
    
Restart the server (if necessary) and run the script. You will see several changes: and enter without input. You'll see two changes: an entry above the

Sample Ajax usage

This is our first venture into using AJAX with Struts. Apparently Eclipse's internal browser does not work with some aspect of the JavaScript, so don't use it to test this.

The same Login action class is used as in the previous example. The relevant script is:

ajaxlogin.jsp
<%@taglib uri="/struts-tags" prefix="s" %> <s:form action="ajax_execute"> <s:textfield name="username" label="User Name" /> <s:submit /> </s:form>
which is essentially the same as before, but the action call differs. The initial action from the navigational link is "ajax_input" and the struts setup is this:
<action name="ajax_*" method="{1}" class="action.Login">
  <result name="initial" type="tiles">ajax
  <result name="success">/info.jsp?info=success
  <result name="input">/info.jsp?info=failed
</action>
The two results "success" (when valid) and "input" (whe not valid) do not generate tiles results. Instead we want these to be simple data outputs gnerated by the info.jsp script which can be used by JavaScript.

info.jsp
${param.info}
We use the jQuery setup with form plugin introduced through the tiles definitions:
<definition name="ajaxBase" extends="baseLayout">
  <put-attribute name="title" value="Ajax example" />
  <put-attribute name="content" value="/ajaxlogin.jsp" />
</definition>

<definition name="ajax" extends="ajaxBase">
  <put-list-attribute name="jsfiles">
    <add-attribute type="string" value="js/jquery.js" />
    <add-attribute type="string" value="js/errors.js" />
    <add-attribute type="string" value="js/jquery.form.js" />
    <add-attribute type="string" value="js/login.js" />
  </put-list-attribute>
</definition>
Finally, the key JavaScript file controls the activations:

login.js
$(function() { $('#ajax_execute').ajaxForm( function(data) { data = data.trim() alert(data) if (data != "success") $('#ajax_execute_username').css("background-color", "red") } ) $('#ajax_execute_username').focus(function(){ $('#ajax_execute_username').css("background-color", "white") }) })
The id values of ajax_execute_username are already created by the struts form construction.


© Robert M. Kline