I have multiple MDBs (and plenty of mdb instances) as consumers for messages. I have to collect certain statistics inside these Beans and send them every X (currently 30) seconds to a JMS destination.
Is it ok to do this in the bean itself?
for example: the bean gathers the data localy and has a writeStatistic() method that uses the @Scheduled annotation.
Is it possible to do the statistic stuff centralised?
a central bean collects all statistics data from all bean instances and sends it of. If this is possible, how can it be done?
I solved my task by writing a singleton session bean that looks like this:
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Asynchronous
public class StatisticsCollector {
private ConcurrentMap<Integer, ConcurrentMap<String, AtomicInteger>> statistics = new ConcurrentHashMap<Integer, ConcurrentMap<String, AtomicInteger>>();
public void trackDatum(int projectId, String property, int increment) {
LOG.debug("Tracking datum: project: " + projectId + ", property: " + property + ", increment: " + increment);
// get statistics for project
ConcurrentMap<String, AtomicInteger> productstats;
if (!statistics.containsKey(projectId)) {
synchronized (statistics) {
if (!statistics.containsKey(projectId)) {
productstats = new ConcurrentHashMap<String, AtomicInteger>();
statistics.put(projectId, productstats);
} else {
productstats = statistics.get(projectId);
}
}
} else {
productstats = statistics.get(projectId);
}
// get current counter for property
AtomicInteger value;
if (!productstats.containsKey(property)) {
synchronized (productstats) {
if (!productstats.containsKey(property)) {
value = new AtomicInteger();
productstats.put(property, value);
} else {
value = productstats.get(property);
}
}
} else {
value = productstats.get(property);
}
// increment
value.addAndGet(increment);
}
@Schedule(minute = "*", hour = "*", second = "*/30", persistent = false)
public void sendStatistics() {
// send statistics to remote consumer via JMS
}
}
I did the concurrency management myself as i wanted to get as much performance out of the bean as i could.
Is it ok to do this in the bean itself?
I suppose you mean this:
@MessageDriven(...)
public MDBean implements MessageListener {
private Statistic statisticForThisBean;
// ...
@Timeout
public void sendStatisticForThisBean() {
// ...
}
}
It's certainly not an option. There's no guarantee that the @Timeout
-annotated method will run for all bean instances at all, not to mention your requirement of running every 30 seconds. The timeout method runs on one of free instances of an MDB, and the container chooses it.
Is it possible to do the statistic stuff centralised?
Personally, I'd do it this way: have all MDBs (all instances) send partial statistics to a queue, at the end of their processing logic. I'd write a separate component that would:
The "component" could be a timer task, or a separate stand-alone application.
Another possible solution could be a single stateful bean, that MDBs would call in turn to update statistics. However, the following limitations apply: