Return to site

🏛️⏱️ EJB ASYNC — RUN WORK IN THE BACKGROUND (SAFELY)

· jakartaee

🔸 TL;DR 🧾

▪️ Session Beans = managed business services (stateless/stateful/singleton)

▪️ @Asynchronous = easy background execution under container control

▪️ MDBs = message consumers for event-driven processing

▪️ Transactions = one of EJB’s strongest features (container-managed by default)

Section image

EJB can feel “old school”… but the ideas are timeless: server-side components, managed lifecycle, transactions, async, and messaging — all handled by the container ✅ If you work with Jakarta EE apps (or legacy Java EE), understanding EJB is still a practical skill.

🔸 SESSION BEANS: THE “SERVICE LAYER” OF EJB 🧠

▪️ Stateless: no client-specific state → best for scalable business services

▪️ Stateful: keeps conversational state per client → useful for multi-step workflows

▪️ Singleton: one instance for the app → shared state + scheduled jobs/caches

🔸 SIMPLE SESSION BEAN: THE CORE IDEA ⚙️

▪️ You write a class annotated like @Stateless / @Stateful

▪️ The container injects it with @EJB (or @Inject in CDI contexts)

▪️ The container manages pooling, lifecycle, security, and transactions

1) You write a session bean (business service)

// 1) You write a session bean (business service)
//    The container will manage lifecycle, pooling, security, and transactions.
import jakarta.ejb.Stateless;
import jakarta.annotation.security.RolesAllowed;
import jakarta.transaction.Transactional;

@Stateless
@RolesAllowed("USER")          // security handled by container
@Transactional                 // transaction boundary handled by container
public class BillingService {

  public String charge(String customerId, int amountCents) {
    // business logic here (DB calls, etc.)
    return "Charged " + amountCents + " cents to " + customerId;
  }
}

2) Your web layer (Servlet here) calls the bean

// 2) Your web layer (Servlet here) calls the bean
//    The container injects the EJB automatically.
import jakarta.ejb.EJB;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class BillingServlet extends HttpServlet {

  @EJB
  private BillingService billingService; // injected by container

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
    String customerId = req.getParameter("customerId");
    int amount = Integer.parseInt(req.getParameter("amountCents"));

    String result = billingService.charge(customerId, amount);

    resp.setContentType("text/plain");
    try {
      resp.getWriter().println(result);
    } catch (Exception ignored) { }
  }
}

(Optional) CDI-style injection instead of @EJB

// (Optional) CDI-style injection instead of @EJB
import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

@Path("/billing")
public class BillingResource {

  @Inject
  BillingService billingService;

  @POST
  public String charge() {
    return billingService.charge("cust-42", 1999);
  }
}

🔸 INVOKING SESSION BEANS FROM A WEB APP 🌐

▪️ Classic setup: a Servlet / JSF / JAX-RS resource calls an injected EJB

▪️ Your web layer stays thin, the EJB holds business logic

▪️ The container takes care of the “enterprise plumbing” (tx, threads, resources)

🔸 ASYNCHRONOUS METHOD CALL ⏱️

Need “fire-and-forget” or background work without blocking the request thread?

▪️ Use @Asynchronous on a method

▪️ Return void for fire-and-forget, or a Future/CompletionStage style result (depending on stack)

▪️ The container runs it in managed threads (safer than manual new Thread())

Service

import jakarta.ejb.Asynchronous;
import jakarta.ejb.Stateless;
import jakarta.ejb.AsyncResult;
import jakarta.enterprise.concurrent.ManagedExecutorService;
import jakarta.annotation.Resource;

import java.util.concurrent.Future;
import java.util.concurrent.CompletableFuture;

// EJB async: container-managed threads (no new Thread())
@Stateless
public class ReportService {

  // 1) Fire-and-forget ✅
  @Asynchronous
  public void generateReport(String reportId) {
    // long-running work (IO, DB, remote calls...)
    // container manages the execution thread
    System.out.println("Generating report " + reportId);
  }

  // 2) Future-based result ✅
  @Asynchronous
  public Future<String> generateReportWithStatus(String reportId) {
    // do work...
    return new AsyncResult<>("DONE:" + reportId);
  }

  // 3) CompletionStage style (common alternative in modern code) ✅
  // If your stack prefers CompletionStage, you can adapt (example).
  @Resource
  ManagedExecutorService executor;

  public CompletableFuture<String> generateReportAsync(String reportId) {
    return CompletableFuture.supplyAsync(() -> "DONE:" + reportId, executor);
  }
}

Resource

import jakarta.ejb.EJB;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

import java.util.concurrent.Future;

@Path("/reports")
public class ReportResource {

  @EJB
  ReportService reportService;

  // Doesn't block: returns immediately, work continues in background
  @POST
  @Path("/fire")
  public String fireAndForget(@QueryParam("id") String id) {
    reportService.generateReport(id);
    return "Accepted: " + id;
  }

  // Optionally wait for result (not always recommended in HTTP handlers)
  @POST
  @Path("/status")
  public String withFuture(@QueryParam("id") String id) throws Exception {
    Future<String> status = reportService.generateReportWithStatus(id);
    return status.get(); // blocks until done
  }
}

🔸 MESSAGE-DRIVEN BEANS (MDB): EVENT CONSUMERS 📩

MDBs are EJBs designed to consume messages (typically JMS).

▪️ The container delivers messages to your bean automatically

▪️ Great for decoupling, buffering spikes, and async processing

🔸 CONTAINER-MANAGED VS BEAN-MANAGED (MDB & TX) 🤝

▪️ Container-managed: you rely on annotations/config → simplest, most common

▪️ Bean-managed: you explicitly control things (like transactions) → more code, more responsibility Rule of thumb: use container-managed unless you have a strong reason not to.

🔸 TRANSACTIONS IN EJB (STATEFUL VS STATELESS) 💳

EJB makes transactions boringly reliable (that’s a compliment 😄):

▪️ Stateless: each call is usually independent → clean, predictable tx boundaries

▪️ Stateful: can span multi-step workflows → tx boundaries can be more subtle

▪️ Use transaction attributes (like “required / requires new”) to express intent clearly

▪️ Keep tx short: do the DB work, commit, move on ✅

🔸 TAKEAWAYS 🎯

▪️ EJB is less about hype, more about production-grade primitives

▪️ Prefer stateless for scalability; use stateful only when workflow state is truly needed

▪️ Use container-managed transactions & messaging first — go bean-managed only when necessary

▪️ EJB concepts map well to modern patterns: services, async jobs, event consumers, tx boundaries

#java #jakartaee #ejb #enterprisejava #softwarearchitecture #transactions #messaging #jms #backend #developers #legacycode #microservices

Go further with Java certification:

Java👇

Spring👇

SpringBook👇

JavaBook👇