Search code examples
timerejb-3.1

EJB 3 timers executed multiple times


I have some issues with my EJB 3 timer where it is launched more than once. Rather than configuring the timer in an annotation, I use programmatic configuration, as explain in many blogs. Here's my code:

@Singleton
@Startup
public class AutoAssignTask extends AbstractDirectoryMonitor {

    @Resource
    private TimerService timer;

    @Inject
    @PropertyResource(name = "timer.hour", resource = "/DL4/app.conf")
    private String hour;
    @Inject
    @PropertyResource(name = "timer.minute", resource = "/DL4/app.conf")
    private String minute;
    @Inject
    @PropertyResource(name = "timer.second", resource = "/DL4/app.conf")
    private String second;

    @EJB
    private AutoAssignService autoAssignService;

    @PostConstruct
    public void init() {
            // initializing with an expression.
        this.initSchedule();
    }
    protected void initSchedule() {
        ScheduleExpression exp = new ScheduleExpression();
        if (!StringUtils.isEmpty(this.getHour())) {
            exp.hour(this.getHour());
        }
        if (!StringUtils.isEmpty(this.getMinute())) {
            exp.minute(this.getMinute());
        }
        if (!StringUtils.isEmpty(this.getSecond())) {
            exp.second(this.getSecond());
        }
        this.getTimer().createCalendarTimer(exp);
    }

    @Timeout
    public void process() {
        // do something
        AutoAssignTask.LOG.info("starting job.");
    }

Now let's say I configure the timer for every minute, when I look at my logs I get twice the "starting job" log, proving it starts the job twice.

Any ideas on what is wrong?


Solution

  • A few thoughts:

    1. Update the code to show the initSchedule() method. The problem could be in the ScheduleExpression.
    2. You never can rule out server bugs. Check to make sure your @PostConstruct is not called twice which would happen if your server created two singletons (common race condition issue).
    3. As you have some sub-classing, make sure you're aware that if AutoAssignTask is further sub-classed the @PostConstruct of AutoAssignTask will still be called. More simply put, the @PostConstruct method of an EJB's super classes are also invoked -- the oldest parent first.

    For number 2 and 3, a simple static AtomicInteger and log statement would work:

    public class AutoAssignTask extends AbstractDirectoryMonitor {
    
        private static final AtomicInteger instances = new AtomicInteger();
    
        @PostConstruct
        public void init() {
            System.out.println("AutoAssignTask instance "+instances.incrementAndGet());
            //....
        }
    }