How to automate Java EE to Jakarta EE namespace migration with Python
The real-world scenario
Imagine you are tasked with upgrading a decade-old enterprise application from Java EE 8 to Jakarta EE 10. The biggest hurdle is the namespace change: almost every import and XML configuration must change from javax.* to jakarta.*. Doing this manually for thousands of .java, web.xml, and persistence.xml files is a recipe for carpal tunnel syndrome and subtle bugs. It is like trying to rename a specific brand of bolt in every machine across a factory floor. This script acts as your automated technician, swapping every relevant label instantly and accurately.
The solution
We will use a Python script leveraging the pathlib library for robust file system navigation and the re module for precise string replacement. The script recursively scans a project directory, identifies source and configuration files, and applies a mapping of legacy javax packages to their modern jakarta equivalents while preserving unrelated namespaces like javax.sql or javax.crypto which did not change.
Prerequisites
Ensure you have the following installed on your system:
- Python 3.7 or higher
- Write access to the project folder you wish to migrate
The code
"""
-----------------------------------------------------------------------
Authors: Sharanam & Vaishali Shah
Recipe: Jakarta EE Namespace Migrator
Intent: Automatically update javax namespaces to jakarta in source files.
-----------------------------------------------------------------------
"""
import re
from pathlib import Path
def migrate_namespaces(target_dir):
# Define the mapping of legacy namespaces to Jakarta EE equivalents
# Note: We focus on namespaces that actually moved to Jakarta
mappings = {
r'javax.servlet': 'jakarta.servlet',
r'javax.persistence': 'jakarta.persistence',
r'javax.ejb': 'jakarta.ejb',
r'javax.el': 'jakarta.el',
r'javax.faces': 'jakarta.faces',
r'javax.inject': 'jakarta.inject',
r'javax.json': 'jakarta.json',
r'javax.jws': 'jakarta.jws',
r'javax.mail': 'jakarta.mail',
r'javax.resource': 'jakarta.resource',
r'javax.security.auth.message': 'jakarta.security.auth.message',
r'javax.security.enterprise': 'jakarta.security.enterprise',
r'javax.websocket': 'jakarta.websocket',
r'javax.ws.rs': 'jakarta.ws.rs',
r'javax.xml.bind': 'jakarta.xml.bind',
r'javax.xml.soap': 'jakarta.xml.soap',
r'javax.xml.ws': 'jakarta.xml.ws',
r'javax.batch': 'jakarta.batch',
r'javax.enterprise': 'jakarta.enterprise',
r'javax.validation': 'jakarta.validation',
r'http://xmlns.jcp.org/xml/ns/javaee': 'https://jakarta.ee/xml/ns/jakartaee'
}
# Define file types that usually contain these namespaces
extensions = {'.java', '.xml', '.properties', '.jsp', '.tag'}
base_path = Path(target_dir)
if not base_path.exists():
print(f"Error: Path {target_dir} does not exist.")
return
print(f"Starting migration in: {base_path.resolve()}")
files_modified = 0
# Walk through the directory recursively
for file_path in base_path.rglob('*'):
if file_path.suffix in extensions:
try:
content = file_path.read_text(encoding='utf-8')
new_content = content
# Apply each mapping using regex
for old_ns, new_ns in mappings.items():
new_content = re.sub(old_ns, new_ns, new_content)
if new_content != content:
file_path.write_text(new_content, encoding='utf-8')
print(f"Updated: {file_path.relative_to(base_path)}")
files_modified += 1
except Exception as e:
print(f"Could not process {file_path}: {e}")
print(f"Migration complete. Total files modified: {files_modified}")
if __name__ == "__main__":
# Change 'my-jakarta-project' to your actual project folder
target_project_directory = "src"
migrate_namespaces(target_project_directory)
Code walkthrough
First, we initialize a mappings dictionary. This is the core logic, containing pairs of old patterns and new strings. We use Raw Strings for the keys because javax.servlet contains dots which are special characters in Regex; we must escape them.
The script uses Path.rglob(‘*’) to perform a recursive search. Unlike older methods, pathlib handles different operating system path separators automatically, making this script cross-platform. We filter for specific extensions like .java and .xml to avoid corrupting binary files like images or compiled classes.
Inside the loop, we read the file content as UTF-8. We use re.sub() to perform the replacement. If the new_content differs from the original content, we write the changes back to the disk. This minimizes disk I/O by only saving files that actually required updates.
Sample output
When you run the script in the root of your project, you will see the following progress in your Terminal:
Starting migration in: /Users/sharanam/projects/legacy-app
Updated: src/main/java/com/example/UserServlet.java
Updated: src/main/resources/META-INF/persistence.xml
Updated: src/main/webapp/WEB-INF/web.xml
Updated: src/main/java/com/example/entity/User.java
Migration complete. Total files modified: 4
Conclusion
Automating the transition to Jakarta EE saves hours of tedious work and eliminates the risk of missing a single import buried in a large codebase. By using this Python utility, you ensure consistency across your entire project. Always remember to run this script on a fresh Git branch so you can review the diffs before committing the architectural shift.
🚀 Don’t Just Learn Jakarta EE — Master It.
This tutorial was just the tip of the iceberg. To truly advance your career and build professional-grade systems, you need the full architectural blueprint.
My book, Jakarta EE 10 For Beginners, takes you from “making it work” to “making it scale.” I cover advanced patterns, real-world case studies, and the industry best practices that senior engineers use daily.