All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.coos.messaging.routing.LinkStateAlgorithm Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
/**
 * 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 org.coos.messaging.Link;
import org.coos.messaging.Message;
import org.coos.messaging.impl.DefaultMessage;
import org.coos.messaging.util.Log;
import org.coos.messaging.util.UuidHelper;

import java.io.StringWriter;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;


/**
 * @author Knut Eilif Husa, Tellu AS
 *
 */
public class LinkStateAlgorithm extends DefaultRoutingAlgorithm implements TopologyMapListener {

    public static final String ALG_NAME = "linkstate";
    public static final String REFRESH_INTERVAL = "refreshInterval";
    public static final String AGING_FACTOR = "agingFactor";
    public static final String EMPTY_SEG_FACTOR = "emptyDynamicSegmentFactor";


    private final Random r = new Random();
    private TopologyMap topologyMap;
    private Timer timer;
    private int refreshInterval = 100; // Default value
    private int agingFactor = 5; // Default value
    private long emptyDynamicSegmentFactor = 2 * agingFactor; // Default value

    public LinkStateAlgorithm() {
    }

    public LinkStateAlgorithm(Router router, String routerUuid) {
        init(routerUuid, router);
    }

    @Override public void init(String routerUuid, Router router) {

        // Setting the refreshinterval property
        String refIntvStr = properties.get(REFRESH_INTERVAL);

        if (refIntvStr != null) {
            refreshInterval = Integer.parseInt(refIntvStr);
        }

        // Setting the aging factor property
        String agefactStr = properties.get(AGING_FACTOR);

        if (agefactStr != null) {
            agingFactor = Integer.parseInt(agefactStr);
        }

        // Setting the aging factor for the dynamic segment property
        String emptySegfactStr = properties.get(EMPTY_SEG_FACTOR);

        if (emptySegfactStr != null) {
            emptyDynamicSegmentFactor = Integer.parseInt(emptySegfactStr);
        }

        topologyMap = new TopologyMap(routerUuid, refreshInterval, agingFactor * refreshInterval);
        topologyMap.addListener(this);
        topologyMap.start();
        super.init(routerUuid, router);

    }

    public TopologyMap getTopologyMap() {
        return topologyMap;
    }

    public void setTopologyMap(TopologyMap topologyMap) {
        this.topologyMap = topologyMap;
    }

    public void publishLink(Link link) {
        List links = new LinkedList();
        links.add(link);
        broadcastRoutingInfo(links);
    }

    @SuppressWarnings("unchecked")
    public synchronized void processRoutingInfo(Message routingInfo) {
        Vector linkCosts = (Vector) routingInfo.getBody();
        String s = "";

        for (int i = 0; i < linkCosts.size(); i++) {
            LinkCost linkCost = linkCosts.elementAt(i);
            s += linkCost.getFromUuid() + "<->" + linkCost.getToUuid() + ": " +
                linkCost.getCost(Link.DEFAULT_QOS_CLASS) + ": " + linkCost.getAliases() + ", ";
        }

        logger.trace("Receiving on " + router.getCOOSInstanceName() + ", from " +
            routingInfo.getSenderEndpointUri() + " linkinfo: " + s);
        topologyMap.update(linkCosts);
    }

    private void calculateOptimalPaths() {
        QoSClasses = router.getQoSClasses();

        for (String qos : QoSClasses) {
            calculateOptimalPaths(qos);
        }
    }

    void calculateOptimalPaths(String qos) {
        logger.debug(this.router.getCOOSInstanceName() + ": Calculating optimal paths for: " +
            topologyMap.getRouterUuid() + " QoS: " + qos);

        Map optimalPath = new HashMap();
        Set uuids = topologyMap.getNodeUuids();
        uuids.remove(topologyMap.getRouterUuid());

        // initialize optimal path with neighbours
        Iterator iter = uuids.iterator();

        while (iter.hasNext()) {
            String uuid = iter.next();
            optimalPath.put(uuid, new LinkCost(topologyMap.getLinkCost(uuid)));
        }

        while (!uuids.isEmpty()) {
            LinkCost minimalCost = null;
            iter = optimalPath.keySet().iterator();

            // identify node with smallest cost
            while (iter.hasNext()) {
                String uuid = iter.next();

                if (uuids.contains(uuid)) {

                    if ((minimalCost == null) ||
                            ((optimalPath.get(uuid)).getCost(qos) < minimalCost.getCost(qos))) {
                        minimalCost = optimalPath.get(uuid);
                    }
                }
            }

            String minimalCostUuid = minimalCost.getToUuid();
            uuids.remove(minimalCostUuid);

            iter = uuids.iterator();

            while (iter.hasNext()) {
                String nodeUuid = iter.next();

                if (topologyMap.isNeighbourNode(minimalCostUuid, nodeUuid)) {
                    int candidateCost = minimalCost.getCost(qos) +
                        topologyMap.getLinkCost(minimalCostUuid, nodeUuid).getCost(qos);
                    int currentCost;

                    if (optimalPath.get(nodeUuid) != null) {
                        currentCost = (optimalPath.get(nodeUuid)).getCost(qos);
                    } else {
                        currentCost = topologyMap.getLinkCost(nodeUuid).getCost(qos); // return
                                                                                      // large
                                                                                      // number
                    }

                    if (candidateCost < currentCost) {
                        LinkCost linkCost = optimalPath.get(nodeUuid);
                        linkCost.setCost(qos, candidateCost);
                        linkCost.setNextLinkCost(optimalPath.get(minimalCostUuid));

                    }
                }
            }
        }

        Iterator valIter = optimalPath.values().iterator();

        // populate routing table with smallest costs paths.
        while (valIter.hasNext()) {
            LinkCost linkCost = valIter.next();
            String toUuid = linkCost.getToUuid();

            while (linkCost.getNextLink() != null) {
                linkCost = linkCost.getNextLink();
            }
            // Now linkCost points to the link from the router

            Link l = links.get(linkCost.getLinkId());

            if (linkCost.getCost(qos) < LinkCost.MAX_VALUE) {

                // insert Link into routing table
                if (l != null) {
                    routingTables.get(qos).put(toUuid, links.get(linkCost.getLinkId()));

                    for (String alias : topologyMap.getAliases(toUuid)) {
                        router.putAlias(alias, toUuid);
                    }
                }
            } else {

                // remove Link from routing table
                routingTables.get(qos).remove(toUuid);

                if ((l != null) && (l.getChannel() != null) && !l.getChannel().isDefaultGw()) {

                    //Never remove the default gateway
                    links.remove(linkCost.getLinkId());

                    if (loggingEnabled) {
                        logger.debug(routerUuid + " removing from routerTable Link to: " +
                            linkCost.getToUuid());
                    }

                    for (String alias : topologyMap.getAliases(toUuid)) {
                        router.removeAlias(alias);
                    }
                }
            }
        }

        if (loggingEnabled) {
            printRoutingTable(routerUuid, qos, routingTables.get(qos), logger);
            printAliasTable(routerUuid, aliasTable, logger);
            printOptimalPath(routerUuid, qos, optimalPath, logger);
        }

    }

    private static synchronized void printOptimalPath(String routerUuid, String qos,
        Map optimalPath, Log logger) {
        StringWriter writer = new StringWriter();
        writer.write("-------------optimal paths for Qos: " + qos + " in router: " + routerUuid +
            "------------\n");

        Iterator keys = optimalPath.keySet().iterator();

        while (keys.hasNext()) {
            String uuid = keys.next();

            writer.write("'" + uuid + "': ");

            LinkCost linkCost = optimalPath.get(uuid);

            while (linkCost != null) {
                writer.write("'" + linkCost.getFromUuid() + "', '" + linkCost.getToUuid() + "': " +
                    linkCost.getCost(qos));
                linkCost = linkCost.getNextLink();

                if (linkCost != null) {
                    writer.write(" --> ");
                }
            }

            writer.write("\n");
        }

        writer.write("-------------------------\n");

        logger.debug(writer.toString());

    }

    public void notifyChanged(TopologyMap topologyMap) {

        try {
            calculateOptimalPaths();
        } catch (Exception e) {
            logger.warn("Exception occured in " + topologyMap.getRouterUuid(), e);
        }

        if (topologyMap.isEmpty() && router.getSegment(segment).isDynamicSegment()) {
            RouterSegment rs = router.getSegment(segment);
            long now = System.currentTimeMillis();

            if (rs.getTimestamp() == 0) {
                rs.setTimestamp(now);
            } else if ((rs.getTimestamp() + (emptyDynamicSegmentFactor * refreshInterval)) > now) {
                router.removeSegment(segment);

                for (TimedConcurrentHashMap routingTable : routingTables.values()) {
                    routingTable.remove(segment);
                }

                stop();
                logger.info("Removing dynamic segment: " + segment);
            }
        }
    }

    public void start() {
        timer = new Timer("LinkStateThread", true);
        broadcastRoutingInfoAndSchedule();
    }

    private void broadcastRoutingInfoAndSchedule() {
        broadcastRoutingInfo(links.values());

        try {
            timer.schedule(new TimerTask() {

                    @Override public void run() {
                        broadcastRoutingInfoAndSchedule();
                    }
                }, refreshInterval + r.nextInt((int) (0.1 * refreshInterval)));
        } catch (IllegalStateException e) {
            // Nothing to be done. This situation occures when the timer is
            // cancelled in the stop routine
            // and the broadcastRoutingInfoAndSchedule is started. I.e. the
            // routing algorithm is about to stop.
        }
    }

    private void broadcastRoutingInfo(Collection links) {

        try {
            String s = "";

            for (Link link : links) {
                s += link.getDestinationUuid() + ": " + link.getAlises() + ", ";
            }

            // Send routinginfo to all router elements in routing table

            Set uuids = new HashSet();
            // Get routing table entries for all qos parameters

            for (Map routingTable : routingTables.values()) {

                for (String uuid : routingTable.keySet()) {

                    if ((routingTable.get(uuid).getChannel() != null) &&
                            !routingTable.get(uuid).getChannel().isReceiveRoutingInfo()) {
                        continue;
                    }

                    uuids.add(uuid);
                }

            }

            /*for (Map routingTable : routingTables.values()) {
                    uuids.addAll(routingTable.keySet());
            }*/

            for (String uuid : uuids) {

                // Only sending routerinfo to routers
                if (UuidHelper.getSegmentFromEndpointNameOrEndpointUuid(uuid).equals(segment) &&
                        UuidHelper.isRouterUuid(uuid)) {
                    sendRouterInfo(uuid, constructRoutingInfo(uuid, links));
                    logger.trace("RouterInfo from: " + router.getCOOSInstanceName() + ", to:" +
                        uuid + ":: " + s);
                }
            }

            // Send info to self
            sendRouterInfo(routerUuid, constructLocalRoutingInfo(links));
        } catch (Exception e) {
            logger.error("Exception ignored.", e);
        }
    }

    @SuppressWarnings("unchecked")
    private Vector constructRoutingInfo(String receivingRouterUuid,
        Collection links) {
        Vector routingInfo = new Vector();

        for (Link link : links) {
            String uuid = link.getDestinationUuid();
            String uuidSegment = UuidHelper.getSegmentFromSegmentOrEndpointUuid(uuid);

            //Do not propagate localcoos aliases to other coos nodes
            Vector aliases = link.getAlises();
            Vector broadCastAliases = new Vector();

            for (String alias : aliases) {

                if (!alias.startsWith(Router.LOCAL_SEGMENT)) {
                    broadCastAliases.add(alias);
                }
            }

            if (uuidSegment.equals(segment) && !UuidHelper.isSegment(uuid)) {

                //The endpoints belonging to the router in the segment except from localcoos
                routingInfo.addElement(new LinkCost(routerUuid, uuid, link.getLinkId(),
                        link.getCostMap(), broadCastAliases));
            } else if (UuidHelper.isSegment(uuid) && !uuidSegment.equals(segment) &&
                    UuidHelper.isInParentChildRelation(uuidSegment,
                        UuidHelper.getSegmentFromSegmentOrEndpointUuid(receivingRouterUuid))) {

                //Other segments
                routingInfo.addElement(new LinkCost(routerUuid, uuid, link.getLinkId(),
                        link.getCostMap(), link.getAlises()));
            }
        }

        return routingInfo;
    }

    @SuppressWarnings("unchecked")
    private Vector constructLocalRoutingInfo(Collection links) {
        Vector routingInfoLocal = new Vector();

        for (Link link : links) {
            String uuid = link.getDestinationUuid();
            String uuidSegment = UuidHelper.getSegmentFromSegmentOrEndpointUuid(uuid);

            if (uuidSegment.equals(segment) && !UuidHelper.isSegment(uuid)) {
                routingInfoLocal.addElement(new LinkCost(routerUuid, uuid, link.getLinkId(),
                        link.getCostMap(), link.getAlises()));
            } else if (UuidHelper.isSegment(uuid) && !uuidSegment.equals(segment)) {
                // Aliases are not part of cross segment linkcost to gateway
                // (local) topology map.

                // Aliases in cross segment linkcosts pointing at other
                // segment must not be added since this is where dico
                // aliases pointing
                // to uuids in own segment is located
                routingInfoLocal.addElement(new LinkCost(routerUuid, uuid, link.getLinkId(),
                        link.getCostMap(), null));
            }
        }

        return routingInfoLocal;
    }

    private void sendRouterInfo(String uuid, Vector routingInfo) {

        try {
            DefaultMessage msg = new DefaultMessage();
            msg.setReceiverEndpointUri("coos://" + uuid);
            msg.setSenderEndpointUri("coos://" + routerUuid);
            msg.setHeader(Message.SERIALIZATION_METHOD, Message.SERIALIZATION_METHOD_JAVA);
            msg.setHeader(Message.TYPE, Message.TYPE_ROUTING_INFO);
            msg.setBody(routingInfo);
            router.processMessage(msg);
        } catch (Exception e) {
            logger.error("Exception ignored.", e);
        }

    }

    public void stop() {
        topologyMap.stop();

        if (timer != null) {
            timer.cancel();
        }
    }

    @Override public String getAlgorithmName() {
        return ALG_NAME;
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy