
org.coos.messaging.routing.DefaultRouter Maven / Gradle / Ivy
/**
* COOS - Connected Objects Operating System (www.connectedobjects.org).
*
* Copyright (C) 2009 Telenor ASA and Tellu AS. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* You may also contact one of the following for additional information:
* Telenor ASA, Snaroyveien 30, N-1331 Fornebu, Norway (www.telenor.no)
* Tellu AS, Hagalokkveien 13, N-1383 Asker, Norway (www.tellu.no)
*/
package org.coos.messaging.routing;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.coos.messaging.ConnectingException;
import org.coos.messaging.Link;
import org.coos.messaging.Message;
import org.coos.messaging.Processor;
import org.coos.messaging.ProcessorException;
import org.coos.messaging.ProcessorInterruptException;
import org.coos.messaging.impl.DefaultProcessor;
import org.coos.messaging.processor.LocalCoosAddressConverter;
import org.coos.messaging.util.Log;
import org.coos.messaging.util.LogFactory;
import org.coos.messaging.util.URIHelper;
import org.coos.messaging.util.UuidHelper;
/**
* The DefaultRouter (actually default point to point router) contains the point to point routing mechanisms
*
* @author Knut Eilif Husa, Tellu AS
*/
public class DefaultRouter extends DefaultProcessor implements Router {
private Collection preProcessors = new ConcurrentLinkedQueue();
private Collection postProcessors = new ConcurrentLinkedQueue();
private Map routingTables = new ConcurrentHashMap();
private Map aliasTable = new ConcurrentHashMap();
// todo let the links be handled by the coos instance
private Map links = new ConcurrentHashMap();
private Map routingAlgorithms = new ConcurrentHashMap();
// The uuid of the router in the different segments it participates in. If
// more than one uuid, this router is a gateway router
private Collection routerUuids = new ConcurrentLinkedQueue();
private Collection QoSClasses = new ConcurrentLinkedQueue();
private String defaultQoSClass;
private String COOSInstanceName;
private boolean running = false;
private boolean enabled = true;
private boolean loggingEnabled = false;
private Link defaultGw = null;
private final Log logger = LogFactory.getLog(this.getClass(), false);
// This constructor is only used by tests
public DefaultRouter(String routerUuid) {
COOSInstanceName = routerUuid;
new LinkStateAlgorithm(this, routerUuid);
addQoSClass(Link.DEFAULT_QOS_CLASS, true);
}
public DefaultRouter() {
addQoSClass(Link.DEFAULT_QOS_CLASS, true);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void removeRouterUuid(String routerUuid) {
routerUuids.remove(routerUuid);
}
public void setLoggingEnabled(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
for (RoutingAlgorithm routingAlgorithm : routingAlgorithms.values()) {
routingAlgorithm.setLoggingEnabled(loggingEnabled);
}
}
public void processMessage(Message msg) {
// Router preprocessors
for (RouterProcessor routerProcessor : preProcessors) {
try {
routerProcessor.processMessage(msg);
} catch (ProcessorInterruptException e) {
return;
} catch (ProcessorException e) {
e.printStackTrace();
}
}
if (msg.getReceiverEndpointUri() == null) {
logger.warn("Message from " + msg.getSenderEndpointUri() + " missing receiver address");
replyErrorReason(msg, Message.ERROR_NO_RECEIVER);
return;
}
String qosClass = msg.getHeader(Message.QOS_CLASS);
if (qosClass == null) {
qosClass = defaultQoSClass;
}
Map routingTable = routingTables.get(qosClass);
String hops = msg.getHeader(Message.HOPS);
if (hops == null) {
hops = "1";
} else {
hops = String.valueOf(Integer.parseInt(hops) + 1);
}
msg.setHeader(Message.HOPS, hops);
if (Integer.parseInt(hops) > 244) {
logger.warn("Message from " + msg.getSenderEndpointUri() + ", to: " + msg.getReceiverEndpointUri() + " too many hops");
replyErrorReason(msg, Message.ERROR_TOO_MANY_HOPS);
return;
}
String uuid = resolveAlias(msg);
if (uuid == null) {
// will never occur
replyErrorReason(msg, Message.ERROR_NO_ALIAS + ":" + msg.getReceiverEndpointUri());
return;
}
Link link;
// check if the message is destined towards this router
if (routerUuids.contains(uuid)) {
logger.putMDC(UUID_PREFIX, uuid);
if (msg.getType().equals(Message.TYPE_ROUTING_INFO)) {
routingAlgorithms.get(UuidHelper.getSegment(uuid)).processRoutingInfo(msg);
} else if (msg.getType().equals(Message.TYPE_ALIAS)) {
try {
setLinkAliases((Vector) msg.getBody(), msg.getMessageContext().getInBoundChannel().getOutLink());
} catch (ProcessorException e) {
e.printStackTrace();
}
}
} else {
// The routing step
link = route(uuid, msg, routingTable);
if(link != null){
msg.getMessageContext().setNextLink(link);
}
// Router postprocessors
for (RouterProcessor routerProcessor : postProcessors) {
try {
routerProcessor.processMessage(msg);
} catch (ProcessorInterruptException e) {
return;
} catch (ProcessorException e) {
e.printStackTrace();
}
}
// The sending step
if (link != null) {
if (msg.getHeader(Message.TRACE_ROUTE) != null) {
String trace = msg.getHeader(Message.TRACE);
if (trace == null) {
trace = COOSInstanceName;
}
msg.setHeader(Message.TRACE, trace + " -> " + link.getDestinationUuid());
}
try {
link.processMessage(msg);
} catch (ProcessorException e) {
replyErrorReason(msg, e.getMessage());
}
} else {
String errorMsg;
URIHelper helper = new URIHelper(msg.getReceiverEndpointUri());
String alias = helper.getEndpoint();
if(uuid.equals("null")){
errorMsg = COOSInstanceName + ": No uuid for alias " + alias + ", No route from " + COOSInstanceName;
} else {
if(uuid.equals(alias)){
errorMsg = COOSInstanceName + ": No route to: " + alias + " from " + COOSInstanceName;
} else {
errorMsg = COOSInstanceName + ": No route to: " + alias +" / " + uuid + " from " + COOSInstanceName;
}
}
logger.warn(errorMsg);
replyErrorReason(msg, Message.ERROR_NO_ROUTE);
}
}
}
/**
* This method does dynamic registers and unregisters aliases to link
*
* @param regAliases
* - An vector containing all aliases to be registered
* @param outlink
* - The link to register the aliases with. This link is always directed towards an endpoint
* @throws ProcessorException
*/
public void setLinkAliases(Vector regAliases, Link outlink) throws ProcessorException {
String segment = UuidHelper.getSegment(outlink.getDestinationUuid());
if (!segment.equals(".")) {
segment += ".";
}
// Qualify aliases
for (int i = 0; i < regAliases.size(); i++) {
String alias = (String) regAliases.get(i);
if (!alias.startsWith(Router.DICO_SEGMENT + ".") && !alias.startsWith(segment)) {
// unqualified aliases are qualified by prefixing with segment
// name
String qualifiedAlias = segment + alias;
;
regAliases.remove(alias);
regAliases.add(qualifiedAlias);
}
}
Vector curAliases = outlink.getAlises();
Iterator itCurAliases = curAliases.iterator();
// remove aliases that are not present anymore
while (itCurAliases.hasNext()) {
String alias = (String) itCurAliases.next();
if (!regAliases.contains(alias)) {
itCurAliases.remove();
removeAlias(alias);
}
}
// Add all aliases
Iterator itRegAliases = regAliases.iterator();
while (itRegAliases.hasNext()) {
String alias = (String) itRegAliases.next();
outlink.addAlias(alias);
String oldToUuid = aliasTable.get(alias);
if (oldToUuid != null && !oldToUuid.equals(outlink.getDestinationUuid())) {
Iterator itRegAliases2 = regAliases.iterator();
while (itRegAliases2.hasNext()) {
removeAlias((String) itRegAliases2.next());
}
throw new ProcessorException("Can not register alias:" + alias + " since this alias is occupied for endpoint with uuid "
+ outlink.getDestinationUuid());
}
putAlias(alias, outlink.getDestinationUuid());
}
}
/**
* This method resolves receiver URIs into uuids. It handles both URIs containing aliases and uuids
*
* @param msg
* the message to resolve alias for
* @return the uuid
*/
public String resolveAlias(Message msg) {
URIHelper helper = new URIHelper(msg.getReceiverEndpointUri());
String uuid;
String alias;
alias = helper.getEndpoint();
String segment;
if (alias != null && !helper.isEndpointUuid()) {
// routing on alias
// To account for that not all messages arrives the router via an
// incoming link.
// I.e. the routingInfo from this router
if (msg.getMessageContext().getInBoundLink() != null && msg.getMessageContext().getInBoundLink().getDestinationUuid() != null) {
segment = UuidHelper.getSegment(msg.getMessageContext().getInBoundLink().getDestinationUuid());
} else {
segment = ".";
}
if (!segment.equals(".")) {
segment += ".";
}
if (!alias.startsWith(segment) && !alias.startsWith(Router.DICO_SEGMENT + ".")) {
// the alias is unqualified, first try to route in segment
// namespace
String qualifiedAlias = segment + alias;
uuid = aliasTable.get(qualifiedAlias);
if (uuid == null) {
// If not found in segment namespace, then try to route in
// dico namespace
qualifiedAlias = DICO_SEGMENT + "." + alias;
uuid = aliasTable.get(qualifiedAlias);
}
} else {
// If fully qualified alias then lookup in aliasTable
uuid = aliasTable.get(alias);
}
if (uuid == null) {
// we always return with a string in order to allow for
// defaultgw
uuid = "null";
}
} else {
uuid = alias;
}
return uuid;
}
/**
* This is the core of the routing algorithm
*
*/
public Link route(String uuid, Message msg, Map routingTable) {
URIHelper helper = new URIHelper(msg.getSenderEndpointUri());
if (msg.getMessageContext().getInBoundChannel() != null) {
// Only populate routingtable with senderSegment/senderUuid if the
// message enters the router through an incoming channel
String destUuid = msg.getMessageContext().getInBoundLink().getDestinationUuid();
String curSegment = UuidHelper.getSegment(destUuid);
String senderEndpointUuid = helper.getEndpoint();
String senderSegment = UuidHelper.getSegment(senderEndpointUuid);
if (senderSegment.equals(curSegment)) {
// If senderEndpointUuid belongs to the same segment as the
// incoming channel
// populate routingtable with senderUuid
if (!routingTable.containsKey(senderEndpointUuid)) {
routingTable.put(senderEndpointUuid, msg.getMessageContext().getInBoundChannel().getOutLink());
}
} else {
// If senderEndpointUuid not belongs to the same segment as the
// incoming channel
// populate routingtable with segment of the senderUuid
if (!routingTable.containsKey(senderSegment)) {
routingTable.put(senderSegment, msg.getMessageContext().getInBoundChannel().getOutLink());
}
}
}
Link link = routingTable.get(uuid);
// route down
if (link == null) {
String toSegment = UuidHelper.getSegment(uuid);
Link inboundLink = msg.getMessageContext().getInBoundLink();
if (inboundLink == null) {
return null; // The message has not arrived this router through
// an inboundLink. It is invalid and can not be
// routed
}
String destUuid = inboundLink.getDestinationUuid();
if (destUuid == null) {
logger.warn(COOSInstanceName + ":destinationUuid is null on incoming link : " + inboundLink.getLinkId());
}
String curSegment = UuidHelper.getSegment(destUuid);
if (!toSegment.equals(curSegment)) {
// route down
while (!toSegment.equals(".")) {
link = routingTable.get(toSegment);
if (link != null) {
break;
} else {
toSegment = UuidHelper.getParentSegment(toSegment);
}
}
// route up
if (link == null) {
if (curSegment != null) {
String parentSegment = UuidHelper.getParentSegment(curSegment);
if (parentSegment != null) {
link = routingTable.get(parentSegment);
}
}
}
}
}
// route along defaultGw if set. Must be different from the incoming Link, Must not be in segment localcoos
if (link == null
&& defaultGw != null
&& defaultGw != msg.getMessageContext().getInBoundChannel().getOutLink()
&& !UuidHelper.getSegment(uuid).equals(Router.LOCAL_SEGMENT)) {
link = defaultGw;
}
return link;
}
public void replyErrorReason(Message msg, String message) {
// Only send error indication on type msg. i.e. from an endpoint
if (msg.getHeader(Message.TYPE).equals(Message.TYPE_MSG)) {
msg.setReceiverEndpointUri(msg.getSenderEndpointUri());
msg.setHeader(Message.TYPE, Message.TYPE_ERROR);
msg.setHeader(Message.ERROR_REASON, message);
processMessage(msg);
}
}
public Processor getDefaultProcessor() {
return this;
}
/**
* Adding a link to the router. Can either be a link to an endpoint/router or a link to another segment
*
* @param routerUuid
* @param link
* @throws ConnectingException
*/
public void addLink(String routerUuid, Link link) throws ConnectingException {
link.setDestinationUuid(routerUuid);
links.put(link.getLinkId(), link);
if (link.getChannel() != null && link.getChannel().isDefaultGw()) {
defaultGw = link;
}
if (UuidHelper.isUuid(routerUuid)) {
// It is a router or endpoint
String seg = UuidHelper.getSegment(routerUuid);
RoutingAlgorithm algorithm = routingAlgorithms.get(seg);
if (algorithm == null) {
throw new ConnectingException("Router is not attached to segment: " + seg);
}
algorithm.publishLink(link);
} else {
// it is a segment
routingAlgorithms.get(routerUuid).publishLink(link);
}
logger.debug(getCOOSInstanceName() + ": Adding link: "+ link);
}
public Link getLink(String destinationUuid) {
if (destinationUuid != null) {
for (Link link : links.values()) {
if (link.getDestinationUuid().equals(destinationUuid)) {
return link;
}
}
}
return null;
}
public void removeLinkById(String linkId) {
Link link = links.get(linkId);
if (link != null) {
link.setCost(LinkCost.MAX_VALUE);
}
}
public void removeLink(String destinationUuid) {
for (Link link : links.values()) {
if (link.getDestinationUuid().equals(destinationUuid)) {
link.setCost(LinkCost.MAX_VALUE);
routingAlgorithms.get(UuidHelper.getSegment(destinationUuid)).publishLink(link);
}
}
}
public void addQoSClass(String QoSClass, boolean isDefaultQoSClass) {
this.QoSClasses.add(QoSClass);
if (defaultQoSClass == null || isDefaultQoSClass) {
defaultQoSClass = QoSClass;
}
// create routing tables
if (routingTables.get(QoSClass) == null) {
routingTables.put(QoSClass, new ConcurrentHashMap());
}
}
public Collection getQoSClasses() {
return this.QoSClasses;
}
public void addPreProcessor(RouterProcessor preProcessor) {
preProcessor.setRouter(this);
preProcessors.add(preProcessor);
}
public void addPostProcessor(RouterProcessor postProcessor) {
postProcessor.setRouter(this);
postProcessors.add(postProcessor);
}
public String getCOOSInstanceName() {
return this.COOSInstanceName;
}
public void setCOOSInstanceName(String instanceName) {
this.COOSInstanceName = instanceName;
}
public synchronized void addRoutingAlgorithm(String routerUuid, RoutingAlgorithm routingAlgorithm) {
if (!UuidHelper.isRouterUuid(routerUuid)) {
throw new IllegalArgumentException("Router uuid must start with prefix " + ROUTER_UUID_PREFIX);
}
Processor processor = null;
if (UuidHelper.getSegment(routerUuid).equals(Router.LOCAL_SEGMENT)) {
routerUuid = UuidHelper.replaceSegment(routerUuid, COOSInstanceName + Router.LOCAL_SEGMENT);
processor = new LocalCoosAddressConverter(COOSInstanceName);
this.routingAlgorithms.put(Router.LOCAL_SEGMENT, routingAlgorithm);
}
this.routingAlgorithms.put(UuidHelper.getSegment(routerUuid), routingAlgorithm);
routerUuids.add(routerUuid);
if (routerUuids.size() > 1) {
for (String uuid : routerUuids) {
if (!routerUuid.equals(uuid)) {
Link link = new Link(0);
link.addFilterProcessor(processor);
link.setChainedProcessor(this);
try {
addLink(UuidHelper.getSegment(uuid), link);
} catch (Exception e) {
e.printStackTrace();
}
link = new Link(0);
link.addFilterProcessor(processor);
link.setChainedProcessor(this);
try {
addLink(UuidHelper.getSegment(routerUuid), link);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
if (running) {
try {
routingAlgorithm.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void aliasesUpdated() {
// remove global aliases
for (Link link : links.values()) {
Collection aliases = link.getAlises();
for (Iterator iterator = aliases.iterator(); iterator.hasNext();) {
String alias = (String) iterator.next();
if (alias.startsWith(Router.DICO_SEGMENT + ".") && !aliasTable.containsKey(alias)) {
iterator.remove();
}
}
}
// add global aliases in crossegment links
for (String alias : aliasTable.keySet()) {
if (alias.startsWith(Router.DICO_SEGMENT + ".")) {
String aliasUuid = aliasTable.get(alias);
String aliasSegment = UuidHelper.getSegment(aliasUuid);
for (Link link : links.values()) {
String segment = UuidHelper.getSegment(link.getDestinationUuid());
if (aliasSegment.equals(segment) && UuidHelper.isSegment(link.getDestinationUuid())) {
link.addAlias(alias);
}
}
}
}
}
public String getDefaultQoSClass() {
return defaultQoSClass;
}
public RoutingAlgorithm getRoutingAlgorithm(String segment) {
return routingAlgorithms.get(segment);
}
public Map getRoutingTables() {
return routingTables;
}
public Map getRoutingTable(String qos) {
return routingTables.get(qos);
}
public Map getLinks() {
return links;
}
public Link getLinkById(String id) {
return links.get(id);
}
public void start() throws Exception {
if (!running && enabled) {
for (RoutingAlgorithm routingAlgorithm : routingAlgorithms.values()) {
routingAlgorithm.start();
}
running = true;
}
}
public void stop() throws Exception {
for (RoutingAlgorithm routingAlgorithm : routingAlgorithms.values()) {
routingAlgorithm.stop();
}
running = false;
}
public String toString() {
return "Router " + COOSInstanceName;
}
public Map getAliasTable() {
return aliasTable;
}
public void putAlias(String alias, String toUuid) {
String oldToUuid = aliasTable.get(alias);
if (oldToUuid != null && !oldToUuid.equals(toUuid)) {
logger.warn("Possible alias conflict for alias: " + alias + ". Was pointing to : " + oldToUuid + ". Now pointing to :" + toUuid + ".");
}
aliasTable.put(alias, toUuid);
aliasesUpdated();
}
public void removeAlias(String alias) {
aliasTable.remove(alias);
aliasesUpdated();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy