/**
*
*/
package com.trendrr.oss.messaging.channel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A synchronous channel. multiple threads can submit tasks to the channel. All requests are handled in
* order by the ChannelRequestHandler. The ChannelRequestHandler operates in a single dedicated thread.
*
* Note that throughput goes up as the number of threads submitting tasks goes up. The wait/notify cycle
* is full of latency, so using this for 1to1 thread communication is slow.
*
*
* @author Dustin Norlander
* @created May 16, 2012
*
*/
public class MessageChannel implements Runnable{
protected static Log log = LogFactory.getLog(MessageChannel.class);
ArrayBlockingQueue<ChannelRequest> requests = new ArrayBlockingQueue<ChannelRequest>(200);
protected String name;
protected ChannelRequestHandler handler;
private static ConcurrentHashMap<String, MessageChannel> channels = new ConcurrentHashMap<String, MessageChannel>();
/**
* creates a new channel with the given name. If a channel already exists with that name, it is stopped and this channel will
* replace it.
* @param name
* @param handler
* @return
*/
public static MessageChannel create(String name, ChannelRequestHandler handler) {
MessageChannel c = new MessageChannel(name, handler);
c.start();
return c;
}
/**
* returns the requested channel.
* @param name
* @return
*/
public static MessageChannel get(String name) {
return channels.get(name);
}
protected MessageChannel(String name, ChannelRequestHandler handler) {
this.handler = handler;
this.name = name;
}
protected void start() {
Thread t = new Thread(this);
t.setDaemon(true);
t.start();
MessageChannel c = channels.put(name, this);
if (c != null) {
c.stop();
}
}
/**
* stops the channel. it will not be available after this call.
*/
public void stop() {
channels.remove(this.name);
try {
requests.put(new ChannelStopRequest());
} catch (InterruptedException e) {
log.warn("Caught", e);
}
}
public Object request(String endpoint, Object ...inputs) throws Exception {
return this.request(new ChannelRequest(endpoint, inputs));
}
/**
* does a request to the channel. will wait for a response.
*
* @param request
* @return
* @throws Exception
*/
public Object request(ChannelRequest request) throws Exception{
this.requests.put(request);
ChannelResponse response = request.awaitResponse();
if (response.getError() != null) {
throw response.getError();
}
return response.getResult();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
ChannelRequest request = new ChannelStopRequest();
while(true) {
try {
request = this.requests.take();
if (request instanceof ChannelStopRequest) {
log.warn("Channel: " + this.name + " stopping");
//TODO: a channel stop callback?
return;
}
Object result = this.handler.handleRequest(request.getEndpoint(), request.getInputs());
request.setResponse(new ChannelResponse(result, null));
} catch (Exception e) {
request.setResponse(new ChannelResponse(null, e));
}
}
}
}