/*
* Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
* License: The Apache Software License, Version 2.0
*/
package com.almende.eve.transport;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.almende.eve.capabilities.handler.Handler;
import com.almende.util.callback.AsyncCallback;
import com.almende.util.jackson.JOM;
import com.almende.util.threads.ThreadPool;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* The Class Router, outbound transport selection based on protocol scheme.
*/
public class Router implements Transport {
private static final Logger LOG = Logger.getLogger(Router.class
.getName());
private final Map<String, Transport> transports = new HashMap<String, Transport>(2);
/**
* Register new transport. If a given protocol is already known, this will
* overwrite the reference.
*
* @param transport
* the transport
*/
public void register(final Transport transport) {
if (transport == null) {
LOG.warning("Not registering a null transport.");
return;
}
for (final String protocol : transport.getProtocols()) {
transports.put(protocol, transport);
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#send(java.net.URI, byte[],
* java.lang.String)
*/
@Override
public <T> void send(final URI receiverUri, final String message,
final String tag, final AsyncCallback<T> callback) throws IOException {
final Transport transport = transports.get(receiverUri.getScheme()
.toLowerCase());
if (transport != null) {
transport.send(receiverUri, message, tag, callback);
} else {
throw new IOException("No transport known for scheme:"
+ receiverUri.getScheme());
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#send(java.net.URI, byte[],
* java.lang.String)
*/
@Override
public <T> void send(final URI receiverUri, final byte[] message,
final String tag, final AsyncCallback<T> callback) throws IOException {
final Transport transport = transports.get(receiverUri.getScheme()
.toLowerCase());
if (transport != null) {
transport.send(receiverUri, message, tag, callback);
} else {
throw new IOException("No transport known for scheme:"
+ receiverUri.getScheme());
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#send(java.net.URI, byte[],
* java.lang.String)
*/
@Override
public <T> void send(final URI receiverUri, final Object message,
final String tag, final AsyncCallback<T> callback) throws IOException {
final Transport transport = transports.get(receiverUri.getScheme()
.toLowerCase(Locale.ENGLISH));
if (transport != null) {
transport.send(receiverUri, message, tag, callback);
} else {
throw new IOException("No transport known for scheme:"
+ receiverUri.getScheme());
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#connect()
*/
@Override
public void connect() throws IOException {
final Set<Transport> result = new HashSet<Transport>(transports.size());
for (final Transport transport : transports.values()) {
result.add(transport);
}
for (final Transport transport : result) {
final long[] sleep = new long[1];
sleep[0] = 1000L;
final double rnd = Math.random();
final ScheduledThreadPoolExecutor STE = ThreadPool
.getScheduledPool();
STE.schedule(new Runnable() {
@Override
public void run() {
try {
transport.connect();
} catch (IOException e) {
LOG.log(Level.WARNING,
"Failed to reconnect agent on new transport.",
e);
if (sleep[0] <= 80000) {
sleep[0] *= 2;
STE.schedule(this, (long) (sleep[0] * rnd),
TimeUnit.MILLISECONDS);
}
}
}
}, (long) (sleep[0] * rnd), TimeUnit.MILLISECONDS);
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#disconnect()
*/
@Override
public void disconnect() {
final Set<Transport> result = new HashSet<Transport>(transports.size());
for (final Transport transport : transports.values()) {
result.add(transport);
}
for (final Transport transport : result) {
transport.disconnect();
}
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#delete()
*/
@Override
public void delete() {
final Set<Transport> result = new HashSet<Transport>(transports.size());
for (final Transport transport : transports.values()) {
result.add(transport);
}
for (final Transport transport : result) {
transport.delete();
}
transports.clear();
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#getHandle()
*/
@Override
public Handler<Receiver> getHandle() {
return null;
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#getAddress()
*/
@Override
public URI getAddress() {
return null;
}
/**
* Gets the addresses.
*
* @return the addresses
*/
public List<URI> getAddresses() {
final Set<URI> result = new HashSet<URI>(transports.size());
for (final Transport transport : transports.values()) {
result.add(transport.getAddress());
}
return new ArrayList<URI>(result);
}
/**
* Gets the addresses by scheme.
*
* @param scheme
* the scheme
* @return the addresses by scheme
*/
public URI getAddressByScheme(String scheme) {
return transports.get(scheme).getAddress();
}
/*
* (non-Javadoc)
* @see com.almende.eve.transport.Transport#getProtocols()
*/
@Override
public List<String> getProtocols() {
return new ArrayList<String>(transports.keySet());
}
/*
* (non-Javadoc)
* @see com.almende.eve.capabilities.Capability#getParams()
*/
@Override
public ObjectNode getParams() {
final ArrayNode transportConfs = JOM.createArrayNode();
for (final Transport transport : transports.values()) {
transportConfs.add(transport.getParams());
}
final ObjectNode result = JOM.createObjectNode();
result.set("transports", transportConfs);
return result;
}
}