Apache Struts 1
Apache Struts 1 is a free, open-source web application framework for Java that implements the Model-View-Controller (MVC) architectural pattern to facilitate the development of scalable and maintainable enterprise-level web applications.[1] Founded in May 2000 by Craig McClanahan and donated to the Apache Software Foundation, it serves as a flexible control layer built on standard technologies including Java Servlets, JavaBeans, ResourceBundles, and XML, enabling developers to create extensible environments based on proven design patterns.[2][1]
At its core, Struts 1 separates application logic into three interconnected components: the Model, which handles data and business logic often integrated with technologies like JDBC or Hibernate; the View, responsible for presentation using JavaServer Pages (JSP), JSTL, or Velocity templates; and the Controller, which processes user requests via the central ActionServlet and dispatches them to appropriate Action classes for execution.[3] Key features include ActionForm beans for form data validation and transfer between the Model and View, as well as configuration through an XML file (struts-config.xml) that defines mappings for actions, forms, and forwards, promoting reusability and modularity in complex web applications with numerous pages.[1] The framework emphasizes extensibility, allowing developers to replace core classes or add plugins for enhanced functionality.[1]
Struts 1 saw its initial release in June 2001 and evolved through multiple versions, with the final stable release, version 1.3.10, issued in December 2008.[4] By April 2013, the Apache Struts Project officially announced the end-of-life for Struts 1.x, shifting community focus to Struts 2, leaving legacy users without official security updates or bug fixes, though documentation, source code, and artifacts remain available for maintenance purposes.[4] Despite its retirement, Struts 1 influenced modern Java web development and continues to power some enterprise systems due to its robust MVC foundation.[4]
Introduction
Overview
Apache Struts 1 is an open-source web application framework for developing Java-based web applications, implementing the Model-View-Controller (MVC) architectural pattern to separate business logic, user interface, and control flow.[4] It leverages standard Java technologies such as Servlets for handling HTTP requests and JavaServer Pages (JSP) for dynamic content rendering, along with JavaBeans, ResourceBundles, and XML for configuration and internationalization.[1] The framework's core purpose is to promote maintainable code by decoupling concerns in web applications, enabling developers to manage complex user interactions, process form submissions, and integrate with backend systems like databases or enterprise JavaBeans more effectively.[5]
Originally created by programmer Craig McClanahan, Struts 1 was donated to the Apache Software Foundation in May 2000, marking the start of its development as an open-source project under the Apache Jakarta subproject.[6] It quickly became a de facto standard for enterprise Java web development in the early 2000s, with its first stable release following shortly thereafter. The Apache Struts Project Team announced the end-of-life for Struts 1.x in April 2013, declaring it legacy software with no further official support or updates, though its last version, 1.3.10, was released in December 2008.[4][7]
In its basic workflow, incoming HTTP requests are intercepted by the central ActionServlet controller, which consults the struts-config.xml file to determine the appropriate Action class for processing the request based on URL mappings.[3] The selected Action executes the business logic, potentially interacting with the model layer, and then returns an ActionForward object to direct the response to a specific view, such as a JSP page, for rendering the final output to the user.[3] This streamlined process ensures consistent handling of requests while allowing extensibility through custom actions and validators.
Design Goals
Apache Struts 1 was designed primarily to promote the Model-View-Controller (MVC) architectural pattern in web applications, aiming to separate application logic (model) from presentation (view) and control flow (controller) to enhance code maintainability and scalability. By mapping MVC concepts to standard web components like servlets and JSPs, the framework encouraged developers to build modular, testable applications that could handle complex enterprise requirements without tightly coupling business logic to user interfaces.[8]
A core objective was seamless integration with the Java Servlet API, leveraging the ActionServlet as the central controller to process HTTP requests and dispatch them efficiently, while extending servlet capabilities without introducing proprietary dependencies. This design ensured portability across any compliant servlet container, such as Apache Tomcat, aligning with J2EE (now Jakarta EE) best practices for building robust, standards-based web applications. The framework also prioritized a configurable request-handling mechanism through XML-based descriptors like struts-config.xml, which allowed flexible mapping of URLs to actions and resources, evolving from simpler servlet conventions to provide greater customization for diverse deployment scenarios.[8][9]
Struts 1 further emphasized support for both rapid prototyping and large-scale enterprise development by offering reusable components, including custom tag libraries for form handling and validation, which reduced boilerplate code and accelerated development cycles. Its extensible architecture enabled integration with complementary technologies like JDBC for data access and EJBs for distributed components, fostering reusable patterns suitable for production environments. Underpinning these goals was a commitment to community-driven development as an Apache Software Foundation project, licensed under the Apache Software License, Version 1.1 to promote open collaboration and widespread adoption.[8][10][11]
History
Origins and Development
Apache Struts 1 originated from the efforts of Craig R. McClanahan, who developed the initial framework and donated it to the Apache Software Foundation's Jakarta Project in May 2000.[12] This donation marked the formal launch of the Struts project as an open-source initiative under Apache, building on McClanahan's prior involvement in the Tomcat servlet container project.[13] The framework drew inspiration from the Model-View-Controller (MVC) design pattern, which had proven effective in desktop graphical user interfaces, adapting it to promote separation of concerns in web application development.
The primary motivation for creating Struts was to overcome the challenges of early Java web development, particularly the tight coupling between presentation logic in JavaServer Pages (JSP) or servlets and underlying business logic, which made applications difficult to maintain and scale.[14] By implementing a standardized MVC architecture for the web—often referred to as "Model 2"—Struts aimed to provide a reusable structure that facilitated cleaner code organization, reusability, and easier testing of components.[12] McClanahan, as the lead developer, envisioned Struts as a community-driven tool to standardize best practices for servlet- and JSP-based applications, addressing the ad-hoc nature of web development at the time.[14]
Key contributors to Struts 1 included McClanahan, who served as the primary architect, along with David Geary, who provided significant code contributions and expertise in user interface patterns. Additional input came from a growing community of developers, including those from organizations like Yahoo, which integrated and extended Struts in enterprise environments. The development process emphasized iterative improvements, with releases shaped by feedback from the Apache mailing lists and early issue tracking systems, fostering collaborative evolution within the Jakarta ecosystem.
In March 2004, following the formation of its own Project Management Committee (PMC) chaired by McClanahan, Jakarta Struts graduated to become the independent top-level Apache Struts project, reflecting its maturity and broad adoption.[15] This transition separated it from the broader Jakarta umbrella, allowing focused governance and continued innovation in response to community needs.
Release Timeline
The Apache Struts project was founded in May 2000, with the production version 1.0 released on June 15, 2001, marking the debut of an open-source MVC framework for Java web applications, with the core ActionServlet serving as the central controller to handle requests and dispatch to appropriate actions.[2]
Key milestones followed to address evolving needs in web development. Version 1.1, finalized on June 30, 2003, introduced integration with the Tiles framework, enabling reusable page layouts and layout components directly within Struts applications.[16] This release maintained backward compatibility while adding support for declarative exception handling and enhanced form validation options.
Version 1.2, released on August 31, 2004, focused on configuration improvements, including multi-module support for larger applications and streamlined deployment descriptors.[17] It also deprecated certain legacy features to encourage modern practices, such as using the new struts-config_1_2.dtd for XML validation.
The 1.3 series began with version 1.3.0 in December 2005, emphasizing security enhancements like pluggable request processors and improved input sanitization to mitigate common web vulnerabilities.[18] Subsequent updates in this branch, such as 1.3.5 in August 2006, provided bug fixes and minor improvements, including better integration with JavaServer Faces.[19]
The final stable release, 1.3.10, arrived on December 4, 2008, incorporating critical bug fixes and security patches to address known issues in earlier 1.3 versions.[20] Security updates continued sporadically until 2013, primarily through minor releases like 1.3.8 and 1.3.9 in 2007.[19]
Deprecation was formally announced on April 5, 2013, declaring Struts 1 end-of-life due to the superior modularity and extensibility of Struts 2, with no further official updates beyond critical fixes post-1.3.10.[4] The project's support lifecycle concluded officially in 2013. Following the official EOL, community initiatives such as the WebLegacy project have emerged to maintain and update Struts 1, releasing versions like 1.3.11 as of 2024.[1]
| Version | Release Date | Key Additions |
|---|
| 1.0 | June 15, 2001 | Basic MVC with ActionServlet |
| 1.1 | June 30, 2003 | Tiles integration, exception handling |
| 1.2 | August 31, 2004 | Module support, improved configuration |
| 1.3.0 | December 2005 | Security enhancements, composable processors |
| 1.3.10 | December 4, 2008 | Final fixes, security patches |
Architecture
Model-View-Controller Implementation
Apache Struts 1 implements the Model-View-Controller (MVC) architectural pattern to structure Java web applications, promoting separation of concerns for enhanced maintainability and scalability. In this framework, the controller layer serves as the central orchestrator, with the ActionServlet functioning as a front controller that intercepts all incoming HTTP requests and delegates processing based on predefined mappings. This centralized approach ensures that business logic remains insulated from presentation details, allowing developers to manage request routing uniformly.[21]
The model layer in Struts 1 consists of plain Java objects (POJOs) or Enterprise JavaBeans (EJBs) that encapsulate the application's business logic and data access, remaining entirely decoupled from the web tier. These model components handle core operations such as data validation, persistence, and computation, interacting with underlying resources like databases without direct exposure to HTTP specifics. This decoupling facilitates reuse of business logic across different interfaces and simplifies testing outside the web environment.[21]
For the view layer, Struts 1 primarily utilizes JavaServer Pages (JSP) to render dynamic content, though it supports alternative renderers for flexibility. Views are not directly accessible via URLs; instead, the controller forwards control to them after processing, preventing direct client exposure and enforcing a clean boundary between presentation and logic. This mechanism ensures that views focus solely on formatting data received from the model, often using tag libraries for streamlined output generation.[21]
The request flow in Struts 1 follows a straightforward sequence: an incoming URL request reaches the ActionServlet, which identifies the appropriate handler via mappings and invokes it to process the request, potentially consulting the model for business operations, before forwarding the result to the designated view for rendering. This pipeline minimizes direct coupling between components, streamlining development for complex applications. Core components like Actions facilitate this delegation within the controller, as detailed in subsequent sections.[21]
By enforcing MVC separation, Struts 1 significantly reduces the risk of "spaghetti code" in multi-tier web applications, enabling modular development where changes in one layer—such as updating a view for a new device—do not propagate to others. This pattern's adoption in Struts 1 supports scalable architectures, particularly beneficial for enterprise systems requiring robust request handling and logic isolation.[21]
Core Components
The core components of Apache Struts 1 form the foundational elements for implementing the Model-View-Controller (MVC) pattern in Java web applications, handling request processing, business logic execution, and response routing. These components are primarily defined within the org.apache.struts.action and org.apache.struts.config packages, enabling developers to build extensible web applications through configuration-driven mechanisms.[21]
ActionServlet serves as the central controller servlet in Struts 1, functioning as a singleton that initializes the framework upon application startup by loading the struts-config.xml file and processing all incoming HTTP requests. It acts as the front controller, intercepting requests mapped to *.do (or a custom extension), delegating to a RequestProcessor for validation, form population, and action invocation, and ensuring thread-safety through its design. This servlet supports modular configurations by allowing multiple instances via servlet initialization parameters, each handling a distinct application module within a single web archive (WAR) file.[21]
The Action class is an abstract adapter that encapsulates the application's business logic, providing the execute() method, which subclasses override to process requests by interacting with the model layer and returning an ActionForward for view selection. Developers subclass Action to implement custom logic, ensuring operations are stateless and thread-safe by avoiding instance variables and delegating to separate business objects or JavaBeans; the method signature is ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response). Subtypes such as DispatchAction extend this functionality to promote code reuse by mapping multiple related operations (e.g., add, edit, delete) to a single class, using request parameters to determine which method to invoke, thus reducing the proliferation of similar action classes.[21][22][23]
ActionForm represents form data as a JavaBean, facilitating the transfer of input between the view (e.g., HTML forms) and the controller, with properties that mirror form field names for automatic population via Apache Commons BeanUtils. It includes lifecycle hooks such as the reset(ActionMapping mapping, HttpServletRequest request) method to reinitialize form properties (essential for session-scoped forms to clear previous data) and the ActionErrors validate(ActionMapping mapping, HttpServletRequest request) method to perform custom validation, returning an ActionErrors object containing any errors to prevent action execution if invalid. ActionForms can be scoped to request or session, enabling state management across multiple requests in multi-page workflows.[21][21]
ActionMapping and ActionForward are configuration-driven objects that handle request routing and response direction, defined in struts-config.xml to decouple implementation from URLs. An ActionMapping instance corresponds to a specific <action> element, storing attributes like the path (URI pattern, supporting wildcards), type (Action class), name (associated ActionForm), and validate flag to control form validation; it is retrieved by the RequestProcessor to instantiate and invoke the appropriate Action. Complementing this, ActionForward encapsulates forward targets, with properties such as name (logical identifier), path (JSP or resource URI), and redirect (boolean for client-side vs. server-side forwarding), allowing Actions to return a named forward for flexible navigation without hardcoding paths. These objects enable declarative control over application flow, with global forwards available across the application.[21][21]
Struts 1's module support allows multiple independent applications to coexist within a single WAR file, each with its own configuration resources like ActionForms, ActionMappings, and ActionForwards, prefixed by a URL path (e.g., /module1/ or /module2/) and initialized via separate config parameters in the ActionServlet declaration. This is managed through ModuleConfig instances, providing isolation while sharing the core framework infrastructure, ideal for large-scale deployments with distinct subsystems.[21]
Configuration and Setup
struts-config.xml Structure
The struts-config.xml file serves as the primary configuration file for Apache Struts 1 applications, defining the mappings, forms, forwards, and other resources essential for routing requests and managing application flow. It is conventionally placed in the /WEB-INF/ directory of the web application to ensure it is not directly accessible via HTTP, and it is parsed by the framework's ActionServlet during the servlet's initialization phase to load the configuration into memory as JavaBeans for runtime use.[24][25]
The file adheres to XML syntax and is typically validated against a Document Type Definition (DTD) to enforce its schema, with versions like Struts 1.3 utilizing struts-config_1_3.dtd for structure enforcement. The root element is <struts-config>, which encapsulates all configuration directives.[26]
Key elements within <struts-config> include <form-beans>, which declares reusable ActionForm subclasses for handling form data; each <form-bean> specifies a unique name attribute for reference and a type attribute pointing to the fully qualified class name. The <action-mappings> element configures URL paths to Action classes, linking them to form beans via the name attribute, setting scope (e.g., request or session), input resources, and validation flags; nested <forward> elements within individual <action> tags define outcome-specific redirects. Additionally, <global-forwards> provides application-wide logical name mappings to resources like JSP pages, using attributes such as name, path, and redirect to specify context-relative paths and HTTP redirect behavior.
For internationalization, the <message-resources> element loads property bundles, with a parameter attribute indicating the base name of the resource bundle (e.g., ApplicationResources), enabling key-based message retrieval across locales. Extensions are integrated via the <plug-in> element, which instantiates classes implementing the PlugIn interface to hook into the ActionServlet lifecycle events like initialization and destruction, often used for modules such as Tiles or custom validators; parameters can be passed as nested <set-property> elements. The <global-exceptions> element may also be included to map exception types to handler classes for centralized error management.
A basic example of an <action> configuration within <action-mappings> might appear as follows, mapping a login path to an Action class while associating it with a form bean:
xml
<action path="/login"
type="com.example.LoginAction"
name="loginForm"
scope="request"
validate="true"
input="/login.jsp">
<forward name="success" path="/welcome.jsp"/>
<forward name="failure" path="/login.jsp" redirect="true"/>
</action>
<action path="/login"
type="com.example.LoginAction"
name="loginForm"
scope="request"
validate="true"
input="/login.jsp">
<forward name="success" path="/welcome.jsp"/>
<forward name="failure" path="/login.jsp" redirect="true"/>
</action>
This structure wires the Action and ActionForm components declared elsewhere in the file, ensuring request handling follows the defined paths and validations.
Deployment and Integration
Apache Struts 1 requires a Java Development Kit (JDK) version 1.4 or later to compile and run applications, with JDK 1.4.2 recommended for compatibility with build tools like Maven.[27] Additionally, it demands a servlet container supporting the Servlet API 2.3 or higher and JSP 1.2 or later, such as Apache Tomcat 4.0 or subsequent versions, to handle web application deployment.[27]
Deployment involves packaging the application as a Web Application Archive (WAR) file, which includes the core struts.jar library placed in the WEB-INF/lib directory.[27] The WEB-INF/web.xml descriptor must configure the ActionServlet with a <servlet-mapping> element, typically mapping requests ending in *.do to the servlet for processing Struts actions.[27] The struts-config.xml file resides in WEB-INF to define action mappings and other configurations, while JSP files include tag library declarations for Struts-specific tags like html and bean.[27] Once assembled, the WAR is deployed by copying it to the servlet container's webapps directory, often requiring a server restart to initialize the application.[27]
Early versions of Struts 1 relied on Apache Ant for building applications, using Ant 1.5.4 or later to compile source code, assemble WAR files, execute tests, and manage deployments through scripted tasks.[28] Starting with Struts 1.2, integration with Apache Maven became available, allowing developers to generate project skeletons via the mvn [archetype](/page/Archetype):create command with a Struts-specific archetype, streamlining dependency management and build processes.[29]
Testing Struts 1 applications incorporates JUnit for unit tests on Action classes, extended by tools like StrutsTestCase, which simulates the Struts environment using mock objects to verify action logic without a full servlet container.[30] This approach enables isolated testing of form handling and validation, often integrated into Ant or Maven build scripts for automated execution.[29]
Common pitfalls in deployment include classpath conflicts arising from missing or incorrectly placed tag library JARs, such as struts-taglib.jar, leading to errors where the container cannot locate Struts tags due to URI mismatches in JSP declarations.[31] Version conflicts also occur in shared environments like application servers (e.g., JBoss), where duplicate or incompatible Struts libraries in the global classpath interfere with the application's isolated dependencies, resulting in ClassNotFoundException or linkage errors during startup.[32]
Key Features
In Apache Struts 1, form submission begins when a user submits data via an HTTP request, triggering the framework's RequestProcessor to instantiate or retrieve an ActionForm subclass associated with the request.[33] The ActionForm, a JavaBean that must implement Serializable, has its properties automatically populated from the request parameters by the controller servlet before any further processing occurs.[33] This population step ensures that form data is encapsulated in a type-safe object, facilitating validation and business logic execution.[21]
Following population, the framework invokes the ActionForm's reset() method to initialize properties to default values, such as setting boolean flags for checkboxes to false, preventing carryover from previous submissions.[33] Next, if validation is enabled in the action mapping, the validate() method is called on the ActionForm; this method, which returns an ActionErrors object containing any validation failures or null if valid, allows developers to implement custom checks on form properties.[33] If errors are present, the framework forwards control back to the input form page specified in the action mapping, repopulating the form with the submitted values for user correction.[21]
Upon successful validation, control passes to the associated Action class, where the execute() method is invoked to process the request.[22] The execute() method has the signature public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) and serves as an adapter between the incoming request and the application's business logic.[22] Developers override this method to perform tasks such as updating model components, querying databases, or preparing data for the view, ultimately returning an ActionForward object to direct the response—either to a success page, failure page, or another resource.[21] Action instances must be thread-safe, as they are shared across multiple requests.[22]
Error handling during form processing relies on the ActionErrors collection, which encapsulates validation messages reported by the ActionForm's validate() method.[34] This class supports both global errors (affecting the entire form) and field-specific errors, added via methods like add(String property, ActionError error), where the property identifies the erroneous field and the ActionError provides a message key with optional arguments for internationalization.[34] If ActionErrors is non-empty after validation, the framework stores it in the request scope and forwards to the designated input page, where JSP tags like <html:errors> can display the messages to the user.[21] In the execute() method, additional errors can be collected similarly and saved using saveErrors(request, errors) before forwarding.[21]
For forms with multiple submit buttons, such as those requiring different actions like "Save" or "Delete," Struts 1 provides the LookupDispatchAction subclass of Action.[35] This class dispatches to specific methods in the subclass based on a request parameter (e.g., "action") whose value matches a key defined in the application's resource bundle, mapped via the developer's implementation of getKeyMethodMap().[35] For instance, a parameter value of "button.save" could invoke a "save" method, enabling modular handling without separate action mappings.[35]
A representative example is handling a login form using an ActionForm subclass like LogonForm, which captures username and password fields.[21] Upon submission, the framework populates LogonForm properties from the request, calls reset() and validate()—where validate() might check for empty fields and return ActionErrors with messages like "Username is required"—and if valid, invokes LogonAction's execute() method.[21] The execute() method authenticates the user against a model (e.g., a user database), then returns an ActionForward to "/Welcome.jsp" on success or saves new ActionErrors and forwards to "/Logon.jsp" on failure, ensuring users see appropriate error messages on the login page.[21]
Validation Framework
The Validation Framework in Apache Struts 1 integrates with the Jakarta Commons Validator library to enable both server-side and client-side validation of form data, ensuring input integrity without embedding validation logic directly in application code.[36] It supports declarative configuration for common checks and programmatic overrides for custom needs, reducing boilerplate while allowing flexibility for complex scenarios.[37]
Declarative validation is configured in a validation.xml file, where rules are defined within <form-validation> and <formset> elements for specific forms.[36] For instance, the required rule mandates that a field cannot be null or empty, as in <field property="userName" depends="required"/>.[36] The minlength rule enforces a minimum character length, specified via a minlength variable, such as <field property="password" depends="required,minlength"> <arg0 key="password.minlength"/> </field>, where the argument references a bundle key for the length value.[38] Similarly, the email rule validates against a standard email pattern using regular expressions, like <field property="email" depends="required,email"/>.[37] These rules are processed automatically during form submission, populating an ActionErrors object with any violations.[36]
For scenarios requiring custom logic, programmatic validation allows developers to override the validate() method in subclasses of ActionForm, such as ValidatorForm.[36] This method, which returns an ActionErrors instance, can add errors via add() calls, for example: ActionErrors errors = new ActionErrors(); if (someCondition) { errors.add("fieldName", new ActionMessage("error.key")); } return errors;.[36] It executes after declarative checks, enabling hybrid approaches where built-in rules handle basics and custom code addresses domain-specific validations.[37]
The framework is enabled through the ValidatorPlugIn, declared in struts-config.xml as <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in>.[36] This plugin loads predefined rules from validator-rules.xml—which includes implementations for required, minlength, email, and others—and custom form rules from validation.xml.[39] Forms must extend ValidatorForm to leverage these features automatically.[36]
Client-side validation is supported by automatic generation of JavaScript from the declarative rules, integrated into JSP forms via the <html:form> tag, which adds an onsubmit handler like onsubmit="return validateUserNameForm(this);".[36] This script performs preliminary checks in the browser using functions defined in validator-rules.xml, mirroring server-side logic to improve user experience without full round-trips.[37] If client-side validation fails, submission is blocked, and errors are displayed immediately.
Validation errors are displayed in JSPs using the <html:errors/> tag, which iterates over the ActionErrors collection and renders messages from resource bundles configured in struts-config.xml, such as <message-resources parameter="ApplicationResources"/>.[36] For example, keys like errors.required={0} is required in the bundle provide localized text, with {0} substituted by the field label.[36] Alternatively, <html:messages id="msg" property="fieldName"> can target specific fields, ensuring errors are presented clearly and accessibly.[37]
Internationalization Support
Apache Struts 1 provides built-in support for internationalization (i18n) by leveraging Java's Locale and ResourceBundle classes to enable multi-language applications without extensive custom coding.[40] This allows developers to define locale-specific messages, labels, and formatting rules in resource bundle files, which are automatically selected based on the user's preferred locale.[41]
Resource bundles in Struts 1 are typically named ApplicationResources.properties for the default locale (e.g., English) and extended for other locales, such as ApplicationResources_fr.properties for French or ApplicationResources_es_MX.properties for Mexican Spanish.[40] These files use a simple key-value format, where keys reference translatable strings (e.g., welcome.message=Welcome to our application) and values provide the localized text.[40] To load these bundles, the struts-config.xml file includes a <message-resources> element specifying the base bundle name, such as <message-resources parameter="ApplicationResources"/>, which places the bundle in application scope under the key org.apache.struts.action.MESSAGE_KEY.[42] Multiple <message-resources> elements can be defined for modular applications, each with a unique key attribute to avoid conflicts.[42] If a key is missing in the selected locale's bundle, Struts displays the key surrounded by question marks (e.g., ???missing.key???) unless the null attribute is set to false in the configuration.[42]
Locale resolution in Struts 1 follows a prioritized order to determine the appropriate language and regional settings for a request.[43] First, it checks the user's session for a stored Locale object under the key org.apache.struts.Globals.LOCALE_KEY; if present, this locale is used.[44] If no session locale exists, Struts falls back to the request's locale, derived from the browser's Accept-Language header sent in the HTTP request.[43] Additionally, a locale request parameter (e.g., en_US) can override the locale for the current request and persist it to the session if the controller's locale attribute is enabled (default: true) in struts-config.xml.[42] This mechanism supports dynamic language switching, such as via a URL like /index.do?locale=fr_FR, which updates the session locale and reloads the appropriate resource bundle for subsequent requests.[44]
In JSP pages, Struts 1 facilitates the display of localized text through the <bean:message> tag from the Bean Tag Library, which retrieves the message for the current locale using a specified key.[45] For example, <bean:message key="welcome.message"/> outputs the translated welcome text, automatically substituting the correct bundle based on the resolved locale.[45] The tag supports parameterization with attributes like arg0, arg1 for dynamic values, leveraging Java's MessageFormat class to insert them into the message string (e.g., welcome.user=Welcome, {0}! with <bean:message key="welcome.user" arg0="<%=userName%>"/>).[40] For more advanced UI integration, the optional Struts-Faces library extends i18n support to JavaServer Faces components, allowing seamless locale propagation in hybrid Struts-JSF applications.[40]
Struts 1 integrates with Java's java.text package for locale-sensitive formatting of dates, numbers, and currency, ensuring outputs adapt to cultural conventions without additional configuration.[40] For instance, the DateFormat and DecimalFormat classes, accessed via resource bundles or directly in actions and JSPs, automatically apply patterns like MM/dd/yyyy for US English or dd/MM/yyyy for UK English based on the current locale.[40] Currency formatting follows similar rules, using NumberFormat.getCurrencyInstance(locale) to produce symbols and decimal separators appropriate to the region (e.g., $1,234.56 in en_US vs. 1 234,56 € in fr_FR).[40] These formats can be embedded in resource bundle messages or applied in custom tags, maintaining consistency across the application.[40]
Extensions
Tiles Layout Framework
The Tiles Layout Framework is an extension integrated into Apache Struts 1 that enables the composition of web pages using reusable, modular components known as tiles, facilitating a templating approach for consistent user interfaces.[46] It operates within the view layer of the MVC pattern by assembling JSP fragments into layouts, allowing developers to define page structures separately from content.[47]
Integration of Tiles into Struts 1 occurs primarily through the TilesPlugIn class, which is declared in the struts-config.xml file to initialize the framework.[46] This plugin references a separate configuration file, typically tiles-config.xml or tiles-defs.xml, where page definitions are specified.[48] For Struts 1.1 and later, the plugin supports module-aware factories for multi-module applications, with the option to share a single definition factory across modules by setting moduleAware="false".[46] An example configuration in struts-config.xml is:
xml
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/>
<set-property property="moduleAware" value="true"/>
</plug-in>
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/>
<set-property property="moduleAware" value="true"/>
</plug-in>
Core components of Tiles include definitions, which map logical names to layouts or sub-components, and attributes, which hold values for insertion into those layouts.[46] Definitions are authored in XML within the configuration file, using elements like <definition> to specify a name, path to a layout JSP, and attributes such as header, body, or footer.[47] Inheritance is supported via the extends attribute, enabling a base definition to be extended for specialized pages; for instance:
xml
<definition name="base" path="/layouts/classicLayout.jsp">
<put name="header" value="/common/header.jsp"/>
<put name="footer" value="/common/footer.jsp"/>
</definition>
<definition name="homePage" extends="base">
<put name="body" value="/home/body.jsp"/>
<put name="title" value="Home Page"/>
</definition>
<definition name="base" path="/layouts/classicLayout.jsp">
<put name="header" value="/common/header.jsp"/>
<put name="footer" value="/common/footer.jsp"/>
</definition>
<definition name="homePage" extends="base">
<put name="body" value="/home/body.jsp"/>
<put name="title" value="Home Page"/>
</definition>
This structure promotes nested definitions, where complex pages can build upon simpler ones.[47]
In JSP pages, Tiles are inserted using the <tiles:insert> tag from the Tiles tag library, referencing a definition by name or directly embedding attributes.[46] Attribute substitution allows dynamic overriding, such as passing a custom body content at runtime with <tiles:put name="body" value="..."/> inside the insert tag.[47] A typical usage example in a JSP is:
jsp
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert definition="homePage" flush="true">
<tiles:put name="title" value="Custom Title"/>
</tiles:insert>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert definition="homePage" flush="true">
<tiles:put name="title" value="Custom Title"/>
</tiles:insert>
The layout JSP, like classicLayout.jsp, then retrieves these attributes using <tiles:get name="header"/> or similar tags to render the composed page.[48]
The primary benefits of Tiles include the creation of reusable templates that minimize JSP code duplication across an application, as common elements like headers and footers can be defined once and extended.[46] Nested and inheritable definitions further enhance modularity, allowing hierarchical page structures that simplify maintenance and updates to site-wide layouts.[47]
Version-specific enhancements in Struts 1.1 and subsequent releases include the introduction of the TilesPlugIn for streamlined initialization and support for dynamic content insertion through improved tag handling and EL integration in later iterations like 1.2.[46] These updates, building on earlier contrib-based implementations in Struts 1.0.x, enabled better extensibility for runtime attribute processing and multiple definition factories.[49]
The Validator extension in Apache Struts 1 serves as a declarative validation mechanism for ActionForm inputs, integrating with the Apache Commons Validator library to enable server-side and client-side checks without embedding logic in Java code.[37] This extension operates as a standalone plugin within Struts applications, configurable via XML files that define reusable rulesets.[37]
Validation rules are specified in validator-rules.xml, which declares built-in and custom validators such as required for mandatory fields, mask for regular expression matching, email for address format verification, and creditcard for Luhn algorithm-based number validation.[50] For instance, a creditcard rule might be defined as follows in validator-rules.xml:
xml
<validator name="creditcard"
classname="org.apache.struts.validator.FieldChecks"
method="validateCreditCard"
msg="errors.creditcard">
<javascript><![CDATA[
function validateCreditCard(form) {
// Luhn algorithm implementation
}
]]></javascript>
</validator>
<validator name="creditcard"
classname="org.apache.struts.validator.FieldChecks"
method="validateCreditCard"
msg="errors.creditcard">
<javascript><![CDATA[
function validateCreditCard(form) {
// Luhn algorithm implementation
}
]]></javascript>
</validator>
This rule can then be applied in validation.xml to a specific form field, like <field property="cardNumber" depends="required,creditcard">, linking it to the corresponding action path in struts-config.xml for automatic invocation during form processing.[50] Custom validators extend this by implementing ValidatorAction interfaces and registering new <validator> elements in validator-rules.xml, allowing pluggable behaviors like domain-specific checks.[37]
Struts 1's custom JSP tag libraries enhance view-layer productivity by providing reusable components for form handling, data display, and conditional logic, divided into HTML, Bean, and Logic sublibraries.[51] The HTML library generates interactive elements tied to form beans; for example, <html:form action="/submitForm" method="post"> creates a form that posts to a Struts action, while <html:text property="userInput" size="30"/> renders a bound text field that populates and repopulates from the form bean on errors.[51]
The Bean library facilitates data output with tags like <bean:write name="userForm" property="fullName" filter="true"/>, which safely renders a property value, escaping HTML characters to prevent injection issues.[51] Complementing this, the Logic library supports flow control, such as <logic:iterate id="item" name="itemList" type="java.lang.String"> <bean:write name="item"/><br/> </logic:iterate>, which iterates over a collection to display list items dynamically.[51]
For tailored functionality, developers extend javax.servlet.jsp.tagext.TagSupport to build custom tags, implementing doStartTag() and doEndTag() for processing, then declare them in a Tag Library Descriptor (TLD) file placed in the /WEB-INF directory.[52] A TLD example might include:
xml
<taglib>
<tlib-version>1.0</tlib-version>
<tag>
<name>customDisplay</name>
<tag-class>com.example.CustomTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
</tag>
</taglib>
<taglib>
<tlib-version>1.0</tlib-version>
<tag>
<name>customDisplay</name>
<tag-class>com.example.CustomTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>value</name>
<required>true</required>
</attribute>
</tag>
</taglib>
This enables usage like <mytags:customDisplay value="Hello"/> after mapping via <%@ taglib uri="/WEB-INF/custom.tld" prefix="mytags" %>.[52]
Struts tags support nesting and composition for complex views; for instance, <html:form> can contain <logic:iterate> loops with embedded <html:text> and <bean:write> for generating dynamic, data-driven forms that adapt to user input or collections. In Struts 2, these tag concepts were retained but unified into a single library with enhanced features like better internationalization, while the Validator was evolved into a more flexible framework supporting annotations alongside XML.
Security Considerations
Common Vulnerabilities
One of the most critical vulnerabilities in Apache Struts 1 is CVE-2016-1181, affecting versions 1.x through 1.3.10, where the ActionServlet mishandles multithreaded access to an ActionForm instance. This flaw allows remote attackers to execute arbitrary code by leveraging a race condition or passing a serialized instance of an ActionForm that has already been used, potentially manipulating components in server memory such as Servlets and the ClassLoader.[54]
Closely related is CVE-2016-1182 in the same versions, where the ActionServlet fails to properly restrict Validator configurations. Attackers can exploit this to conduct denial-of-service (DoS) attacks by overwhelming validation resources or perform cross-site scripting (XSS) through unescaped outputs in form handling and tag libraries, injecting malicious scripts into user sessions.[55]
Parameter manipulation issues in Struts 1, particularly through ActionForm objects, serve as precursors to more sophisticated expression evaluation problems in subsequent frameworks. These allow attackers to tamper with request parameters to invoke unintended methods or access restricted resources, potentially leading to arbitrary code execution under concurrent request conditions.[56]
CSRF vulnerabilities arise from inadequate token handling in Struts 1 forms, where the built-in token mechanism may not sufficiently validate request origins if misconfigured, enabling attackers to forge requests on behalf of authenticated users. Complementing this are XSS risks from unescaped outputs in tags like <html:errors>, which lacks automatic sanitization and may require manual escaping for user-generated content; <bean:write> sanitizes by default unless the filter="false" attribute is explicitly set, but misconfiguration can still allow script injection in responses.[57]
Historical exploits of unpatched Struts 1 versions have stemmed from these persistent flaws in older deployments. The framework's widespread adoption in legacy Java web applications amplified the impact, resulting in breaches where attackers leveraged XSS and parameter tampering for data exfiltration or further compromise.
Mitigation Strategies
To mitigate security risks in Apache Struts 1 applications, administrators should first ensure deployment of the final stable release, version 1.3.10 (December 2008), which includes patches for vulnerabilities known prior to its release, such as certain cross-site scripting issues.[4] However, as Struts 1 reached end-of-life in April 2013 with no further official updates, it remains vulnerable to issues discovered afterward, including recent ones like CVE-2025-48734 (remote code execution via BeanUtils classloader manipulation, as of August 2025). To further reduce exposure, developers must disable or avoid dangerous configuration options, such as exposing the class parameter in action mappings, which can enable classloader manipulation; this is achieved by reviewing and sanitizing struts-config.xml to exclude unnecessary parameters and plugins, for example, by setting the "parameter" attribute to suppress dangerous keys.
Input sanitization forms a core defense layer, requiring validation of all incoming parameters using the framework's validate() method in ActionForm subclasses to enforce rules like required fields, length limits, and type checks before processing. Complementing this, output escaping prevents injection attacks by rendering dynamic content with the <bean:write> tag's filter="true" attribute (the default), which automatically converts HTML-sensitive characters (e.g., < to <) to their entity equivalents, ensuring safe display in JSP views.[58]
For cross-site request forgery (CSRF) protection, Struts 1 includes built-in token handling: generate a unique token via saveToken() in the originating Action, embed it as a hidden field in forms using <html:hidden property="token"/>, and validate it on submission with isTokenValid() or resetToken() to confirm request authenticity and prevent replay. If additional robustness is needed, integrate Apache Commons extensions like the CSRFGuard library to automate token management across non-form interactions.
Ongoing auditing is essential for legacy systems; conduct regular vulnerability scans using tools like OWASP ZAP, which automates detection of issues such as insecure direct object references or misconfigurations in Struts actions through active and passive scanning modes. Complement scans by restricting Action access via role-based controls defined in web.xml security constraints, mapping user roles to specific URL patterns (e.g., /secure/*) to enforce authentication and authorization before invocation.
At the infrastructure level, implement firewall rules to block direct access to sensitive resources like configuration files under WEB-INF, although Struts 1 protects struts-config.xml by default via servlet container conventions; additionally, enforce HTTPS for all traffic using server configurations (e.g., Tomcat's SSL connector) to encrypt sessions and prevent man-in-the-middle interception of tokens or credentials. Due to the end-of-life status, migration to Struts 2 or modern frameworks is strongly recommended for long-term security.
Legacy and Migration
Differences from Struts 2
Apache Struts 2 represents a fundamental redesign of the framework, originally derived from the WebWork 2 project rather than an incremental update to Struts 1, resulting in significant architectural and functional divergences that improved modularity and developer productivity.[9] This shift addressed limitations in Struts 1, such as tight coupling to the servlet API and verbose configuration, making Struts 2 more suitable for modern web development practices.[59]
In terms of configuration, Struts 1 relies heavily on XML files like struts-config.xml to define actions, form beans, and mappings, with no support for annotations or convention-over-configuration principles, leading to boilerplate code and maintenance challenges.[59] Conversely, Struts 2 supports XML options in struts.xml and introduces annotation-driven configuration (e.g., @Action and @Result annotations)[60], alongside convention-over-configuration via its Convention Plugin to automatically map actions based on class names and methods, reducing explicit setup.[61][59]
Architecturally, Struts 1 employs singleton Action instances managed by the ActionServlet, requiring developers to ensure thread-safety through synchronization, while form handling depends on ActionForm subclasses tightly coupled to the servlet lifecycle.[59] Struts 2, however, instantiates Actions per request as plain old Java objects (POJOs), eliminating thread-safety concerns and decoupling them from the servlet API by using Maps to represent request, session, and application contexts for better testability.[59] Additionally, Struts 2 replaces Struts 1's RequestProcessor with an extensible interceptor stack, allowing modular handling of cross-cutting concerns like validation and logging on a per-action basis.[59]
For tags and extensibility, Struts 1 provides a legacy tag library based on JSP with limited expression language support via JSTL, restricting dynamic UI interactions and integration with modern frontend technologies.[59] Struts 2 enhances this with a modern UI tag set leveraging the Object-Graph Navigation Language (OGNL) for powerful expressions and a ValueStack for flexible data access, alongside a plugin architecture that facilitates seamless extensions for features like REST support and integration with frameworks such as Spring or Hibernate.[59]
Performance-wise, Struts 1 incurs a heavier footprint due to the mandatory use of form beans extending base classes and singleton Actions that demand careful synchronization, potentially introducing bottlenecks in high-concurrency scenarios.[59] Struts 2's POJO-based approach and per-request instantiation minimize overhead, as the servlet container's object pooling offsets creation costs, resulting in a lighter, more efficient runtime.[59]
Struts 2 was rewritten from scratch without backward compatibility to Struts 1, breaking API contracts in areas like action execution and configuration to enable these improvements, which ultimately led to its adoption as the primary framework while Struts 1 entered end-of-life without further maintenance.[59][4]
Migration Approaches
Migrating from Apache Struts 1 to Struts 2 requires careful planning due to architectural differences, such as the shift from singleton actions in Struts 1 to per-request POJOs in Struts 2, and the elimination of mandatory servlet dependencies.[59] The official migration strategies outline three non-exclusive paths that allow incremental adoption while minimizing disruption.[62]
The first approach, dual processor with shared resources, involves integrating Struts 2 JARs into an existing Struts 1.3 application and configuring distinct URL patterns—such as *.action for Struts 2 and *.do for Struts 1—to run both frameworks side-by-side. This enables gradual migration of features, with shared elements like message resources, validation rules, and the Tiles layout framework to reduce redundancy.[62] Developers can incrementally route requests to the new framework, leveraging plugins like the Tiles integration for Struts 2 to maintain layout consistency.[63]
A second strategy emphasizes studying rewritten applications, such as the Struts Mailreader demo, which has been ported from Struts 1 to Struts 2, to identify best practices for refactoring actions, forms, and configurations.[62] This path aids in understanding how to adapt Struts 1 patterns, like ActionForms, to Struts 2's model-driven validation and interceptor-based processing.
The third path utilizes a conversion wizard tool, which parses Struts 1 files like struts-config.xml and web.xml to generate equivalent Struts 2 configurations, such as struts.xml, while flagging areas requiring manual intervention.[62] XSLT transformations and text-processing utilities can automate conversions for validation XML to Struts 2's global validation files and message resources to properties files, though full automation is not guaranteed due to semantic differences.[63]
Additionally, bridge objects and utilities facilitate reuse of Struts 1 components in Struts 2 environments, including TextProvider for accessing legacy message resources, interceptors for applying Struts 1 validations, and emulation classes that wrap Struts 1 actions to implement the Struts 2 Action interface.[63] These bridges support executing Commons Chain instances via interceptors and session-aware invocations of Struts 1's execute() method, easing the transition for complex applications.
Challenges in migration include the non-trivial conversion of JSP tags from Struts 1's HTML and Bean libraries to Struts 2's UI and other tag sets, often requiring manual rewriting for compatibility.[63] Additionally, while tools provide a starting point, they depend on community contributions and may not achieve 100% compatibility, necessitating thorough testing of action mappings, form handling, and internationalization support.[62]