Apache Struts
Apache Struts is a free, open-source web application framework for Java that implements the Model-View-Controller (MVC) architectural pattern to facilitate the development of elegant, modern, and enterprise-ready web applications.[1]
The framework emphasizes convention over configuration, reducing boilerplate code while providing extensibility through a robust plugin architecture that supports integrations like REST services, AJAX, and JSON handling.[1]
Originally launched as Struts 1 in 2000, the project reached its end-of-life with version 1.3.10 in December 2008, after which development shifted to Struts 2—a complete rewrite that evolved from the merger of the WebWork framework and elements of the original Struts codebase around 2005–2006.[2][3]
Struts 2, now simply referred to as Apache Struts in its latest iterations (up to version 7.1.1 as of October 2025), builds on Java Servlet API extensions and object-oriented principles to streamline the creation of scalable web applications, with ongoing releases addressing security enhancements and modern tooling compatibility.[4][3]
Introduction
Overview
Apache Struts is a free, open-source framework for developing Java web applications using the Model-View-Controller (MVC) architectural pattern.[1]
This framework simplifies web application development by handling HTTP requests, managing navigation, and enforcing separation of concerns between the model (business logic), view (presentation), and controller (request processing) components.[5][6]
Apache Struts integrates with Jakarta EE (formerly Java EE) technologies, including Servlets, JSP, and session management, to support the creation of robust enterprise web applications that are scalable and maintainable.[7]
As of 2025, it is actively maintained by the Apache Software Foundation, with development focused on the Struts 2 lineage, now encompassing versions 6.x and 7.x (latest 7.1.1 as of October 2025) as the primary active branches following the end-of-life of Struts 2.5.x.[1][8][9][10]
Development and Maintenance
Apache Struts originated from early MVC frameworks and was founded in May 2000 by Craig McClanahan as part of the Apache Jakarta Project. It graduated to become a top-level Apache project in March 2004, marking its transition to independent governance under the Apache Software Foundation.[11][12]
The Apache Struts Project Management Committee (PMC), established in 2004, oversees day-to-day operations, including decision-making on releases, code changes, security patches, and ensuring compliance with Apache bylaws. The PMC, chaired by the project Vice President, consists of active contributors who guide community development and maintain the project's intellectual property.[13][14]
Community involvement is facilitated through public mailing lists for discussions on development, user support, and announcements, as well as the Apache JIRA issue tracker for reporting bugs, proposing enhancements, and submitting patches. Contribution guidelines encourage volunteers to join as committers or PMC members after demonstrating consistent participation, fostering a collaborative open-source environment.[15][16]
Security practices emphasize proactive vulnerability management, with the project issuing patches on the same day vulnerabilities are disclosed, as seen in the rapid response to CVE-2017-5638 in March 2017—a remote code execution flaw exploited in the Equifax breach later that year due to unapplied updates. The team maintains the plugin ecosystem by recommending restrictions on development-only plugins like Config Browser in production and provides guidelines for secure configurations, such as disabling devMode and using allowlists for OGNL expressions.[17][18]
Apache Struts is distributed under the Apache License 2.0, a permissive open-source license that permits free use, modification, and distribution in both open-source and commercial projects without requiring derivative works to be open-sourced.[19]
History
Origins and Early Versions
Apache Struts originated in May 2000 when Craig R. McClanahan developed it as an open-source MVC framework for Java web applications, drawing inspiration from the Model 2 architecture and earlier servlet-based MVC implementations to separate concerns in web development.[11] McClanahan, involved in the Java Servlet and JSP expert groups as well as Tomcat implementation, donated the framework to the Apache Software Foundation later that year, where it became a subproject of the Jakarta initiative.[20]
The inaugural public release, Struts 1.0, arrived on June 15, 2001, marking a significant milestone by introducing foundational elements like the ActionServlet for orchestrating request processing, Action classes to encapsulate application logic, and form beans to handle user input binding and basic validation.[11] This version provided a standardized approach to building dynamic web applications using Java servlets and JSPs, addressing the growing complexity of enterprise web development in an era before widespread alternatives like Spring MVC emerged in 2003.[21]
Subsequent early releases built on this foundation with key enhancements. In 2002, Struts integrated with Jakarta Tag Libraries to streamline JSP templating and custom tag usage, improving developer productivity for view-layer construction.[11] Struts 1.1, with its beta versions appearing in late 2002 and final release in June 2003, incorporated the Tiles framework for modular page layouts, enabling reusable UI components without tight coupling to specific actions.[22] By Struts 1.2 in 2004, the Validator framework saw notable improvements, including declarative validation rules via XML configuration and expanded support for client-side checks, further solidifying Struts as a robust choice for servlet-centric web applications.[23]
Struts' early adoption was driven by its provision of a mature, convention-based structure for handling HTTP requests in servlet containers, filling a critical gap for standardized MVC patterns in Java EE before dependency injection frameworks gained prominence. By the time of its 1.0 release, the project already boasted contributions from 26 developers and a mailing list with over 1,300 subscribers, reflecting rapid community interest in its practical tools for form processing and navigation.[11]
Evolution to Struts 2
By the mid-2000s, the Apache Struts 1 framework faced significant limitations that hindered its adaptability to evolving web development needs, including heavy reliance on XML-based configuration files, tight coupling between components that complicated extension and testing, and scalability challenges in handling complex request processing. These issues prompted the Struts development team to initiate a comprehensive rewrite between 2005 and 2006, aiming to modernize the architecture while preserving core MVC principles. A key influence was the merger with the WebWork framework, whose community and codebase were integrated into the Struts project to leverage WebWork's more flexible, interceptor-based design and POJO-oriented actions, ultimately rebranding WebWork 2 as Apache Struts 2.[24][6]
The first general availability release of Apache Struts 2, version 2.0.0, arrived in February 2007, marking a pivotal shift by introducing annotation-based configuration options to reduce XML verbosity and enabling Plain Old Java Object (POJO) actions that eliminated the need for extending specific base classes, thereby improving testability and loosening servlet dependencies. This redesign addressed Struts 1's singleton action model, which required thread-safety measures, by adopting per-request action instantiation for simpler lifecycle management. Subsequent milestones built on this foundation; for instance, Struts 2.1 in 2008 introduced the Convention plugin, enabling zero-configuration setups through class naming conventions and package structures, further minimizing boilerplate code.[6][24][25]
Later advancements focused on integration with modern web standards, such as the addition of robust REST plugin support in Struts 2.5 released in 2016, which facilitated easier creation of RESTful web services with JSON and XML handling out of the box. Security received heightened attention following the 2017 Equifax data breach, which exploited an unpatched vulnerability in the Jakarta Multipart parser (CVE-2017-5638); in response, the project issued version 2.5.10 and subsequent updates in 2017, incorporating stricter input validation and other protections to mitigate remote code execution risks.[26]
As of 2025, Apache Struts continues to evolve toward greater compatibility with contemporary enterprise standards, emphasizing support for Jakarta EE 9 and later specifications through its 7.x series, with the latest stable release 7.1.1 incorporating modularization for better plugin independence and alignment with Java 17+. This ongoing development maintains Struts' relevance in cloud-native and microservices environments while ensuring backward compatibility for legacy migrations.[10][27]
Architecture
Model-View-Controller Implementation
Apache Struts implements the Model-View-Controller (MVC) architectural pattern, specifically the Model 2 variant, to separate concerns in web application development, enabling scalable and maintainable Java-based applications.[7] In this implementation, the framework provides a central controller to handle incoming requests, delegates business logic to independent model components, and renders responses through decoupled view technologies, promoting loose coupling between layers.[6]
The Model in Struts represents the application's business logic and data persistence, typically implemented using plain old Java objects (POJOs) or Enterprise JavaBeans (EJBs) that operate independently of the framework.[7] These components encapsulate domain-specific operations, such as data validation, database interactions, and rule enforcement, without direct dependencies on Struts classes, allowing reuse across different presentation layers.[6]
The View layer focuses on presentation and user interface rendering, primarily utilizing JavaServer Pages (JSP), Apache FreeMarker, or Velocity templates to generate dynamic content.[7] Struts enhances views with custom tag libraries, such as the <s:property> tag, which retrieves and displays values from the model or action context, ensuring clear separation from controller logic and facilitating reusable UI components.[28]
The Controller serves as the mediator, intercepting HTTP requests and routing them to appropriate handlers based on configuration mappings.[7] In Struts 1.x, this is managed by the central ActionServlet, while Struts 2.x employs the StrutsPrepareAndExecuteFilter to process requests through a filter chain, invoking action classes that coordinate with the model.[6]
The overall flow in Struts follows a structured sequence: an incoming request reaches the controller, which maps it to an action class for processing; the action interacts with the model to execute business logic; finally, the controller forwards the results to the view for rendering the response, maintaining decoupling and facilitating testability.[7] This pattern ensures that changes in one layer, such as updating the view technology, minimally impact others.[6]
Core Components and Flow
Apache Struts implements the Model-View-Controller (MVC) pattern through several core components that facilitate request handling and response generation. In both Struts 1 and Struts 2, Action classes serve as the primary controllers, encapsulating the business logic to process HTTP requests and interact with the model layer. In Struts 1.x, these classes extend a base Action class and implement an execute method returning an ActionForward to determine the next view. In Struts 2.x, action classes are typically plain old Java objects (POJOs) with an execute method (or equivalent) that returns a string result code. Additionally, Result types define how responses are rendered, such as the dispatcher result for forwarding to JSP pages or redirect results for HTTP redirects, configured to map action outcomes to view resources.[7][29]
Struts 2 introduces advanced data binding mechanisms absent in earlier versions, including the ValueStack and OGNL (Object-Graph Navigation Language). The ValueStack acts as a runtime stack of objects, starting with the Action instance at the top, enabling seamless data transfer between layers without explicit getters and setters. OGNL expressions evaluate against this stack for dynamic property access in views, such as referencing action properties directly (e.g., postalCode) or non-stack objects via the # prefix (e.g., #session.user), supporting collections and projections for flexible data manipulation. This contrasts with Struts 1, where data binding relies on simpler form beans and request parameters without such an integrated expression language.[30]
The request-response lifecycle in Struts follows a structured flow, varying slightly between versions. In Struts 1, an incoming HTTP request reaches the central ActionServlet, which uses the configuration file (struts-config.xml) to map the URL to an appropriate Action class via the RequestProcessor. The processor populates form beans from request parameters, invokes the Action's execute method to engage the model, and forwards the result to a view based on mappings. Struts 2 modernizes this with a filter-based approach: the StrutsPrepareAndExecuteFilter, which internally uses a Dispatcher, receives the request, consults struts.xml or annotations to identify the Action via the ActionMapper, creates an ActionProxy, and builds an ActionInvocation that executes a stack of interceptors before and after the Action. Interceptors provide modular pre- and post-processing, such as parameter binding or workflow control, replacing Struts 1's monolithic RequestProcessor with a more extensible interceptor stack. Finally, the selected Result type renders the view, completing the cycle.[7][31]
Error handling in Struts ensures robust application stability through declarative mappings for exceptions. Both versions support global exception configurations: Struts 1 uses struts-config.xml to define that map exception types to forward names, often integrated with web.xml for servlet-level error pages. Struts 2 extends this with and in struts.xml, allowing mappings like java.lang.Exception to an "error" result rendering a dedicated JSP, with the ExceptionInterceptor automatically catching unhandled errors during the invocation. Action-specific mappings override globals, and exceptions are exposed in views via stack properties for logging or display, such as <s:property value="exception"/>.[32][33]
Versions and Compatibility
Struts 1.x
Apache Struts 1.x serves as the foundational version of the framework, implementing the Model-View-Controller (MVC) pattern through a centralized servlet-based architecture designed for Java web applications. The core controller component is the ActionServlet, which acts as the central dispatcher for handling incoming HTTP requests, processing them according to configuration rules, and forwarding to appropriate Action classes for business logic execution. This servlet initializes the framework upon application startup, parses configuration files, and delegates request processing to a RequestProcessor instance, ensuring a consistent flow from client input to model interaction and view rendering.[34][35]
Integral to this architecture are FormBeans, implemented as subclasses of ActionForm, which encapsulate user input data from HTML forms and facilitate validation before passing it to the model layer. These beans provide getter and setter methods that map directly to form fields, enabling automatic population from request parameters and supporting scope management (e.g., request or session) to maintain state across interactions. All mappings, including those for FormBeans, Actions, and forward paths, are defined declaratively in the struts-config.xml file, which uses XML elements like <action-mappings>, <form-beans>, and <global-forwards> to wire the application's components without embedding configuration logic in code. This XML-driven approach centralizes the framework's wiring but requires manual maintenance for larger applications.[34][35]
Struts 1.x includes several key modules to extend its core functionality. The Tiles module, originally developed as part of the Struts ecosystem, enables the creation of composable views by assembling reusable page fragments (e.g., headers, footers, and body content) into full layouts defined in a tiles configuration file, promoting modular UI design and reducing JSP duplication. The Validator module provides a reusable framework for both client-side (JavaScript) and server-side input validation, integrating with ActionForms through XML rules in validator-rules.xml and form-specific settings in validation.xml to check fields against predefined or custom rules like required fields or email formats. Additionally, DynaActionForms offer a dynamic alternative to traditional ActionForms, allowing form definitions entirely in struts-config.xml without requiring a custom Java class; properties are specified with types (e.g., String, int), enabling runtime creation and access via bean utilities for prototyping or simple forms.[36][37][35]
Despite its robustness for enterprise applications at the time, Struts 1.x exhibits several limitations that have contributed to its obsolescence. The reliance on verbose XML configuration in struts-config.xml for nearly all aspects—from action mappings to validation rules—results in boilerplate code that scales poorly and hinders rapid development compared to more declarative or annotation-based alternatives. Action classes are instantiated as singletons by the framework, necessitating explicit thread-safety measures such as avoiding mutable instance variables or using synchronization, which increases development complexity and risk of concurrency issues in multi-threaded servlet environments. Furthermore, the architecture's tight coupling to the servlet API and requirement for subclassing base classes like Action and ActionForm limit extensibility and testability, making it challenging to mock dependencies or integrate with modern inversion-of-control containers without significant refactoring. Struts 1.x reached end-of-life status in 2013, with its final release (1.3.10) in 2008, after which no official security patches or bug fixes have been provided by the Apache project; however, it persists in many legacy systems due to the high cost of migration in large-scale deployments.[24][38][24][2]
In terms of compatibility, Struts 1.x requires a servlet container supporting Servlet API 2.3 or later (e.g., Tomcat 4.x) and JSP 1.2, allowing integration with older Java EE (now Jakarta EE) environments through standard JDBC, EJB, or ORM tools like Hibernate for the model layer. It does not support newer servlet specifications without adaptation. For migration to subsequent versions, the Apache Struts project provides strategies including the Struts 1 Plugin for Struts 2, which enables gradual integration by allowing existing Struts 1 Actions and Forms to run within a Struts 2 application via compatibility wrappers, alongside tools like XML transformation utilities (e.g., XSLT-based config converters) and tutorials for rewriting core components. These paths support hybrid deployments during transition, minimizing disruption for legacy codebases.[39][3]
Struts 2.x and Beyond
Struts 2.x represents a significant evolution in the framework, emphasizing simplicity and flexibility through POJO-based actions that do not require extending framework-specific classes, allowing developers to focus on business logic using standard Java objects. This design choice reduces coupling and enhances testability, as actions can be instantiated and tested independently of the framework.[40]
Annotation-driven configuration further streamlines development by enabling declarations like @Action for mapping URLs to methods and @Result for specifying outcomes, serving as an alternative to traditional XML files and promoting cleaner code. Complementing this, the Convention Plugin automates the discovery of actions, results, and packages based on naming conventions, eliminating much of the boilerplate configuration while maintaining explicit control where needed.[41][25]
The framework supports modular extensions through plugins, including the JSON Plugin, which facilitates the creation of RESTful web services by serializing action results to JSON format for easy consumption by clients. For ORM integration, Struts 2 commonly pairs with JPA via the Spring Plugin, enabling dependency injection of entity managers and repositories for seamless data persistence in enterprise applications. Additionally, the Portlet Plugin provides support for developing JSR-286 portlets in portal environments, though it has been deprecated since version 6.0.0 in favor of broader Jakarta EE compatibility.[42][43][44]
In terms of compatibility, Struts 2 offers full support for Jakarta EE 10 and later as of version 7.0.0 released in December 2024, ensuring alignment with modern servlet containers and Java 17+. The latest stable release is version 7.1.1 (October 2025), which maintains these compatibility requirements. Asynchronous processing is enabled through async actions and the Async Plugin, leveraging Servlet API 3.0+ for non-blocking operations in high-throughput scenarios. Security enhancements include built-in CSRF protection via the Token Interceptor, which validates unique session tokens to prevent unauthorized requests.[45][46][47][1]
Key Features
Configuration and Interceptors
Apache Struts 2 applications are primarily configured through the struts.xml file, which defines packages, actions, and results to map URLs to handler classes and specify outcomes such as JSP views or redirects.[48] Packages in struts.xml serve as logical units that group related actions, results, result types, interceptors, and interceptor stacks, allowing inheritance from base packages like struts-default for modular configuration.[49] For zero-XML configuration, Struts 2 supports Java 5 annotations via the Convention Plugin, where developers can use @Action to map methods to URLs, @Result to define outcomes, and @Namespace to organize paths without editing XML files.[50] Constants, such as struts.enable.DynamicMethodInvocation to enable dynamic method calls on actions, are set in properties files like struts.properties or directly in struts.xml, overriding defaults from struts-default.xml for framework behavior customization.[51]
Interceptors in Struts 2 form a stackable pipeline that processes requests before and after action invocation, implementing core features like parameter binding and exception handling in a modular, extensible manner.[52] The default interceptor stack, named defaultStack, includes essential interceptors such as params for setting request parameters on the action, validation for input checking, and workflow for handling validation errors by redirecting to input views if needed.[52] Interceptors are configured in struts.xml under the <interceptors> element, where individual ones are registered and grouped into reusable stacks like basicStack or custom ones that reference the default for layered processing.[53] To create custom interceptors, developers extend AbstractInterceptor and override the intercept method to add logic, such as logging request details or performing authentication checks before invoking the next element in the chain via ActionInvocation.invoke().[54]
Best practices for Struts 2 configuration emphasize namespaces to modularize applications by prefixing action paths (e.g., /user namespace for user-related actions), preventing name conflicts and enabling declarative security on namespace levels while using the root / or default empty namespace for global elements.[55] Wildcards in action names, such as <action name="/user/*" class="com.example.User{1}Action">, allow a single mapping to handle multiple URLs like /user/profile or /user/edit, with {1} substituting the captured segment for dynamic class or result resolution, reducing configuration verbosity when ordered properly in struts.xml.[56] For development efficiency, Struts 2 supports reloading XML configurations like struts.xml without application restart by setting struts.configuration.xml.reload=true in struts.properties, though this incurs a minor performance cost unsuitable for production.[57]
A key Struts 2-specific feature is the ModelDriven interface, which actions implement by providing a getModel() method returning a model object, enabling automatic population of that object with request parameters and placing it atop the value stack for form handling and validation.[58] The Model Driven Interceptor, part of the defaultStack, detects ModelDriven actions and pushes the model onto the stack, facilitating a form-bean-like approach where JSP tags bind directly to model properties without cluttering the action class.[58]
Validation and Internationalization
Apache Struts provides robust data validation mechanisms to ensure input integrity in web applications, supporting both server-side and client-side approaches. Validation rules can be defined using XML configuration files, such as validators.xml, where developers specify checks for required fields, email formats, string lengths, and numeric ranges using built-in validators like requiredstring, email, and int.[59] Alternatively, annotations such as @Validations and @RequiredFieldValidator enable declarative validation directly in action classes, allowing for concise rule definitions without external XML files.[60] These methods integrate seamlessly with the framework's validation interceptor, which automatically invokes validation prior to action execution and populates error messages if checks fail.[61]
The framework handles validation errors by mapping them to specific UI fields through FieldError objects, which can be displayed in views using tags like <s:fielderror>. This ensures user-friendly feedback, such as highlighting invalid inputs with localized messages. For client-side validation, Struts supports JavaScript generation via the Validator plugin, enabled by setting validate="true" on form tags, which mirrors server-side rules to provide immediate browser-based checks without full page reloads.[62] Developers can extend validation capabilities by creating custom validators that inherit from ValidatorSupport, overriding methods like validate to implement domain-specific logic, such as business rule enforcement or integration with external services.[59]
Internationalization (i18n) in Struts facilitates multi-language support by leveraging Java's ResourceBundle for locale-specific resources, typically stored in properties files like global.properties under the classpath. These bundles contain key-value pairs for messages, labels, and validation errors, loaded based on the user's locale detected from request parameters, cookies, or browser settings.[63] In views, the <s:text> tag retrieves and renders translated text dynamically, for example, <s:text name="welcome.message" />, pulling the appropriate string from the bundle while supporting parameter substitution for personalized content.[64]
Advanced i18n features include formatting dates, times, and numbers according to locale conventions, using Struts' built-in support for Java's DateFormat and NumberFormat classes integrated with ResourceBundle. For instance, a date key in the bundle can render as "MM/dd/yyyy" for en_US or "dd/MM/yyyy" for en_GB, ensuring cultural appropriateness without hardcoding formats. Custom resource bundles can be registered globally via struts.custom.i18n.resources in struts.properties, allowing hierarchical loading for package-specific overrides.[65] This approach extends to action classes and interceptors, where getText() methods access bundles for dynamic messaging, promoting scalable, locale-aware applications.[63]
Usage and Implementation
Basic Setup and Configuration
To begin using Apache Struts 2, developers must meet specific prerequisites, including Java 17 or higher for compatibility with the framework's requirements, the Jakarta Servlet API version 6 or later to handle web application lifecycles, and a build tool such as Maven for managing dependencies like the struts2-core artifact.[66] A servlet container, such as Apache Tomcat 10.1 or later (for Jakarta Servlet 6.0 support), is necessary to deploy and run the web application.[66][67] Basic knowledge of Java, JSP, filters, HTML, HTTP, and XML is also recommended to understand the configuration processes.[66]
The initial setup involves several key steps to integrate Struts into a web project. First, create a standard Maven-based Java web application project, ensuring the pom.xml file includes the Struts core dependency:
xml
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>7.1.1</version> <!-- Latest stable version as of November 2025 -->
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>7.1.1</version> <!-- Latest stable version as of November 2025 -->
</dependency>
Additionally, include logging dependencies like Log4j 2 for runtime diagnostics.[66] Next, configure the web.xml file in the WEB-INF directory to register the Struts filter, which intercepts requests and dispatches them through the framework:
xml
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.[StrutsPrepareAndExecuteFilter](/page/StrutsPrepareAndExecuteFilter)</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.[StrutsPrepareAndExecuteFilter](/page/StrutsPrepareAndExecuteFilter)</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This filter, [StrutsPrepareAndExecuteFilter](/page/StrutsPrepareAndExecuteFilter), serves as the entry point for Struts processing all incoming requests.[66] Finally, place a struts.xml configuration file in the src/main/resources directory (which maps to WEB-INF/classes at runtime) to define packages, actions, and results; a basic version enables development mode and maps a default action:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN" "https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<action name="[index](/page/Index)">
<result>/index.jsp</result>
</action>
</package>
</struts>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN" "https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<action name="[index](/page/Index)">
<result>/index.jsp</result>
</action>
</package>
</struts>
This setup allows the framework to handle requests routed to the [index](/page/Index) action, forwarding to index.jsp.[66]
For a minimal functional example, define a package in struts.xml and create an action class that extends ActionSupport, which provides default implementations for common methods like execute(). Note that in Struts 7.x, XWork classes have been refactored from com.opensymphony.xwork2 to org.apache.struts2. Consider a simple "Hello World" action in the package org.apache.struts.helloworld.action:
java
package org.apache.struts.helloworld.action;
import org.apache.struts2.action.ActionSupport;
public class HelloWorldAction extends ActionSupport {
public String execute() throws Exception {
return SUCCESS;
}
}
package org.apache.struts.helloworld.action;
import org.apache.struts2.action.ActionSupport;
public class HelloWorldAction extends ActionSupport {
public String execute() throws Exception {
return SUCCESS;
}
}
Map this action in struts.xml under the package:
xml
<package name="helloworld" extends="struts-default">
<action name="hello" class="org.apache.struts.helloworld.action.HelloWorldAction" method="execute">
<result name="success">/HelloWorld.jsp</result>
</action>
</package>
<package name="helloworld" extends="struts-default">
<action name="hello" class="org.apache.struts.helloworld.action.HelloWorldAction" method="execute">
<result name="success">/HelloWorld.jsp</result>
</action>
</package>
The corresponding result is a JSP file, HelloWorld.jsp, placed in src/main/webapp, which displays output using Struts tags:
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>[Hello World](/page/Hello_World)</title></head>
<body>
<h2>Hello from [Struts](/page/The_Struts)!</h2>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>[Hello World](/page/Hello_World)</title></head>
<body>
<h2>Hello from [Struts](/page/The_Struts)!</h2>
</body>
</html>
Accessing /hello.[action](/page/Action) invokes the action and renders the JSP upon successful execution.[68]
Environment considerations include IDE support to streamline development. The official Apache Struts IntelliJ IDEA plugin provides features like XML validation, code completion for struts.xml, and action navigation, compatible with recent IntelliJ IDEA versions including 2025.x and supporting Struts 7.x.[69][70] For Eclipse, the StrutsClipse plugin offers content assistance, hyperlinks, and validation for Struts 2 configurations, though it is outdated (last updated 2015) and compatibility with Struts 7 and modern Eclipse should be verified.[71] Testing can be facilitated with the Struts 2 JUnit Plugin (latest 7.1.1), which includes StrutsTestCase—an extension of JUnit's TestCase—to simulate requests and verify action outcomes without a full server deployment. Note package changes for imports in tests.[72]
Building a Simple Application
To build a simple Apache Struts application, consider a basic user login scenario where a form collects username and password inputs, an Action processes the submission, performs validation, and directs to success or error pages based on the outcome. This example uses Struts 7.x (as of November 2025), assuming a prior setup with the necessary JAR files in the WEB-INF/lib directory and web.xml configured for the Struts dispatcher. Update imports to use org.apache.struts2 packages.
Begin by creating the Action class to handle the login logic. Extend the ActionSupport class and override the execute() method to process the POST request, validate inputs, and return a result string like "success" or "input" for error handling. For Struts 7.x, annotate parameters with @StrutsParameter if injection is needed (enabled by default). For instance:
java
import org.apache.struts2.action.ActionSupport;
import org.apache.struts2.validator.annotations.Validation;
import org.apache.struts2.interceptor.validation.annotations.ValidationParameter;
@Validation
public class LoginAction extends ActionSupport {
@ValidationParameter(validatorType = "required")
private String username;
@ValidationParameter(validatorType = "required")
private String password;
// Getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
@Override
public String execute() {
// Simple validation check (detailed rules covered in the Validation and Internationalization section)
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
addActionError(getText("login.errors.required"));
return INPUT;
}
// Simulate authentication
if ("admin".equals(username) && "pass".equals(password)) {
return SUCCESS;
} else {
addActionError(getText("login.errors.invalid"));
return INPUT;
}
}
}
import org.apache.struts2.action.ActionSupport;
import org.apache.struts2.validator.annotations.Validation;
import org.apache.struts2.interceptor.validation.annotations.ValidationParameter;
@Validation
public class LoginAction extends ActionSupport {
@ValidationParameter(validatorType = "required")
private String username;
@ValidationParameter(validatorType = "required")
private String password;
// Getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
@Override
public String execute() {
// Simple validation check (detailed rules covered in the Validation and Internationalization section)
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
addActionError(getText("login.errors.required"));
return INPUT;
}
// Simulate authentication
if ("admin".equals(username) && "pass".equals(password)) {
return SUCCESS;
} else {
addActionError(getText("login.errors.invalid"));
return INPUT;
}
}
}
This Action uses annotations for validation and the addActionError method to queue error messages for display.
Next, develop the JSP form for user input, leveraging Struts tags from the struts-tags library. Place this in a file like login.jsp under the web application's root or a subdirectory, using <s:form> for form handling and <s:textfield> for inputs, with <s:submit> to trigger the Action. An example login form might look like:
jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>[Login](/page/Login)</title></head>
<body>
<s:form action="login" method="post">
<s:textfield name="username" key="login.username" size="20" />
<s:password name="password" key="login.password" size="20" />
<s:submit key="login.submit" />
</s:form>
<s:fielderror />
</body>
</html>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>[Login](/page/Login)</title></head>
<body>
<s:form action="login" method="post">
<s:textfield name="username" key="login.username" size="20" />
<s:password name="password" key="login.password" size="20" />
<s:submit key="login.submit" />
</s:form>
<s:fielderror />
</body>
</html>
The key attributes reference properties from a message resource bundle for internationalization, and <s:fielderror> displays validation errors. For success and error pages, create success.jsp and error.jsp with simple content like welcome messages or error notifications, mapped via result strings.
Configure the application flow in struts.xml, located in WEB-INF/classes or src/main/resources. Define the package, Action mapping, and result types to link the URL "/login" to the LoginAction class, specifying views for "success", "input", and other outcomes. A minimal configuration (using Struts 6.0 DTD for compatibility with 7.x):
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<action name="login" class="com.example.LoginAction">
<result name="success">/success.jsp</result>
<result name="input">/login.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<action name="login" class="com.example.LoginAction">
<result name="success">/success.jsp</result>
<result name="input">/login.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
This setup enables the framework to intercept requests to "/login.action" and invoke the Action.[66]
For testing and debugging, deploy the application to a servlet container like Apache Tomcat by packaging it as a WAR file and starting the server. Access the login form at http://localhost:8080/yourapp/login.action; submitting valid credentials (e.g., username "admin", password "pass") should redirect to success.jsp, while invalid ones return to login.jsp with errors. Common issues include the "NoActionFound" error, often due to missing .action extensions in URLs or incorrect mappings in struts.xml—verify the action name and namespace. Integrate Log4j for logging by adding its JAR and configuring log4j.properties to capture Action execution traces, such as:
properties
log4j.rootLogger=DEBUG, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.logger.org.apache.struts2=DEBUG
log4j.rootLogger=DEBUG, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.logger.org.apache.struts2=DEBUG
This logs framework events to the console during development. Note the logger name updated for new packages.
To extend the application, add a simple interceptor for request logging by defining a custom one that implements Interceptor and logs incoming parameters before the Action executes. Register it in struts.xml within the default package's interceptor stack:
xml
<interceptors>
<interceptor name="logger" class="com.example.LoggingInterceptor"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="logger"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="defaultStack"/>
<interceptors>
<interceptor name="logger" class="com.example.LoggingInterceptor"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="logger"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="defaultStack"/>
The LoggingInterceptor class (with updated imports for Struts 7.x) could log details like:
java
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.interceptor.Interceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LoggingInterceptor implements Interceptor {
private static final Logger logger = LogManager.getLogger(LoggingInterceptor.class);
@Override
public void destroy() {}
@Override
public void init() {}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
logger.info("Request intercepted: " + invocation.getProxy().getActionName());
return invocation.invoke();
}
}
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.interceptor.Interceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LoggingInterceptor implements Interceptor {
private static final Logger logger = LogManager.getLogger(LoggingInterceptor.class);
@Override
public void destroy() {}
@Override
public void init() {}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
logger.info("Request intercepted: " + invocation.getProxy().getActionName());
return invocation.invoke();
}
}
This prepends logging to the default stack without altering core behavior.[73]
Adoption and Impact
Notable Projects and Usage
Apache Struts has seen widespread adoption in enterprise environments, particularly for building robust Java-based web applications in sectors such as banking, e-commerce, and defense. Notable users include Bank of America for financial systems, Amazon for e-commerce platforms, and Lockheed Martin for government and aerospace-related portals.[74] These organizations leverage Struts' MVC architecture to handle complex workflows and large-scale data processing, often in legacy systems that remain operational due to the framework's stability and integration capabilities.[75]
As of mid-2025, surveys indicate that over 4,567 companies worldwide continue to use Apache Struts, with a significant portion being large enterprises employing more than 10,000 people and generating over $1 billion in revenue.[76] This represents approximately 0.8% of the overall backend framework market share, underscoring its persistent role in Java web development despite the rise of newer alternatives.[77][78]
A prominent case study highlighting Struts' impact is the 2017 Equifax data breach, where attackers exploited a vulnerability in Apache Struts (CVE-2017-5638) to access sensitive information of 147 million individuals.[79] The incident, which stemmed from an unpatched version of the framework in Equifax's dispute portal, exposed critical risks in legacy deployments and prompted widespread security audits across enterprises using Struts. In 2025, additional critical vulnerabilities (e.g., CVEs 2025-54656, 2025-48976, and 2025-48734) have further emphasized the need for updates in ongoing legacy systems.[80][81] Successful migrations, such as those in telecom and financial sectors, have involved updating to Struts 2.x or integrating modern security practices to mitigate similar vulnerabilities without full rewrites.[75]
The Struts project's community remains active, with its official GitHub repository garnering 959 stars and reflecting ongoing contributions from developers maintaining and extending the framework.[82] On Stack Overflow, the 'struts' tag supports extensive developer discussions, aiding troubleshooting and best practices for implementations in production environments.[83]
Comparisons with Alternatives
Apache Struts, as an action-oriented MVC framework, is frequently contrasted with other Java web frameworks like Spring MVC, JavaServer Faces (JSF), and Play Framework, highlighting differences in architecture, flexibility, and suitability for various application types. These comparisons reveal Struts' strengths in straightforward request handling and maturity within servlet-based environments, while exposing limitations in modern reactive paradigms or component-rich UIs.
Compared to Spring MVC, Struts offers a simpler out-of-the-box MVC structure with less emphasis on Inversion of Control (IoC), making it easier to set up basic action mappings without extensive configuration. However, Spring MVC excels in full-stack development through robust dependency injection and loose coupling, allowing for reusable components across layers like data access and security. Struts' actions are instantiated per request, potentially increasing overhead in high-traffic scenarios, whereas Spring's singleton controllers promote efficiency and testability.[84][85]
In contrast to JSF, Struts adopts a lightweight, action-oriented approach focused on routing requests to handlers, which suits RESTful APIs and simpler web services but provides less support for complex, stateful user interfaces. JSF, being component-based, enables richer UI development with reusable widgets and event-driven rendering, offering multiple mechanisms for element rendering beyond Struts' direct HTML generation via tags. Struts is thus preferable for backend-heavy applications, while JSF better serves interactive, form-centric frontends in enterprise settings.[86][87]
Against the Play Framework, Struts remains servlet-centric and mature for traditional Jakarta EE integrations, leveraging established plugins for validation and internationalization, but it lacks Play's reactive, stateless model optimized for scalable, real-time applications. Play emphasizes minimal resource use and hot-reloading for rapid iteration, making it more aligned with microservices, whereas Struts is favored for maintaining legacy systems requiring servlet container compatibility.[88][89]
Key decision factors include learning curve, where Struts is more accessible for beginners due to its prescriptive MVC flow compared to Spring's broader ecosystem demands; performance, with benchmarks showing similar throughput in standard MVC tasks but Play edging out in asynchronous handling; and ecosystem maturity, pitting Struts' targeted plugins against Spring Boot's extensive starters for quicker integrations. Ultimately, Struts suits projects prioritizing simplicity and servlet heritage, while alternatives like Spring or Play better address enterprise-scale or reactive needs.[90][91]