JBoss EAPSpring BootJava 21Jakarta EE系統升級

JBoss EAP and Spring Boot migration: understand the runtime, then verify behavior

··10 min read
Need Spring Boot fundamentals first? Start the standalone learning seriesContinue with the independent JBoss EAP series: foundations and practical work

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.

text
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 / MQ

The 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

LayerBeforeTarget
Java / EAPJDK 7/8; EAP 6.4JDK 21; EAP 8.1
Spring / Security3.2.9/4.3.x; 3.x/4.1Framework 6.2.19; Security 6.5.11
Boot / HibernateNo Boot; Hibernate 4.2/4.3Boot 3.5.16; Hibernate 6.6 family
REST / buildCXF 2.6 JAX-RS; Ant/MavenSpring MVC 6.2; Maven
MQ / DB driverIBM MQ client 9.0.4; legacy JDBC jarMQ client 9.4.5.1; driver for the DB2 11.5 line
FrontendAngular 11Angular 22
Reference scope; verify actual artifacts, patch levels, and support matrices.

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

CategoryExamplesTreatment
Jakarta EE APIjavax.servlet, javax.persistence, javax.jms, javax.ejbMigrate to matching jakarta packages and compatible dependencies
Java SE APIjavax.sql, javax.naming, javax.cryptoStill JDK APIs; do not globally replace
Third-party / historical APILegacy JAXB, validators, generated clientsIdentify origin and compatible versions; regenerate or migrate deliberately
Classify APIs before editing imports, dependencies, and deployment descriptors.

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.

bash
rg -n "javax\.|jakarta\." src pom.xml
mvn -version
mvn help:effective-pom -Doutput=target/effective-pom.xml
mvn dependency:tree -Dverbose

These 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.

java
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.

java
// 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.

properties
# Example only: this resource must already exist in EAP
spring.datasource.jndi-name=java:jboss/datasources/ExampleDS

Hibernate 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.

EvidenceMinimum comparisonDifference handling
HTTPStatus, headers, schema, types, nulls, dates/amountsCompare fields and error forms against the contract
DBInserts/updates/deletes, constraints, commit/rollbackSort only where order is not part of the contract
MQBody, business properties, counts, redelivery, correlationCompare dynamic IDs by relationships; do not blindly ignore
LogsKey events, severity, errors, trace relationshipsNormalize timestamps/thread names only by approved rules
Compare all four evidence types; HTTP 200 does not cover the other three.

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.

yaml
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

SymptomFirst place to inspect
404Deployment, context root, proxy path, MVC mapping
401/403 or login redirectSecurity filters, session/token, CSRF, authorization
ClassNotFound / NoSuchMethodMaven resolution, WAR contents, EAP modules, actual class origin
Missing DataSourceJNDI name, EAP configuration, active property sources
Missing or repeated MDB processingRAR, activation, selectors, transactions, redelivery
Batch exits with wrong dataArguments, data snapshots, transactions, exit codes, restart behavior
These are investigation starting points, not automatic fixes.

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