Reputation: 21
I use an external framework that has an Event system, for simplicity let's say it is like:
public interface EventListener {
public void eventA(int a);
public void eventB(int a, long b);
}
So the properties of the Event system are that after an eventA there may or may not be an eventB for that same a. But if there is an eventB for that a it needs to be within 3000 ms.
I'm not sure what clean solution there is for that problem, I looked in the java.util.concurrency
package and I didn't find anything that delivers a nice clean solution for that problem. I thought I might use Future<?>
but I couldn't come up with a nice solution.
I'll write in pseudo-code what I want to do:
public void eventA(int a) {
// fired Event A
if (within the next 3 seconds there was an eventB for _exactly_ this a)
doSomething(a, b)
else
doSomething(a)
}
public void eventB(int a, long b) {
}
So far I use a ConcurrentHashmap<Int, Long>
where I store all a-b Combinations from eventB and I check if within the last 3000 ms I got such an a-b Combination in eventA
, but I'm not really happy with that solution. I feel like there needs to be a cleaner way with the Java 1.5 Higher Level Concurrency API.
I would be happy about any hint for a clean solution to that problem.
Upvotes: 2
Views: 348
Reputation: 153
I'm not entirely sure if you need it to work like this, but the logic behind it is that when eventB is fired, it adds the data to a ConcurrentHashMap, and schedules itself to be removed after 3 seconds. When eventA is fired, it removes the data from the map (thus clearing the event for that key), and if there was an event, it calls doSomething(int, long), otherwise it calls doSomething(int).
import java.util.concurrent.*;
public class EventListenerImpl implements EventListener {
// You can change how many threads there are, or use a scheduler that's
// already in the application.
private final static ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(1);
private ConcurrentHashMap<Integer, Data> data = new ConcurrentHashMap<Integer, Data>();
@Override
public void eventA(int a) {
Data b = data.remove(a);
if (b != null) {
doSomething(a, b.getB());
} else {
doSomething(a);
}
}
@Override
public void eventB(final int a, final long b) {
final Data thisData = new Data(a);
// Needs to be synchronized because it may be removed by a scheduled event
synchronized (data) {
Data d = data.put(a, thisData);
if (d != null) {
d.getFuture().cancel(true);
}
}
thisData.setFuture(scheduler.schedule(new Runnable() {
@Override
public void run() {
data.remove(a, thisData);
}
}, 3, TimeUnit.SECONDS));
}
private class Data {
private long b;
private ScheduledFuture<?> future;
public Data(long b) {
this.b = b;
}
public long getB() {
return b;
}
public void setFuture(ScheduledFuture<?> future) {
this.future = future;
}
public ScheduledFuture<?> getFuture() {
return future;
}
}
}
Let me know if this isn't quite what you need, because I was a bit confused, and ended up guessing what you were trying to do. For example, if a
isn't a key of some sort, then this can be greatly simplified... It can also be simplified if eventA waits three seconds before calling doSomething, rather than tracking the last three seconds at all times.
Upvotes: 1
Reputation: 692073
How about using a map containing, for each received event A, an object (let's call it Foo
) containing the time at which A
was received, and a potential B
(null
initially).
When you receive an event A
, you create a Foo
instance with the current time and a null B
, you store it in the map, and you start a thread that sleeps for 3 seconds, then checks if the Foo
associated with A
contains a B
, removes the entry from the map, and acts accordingly (i.e. call doSomething
with the appropriate arguments).
When you receive a B
, you check if there is a Foo
in the map and if it has been received for less than 3 seconds (although that shouldn't happen), and store the B
in the Foo
.
You must of course synchronize when needed.
Upvotes: 0