Enterprise intermediate

EJB Timer vs Jakarta Scheduler

Replace heavyweight EJB timers with Jakarta Concurrency's ManagedScheduledExecutorService for simpler scheduling.

✕ Java EE
@Stateless
public class ReportGenerator {
    @Resource
    TimerService timerService;

    @PostConstruct
    public void init() {
        timerService.createCalendarTimer(
            new ScheduleExpression()
                .hour("2").minute("0"));
    }

    @Timeout
    public void generateReport(Timer timer) {
        // runs every day at 02:00
        buildDailyReport();
    }
}
✓ Jakarta EE 10+
@ApplicationScoped
public class ReportGenerator {
    @Resource
    ManagedScheduledExecutorService scheduler;

    @PostConstruct
    public void init() {
        scheduler.scheduleAtFixedRate(
            this::generateReport,
            0, 24, TimeUnit.HOURS);
    }

    public void generateReport() {
        buildDailyReport();
    }
}
🪶

Reduced boilerplate

No @Timeout callback or ScheduleExpression — use the standard ScheduledExecutorService API.

🧪

Better testability

Plain methods and executor mocks make unit testing straightforward without EJB container.

☁️

Cloud-native friendly

Managed executors integrate with container lifecycle and work in lightweight runtimes.

Old Approach
EJB TimerService
Modern Approach
ManagedScheduledExecutorService
Since JDK
11
Difficulty
intermediate
EJB Timer vs Jakarta Scheduler
Available

Available since Jakarta EE 10 / Concurrency 3.0

EJB timers require a @Stateless or @Singleton bean with a @Timeout callback and XML or annotation-based schedule expressions. Jakarta Concurrency provides ManagedScheduledExecutorService, which uses the familiar java.util.concurrent scheduling API. The result is less boilerplate, easier unit testing, and no EJB container dependency.

Share 𝕏 🦋 in