Skip to content
PYD ENTERPRISES

Migration guide

Java EJB to Quarkus: recover the contracts the container was enforcing

Half of an EJB application's behavior lives in deployment descriptors and container semantics, not in Java. This guide shows how to extract both so a Quarkus team builds against the full contract.

Java EE / EJBQuarkus
DIFFICULTY
High
OUTPUT
OpenAPI / YAML
SYNTAX TRAPS
5 covered
WORKFLOW
6 steps

By Pranav Kulkarni, Head of Language Engineering · Updated 15 Oct 2026

Technical syntax challenges

Where Java EE / EJB migrations go wrong.

01

Transaction attributes live in XML

In EJB 2.x, <container-transaction> entries in ejb-jar.xml set Required, RequiresNew, Mandatory or NotSupported per method, sometimes with * wildcards. Quarkus has no EJB container, so each attribute must become an explicit @Transactional(TxType.X) on a CDI bean, and the spec has to say which method had which.

02

Application exceptions do not roll back

A checked application exception thrown from a CMT method leaves the transaction to commit unless the bean called ctx.setRollbackOnly(); a system exception rolls it back. Jakarta @Transactional follows similar defaults, but a port that wraps exceptions differently changes which partial updates persist.

03

Home and remote interfaces hide the real API

EJB 2.x splits a bean into home, remote or local, and implementation classes, wired by JNDI names such as java:comp/env/ejb/Account. The business operations are on the remote interface; the create and finder methods on the home interface are lifecycle plumbing.

04

Declarative security in descriptors

<method-permission> and <security-role-ref> entries restrict who can call each method. They are rules in their own right and map to @RolesAllowed checks in the target, but only if someone extracts them.

05

Vendor descriptors and stateful beans

WebLogic and WebSphere binding files add pool sizes, timeouts and JNDI mappings. Stateful session beans keep conversation state on the server, which a stateless Quarkus REST service must replace with explicit resources or tokens.

Extraction workflow

6 steps from source to signed-off spec.

The sequence a PYD ENTERPRISES Discovery Scan runs for this stack. Every output links back to source file and line range.

  1. 1

    Ingest EAR or source with all descriptors

    Provide source or compiled EAR, WAR and EJB JAR modules with ejb-jar.xml, application.xml, vendor descriptors and any annotations used on EJB 3.x beans.

  2. 2

    Merge annotations and XML into one bean model

    PYD ENTERPRISES resolves each bean's interfaces, JNDI names, transaction attributes and security roles, with XML overriding annotations as the spec requires.

  3. 3

    Build call graphs across JNDI lookups

    Lookups and injected references are resolved to concrete beans, so a remote call chain from a servlet through three session beans is one traced path.

  4. 4

    Extract business rules from session bean methods

    Program slicing from each persisted field and each thrown application exception isolates validation and calculation rules, emitted as DMN tables with source links.

  5. 5

    Export service contracts with container semantics

    Remote interfaces become OpenAPI 3.1 operations annotated with transaction attribute, required roles and exception-to-status mappings.

  6. 6

    Generate fixtures including rollback behavior

    Equivalence fixtures cover success, application exception and system exception paths and assert the persisted state after each, so the Quarkus service proves the same commit behavior.

Legacy in, specification out

The source, and what PYD ENTERPRISES extracts from it.

Left: the Java EE / EJB construct as it sits in production. Right: the reviewed artifact your Quarkus engineers build against. PYD ENTERPRISES produces the spec and the tests; your team writes the service.

LEGACY SOURCE

TransferBean.java + ejb-jar.xmlJava
1// ejb-jar.xml: TransferBean.transfer trans-attribute=Required
2// ejb-jar.xml: method-permission role-name=teller, supervisor
3public class TransferBean implements SessionBean {
4 private SessionContext ctx;
5
6 public void transfer(String fromId, String toId, BigDecimal amount)
7 throws InsufficientFundsException, FinderException {
8 AccountLocalHome home = (AccountLocalHome)
9 new InitialContext().lookup("java:comp/env/ejb/Account");
10 AccountLocal from = home.findByPrimaryKey(fromId);
11 AccountLocal to = home.findByPrimaryKey(toId);
12 if (amount.compareTo(new BigDecimal("10000.00")) > 0
13 && !ctx.isCallerInRole("supervisor")) {
14 throw new EJBException("Approval limit exceeded");
15 }
16 if (from.getBalance().compareTo(amount) < 0) {
17 throw new InsufficientFundsException(fromId);
18 }
19 from.setBalance(from.getBalance().subtract(amount));
20 to.setBalance(to.getBalance().add(amount));
21 }
22}

EXTRACTED ARTIFACT

transfers.openapi.yamlYAML
1openapi: 3.1.0
2info: { title: Funds transfer (from TransferBean), version: 1.0.0 }
3paths:
4 /transfers:
5 post:
6 operationId: createTransfer
7 x-pyd-source: TransferBean.java:5-21
8 x-pyd-transaction: REQUIRED
9 x-pyd-roles: [teller, supervisor]
10 x-pyd-decision: XFER-LIMIT-01
11 requestBody:
12 content:
13 application/json:
14 schema:
15 type: object
16 required: [fromId, toId, amount]
17 properties:
18 fromId: { type: string }
19 toId: { type: string }
20 amount: { type: string, pattern: "^\\d+\\.\\d{2}$" }
21 responses:
22 "201": { description: Transfer posted }
23 "403": { description: amount > 10000.00 and caller not supervisor (system exception, rollback) }
24 "404": { description: FinderException, account not found }
25 "422": { description: InsufficientFundsException, no state change }

Architectural best practices

Build the Quarkus side to the spec, not to the old shape.

  • Carry every transaction attribute into the spec and the target code explicitly; never rely on the default of a different container.
  • Turn isCallerInRole checks and method permissions into documented authorization rules with tests, not scattered annotations.
  • Design Quarkus services around business operations on the remote interfaces; drop home interfaces and JNDI lookups entirely.
  • Replace stateful session beans with explicit resources (a draft transfer, a cart) that have their own identifiers and lifecycle.
  • Assert persisted state, not only return values, in equivalence tests, because rollback semantics are where EJB ports diverge.

Quarkus is not an EJB container

Quarkus implements CDI (through ArC), Jakarta REST, Jakarta Transactions and Jakarta Persistence, but not Enterprise Beans. That is the point: you get fast startup and a small footprint by dropping the container that was silently managing transactions, pooling, security and remote invocation.

The consequence is that everything the container did must become explicit, and the only complete record of what it did is the combination of Java code and descriptors. PYD ENTERPRISES merges the two into one model per bean and exports the business operations, their transaction and security semantics and their rules as reviewed specs. It does not write Quarkus code; your engineers do, against those specs.

What the example tells a reviewer

The transfer method has two rules and one trap. The rules: transfers over 10,000.00 need the supervisor role, and the source balance must cover the amount. The trap: the limit check throws EJBException, a system exception that rolls back and is reported to remote clients as a RemoteException, while the balance check throws a checked application exception that does not roll back. Because both checks run before any write, no partial update happens today. If a later change moves a setter above the balance check, the application exception path would commit half a transfer. The extracted spec records both exception classes and their rollback behavior so the Quarkus design makes that choice on purpose.

FAQ

Java EE / EJB to Quarkus questions

Does PYD ENTERPRISES handle EJB 3.x annotations as well as 2.x descriptors?

Yes. Annotations such as @Stateless, @TransactionAttribute and @RolesAllowed are read alongside XML descriptors, and XML overrides are applied as the EJB specification defines.

Can it analyze compiled EARs without source?

Bytecode analysis covers structure, call graphs and descriptors. Rule extraction is more precise with source, so most teams provide source for business modules and compiled jars for libraries.

Where do entity beans fit in?

CMP and BMP entity beans are covered in the EJB entity beans to JPA and Spring Data guide, including CMR relationships and EJB QL finders.

Get the spec for your Java EE / EJB estate.

Request a codebase scan and review extracted rules, contracts and fixtures for your own Java EE / EJB estate, inside your VPC.