5. Configuring Applications
5.1 Overview
Before you can build an application, you need to lay a solid foundation. There are several setup tasks you need to perform before deploying your Struts application. These include components in the Struts configuration file and in the Web Application Deployment Descriptor.
5.2 The Struts configuration file
The Building Controller Components chapter covered writing the <form-bean> and <action-mapping> portions of the Struts configuration file. These elements usually play an important role in the development of a Struts application. The other elements in Struts configuration file tend to be static: you set them once and leave them alone.
These "static" configuration elements are:
<controller>
<message-resources>
<plug-in>
<data-sources>
5.2.1 Controller Configuration
The <controller> element allows you to configure the ActionServlet. Many of the controller parameters were previously defined by servlet initialization parameters in your web.xml file but have been moved to this section of struts-config.xml in order to allow different modules in the same web application to be configured differently. For full details on available parameters see the struts-config_1_2.dtd or the list below.
bufferSize - The size (in bytes) of the input buffer used when processing file uploads. [4096] (optional)
className - Classname of configuration bean. [org.apache.struts.config.ControllerConfig] (optional)
contentType - Default content type (and optional character encoding) to be set on each response. May be overridden by the Action, JSP, or other resource to which the request is forwarded. [text/html] (optional)
forwardPattern - Replacement pattern defining how the "path" attribute of a <forward> element is mapped to a context-relative URL when it starts with a slash (and when the contextRelative property is false). This value may consist of any combination of the following:
$M - Replaced by the module prefix of this module.
$P - Replaced by the "path" attribute of the selected <forward> element.
$$ - Causes a literal dollar sign to be rendered.
$x - (Where "x" is any character not defined above) Silently swallowed, reserved for future use. If not specified, the default forwardPattern is consistent with the previous behavior of forwards. [$M$P] (optional)
inputForward - Set to true if you want the input attribute of <action> elements to be the name of a local or global ActionForward, which will then be used to calculate the ultimate URL. Set to false to treat the input parameter of <action> elements as a module-relative path to the resource to be used as the input form. [false] (optional)
locale - Set to true if you want a Locale object stored in the user's session if not already present. [true] (optional)
maxFileSize - The maximum size (in bytes) of a file to be accepted as a file upload. Can be expressed as a number followed by a "K", "M", or "G", which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. [250M] (optional)
multipartClass - The fully qualified Java class name of the multipart request handler class to be used with this module. [org.apache.struts.upload.CommonsMultipartRequestHandler] (optional)
nocache - Set to true if you want the controller to add HTTP headers for defeating caching to every response from this module. [false] (optional)
pagePattern - Replacement pattern defining how the page attribute of custom tags using it is mapped to a context-relative URL of the corresponding resource. This value may consist of any combination of the following:
$M - Replaced by the module prefix of this module.
$P - Replaced by the "path" attribute of the selected <forward> element.
$$ - Causes a literal dollar sign to be rendered.
$x - (Where "x" is any character not defined above) Silently swallowed, reserved for future use. If not specified, the default pagePattern is consistent with the previous behavior of URL calculation. [$M$P] (optional)
processorClass - The fully qualified Java class name of the RequestProcessor subclass to be used with this module. [org.apache.struts.action.RequestProcessor] (optional)
tempDir - Temporary working directory to use when processing file uploads. [{the directory provided by the servlet container}]
This example uses the default values for several controller parameters. If you only want default behavior you can omit the controller section altogether.
<controller
processorClass="org.apache.struts.action.RequestProcessor"
contentType="text/html"/>;
5.2.2 Message Resources Configuration
Struts has built in support for internationalization (I18N). You can define one or more <message-resources> elements for your webapp; modules can define their own resource bundles. Different bundles can be used simultaneously in your application, the 'key' attribute is used to specify the desired bundle.
className - Classname of configuration bean. [org.apache.struts.config.MessageResourcesConfig] (optional)
factory - Classname of MessageResourcesFactory. [org.apache.struts.util.PropertyMessageResourcesFactory] (optional)
key - ServletContext attribute key to store this bundle. [org.apache.struts.action.MESSAGE] (optional)
null - Set to false to display missing resource keys in your application like '???keyname???' instead of null. [true] (optional)
parameter - Name of the resource bundle. (required)
Example configuration:
<message-resources
parameter="MyWebAppResources"
null="false" />
This would set up a message resource bundle provided in the file MyWebAppResources.properties under the default key. Missing resource keys would be displayed as '???keyname???'.
5.2.3 PlugIn Configuration
Struts PlugIns are configured using the <plug-in> element within the Struts configuration file. This element has only one valid attribute, 'className', which is the fully qualified name of the Java class which implements the org.apache.struts.action.PlugIn interface.
For PlugIns that require configuration themselves, the nested <set-property> element is available.
This is an example using the Tiles plugin:
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property
property="definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
</plug-in>
5.2.4 Data Source Configuration
Besides the objects related to defining ActionMappings, the Struts configuration may contain elements that create other useful objects.
The <data-sources> section can be used to specify a collection of DataSources [javax.sql.DataSource] for the use of your application. Typically, a DataSource represents a connection pool to a database or other persistent store. As a convenience, the Struts DataSource manager can be used to instantiate whatever standard pool your application may need. Of course, if your persistence layer provides for its own connections, then you do not need to specify a data-sources element.
Since DataSource implementations vary in what properties need to be set, unlike other Struts configuration elements, the <data-source> element does not pre-define a slate of properties. Instead, the generic <set-property> feature is used to set whatever properties your implementation may require. Typically, these settings would include:
A driver class name
A url to access the driver
A description
And other sundry properties.
<data-source type="org.apache.commons.dbcp.BasicDataSource">
<!-- ... set-property elements ... -->
</data-source>
Since Struts 1.2.0, the GenericDataSource has been removed, and it is recommended that you use the Commons BasicDataSource or other DataSource implementations instead. In practice, if you need to use the DataSource manager, you should use whatever DataSource implementation works best with your container or database.
For examples of specifying a <data-sources> element and using the DataSource with an Action, see the Accessing a Database HowTo.
5.3 Configuring your application for modules
Very little is required in order to start taking advantage of the Struts module feature. Just go through the following steps:
Prepare a configuration file for each module.
Inform the controller of your module.
Use Actions to refer to your pages.
5.3.1 Module Configuration Files
Back in Struts 1.0, a few "boot-strap" options were placed in the web.xml file, and the bulk of the configuration was done in a single struts-config.xml file. Obviously, this wasn't ideal for a team environment, since multiple users had to share the same configuration file.
Since Struts 1.1, you have two options: you can list multiple struts-config files as a comma-delimited list, or you can subdivide a larger application into modules.
With the advent of modules, a given module has its own configuration file. This means each team (each module would presumably be developed by a single team) has their own configuration file, and there should be a lot less contention when trying to modify it.
5.3.2 Informing the Controller
Since Struts 1.0, you listed your configuration file as an initialization parameter to the action servlet in web.xml. This is still done since Struts 1.1, but the parameter can be extended. In order to tell the Struts machinery about your different modules, you specify multiple 'config' initialization parameters, with a slight twist. You'll still use 'config' to tell the ActionServlet about your "default" module, however, for each additional module, you will list an initialization parameter named "config/module", where module is the name of your module (this gets used when determining which URIs fall under a given module, so choose something meaningful!). For example:
...
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/conf/struts-default.xml</param-value>
</init-param>
<init-param>
<param-name>config/module1</param-name>
<param-value>/WEB-INF/conf/struts-module1.xml</param-value>
</init-param>
...
Here we have two modules. One happens to be the "default" module, which has no "/module" in its name, and the other one named "module1" (config/module1). The controller is configured to find the respective configuration files under /WEB-INF/conf/ (which is the recommended place to put all configuration files). Pretty simple!
(The struts-default.xml would be equivalent to what most folks call struts-config.xml. I just like the symmetry of having all my Struts module configuration files being named struts-modulename.xml)
If you'd like to vary where the pages for each module are stored, see the forwardPattern setting for the Controller.
5.3.3 Switching Modules
There are three approaches for switching from one module to another. You can use the built-in org.apache.struts.actions.SwitchAction, you can use a <forward> (global or local) and specify the contextRelative attribute with a value of true, or you can specify the "module" parameter as part of any of the Struts hyperlink tags (Include, Img, Link, Rewrite, or Forward).
You can use org.apache.struts.actions.SwitchAction like so:
...
<action-mappings>
<action path="/toModule"
type="org.apache.struts.actions.SwitchAction"/>
...
</action-mappings>
...
Now, to change to ModuleB, we would use a URI like this:
http://localhost:8080/toModule.do?prefix=/moduleB&page=/index.do
If you are using the "default" module as well as "named" modules (like "/moduleB"), you can switch back to the "default" module with a URI like this:
http://localhost:8080/toModule.do?prefix=&page=/index.do
Here's an example of a global forward:
<global-forwards>
<forward name="toModuleB"
contextRelative="true"
path="/moduleB/index.do"
redirect="true"/>
...
</global-forwards>
You could do the same thing with a local forward declared in an ActionMapping:
<action-mappings>
<action ... >
<forward name="success"
contextRelative="true"
path="/moduleB/index.do"
redirect="true"/>
</action>
...
</action-mappings>
Or, you can use org.apache.struts.actions.SwitchAction:
<action-mappings>
<action path="/toModule"
type="org.apache.struts.actions.SwitchAction"/>
...
</action-mappings>
Now, to change to ModuleB, we would use a URI like this:
http://localhost:8080/toModule.do?prefix=/moduleB&page=/index.do
Using the module parameter with a hyperlink tag is even simplier:
<html:link module="moduleB" path="/index.do"/>
That's all there is to it! Happy module-switching!
5.4 The Web Application Deployment Descriptor
The final step in setting up the application is to configure the application deployment descriptor (stored in file WEB-INF/web.xml) to include all the Struts components that are required. Using the deployment descriptor for the example application as a guide, we see that the following entries need to be created or modified.
5.4.1 Configure the ActionServlet Instance
Add an entry defining the action servlet itself, along with the appropriate initialization parameters. Such an entry might look like this:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
The initialization parameters supported by the action servlet are described below. (You can also find these details in the Javadocs for the ActionServlet class.) Square brackets describe the default values that are assumed if you do not provide a value for that initialization parameter.
config - Context-relative path to the XML resource containing the configuration information for the default module. This may also be a comma-delimited list of configuration files. Each file is loaded in turn, and its objects are appended to the internal data structure. [/WEB-INF/struts-config.xml].
WARNING - If you define an object of the same name in more than one configuration file, the last one loaded quietly wins.
config/${module} - Context-relative path to the XML resource containing the configuration information for the application module that will use the specified prefix (/${module}). This can be repeated as many times as required for multiple application modules. (Since Struts 1.1)
convertNull - Force simulation of the Struts 1.0 behavior when populating forms. If set to "true", the numeric Java wrapper class types (like java.lang.Integer) will default to null (rather than 0). (Since Struts 1.1) [false]
rulesets - Comma-delimited list of fully qualified classnames of additional org.apache.commons.digester.RuleSet instances that should be added to the Digester that will be processing struts-config.xml files. By default, only the RuleSet for the standard configuration elements is loaded. (Since Struts 1.1)
validating - Should we use a validating XML parser to process the configuration file (strongly recommended)? [true]
configFactory - The Java class name of the ModuleConfigFactory used to create the implementation of the ModuleConfig interface. [org.apache.struts.config.impl.DefaultModuleConfigFactory]
WARNING - Struts will not operate correctly if you define more than one <servlet> element for a controller servlet, or a subclass of the standard controller servlet class. The controller servlet MUST be a web application wide singleton.
5.4.2 Configure the ActionServlet Mapping
Note: The material in this section is not specific to Struts. The configuration of servlet mappings is defined in the Java Servlet Specification. This section describes the most common means of configuring a Struts application.
There are two common approaches to defining the URLs that will be processed by the controller servlet -- prefix matching and extension matching. An appropriate mapping entry for each approach will be described below.
Prefix matching means that you want all URLs that start (after the context path part) with a particular value to be passed to this servlet. Such an entry might look like this:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>
which means that a request URI to match the /logon path described earlier might look like this:
http://www.mycompany.com/myapplication/do/logon
where /myapplication is the context path under which your application is deployed.
Extension mapping, on the other hand, matches request URIs to the action servlet based on the fact that the URI ends with a period followed by a defined set of characters. For example, the JSP processing servlet is mapped to the *.jsp pattern so that it is called to process every JSP page that is requested. To use the *.do extension (which implies "do something"), the mapping entry would look like this:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
and a request URI to match the /logon path described earlier might look like this:
http://www.mycompany.com/myapplication/logon.do
WARNING - Struts will not operate correctly if you define more than one <servlet-mapping> element for the controller servlet.
WARNING - If you are using the new module support since Struts 1.1, you should be aware that only extension mapping is supported.
5.4.3 Configure the Struts Tag Libraries
Next, you must add an entry defining the Struts tag libraries.
The struts-bean taglib contains tags useful in accessing beans and their properties, as well as defining new beans (based on these accesses) that are accessible to the remainder of the page via scripting variables and page scope attributes. Convenient mechanisms to create new beans based on the value of request cookies, headers, and parameters are also provided.
The struts-html taglib contains tags used to create Struts input forms, as well as other tags generally useful in the creation of HTML-based user interfaces.
The struts-logic taglib contains tags that are useful in managing conditional generation of output text, looping over object collections for repetitive generation of output text, and application flow management.
The struts-tiles taglib contains tags used for combining various view components, called "tiles", into a final composite view.
The struts-nested taglib is an extension of other struts taglibs that allows the use of nested beans.
Below is how you would define all Struts taglibs for use within your application. In practice, you would only specify the taglibs that your application uses:
<taglib>
<taglib-uri>
http://struts.apache.org/tags-bean
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-html
</taglib-uri>
<taglib-location>
/WEB-INF/struts-html.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-logic
</taglib-uri>
<taglib-location>
/WEB-INF/struts-logic.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-tiles
</taglib-uri>
<taglib-location>
/WEB-INF/struts-tiles.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-nested
</taglib-uri>
<taglib-location>
/WEB-INF/struts-nested.tld
</taglib-location>
</taglib>
This tells the JSP system where to find the tag library descriptor for this library (in your application's WEB-INF directory, instead of out on the Internet somewhere).
5.4.3.1 Configure the Struts Tag Libraries (Servlet 2.3)
Servlet 2.3 users only: The Servlet 2.3 specification simplifies the deployment and configuration of tag libraries. The instructions above will work on older containers as well as 2.3 containers (Struts only requires a servlet 2.2 container); however, if you're using a 2.3 container such as Tomcat 4.x, you can take advantage of a simplified deployment.
All that's required to install the Struts tag libraries is to copy struts.jar into your /WEB-INF/lib directory and reference the tags in your code like this:
<%@ taglib
uri="http://struts.apache.org/tags-html"
prefix="html" %>
Note that you must use the full uri defined in the various tlds (see the example configuration for reference) so that the container knows where to find the tag's class files. You don't have to alter your web.xml file or copy tlds into any application directories.
5.5 Add Struts Components To Your Application
To use Struts, you must copy the .tld files that you require into your WEB-INF directory, and copy struts.jar (and all of the commons-*.jar files) into your WEB-INF/lib directory.
Servlet 2.3 Users: See section 4.5.3.1 for how to avoid copying the tlds into your application.
Sidebar: Sharing JAR Files Across Web Applications
Many servlet containers and application servers provide facilities for sharing JAR files across multiple web applications that depend on them. For example, Tomcat 4.1 allows you to put JAR files into the $CATALINA_HOME/shared/lib or $CATALINA_HOME/common/lib directories, and the classes in those JAR files will be available in all applications, without the need to place them in every web application's /WEB-INF/lib directory. Usually, the sharing is accomplished by creating a separate class loader that is the parent of the class loader (created by your container) for each individual web application.
If you have multiple Struts-based web applications, it is tempting to consider taking advantage of this container feature, and placing struts.jar and the various commons-*.jar files in the shared directory, rather than in each web application. However, there are several potential, and actual, problems with this approach:
Classes loaded from the shared class loader cannot see classes in the web application's class loader, unless they are specifically programmed to use the Thread context class loader. For example, Struts dynamically loads your action and form bean classes, and normally would not be able to find those classes. Struts has been programmed to deal with this in most scenarios, but it has not been thoroughly audited to ensure that it works in all scenarios. The Commons libraries that Struts uses have NOT been audited to catch all possible scenarios where this might become a problem.
When a class is loaded from a shared class loader, static variables used within that class become global as well. This can cause inter-webapp conflicts when the underlying code assumes that the statics are global only within a particular web applicaiton (which would be true if the class was loaded from the webapp class loader). There are many cases where Struts, and the Commons libraries it relies on, use static variables to maintain information that is presumed to be visible only within a single web applicaiton. Sharing these JAR files can cause unwanted interactions, and probably cause incorrect behavior.
When JAR files are shared like this, it is not possible to update the JAR file versions employed by a single web application without updating all of them. In addition, because updating a Struts version normally requires recompilation of the applications that use it, you will have to recompile all of your applications as well, instead of being able to manage them independently.
In spite of these difficulties, it is possible that sharing the Struts and Commons JAR files might appear to work for you. However, this is NOT a supported configuration.
If you file a bug report for ClassNotFoundException or NoClassDefFoundError exceptions, or similar situations where it appears that the wrong version of a class is being loaded, the bug report will NOT be processed unless the problem exists with the JAR files in their recommended location, in the /WEB-INF/lib subdirectory of your webapp.
5.6 Logging in Struts Based Applications
Since Struts 1.0, the logging functionality was fairly limited. You could set a debugging detail level with a servlet initialization parameter, and all log messages were written to wherever ServletContext.log() output is sent by your servlet container. With Struts 1.1, however, all logging messages written by Struts itself, as well as the commons libraries that it utilizes, flow through an abstract wrapper called Commons Logging, which can be used as a wrapper around any logging implementation. The most common implementations used are simple logging to System.err, the Apache Log4J package, or the built-in logging capabilities of JDK 1.4 or later in the java.util.logging package.
This section does not attempt to fully explain how Commons Logging is configured and used. Instead, it focuses on pertinent details of using Commons Logging in a Struts based environment. For complete documentation on using Commons Logging, consult the documentation for the logging system you are using, plus the Commons Logging Javadocs.
Commons Logging provides fine-grained control over the logging messages created by a Log instance. By convention, the Log instances for Struts (and the Commons packages in general) are named the fully qualified class name of the class whose messages are being logged. Therefore, log messages created by the RequestProcessor class are, naturally enough, directed to a logger named org.apache.struts.action.RequestProcessor.
The advantage of this approach is that you can configure the level of detail in the output you want from each class, individually. However, it would be a burden to be required to maintain such settings for every possible class, so the logging environment supports the notion of logging hierarchies as well. If a detail level configuration for a particular class has not been set, the logging system looks up the hierarchy until it finds a configuration setting to use, or else uses the default detail level if no configuration for any level of the hierarchy has been explicitly set. In the case of our messages from RequestProcessor, the logging system will look for explicit settings of the following loggers, in this order, until it finds one:
org.apache.struts.action.RequestProcessor
org.apache.struts.action
org.apache.struts
org.apache
org
The default logging detail level for your log implementation.
In a similar manner, the detail level for messages from PropertyUtils (from the Commons BeanUtils library) is set by a search for configuration settings for:
org.apache.commons.beanutils.PropertyUtils
org.apache.commons.beanutils
org.apache.commons
org.apache
org
The default logging detail level for your log implementation.
You can seamlessly integrate logging from your own components into the same logging implementation that Struts and the Commons libraries use, by following the instructions in Section 4.10. If you do this, you are strongly encouraged to follow the same naming convention for loggers (based on the class name of the messages being logged) for maximum configuration flexibility.
For more about putting it all together, see the Building Applications HowTo.
Next: Release Notes