Struts1 Spring Boot Migration
Walkthrough migration of a Struts1 Application to operate in a Spring Boot environment.
This document outlines the steps necessary to make the bare minimum changes to a Struts 1 Web application, to eliminate the dependency on a hosted container and instead, operate as a Spring Boot application with embedded container.
Dependencies
<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>struts-core</artifactId>
<version>1.3.10</version>
</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>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>2.0.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
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
- Keep
web.xml
as is. It is required because the Struts 1.x ActionServlet eagerly looks for the presence of the web.xml. - Add a
ServletRegistration
Bean in the Spring Boot Application class, that configures thestruts-config.xml
in the actionServlet.
@Configuration
public class FilterRegistrationConfig {
@Bean
public ServletRegistrationBean actionServlet() {
ActionServlet servlet = new ActionServlet();
ServletRegistrationBean bean = new ServletRegistrationBean(servlet, "*.do");
bean.addInitParameter("config", "/WEB-INF/struts-config.xml");
bean.setLoadOnStartup(1);
bean.setName("action");
return bean;
}
}
- Instead of annotating each Action as Spring Component (which is not possible because the Action classes are managed and loaded by the Struts Classloader and not the Spring Class loader), we would create an abstract base class which would be inherited by all the Actions.
public abstract class ApplicationContextAwareAction extends Action {
protected AutowireCapableBeanFactory ctx;
@Override
public void setServlet(ActionServlet servlet) {
super.setServlet(servlet);
WebApplicationContext context =
WebApplicationContextUtils.getWebApplicationContext(servlet.getServletContext());
ctx = context.getAutowireCapableBeanFactory();
ctx.autowireBean(this);
}
}
Sample Action Class
public class LoginAction extends ApplicationContextAwareAction {
private static Logger logger = LoggerFactory.getLogger(LoginAction.class);
@Value("${helloworld.message}")
private String helloWorldMessage;
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
logger.info("Autowired message for Hello world: " + helloWorldMessage);
String path = "error";
LoginForm lf = (LoginForm) form;
String userName = lf.getUserName();
String password = lf.getPassword();
if((userName != null && "admin".equals(userName)) && //
(password != null && "admin".equals(password))){
path = "success";
request.setAttribute("userName", userName);
}
return mapping.findForward(path);
}
}