Struts2 Spring Boot Migration

Walkthrough migration of a Struts2 Application to operate in a Spring Boot environment.

Param V V

This document outlines the steps necessary to make the bare minimum changes to a Struts 2 Web application, to eliminate the dependency on a hosted container and instead, operate as a Spring Boot application with embedded container.

Dependencies

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>9.0.22</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
            <version>2.5.20</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-spring-plugin</artifactId>
            <version>2.5.20</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

Plugins

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.7.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>

Properties

    <properties>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
    </properties>

Code Changes

  • Remove web.xml and add a Spring Boot application class.
  • Add a Spring Configuration to register the Struts Filter Dispatcher
@Configuration
public class FilterRegistrationConfig {

    @Bean
    public FilterRegistrationBean<StrutsPrepareAndExecuteFilter> filterRegistrationBean() {
        FilterRegistrationBean<StrutsPrepareAndExecuteFilter> registrationBean = new FilterRegistrationBean<StrutsPrepareAndExecuteFilter>();
        StrutsPrepareAndExecuteFilter struts = new StrutsPrepareAndExecuteFilter();
        registrationBean.setFilter(struts);
        registrationBean.setUrlPatterns(Arrays.asList("/*"));
        registrationBean.setOrder(1);
        registrationBean.setInitParameters(Collections.singletonMap("actionPackages", "example.actions"));
        return registrationBean;
    }
}
  • Annotate all the Action Classes and depending components to be autowired as @Component.
  • Add a file in src/main/resources. Name: struts.properties
struts.convention.exclude.parentClassLoader=false
struts.convention.action.fileProtocols=jar,code-source
  • Replace the actions and results from struts.xml with corresponding annotations in the Action classes.

HelloAction.java

@Service
@Namespaces({
        @Namespace("/")
})
public class HelloAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    private static final Logger log = LogManager.getLogger(HelloAction.class);

    private String message;

    @Action(value = "hello", results = {@Result(name = "success", location = "/WEB-INF/content/hello-success.jsp")})
    public String execute() throws Exception {
        log.info("In execute method of class Hello");
        message = "Hello from Struts 2 with no XML configuration.";
        return SUCCESS;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

RegisterAction.java

@Service
@Namespaces({
        @Namespace("/")
})
public class RegisterAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    private static final Logger log = LogManager.getLogger(RegisterAction.class);

    @Autowired
    private Person personBean;

    @Action(value = "register-input", results = {@Result(name = "input", location = "/WEB-INF/content/register-input.jsp")})
    public String input() throws Exception {
        log.info("In input method of class RegisterAction");
        return INPUT;
    }

    @Action(value = "register", results = {@Result(name = "success", location = "/WEB-INF/content/register-success.jsp")})
    public String execute() throws Exception {
        log.info("In execute method of class RegisterAction");
        return SUCCESS;
    }

    public Person getPersonBean() {
        return personBean;
    }

    public void setPersonBean(Person person) {
        personBean = person;
    }
}
  • Remove the struts.xml file