Sponsored by the Apache Software Foundation, Struts is an open source framework developed by the Jakarta Project. As stated on its homepage, Struts encourages architectures based on the Model-View-Controller (MVC), or Model 2, design paradigm. As many Web application developers have discovered, part of Struts's power lies in how easily developers can add extensions to its controller API classes as well as the tag libraries that comprise its view capabilities.
In "Mix Protocols Transparently in Web Applications" (JavaWorld, February 2002), I proposed extending Struts to incorporate a solution for mixing the HTTP and HTTPS protocols in Web applications. In this article, I walk through the steps necessary to extend Struts in that way and leverage the framework to add even more features to our solution.
Review
In my previous article, I discussed how many Web applications today transmit sensitive data, such as financial information, between the browser and Web server, in both directions. These applications usually employ the Secure Sockets Layer (SSL) and its companion protocol, HTTP over SSL (HTTPS), to prevent anyone monitoring Web transmission from viewing application data. Developing, deploying, and maintaining an application with mixed protocols can prove difficult, so I developed a solution utilizing J2EE's (Java 2 Platform, Enterprise Edition) redirect mechanism for MVC Web applications using servlets and JSPs (JavaServer Pages). The solution's full algorithm completes the following:
- It determines the protocol and port number used to request the Web resource
- If that protocol matches the protocol and port number we want for this resource, it does nothing
- Otherwise, it checks for a query string
- If no query string exists, it checks for request body parameters
- If request body parameters exist, it converts these parameters to a query string
- If any request attributes exist, it temporarily moves them to the session in a
Map
object - Finally, the algorithm performs a redirect to the same URL, using the correct protocol, modifying the specified port number, if necessary, and adding any existing or generated query string
Near the end of my previous article, I demonstrated the same mechanism for a Struts application and suggested extending Struts to incorporate the solution.
The Struts framework
Before we begin extending Struts, we must first understand how Struts works (for this article, we use the 1.0.1 release). In a typical Struts MVC application, a request made to a Struts action (in the controller role) might invoke some database or business operation and then forward the resulting JavaBean data objects (the model) to a JSP (the view) for display. The relationships between the classes that comprise the actions and the JSPs are defined at deployment time in an XML configuration file typically named struts-config.xml
.
To better see how this happens, let's look closer at the Struts classes and files involved. The ActionServlet
class defined in the org.apache.struts.action
package provides the power behind the Struts framework. By processing all Struts requests and then calling the proper Struts action, this servlet serves as a Struts application's main controller. The servlet
entry in our servlet's web.xml
deployment descriptor looks like this:
<servlet id="Servlet_1"> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!-- Struts Config --> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <!- other initialization parameters --> <load-on-startup>1</load-on-startup> </servlet>
The config
initialization parameter defines the location of the Struts definition file, struts-config.xml
.
To correctly distinguish Struts action requests, we create a servlet-mapping
entry in the deployment descriptor, indicating that any URL request matching the pattern /do/*
should forward to ActionServlet
. That entry looks like this:
<servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>/do/*</url-pattern> </servlet-mapping>
For example, the ActionServlet
will process a request to the URL /do/myAction
, whereas it won't process a request to /doMyAction
.
A typical action
definition entry in the struts-config.xml
file resembles this:
<action path="/myAction" type="test.ssl.MyAction" > <forward name="success" path="/home.jsp" /> <forward name="failure" path="/error.jsp" /> </action>
This entry specifies that the test.ssl.MyAction
class will execute the action named myAction
. In addition to whatever request-processing logic MyAction
contains, it also includes logic to determine which Web resource named in the forward
tags to forward the request to. The MyAction
class, or any class defined as an action, must extend the org.apache.struts.action.Action
class. The Action
class includes a method named perform()
for processing HTTP (or HTTPS) requests. When extending Action
to define a Struts action, you should override perform()
to define logic similar to what you might define in a servlet's doGet()
or doPost()
methods. ActionServlet
calls this perform()
method when it processes a Struts action request.
When a Struts Web application deploys, the ActionServlet
reads the struts-config.xml
file and creates a mapping for each action defined therein for its specified class type and forward page elements. Each mapping is stored within an ActionMapping
class instance, also found in the org.apache.struts.action
package. ActionMapping
contains a property for every attribute or child element of the action
file element. All ActionMapping
instances are then stored in an ActionMappings
class instance. Figure 1 displays the diagram for these four classes.

Putting it all together, we see that when the ActionServlet
receives a request, it searches its associated ActionMappings
object to find the specific ActionMapping
instance with the same name as the Struts action specified in the request. The ActionServlet
then creates an instance of the Action
subclass mapped to that action (if an instance does not already exist) and calls the perform()
method on that instance.
For good measure, Struts also includes numerous custom tags to aid in JSP development. These tags are grouped into four libraries: bean, html, logic, and template.
Extend Struts
To incorporate our protocol-switching mechanism as a Struts extension, we first need a way to specify which action requests should transmit securely via HTTPS and which should transmit using HTTP. We specify that information in the struts-config.xml
file.
Adding an attribute to the action
tag element for specifying this secure/nonsecure choice appears tempting. However, that would require us to alter that file's document type definition (DTD), an action we should avoid. Fortunately, Struts provides a mechanism for easily adding properties to an action mapping: the set-property
tag element. This subelement of the action
tag element allows us to specify a property name and value for our action definition. After adding this element, our action definition now looks like this:
<action path="/myAction" type="test.ssl.MyAction" > <set-property property="secure" value="true"/> <forward name="success" path="/home.jsp" /> <forward name="failure" path="/error.jsp" /> </action>
We will use the secure
property as a flag where a value of true specifies transmission via HTTPS and false specifies HTTP. In our mixed protocol solution for this case, if ActionServlet
receives a request for myAction
via HTTP, ActionServlet
will redirect that request back to myAction
via HTTPS.
To use this new property, we define our own extension to the ActionMapping
class named SecureActionMapping
, which simply specifies the secure
property addition. Here is the class definition:
package org.apache.struts.action; public class SecureActionMapping extends ActionMapping { protected boolean secure; public void setSecure(boolean b){ this.secure = b; } public boolean getSecure(){ return this.secure; } }
To actually use SecureActionMapping
, we must tell the ActionServlet
to use that class instead of ActionMapping
. Struts again provides a mechanism for doing that: the ActionServlet
's servlet entry in the web.xml
deployment descriptor file. We simply need to add another initialization parameter, mapping
, to tell ActionServlet
which class to use for action mappings. The restriction: The class specified in the mapping element must extend ActionMapping
. The ActionServlet
's web.xml
entry now looks like this:
<servlet id="Servlet_1"> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!-- Struts Config --> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>mapping</param-name> <param-value>org.apache.struts.action.SecureActionMapping</param-value> </init-param> <!- other initialization parameters --> <load-on-startup>1</load-on-startup> </servlet>
Now when ActionServlet
parses the struts-config.xml
file, it creates a SecureActionMapping
instance for every action defined in that file. When it encounters a set-property
element for the secure
property, ActionServlet
uses introspection to find the secure
property mutator method, setSecure()
, on that SecureActionMapping
instance. ActionServlet
's parsing logic is also smart enough to convert the specified value into the correct Boolean value.
SecureActionServlet
In addition to the standard config
and mapping
initialization parameters, we must specify two new properties: our application's port numbers for the HTTP and HTTPS protocols. We specify these the same way we specify the other initialization parameters. The values we assign to the port number parameters are stored in the two properties we define for our extension to the ActionServlet
class.
This new extension, called SecureActionServlet
, initializes these new properties. The servlet
entry in the web.xml
now has the following code:
<servlet id="Servlet_1"> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.SecureActionServlet</servlet-class> <!-- Struts Config --> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>mapping</param-name> <param-value>org.apache.struts.action.SecureActionMapping</param-value> </init-param> <!- other initialization parameters --> <init-param> <param-name>http-port</param-name> <param-value>8080</param-value> </init-param> <init-param> <param-name>https-port</param-name> <param-value>8443</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Note that we substituted the name of our new SecureActionServlet
subclass where ActionServlet
was previously specified.
The default values for the port number properties, used in cases where values are not assigned through the initialization parameters, are the standard HTTP and HTTPS port numbers of 80 and 443, respectively. SecureActionServlet
implements the logic necessary to determine whether to perform a redirect for channeling an action request to the proper protocol. This logic first finds the SecureActionMapping
object for a Struts action. Next, it checks the value of that object's secure
property to determine whether it must execute a redirect to the specified protocol for calling the Action
subclass's perform()
method. All other ActionServlet
behavior remains unchanged in its subclass. The full source code for SecureActionServlet
appears below:
package org.apache.struts.action; import org.apache.struts.util.SecureRequestUtils; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; /** * An extension of the ActionServlet class to add properties for * http and https ports. Performs logic to determine if * requests should be redirected to another (http or https) protocol. */ public class SecureActionServlet extends ActionServlet { protected String httpPort = "80"; protected String httpsPort = "443"; public void init() throws ServletException { super.init(); initPorts(); } /** * Initializes the http and http port properties * from init parameters specified in the web.xml file. * If not found there, default values remain. */ protected void initPorts(){ String value = getServletConfig().getInitParameter("http-port"); if( value != null ){ // Other validation code here. httpPort = value; } value = getServletConfig().getInitParameter("https-port"); if( value != null ){ // Other validation code here. httpsPort = value; } } public void setHttpsPort( String s ){ this.httpsPort = s; } public void setHttpPort( String s ){ this.httpPort = s; } public String getHttpsPort( ){ return this.httpsPort; } public String getHttpPort( ){ return this.httpPort; } /** * Checks whether servlet should redirect to another protocol * before calling the superclass process method. * @param aRequest is the current request object. * @param aResponse is the current response object. * @throws IOException if an input/output error occurs. * @throws ServletException if a servlet error occurs. */ protected void process(HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { // Identify the path component we will use to select a mapping. String path = processPath(request); if (path == null) { if (debug >= 1) log(" No path available for request URI " + request.getRequestURI()); response.sendError(HttpServletResponse.SC_BAD_REQUEST, internal.getMessage("processPath")); return; } // Look up the corresponding mapping. SecureActionMapping mapping = (SecureActionMapping)processMapping(path, request); if (mapping == null) { if (debug >= 1) log(" No mapping available for path " + path); response.sendError(HttpServletResponse.SC_BAD_REQUEST, internal.getMessage("processInvalid", path)); return; } // Redirect to https/http if necessary. if( checkSsl(mapping, request, response) ){ return ; } super.process(request, response); } /** Checks to see if SSL should be toggled for this * action. * @param aMapping is the mapping object for this Action. * @param aRequest is the current request object. * @param aResponse is the current response object. */ private boolean checkSsl(SecureActionMapping aMapping, HttpServletRequest aRequest, HttpServletResponse aResponse){ String redirectString = SecureRequestUtils.getRedirectString(aRequest, getHttpPort(), getHttpsPort(), aMapping.getSecure()); if( redirectString != null ){ try{ // Redirect the page to the desired URL. aResponse.sendRedirect(aResponse.encodeRedirectURL( redirectString)); return true; }catch(Exception ioe){ System.out.println("IOException in redirect" + ioe.getMessage()); } } return false; } }
Figure 2 illustrates the class diagram with our new extensions.

PageScheme custom tag
In my previous article, I discussed another piece of our mixed protocol solution, the sslext:pageScheme
custom tag. You could use this tag to specify that a JSP should display with HTTPS by assigning a true value to the JSP's secure
attribute. A false value would specify HTTP. We will implement this custom tag with its defining code and usage virtually unchanged from its description in the previous article. I include its source code and tag library descriptor, as well as the source code and tag library descriptors for all tags introduced in this article, in Resources.
SecureRequestUtils
The final part of our mixed protocol solution is the utility class SecureRequestUtils
, which performs our algorithm's heavy lifting. From its introduction in the previous article, I've incorporated it here with few changes.
Solution complete?
If we stopped here, we would have finished implementing the mixed protocol solution as a Struts extension. Through careful specification of secure actions and JSPs, we could build Web applications on this framework that protect sensitive data during its transmission.
Some concerns can arise from less careful specification of secure Struts actions and JSPs. For instance, posting a large form from an HTTP-specified page to an HTTPS-specified Struts action could result in a query string from the posted request body parameters during the redirect that exceeds the browser or Web server's capacity. Also, although SSL protects the query string during the redirected transmission, users might feel uncomfortable with posted data appearing as part of the query string in the browser's location display.
We should make the solution foolproof. Fortunately, we can by again building upon the Struts infrastructure to extend some of Struts's custom tags.
Custom form tag
You can use the Struts custom tag html:form
tag to render an HTML input form at runtime in the JSP. This tag's typical usage in its simplest form looks like this:
<html:form action="/submitAction" > <!- The form's input elements specified here --> </html:form>
At runtime, the tag would produce the following HTML form tag:
<form name="testForm" method="POST" action="/testssl/do/submitAction "> <!- The form's input elements specified here --> </form>
The name testForm
originates from the form bean's name we specified for the HTML form tag immediately above in our struts-config.xml
file. The default method for html:form
is POST
, although you can specify GET
submission methods for your forms using the method
attribute. Finally, the action specified in the HTML form tag immediately above comes from the action we specified in the html:form
tag. We just added the Web application's context root, testssl
, and our SecureActionServlet
's URL mapping, /do/*
, to it.
Just as in SecureActionServlet
's redirect logic, we can look at the secure
property's value in the SecureActionMapping
object for the action specified in the html:form
tag to determine whether the form should be submitted via HTTP or HTTPS. We compare that submission specification with the protocol used to transmit the page containing the form. If the submission protocol fails to match the current page's protocol, you can specify the correct submission protocol in the resulting HTML definition for the form.
We code this behavior into an html:form
tag extension called sslext:form
by extending the class org.apache.struts.taglib.FormTag
with our own org.apache.struts.taglib.SecureFormTag
class. As an example of this tag in use, assume a JSP transmitted using HTTP contained the following two forms:
<sslext:form action="/secureAction" > <!- The form's input elements specified here --> </sslext:form> <sslext:form action="/nonsecureAction" > <!- The form's input elements specified here --> </sslext:form>
The resulting HTML would look like this:
<form name="testForm" method="POST" action="https://localhost:8443/testssl/do/secureAction"> <!- The form's input elements specified here --> </form> <form name="testForm" method="POST" action="/testssl/do/nonsecureAction"> <!- The form's input elements specified here --> </form>
The use of this new custom tag should eliminate any redirects that might otherwise have occurred during form submissions.
Custom link tag
Struts also provides a custom html:link
tag. You substitute this tag for the HTML a
to specify anchors or links to other Web resources. The custom tag also offers other features, such as the ability to build a query string from specified bean component properties and add that query string to the specified link.
Applying the same logic outlined for our sslext:form
custom tag, we extend the html:link
to exhibit the same behavior. This tag extension, called sslext:link
, renders HTML a
tags that link directly to the specified actions using the correct protocol. This will further minimize the number of redirects performed by our mixed protocol solution's implementation. Our original and new extension classes: org.apache.struts.taglib.FormTag
and org.apache.struts.taglib.SecureFormTag
, respectively.
For an example of our new sslext:link
custom tag, assume a JSP transmitted using HTTPS includes the following two links:
<sslext:link page="/do/secureLinkAction" >Secure Link</sslext:link> <br> <sslext:link page="/do/nonsecureLinkAction" >Non-Secure Link</sslext:link>
The resulting HTML:
<a href="/testssl/do/secureLinkAction">Secure Link</a> <br> <a href="http://localhost:8080/testssl/do/nonsecureLinkAction">Non-Secure Link</a>
Had the same page been transmitted via HTTP, the resulting HTML would be:
<a href="https://localhost:8443/testssl/do/secureLinkAction">Secure Link</a> <br> <a href="/testssl/do/nonsecureLinkAction">Non-Secure Link</a>
Through the use of these two new custom tags, a Struts action request should generate a redirect as part of our solution in two instances only:
- When a user directly inputs a URL with an incorrect protocol into the browser location field
- When an action forwards directly to another action with a different
secure
property specification
Redirects would still be required for a JSP that utilizes the sslext:pageScheme
tag to specify a protocol that fails to match the protocol of the Struts action that forwarded to it.
Extension ease amplifies Struts strength
After my first article published, many developers told me they had faced the mixed protocol problem multiple times and had longed for a solution such as the one I presented. The solution's flexibility lets you easily change a Web resource's transmission protocol specification any time during development or even after deployment. Combining that strength with the power of Struts and then building upon that framework produces an even greater tool. Because Struts is an open source framework, it relies upon the innovation and contribution from numerous developers. The ease with which you can extend Struts encourages this innovation.
In this article, we added the features of the mixed protocol solution to the Struts framework with just a few extensions to the existing Struts codebase. With a few more changes, we improved upon the original solution by extending some tags from Struts's tag library. By minimizing the number of redirects, these new extended tags improved our solution's performance. For its power and ease of extension, Struts's popularity among dynamic Web application developers should continue its rapid rise.
I would like to thank Max Cooper, Prakash Malani, and Danny Trieu for their assistance and suggestions in preparing this article.
Learn more about this topic
- Download the source code and resource files that accompany this article
http://images.techhive.com/downloads/idge/imported/article/jvw/2002/05/jw-0510-struts.zip - Information about Apache's Struts project, including links to tutorials, user guide, and Javadocs
http://jakarta.apache.org/struts/index.html - Read the Java Servlet Specification 2.3 for the latest servlet deployment descriptor and API information
ftp://ftp.java.sun.com/pub/servlet/135790dwh/servlet-2_3-fcs-spec.pdf - Still the definitive text for learning and relearning Java ServletsJava Servlet Programming, Second Edition, Jason Hunter and William Crawford (O'Reilly & Associates, 2001; ISBN0596000405)
http://www.amazon.com/exec/obidos/ASIN/0596000405/javaworld - The source for JSP information and links to many more sources and custom tag libraries
http://java.sun.com/products/jsp/index.html - Learn JSP and how to create your own custom tagsWeb Development with JavaServer Pages, Duane K. Fields and Mark A. Kolb (Manning Publications, 2000; ISBN1884777996)
http://www.amazon.com/exec/obidos/ASIN/1884777996/javaworld - Build your own custom JSP tags with Tomcat in "Encapsulate Reusable Functionality in JSP Tags," Simon Brown (JavaWorld, August 2000)
http://www.javaworld.com/javaworld/jw-08-2000/jw-0811-jsptags.html - "Mix Protocols Transparently in Web Applications," Steve Ditlinger (JavaWorld, February 2002)
http://www.javaworld.com/javaworld/jw-02-2002/jw-0215-ssl.html - Search the Security section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-security-index.shtml - Search the JavaServer Pages section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-jsp-index.shtml - Search the Servlets section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-servlets-index.shtml - Sign up for JavaWorld's free weekly email newsletters
http://www.javaworld.com/jw-subscribe - Share your thoughts on SSL with your fellow developers in JavaWorld's Java Security discussion
http://forums.idg.net/webx?50@@.ee6b80e - You'll find a wealth of IT-related articles from our sister publications at IDG.net