JBoss EAP and Spring Boot migration: understand the runtime, then verify behavior
A legacy Java upgrade often starts with dependency versions. A successful startup can still hide an HTML error where JSON was expected, a redelivered message, or a batch that exits without committing its data. These are teaching scenarios, not reported incidents from a deployed project.
The reference scenario moves EAP 6.4, JDK 7/8, and Spring 3/4 to EAP 8.1, JDK 21, and Spring Boot 3.5.16. The goal is to compare HTTP, database, MQ, and log behavior with the same cases. Sources were checked on 2026-10-03. This is a documentation-based learning and verification guide, not an integration test of that enterprise environment.
Start here: what EAP, Spring, and Boot each own
JBoss EAP is the application server: it provides a Servlet container, an EJB/MDB runtime, and configurable data sources, transactions, and management. Spring Framework lives in the application and provides beans, dependency injection, MVC, and transaction integration. Spring Boot assembles a Spring application through starters, dependency management, and conditional auto-configuration. Existing Spring business code still has a role.
A bean is an object created and managed by Spring; dependency injection supplies the objects it needs. EAP also manages components of its own. A Spring bean, CDI bean, and EJB do not automatically share a lifecycle merely because they are packaged in one EAR. Crossing those boundaries requires explicit integration.
WAR on EAP:
Client → EAP / Undertow → Filters → DispatcherServlet
→ Controller → Service → Repository → DataSource → DB
Executable Spring Boot application:
Client → Embedded web server → Filters → DispatcherServlet
→ Controller → Service → Repository → DataSource → DB
MDB on EAP:
MQ → Jakarta resource adapter → EAP MDB → Business logic
Standalone batch:
Scheduler → JVM main / Boot runner → Business logic → DB / MQThe key Web distinction is who starts and owns the HTTP container. EAP loads an externally deployed WAR; an executable Boot application usually starts an embedded server from main. MDBs and standalone batches have different entry points, so java -jar is not a universal migration recipe.
Align versions: a target list is not a verified compatibility matrix
| Layer | Before | Target |
|---|---|---|
| Java / EAP | JDK 7/8; EAP 6.4 | JDK 21; EAP 8.1 |
| Spring / Security | 3.2.9/4.3.x; 3.x/4.1 | Framework 6.2.19; Security 6.5.11 |
| Boot / Hibernate | No Boot; Hibernate 4.2/4.3 | Boot 3.5.16; Hibernate 6.6 family |
| REST / build | CXF 2.6 JAX-RS; Ant/Maven | Spring MVC 6.2; Maven |
| MQ / DB driver | IBM MQ client 9.0.4; legacy JDBC jar | MQ client 9.4.5.1; driver for the DB2 11.5 line |
| Frontend | Angular 11 | Angular 22 |
At the time of checking, the Boot 3.5.16 dependency table lists Framework 6.2.19, Security 6.5.11, and Hibernate ORM 6.6.53.Final. Red Hat lists Hibernate ORM 6.6.18.Final for EAP 8.1.0. Both being 6.6 does not make them identical, and EAP patches can update components. Decide whether EAP or the application supplies the JPA provider, then check deployment modules and vendor support.
Boot 3.5 requires at least Java 17 and Servlet 5.0 or later for traditional deployment; EAP 8.1 Undertow provides Servlet 6.0. That satisfies one requirement, not the entire dependency and configuration contract. Boot 4.0 requires Servlet 6.1 for external deployment, so it cannot simply replace Boot 3.5 in this EAP 8.1 guide.
An MQ client version does not establish the supported combination of queue manager, RAR, and operating system. “DB2 11.5 line” also does not uniquely identify a JDBC artifact: record its JAR or Maven coordinates, actual version, and database server version. Keep missing values explicit instead of letting AI infer them.
javax to jakarta: identify the owner of each API
| Category | Examples | Treatment |
|---|---|---|
| Jakarta EE API | javax.servlet, javax.persistence, javax.jms, javax.ejb | Migrate to matching jakarta packages and compatible dependencies |
| Java SE API | javax.sql, javax.naming, javax.crypto | Still JDK APIs; do not globally replace |
| Third-party / historical API | Legacy JAXB, validators, generated clients | Identify origin and compatible versions; regenerate or migrate deliberately |
JDK 21 still contains javax.sql.DataSource and javax.naming.InitialContext. Replacing every javax occurrence breaks valid JDBC and JNDI code. Imports are only part of the migration: old JARs, XML schemas, service loading configuration, and class names used through reflection may still refer to the old APIs.
rg -n "javax\.|jakarta\." src pom.xml
mvn -version
mvn help:effective-pom -Doutput=target/effective-pom.xml
mvn dependency:tree -DverboseThese commands collect evidence, not proof of a correct migration. Run Maven in a recoverable development checkout: project plugins may download dependencies or perform configured work. The effective POM shows merged configuration, and the dependency tree shows Maven resolution. Neither establishes which EAP module is loaded at runtime.
Web path: follow one HTTP request through Spring MVC
Treat the controller as the HTTP boundary: it receives paths, parameters, and bodies, calls a service, and produces a response. Services hold business rules; repositories handle persistence. DispatcherServlet dispatches requests to handlers. An earlier filter may reject a request, so failure to reach a controller does not necessarily mean the route is wrong.
package example.migration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
class PingController {
@GetMapping("/api/ping")
java.util.Map<String, String> ping() {
return java.util.Map.of("status", "ok");
}
}This routing fragment needs an application entry point, MVC dependencies, and placement within Spring component scanning. It is not a complete deployable project. Its fixed response never touches a database or MQ; a successful call proves this HTTP path only.
Moving CXF JAX-RS endpoints to Spring MVC requires explicit mapping of paths, methods, parameter defaults, content types, validation errors, exception translation, and JSON serialization. Replacing @Path with @RequestMapping is only an entry-point change. Date strings, null fields, or error formats can affect Angular and other clients even if the business service is unchanged.
Treat Security 3/4 to 6 as a separate migration. Compare unauthenticated access, denied authorization, session expiry, CSRF, and CORS cases. Do not disable controls merely to make cases pass. If migration exposes a legacy vulnerability, record its repair as a reviewed behavior change rather than preserving unsafe behavior indefinitely.
WAR on EAP: who starts the application and supplies libraries?
For traditional Spring MVC WAR deployment, Boot documents SpringBootServletInitializer, WAR packaging, and a provided embedded-container dependency. These are packaging steps; EAP logging, JPA, JAX-RS, and module loading still require application-specific checks. Spring WebFlux does not support this traditional WAR deployment path.
// Fragment for an existing Spring Boot 3.5 application
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}An external container uses the Servlet initialization path, so a working main method is insufficient. server.port primarily controls an embedded Boot server; it does not reconfigure EAP listeners. For a 404, inspect deployment state, context root, proxy prefix, and controller mapping. EAP standalone mode describes server management and is different from an executable Boot JAR.
When EAP manages a DataSource, Boot can look it up using spring.datasource.jndi-name. JNDI acts as a resource directory: the application must use the name configured in EAP. Tune pooling where the pool is actually created; Hikari settings in application.yaml may not apply to an EAP-owned pool.
# Example only: this resource must already exist in EAP
spring.datasource.jndi-name=java:jboss/datasources/ExampleDSHibernate and transactions: a working query does not prove commit behavior
Hibernate translates object operations into database access. Moving from 4.x to 6.6 calls for checks of query syntax, type mappings, identifier generation, lazy loading, and flush/commit timing. Migration documents cover successive transitions; the 6.6 migration guide alone does not cover every change since 4.x.
For each transaction, identify its initiator, participating resources, commit point, and rollback owner. Spring @Transactional does not automatically place every DB and MQ operation in one XA transaction. Inspect the transaction manager, DataSource, resource adapter, and actual enlistment. An annotation alone cannot describe the runtime contract.
A useful negative case injects a failure at a defined test point after a DB write. Compare persisted rows, message acknowledgment or redelivery, and whether retry creates duplicate business effects. Establish expected behavior from the original contract first, and run against isolated data and queues.
MDB and IBM MQ: preserve the consumer lifecycle deliberately
An MDB is a message-driven component managed by EAP’s EJB container. The IBM MQ resource adapter connects the messaging system to the server and delivers inbound messages to the MDB. An MQ client JAR used by a standalone batch and a RAR deployed to EAP are different deliverables; upgrading the client alone does not migrate the MDB.
IBM supplies wmq.jmsra.rar for Java EE/JMS and wmq.jakarta.jmsra.rar for Jakarta Messaging. For the EAP 8.1 Jakarta path, verify the latter and its supported versions, then check activation settings, JNDI names, selectors, consumer concurrency, transaction mode, and failure redelivery. Do not mix JMS 2.0 and Jakarta Messaging APIs within one application.
Replacing an MDB with @JmsListener changes management and integration and deserves a separate design decision. For behavior preservation, retain the consumer boundary first and verify bodies, correlation semantics, business properties, ordering requirements, and duplicate effects. Never establish a baseline by draining a production queue.
Standalone batches and Angular: verify each boundary separately
A batch may run directly under a scheduler with no EAP involved. If introducing Boot, decide whether it needs Web features, then preserve arguments, working directory, encoding, timezone, exit codes, commit frequency, and restart semantics. Specify whether a partially processed input restarts from the beginning or resumes before changing the implementation.
When replacing Ant with Maven, inventory resource copying, code generation, packaging exclusions, manifests, and environment parameters as well as dependencies. Compilation on JDK 21 does not establish compatibility of reflection, removed JDK components, or runtime options; examine the APIs actually used against the Java migration guide.
For Angular 11 to 22, follow the official Update Guide across successive major versions and check Node.js, TypeScript, and RxJS requirements. Frontend and backend work can proceed separately against a fixed API case set. Matching responses are insufficient for the UI: test login, error presentation, date formatting, routing, and user workflows.
Turn “same behavior” into comparable evidence
Capture raw outputs from cases on an isolated old environment, then reproduce initial data, inputs, and external dependency conditions on the new environment. Restore separate data snapshots for cases with side effects; do not replay a payment or message twice against production. Define completion conditions and observation time limits for asynchronous work.
| Evidence | Minimum comparison | Difference handling |
|---|---|---|
| HTTP | Status, headers, schema, types, nulls, dates/amounts | Compare fields and error forms against the contract |
| DB | Inserts/updates/deletes, constraints, commit/rollback | Sort only where order is not part of the contract |
| MQ | Body, business properties, counts, redelivery, correlation | Compare dynamic IDs by relationships; do not blindly ignore |
| Logs | Key events, severity, errors, trace relationships | Normalize timestamps/thread names only by approved rules |
Keep both raw evidence and normalized results. Define ignored fields and permitted sorting in versioned rules before comparison. AI must not remove failure messages or mask all numbers to make a diff disappear. Runtime startup logs may differ textually while business audit events require strict correspondence. Leave unexplained differences unresolved for an accountable reviewer.
case: order-validation-example
fixture: synthetic-data-v1
baseline: old-build-id
candidate: new-build-id
request: cases/order-validation/request.json
observations:
http: [status, headers, body]
db: [rows_before, rows_after]
mq: [messages, redelivery_observations]
logs: [business_events, errors]
normalization: reviewed-rules-v1
result: pending
unexplained_differences: []
# Teaching template only; no comparison has been executed.Locate the layer before changing code
| Symptom | First place to inspect |
|---|---|
| 404 | Deployment, context root, proxy path, MVC mapping |
| 401/403 or login redirect | Security filters, session/token, CSRF, authorization |
| ClassNotFound / NoSuchMethod | Maven resolution, WAR contents, EAP modules, actual class origin |
| Missing DataSource | JNDI name, EAP configuration, active property sources |
| Missing or repeated MDB processing | RAR, activation, selectors, transactions, redelivery |
| Batch exits with wrong data | Arguments, data snapshots, transactions, exit codes, restart behavior |
Ask AI to hand over one change, not another stack of documents
Choose one Web endpoint or one MDB case, record its environment and behavior contract, and ask AI to change one boundary. Request only the before/after behavior, design basis, up to three key locations, verification evidence, and unresolved issues. Asking where a particular exception rolls back a transaction is easier to ground in code than asking for an explanation of all of Spring.
Download the migration inventory and behavior comparison worksheet (Markdown)For a first exercise, pick one feature you own and explain its entry point, container, transaction owner, and four evidence types in your own words. Investigate only the fields you cannot answer. Expand to the next feature once you can point to the configuration and record you would inspect after a failure.
Sources
- EAP 支援標準/Supported standards
- EAP 元件版本/Component details
- EAP 8.1 概念/Introduction
- EAP 8.1 遷移/Migration
- Boot 3.5 系統要求/Requirements
- Boot 3.5 依賴版本/Managed dependencies
- Boot 4.0 外部容器要求/Servlet requirements
- Spring Bean 與 IoC/Beans and IoC
- Spring MVC DispatcherServlet
- Boot WAR 部署/Traditional deployment
- Boot DataSource/JNDI
- Boot 設定來源/Externalized configuration
- Spring Security 遷移/Migration
- Hibernate 6.6 遷移/Migration
- IBM MQ Resource Adapters
- IBM MQ RAR 取得與命名/Obtaining adapters
- IBM MQ Adapter 支援/Support
- JDK 21 javax.sql
- JDK 21 javax.naming
- JDK 21 遷移/Migration
- Angular Update Guide