Apache Struts has changed considerably since the old Struts 2.5 examples. The current best-available release is Struts 7.2.1. Struts 7 requires Java 17 or newer and Jakarta Servlet 6 or newer, so Java 25 is a natural runtime for a new application.

This tutorial deliberately uses struts.xml rather than annotations so the framework's request mapping is easy to see. The request-response flow is simple:

  1. An HTML form posts a name to hello.action.
  2. Struts maps that action to HelloWorldAction.
  3. The action returns success.
  4. Struts renders results.jsp.

Struts 7 struts.xml configuration

Place struts.xml in src/main/resources. The action name and result mapping can be configured explicitly:

<?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>
    <package
        name="default"
        namespace="/"
        extends="struts-default">

        <action
            name="hello"
            class="com.mcnz.struts.HelloWorldAction">

            <result name="success">
                /results.jsp
            </result>
        </action>

    </package>
</struts>

The URL hello.action resolves to the action named hello, which Struts instantiates and executes.

HTML input form

The browser submits a parameter named firstName. Struts populates the matching JavaBean property on the action before execute() runs.

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Struts Hello World</title>
</head>
<body>

    <form action="hello.action" method="post">
        <label for="firstName">
            Please tell me your name:
        </label>

        <input
            id="firstName"
            name="firstName"
            type="text"
            value="World"
            required
        >

        <button type="submit">Submit</button>
    </form>

</body>
</html>

Struts action class with Java 25

The action remains a plain Java class. Extending ActionSupport provides the standard SUCCESS result constant and other Struts conveniences.

package com.mcnz.struts;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport {

    private String firstName;

    @Override
    public String execute() {
        IO.println("Value of firstName: " + firstName);
        return SUCCESS;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

IO.println() is fine for a teaching example. In a production web application, use the application's logging framework rather than writing diagnostics directly to standard output.

JSP result page

The JSP uses the standard Struts tag library to render the property populated on the action.

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Struts Result</title>
</head>
<body>

    <h1>
        Hello <s:property value="firstName" />!
    </h1>

</body>
</html>

Jakarta Servlet web.xml for Struts 7

Struts 7 uses Jakarta Servlet APIs. The old java.sun.com/xml/ns/javaee namespace and javax.servlet dependencies belong to pre-Jakarta applications.

<?xml version="1.0" encoding="UTF-8"?>

<web-app
    xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        https://jakarta.ee/xml/ns/jakartaee
        https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
    version="6.0">

    <display-name>Struts 7 Hello World</display-name>

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

Maven POM for Struts 7.2.1 and Java 25

A new project should not copy the old Java 8, Struts 2.5 or javax.servlet dependencies. Use Struts 7.2.1, Jakarta Servlet 6 and Java 25.

<project xmlns="https://maven.apache.org/POM/4.0.0"
         xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
             https://maven.apache.org/POM/4.0.0
             https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mcnz</groupId>
    <artifactId>struts-hello-world</artifactId>
    <version>1.0.0</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <struts.version>7.2.1</struts.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.struts</groupId>
                <artifactId>struts2-bom</artifactId>
                <version>${struts.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
        </dependency>

        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>struts-hello-world</finalName>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.1</version>
                <configuration>
                    <release>25</release>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Run the Struts 7 Hello World app

Package the application as a WAR and deploy it to a Jakarta Servlet 6 compatible container. Current Tomcat and Jetty releases are common choices.

The request path is now easy to trace: the browser submits firstName, the Struts filter receives the request, struts.xml selects HelloWorldAction, the action returns SUCCESS, and Struts renders results.jsp.

The MVC pattern is still recognizably Struts. The important modernization is the surrounding platform: supported Struts 7 binaries, Jakarta APIs and a current Java runtime.


Cameron McKenzie Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.