package com.workshare.msnos.core.routing;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.workshare.msnos.core.Cloud;
import com.workshare.msnos.core.Gateway;
import com.workshare.msnos.core.Identifiable;
import com.workshare.msnos.core.LocalAgent;
import com.workshare.msnos.core.Message;
import com.workshare.msnos.core.Receipt;
import com.workshare.msnos.core.RemoteAgent;
import com.workshare.msnos.core.cloud.MessageValidators;
import com.workshare.msnos.core.cloud.MessageValidators.Result;
import com.workshare.msnos.core.payloads.TracePayload;
import com.workshare.msnos.core.protocols.ip.Endpoint;
import com.workshare.msnos.core.protocols.ip.Endpoint.Type;
import com.workshare.msnos.core.protocols.ip.NullGateway;
import com.workshare.msnos.core.protocols.ip.http.HttpGateway;
import com.workshare.msnos.core.protocols.ip.udp.UDPGateway;
import com.workshare.msnos.core.protocols.ip.www.WWWGateway;
import com.workshare.msnos.core.receipts.SingleReceipt;
public class Router {
public static final Gateway NOOP_GATE = new NullGateway();
public static final String SYSP_MAXIMUM_HOPS_CLOUD = "com.ws.nsnos.core.router.hops.cloud.max";
public static final String SYSP_MAXIMUM_HOPS_DIRECT = "com.ws.nsnos.core.router.hops.direct.max";
public static final String SYSP_MAXIMUM_MESSAGES_PER_RING = "com.ws.nsnos.core.router.ring,messages.max";
private static final Logger routing = LoggerFactory.getLogger("routing");
private static final Logger logger = LoggerFactory.getLogger(Router.class);
private final Cloud cloud;
private final Gateway udpGate;
private final Gateway wwwGate;
private final Gateway httpGate;
private final MessageValidators validators;
private final Route[] routes;
private static enum Mode {TXX, FWD};
private final ThreadLocal<Mode> mode = new ThreadLocal<Router.Mode>();
public Router(Cloud cloud, Set<Gateway> gates) {
this(cloud, getGate(gates, UDPGateway.class), getGate(gates, HttpGateway.class), getGate(gates, WWWGateway.class));
}
Router(Cloud cloud, UDPGateway udpGate, HttpGateway httpGate, WWWGateway wwwGate) {
this.cloud = cloud;
this.udpGate = (udpGate != null ? udpGate : NOOP_GATE);
this.wwwGate = (wwwGate != null ? wwwGate : NOOP_GATE);
this.httpGate = (httpGate != null ? httpGate : NOOP_GATE);
this.validators = cloud.validators();
this.routes = new Route[] {
new TerminalRouteOnZeroHops(this),
new WWWRouteBroadcast(this),
new CloudRouteBroadcast(this),
new HTTPRouteDirect(this),
new UDPRouteSameRing(this),
new HTTPRouteViaRing(this),
new UDPRouteBroadcast(this),
};
}
Router(Cloud cloud, UDPGateway udpGate, HttpGateway httpGate, WWWGateway wwwGate, Route[] routes) {
this.cloud = cloud;
this.udpGate = (udpGate != null ? udpGate : NOOP_GATE);
this.wwwGate = (wwwGate != null ? wwwGate : NOOP_GATE);
this.httpGate = (httpGate != null ? httpGate : NOOP_GATE);
this.validators = cloud.validators();
this.routes = routes;
}
public Gateway udpGateway() {
return udpGate;
}
public Gateway httpGateway() {
return httpGate;
}
public Gateway wwwGateway() {
return wwwGate;
}
public Receipt send(Message message) {
mode.set(Mode.TXX);
return route(message);
}
public Receipt forward(Message message) {
mode.set(Mode.FWD);
final Result result = validators.isForwardable(message);
if (result.success()) {
return route(message);
} else
return skip(message, result.reason());
}
Receipt route(Message message) {
logger.debug("Routing message {}", message);
for (Route route : routes) {
Receipt receipt = route.send(message);
if (receipt != null) {
if (logger.isDebugEnabled())
logger.debug("Message {} routed via {}, result is {}", message, route.getClass().getSimpleName(), receipt);
return receipt;
}
}
routing.info("{} {} {} {} {} {}", "N/A", "N/A", "NO-ROUTE", mode.get(), message);
logger.warn("Unable to send message {} no route found", message);
return SingleReceipt.failure(message);
}
Cloud cloud() {
return cloud;
}
boolean hasRouteFor(RemoteAgent remote) {
Set<Endpoint> endpoints = remote.getEndpoints(Type.HTTP);
return endpoints.size() > 0;
}
Receipt sendViaWWW(Message message, String how) {
return this.send(message, null, message.getHops(), wwwGate, how);
}
Receipt sendViaUDP(Message message, int hops, String how) {
if (udpGate.name().equals(message.getReceivingGate())) {
routing.info("{} {} {} {} {} {}", mode.get(), how, udpGate.name(), "UDP-TO-UDP", message);
return SingleReceipt.failure(message);
}
return this.send(message, null, hops, udpGate, how);
}
Receipt sendViaHTTP(Message message, Identifiable to, int hops, String how) {
return this.send(message, to, hops, httpGate, how);
}
private Receipt send(Message message, Identifiable to, int hops, Gateway gate, String how) {
if (message.getType() == Message.Type.TRC) {
TracePayload payload = (TracePayload) message.getData();
payload = payload.crumbed(findSource(), findDestination(to, message), gate, message.getHops());
message = message.data(payload);
}
Receipt receipt;
try {
final Message hoppedMessage = message.withHops(hops);
receipt = gate.send(cloud, hoppedMessage, to);
routing.info("{} {} {} {} {} {} {}", mode.get(), how, gate.name(), receipt.getStatus(), hops, hoppedMessage);
} catch (IOException e) {
receipt = SingleReceipt.failure(message);
routing.info("{} {} {} {} {} {}", mode.get(), how, gate.name(), "GATE-FAILURE", message);
logger.warn("Unable to send message {} trough gateway {}", message, gate);
}
return receipt;
}
private UUID findDestination(Identifiable to, Message message) {
if (to != null)
return to.getIden().getUUID();
else
return message.getTo().getUUID();
}
private UUID findSource() {
Collection<LocalAgent> locals = cloud.getLocalAgents();
if (!locals.isEmpty())
return locals.iterator().next().getIden().getUUID();
else
return cloud.getRing().uuid();
}
Receipt skip(Message message, String how) {
routing.debug("{} {} {}", mode.get(), how, message);
return SingleReceipt.failure(message);
}
Receipt terminal(Message message, String how) {
routing.info("{} {} {} {}", mode.get(), how, "NONE", message);
return SingleReceipt.failure(message);
}
@SuppressWarnings("unchecked")
private static <T> T getGate(Set<? super T> gates, Class<T> clazz) {
for (Object gate : gates) {
if (gate.getClass() == clazz)
return (T)gate;
}
return null;
}
}