package VASSAL.tools.ipc;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
import VASSAL.tools.concurrent.listener.DefaultMultiEventListenerSupport;
import VASSAL.tools.concurrent.listener.EventListener;
import VASSAL.tools.concurrent.listener.MultiEventListenerSupport;
import com.google.common.util.concurrent.ValueFuture;
public class IPCMessenger {
protected final AtomicLong next_id = new AtomicLong(0L);
protected final Map<Long,ValueFuture<IPCMessage>> waiting =
new ConcurrentHashMap<Long,ValueFuture<IPCMessage>>();
protected final BlockingQueue<IPCMessage> outqueue =
new LinkedBlockingQueue<IPCMessage>();
protected final ObjectInputStream in;
protected final ObjectOutputStream out;
protected final MultiEventListenerSupport lsup;
public IPCMessenger(InputStream in, OutputStream out,
MultiEventListenerSupport lsup) throws IOException {
if (in == null) throw new IllegalArgumentException("in == null");
if (out == null) throw new IllegalArgumentException("out == null");
if (lsup == null) throw new IllegalArgumentException("lsup == null");
this.out = new ObjectOutputStream(out);
this.in = new ObjectInputStream(in);
this.lsup = lsup;
}
public IPCMessenger(Socket sock) throws IOException {
this(sock.getInputStream(), sock.getOutputStream());
}
public IPCMessenger(InputStream in, OutputStream out) throws IOException {
if (in == null) throw new IllegalArgumentException("in == null");
if (out == null) throw new IllegalArgumentException("out == null");
this.out = new ObjectOutputStream(out);
this.in = new ObjectInputStream(in);
this.lsup = new DefaultMultiEventListenerSupport(this);
lsup.addEventListener(IPCMessage.class, new EventListener<IPCMessage>() {
public void receive(Object src, IPCMessage msg) {
if (msg.isReply()) {
final ValueFuture<IPCMessage> f = waiting.remove(msg.getInReplyTo());
if (f == null) throw new IllegalStateException(msg.toString());
f.set(msg);
}
}
});
lsup.addEventListener(Halt.class, new EventListener<Halt>() {
public void receive(Object src, Halt halt) {
try {
send(new Fin(halt));
}
catch (IOException e) {
// FIXME: communcate this to a handler?
}
}
});
}
public void start() throws IOException {
final IPCMessageReceiver mr = new IPCMessageReceiver(in, lsup);
new Thread(mr, "IPC receiver for " + hashCode()).start();
final IPCMessageDispatcher md = new IPCMessageDispatcher(outqueue, out);
new Thread(md, "IPC dispatcher for " + hashCode()).start();
}
public void stop() throws IOException {
final Future<IPCMessage> f = send(new Halt());
try {
f.get();
}
catch (CancellationException e) {
throw new IllegalStateException(e);
}
catch (ExecutionException e) {
throw new IllegalStateException(e);
}
catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
public Future<IPCMessage> send(IPCMessage msg) throws IOException {
if (msg == null) throw new IllegalArgumentException("msg == null");
msg.setId(next_id.getAndIncrement());
final ValueFuture<IPCMessage> f = ValueFuture.<IPCMessage>create();
if (msg.expectsReply()) {
waiting.put(msg.getId(), f);
}
else {
f.set(null);
}
outqueue.offer(msg);
return f;
}
/**
* Adds an {@link EventListener}.
*
* @param c the class to listen for
* @param l the listener to add
*/
public <T> void addEventListener(Class<T> c, EventListener<? super T> l) {
lsup.addEventListener(c, l);
}
/**
* Removes an {@link EventListener}.
*
* @param c the class to check
* @param l the listener to remove
*/
public <T> void removeEventListener(Class<T> c, EventListener<? super T> l) {
lsup.removeEventListener(c, l);
}
/**
* Checks whether there are any {@link EventListener}s.
*
* @param c the class to check
* @return <code>true</code> if there are any listeners for the given class
*/
public boolean hasEventListeners(Class<?> c) {
return lsup.hasEventListeners(c);
}
/**
* Notify the listeners of an event.
*
* @param event the event to send
*/
public <T> List<EventListener<? super T>> getEventListeners(Class<T> c) {
return lsup.getEventListeners(c);
}
}