I have a Stateless Session Bean with a TimerService.
On timeout, it starts consuming a JMS queue. In the processing of the message it requires access to an external resource which may be temporary unavailable. The timeout method calls MessageConsumer.receiveNoWait() in a loop until:
- There are no more messages to process: it registers a new timer= now + 10minutes. And ends.
- an error occured during the processing: it rollbacks the message and registers a new timer: now + 30minutes. And ends.
This way I'm in control when to restart and I have no sleeping threads thanks to the TimerService callback.
I would like to have multiple occurances of this session bean to anticipate bottlenecks on the queue:
+-----<ejb>-------+
| timerService |
| | +---------------------+
----| onTimeout() {} | -----------> | external dependency |
/ | | / +---------------------+
/ +-----------------+ /
/ /
+---------+ / /
|||queue|||K /
+---------+ \ /
\ +-----<ejb>-------+ /
\ | timerService | /
\ | | /
----| onTimeout() {} |
| |
+-----------------+
My session bean looks like this (simplified of course):
@Stateless
public class MyJob {
@Resource
private TimerService timerService;
@PostConstruct
public void init() {
registerNewTimer(1000L); // -> problem: timerService not accessible
System.out.println("Initial Timer created");
}
private void registerNewTimer(long duration) {
TimerConfig config = new TimerConfig();
config.setPersistent(false);
timerService.createSingleActionTimer(duration, config);
}
@Timeout
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void execute() {
try {
// instantiate JMS session and consumer
while ((message = consumer.receiveNoWait()) != null) {
// business logic with message
message.acknowledge();
}
// queue empty, let's stop for now and register a new timer + 10min
registerNewTimer(10*60*1000);
} catch (ResourceException re) {
// external resource unavailable, let's wait 30min
registerNewTimer(30*60*1000);
// last message not acknowledged, so rolled back
}
}
}
I don't want to use Message Driven Beans as I would like to stay in control when to consume messages (see the delay logic in case of errors).
The problem:
The error is in the @PostConstruct annotated init() method: at this moment it is not allowed to use the timerService. It is allowed when I make the sessionbean @Singleton but then I lose the possibility to process the queue in parallel. Does anyone has an idea how to solve this ? If TimerService is not the right mechanism, what can be an alternative. Is there a PostConstruct alternative which allows access to the referenced resources and is only called once after instantiation ?
Thanks in advance for any constructive information.