DEV Community

Cover image for Modernizing Legacy Struts2 Applications with Claude Code: A Developer's Journey
Damien Gallagher
Damien Gallagher

Posted on

Modernizing Legacy Struts2 Applications with Claude Code: A Developer's Journey

Introduction

As a developer interested in the maintenance of legacy Java web applications, I recently tackled the challenge of modernizing an aging Struts2 project. I found a random Struts2 project on GitHub that I felt was a good candidate for this experiment. Overall, I spent 1 hour on this and the results in that time were amazing. The original application, built with technologies from the early 2010s, needed a significant upgrade to meet modern development standards. This article details how I leveraged Claude Code, Anthropic's AI-powered coding assistant, to transform a traditional Struts2 application into a modern Spring Boot microservice.

The Starting Point: Legacy Struts2 Architecture

The original project (github.com/josesanchezr/struts2-project) represented a typical enterprise Java application from its era:
Original Technology Stack:

  • Framework: Apache Struts 2.3.x
  • Build Tool: Maven 3.x
  • Java Version: JDK 7
  • Server: Apache Tomcat 7 (external deployment)
  • Configuration: XML-based (struts.xml, web.xml)
  • View Layer: JSP pages with Struts tags
  • Dependency Injection: Basic Struts2 IoC container
  • Logging: Log4j 1.x

The application followed the traditional WAR deployment model, requiring manual deployment to an external Tomcat server. Configuration was scattered across multiple XML files, making maintenance challenging.

The Modernization Goal

My objective was to transform this legacy application into a modern, cloud-ready microservice. The target architecture needed to include:

  • Spring Boot with embedded server
  • Annotation-based configuration
  • RESTful API endpoints
  • Modern logging with SLF4J/Logback
  • Docker compatibility
  • Improved testing capabilities
  • Simplified deployment process

How Claude Code Accelerated the Migration

Claude Code proved invaluable throughout the migration process. Here's how I used it at each stage:

1. Initial Assessment and Planning

I started by having Claude Code analyze the existing codebase structure.
The prompt I used was:

Can you upgrade this project to be based on java 21 and use Spring Boot instead of Struts. Also ensure the code is 100% unit tested and will run after all transformations are made. Please update the readme with simple steps to build and run the project
Enter fullscreen mode Exit fullscreen mode

By sharing the project's pom.xml and key configuration files, Claude helped me:

Identify all Struts2-specific dependencies
Map out the application's action classes and their relationships
Create a migration checklist prioritizing components by complexity
Suggest a phased migration approach to minimize risk

2. Dependency Migration

Claude Code automated the tedious process of updating dependencies:

<!-- Old Struts2 dependencies -->
<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.3.37</version>
</dependency>

<!-- Migrated to Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

3. Configuration Transformation

One of the most time-consuming aspects of migration is converting XML configurations to annotations. Claude Code excelled here:
Before (struts.xml):

<action name="login" class="com.example.LoginAction">
    <result name="success">/WEB-INF/jsp/welcome.jsp</result>
    <result name="error">/WEB-INF/jsp/login.jsp</result>
</action>
Enter fullscreen mode Exit fullscreen mode

After (Spring Boot Controller):

@RestController
@RequestMapping("/api")
public class LoginController {

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // Modern REST endpoint with JSON handling
        if (authService.authenticate(request)) {
            return ResponseEntity.ok(new LoginResponse("success", generateToken()));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                           .body(new ErrorResponse("Invalid credentials"));
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Action Class Conversion

Claude Code helped transform Struts2 Action classes into Spring MVC controllers systematically:
Original Struts2 Action:

public class UserAction extends ActionSupport {
    private String username;
    private UserService userService;

    public String execute() {
        User user = userService.findByUsername(username);
        if (user != null) {
            return SUCCESS;
        }
        return ERROR;
    }

    // Getters and setters...
}
Enter fullscreen mode Exit fullscreen mode

Modernized Spring Controller:

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    @GetMapping("/{username}")
    public ResponseEntity<UserDto> getUser(@PathVariable String username) {
        return userService.findByUsername(username)
                .map(user -> ResponseEntity.ok(UserMapper.toDto(user)))
                .orElse(ResponseEntity.notFound().build());
    }
}
Enter fullscreen mode Exit fullscreen mode

5. View Layer Modernization

Claude Code assisted in transitioning from JSP to a modern frontend approach:

Converted JSP pages to Thymeleaf templates for server-side rendering needs
Created REST endpoints for Single Page Application (SPA) integration
Generated OpenAPI documentation for the new API endpoints

6. Testing Strategy

Claude Code generated comprehensive test suites for the migrated components:

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void testGetUser_Success() throws Exception {
        // Test implementation generated by Claude Code
        User mockUser = new User("testuser", "[email protected]");
        when(userService.findByUsername("testuser"))
            .thenReturn(Optional.of(mockUser));

        mockMvc.perform(get("/users/testuser"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.username").value("testuser"))
                .andExpect(jsonPath("$.email").value("[email protected]"));
    }
}
Enter fullscreen mode Exit fullscreen mode

The Final Result

The migrated application (github.com/damogallagher/struts2-project-migration) showcases modern Java development practices:
New Technology Stack:

  • Framework: Spring Boot 3.2.x
  • Build Tool: Maven 3.9.x with Spring Boot plugin
  • Java Version: JDK 17/21
  • Server: Embedded Tomcat/Undertow
  • Configuration: Annotation-based with application.yml
  • API: RESTful JSON endpoints
  • Database: Spring Data JPA with Hibernate
  • Logging: SLF4J with Logback
  • Documentation: OpenAPI/Swagger
  • Containerization: Docker-ready with multi-stage builds

Key Benefits Achieved

Simplified Deployment: From WAR files requiring external servers to executable JARs with embedded servers
Improved Developer Experience: Hot reload, better IDE support, and streamlined configuration
Enhanced Testability: Comprehensive testing with Spring Boot Test framework
Cloud-Ready: Easy containerization and deployment to cloud platforms
Modern API Design: RESTful endpoints replacing action-based URLs
Better Performance: Reduced startup time and memory footprint
Maintainability: Cleaner code structure with dependency injection and AOP

Lessons Learned

What Worked Well with Claude Code:

Pattern Recognition: Claude quickly identified common Struts2 patterns and suggested appropriate Spring equivalents
Bulk Conversions: Automated repetitive tasks like converting action mappings to controller endpoints
Best Practices: Consistently applied modern Java conventions and Spring Boot idioms
Error Prevention: Caught potential issues during migration, such as thread safety concerns
Documentation: Generated clear comments and API documentation

Challenges and Solutions:

Complex Business Logic: Some Struts2 interceptors required careful analysis to convert to Spring AOP
Session Management: Migrating from Struts2 session handling to stateless REST required architectural changes
Custom Tags: JSP custom tags needed complete reimplementation or replacement
Integration Testing: Required additional setup for testing the migrated components

Tips for Your Own Migration

Based on my experience, here are recommendations for using Claude Code in similar projects:

Start Small: Begin with simple CRUD operations before tackling complex business logic
Maintain Backwards Compatibility: Consider creating a facade layer during transition
Incremental Migration: Use the Strangler Fig pattern to gradually replace old components
Comprehensive Testing: Generate tests for both old and new implementations to ensure parity
Document Everything: Have Claude Code generate migration notes and decision logs

Conclusion

Modernizing legacy applications doesn't have to be a daunting task. With Claude Code as a capable pair programmer, I transformed a traditional Struts2 application into a modern Spring Boot microservice efficiently and confidently. The AI assistant not only accelerated the mechanical aspects of migration but also helped implement best practices and modern patterns throughout the codebase.
The resulting application is more maintainable, testable, and ready for cloud deployment. Most importantly, the development team can now work with modern tools and practices, improving both productivity and job satisfaction.
Whether you're facing a similar migration challenge or maintaining legacy systems, consider leveraging AI-powered coding assistants like Claude Code. They can turn a potentially months-long migration into a manageable project while ensuring the quality and consistency of the transformed codebase.

Top comments (0)