/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.sal.connector.remoterpc.impl; import com.google.common.base.Preconditions; import org.apache.felix.dm.Component; import org.opendaylight.controller.clustering.services.*; import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener; import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable; import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException; import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import java.util.*; import java.util.concurrent.ConcurrentMap; /** * @author: syedbahm */ public class RoutingTableImpl implements RoutingTable,ICacheUpdateAware { public static final String ROUTING_TABLE_GLOBAL_CACHE = "routing_table_global_cache"; private Logger log = LoggerFactory .getLogger(RoutingTableImpl.class); private IClusterGlobalServices clusterGlobalServices = null; private RoutingTableImpl routingTableInstance = null; private ConcurrentMap routingTableCache = null; private List routeChangeListeners = new ArrayList(); private ServiceRegistration cacheAwareRegistration = null; public RoutingTableImpl() { } @Override public void addRoute(I routeId, R route) throws RoutingTableException { throw new UnsupportedOperationException(" Not implemented yet!"); } @Override public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException { Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!"); Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!"); try { Set existingRoute = null; // ok does the global route is already registered ? if ((existingRoute = getRoutes(routeId)) == null) { if(log.isDebugEnabled()){ log.debug("addGlobalRoute: adding a new route with id"+ routeId + " and value = "+route); } // lets start a transaction clusterGlobalServices.tbegin(); Set routes = new HashSet(); routes.add(route); routingTableCache.put(routeId, routes); clusterGlobalServices.tcommit(); } else { throw new DuplicateRouteException(" There is already existing route " + existingRoute); } } catch (NotSupportedException e) { throw new RoutingTableException("Transaction error - while trying to create route id=" + routeId + "with route" + route, e); } catch (HeuristicRollbackException e) { throw new RoutingTableException("Transaction error - while trying to create route id=" + routeId + "with route" + route, e); } catch (RollbackException e) { throw new RoutingTableException("Transaction error - while trying to create route id=" + routeId + "with route" + route, e); } catch (HeuristicMixedException e) { throw new RoutingTableException("Transaction error - while trying to create route id=" + routeId + "with route" + route, e); } catch (javax.transaction.SystemException e){ throw new SystemException ( "System error occurred - while trying to create with value",e); } } @Override public void removeRoute(I routeId, R route) { throw new UnsupportedOperationException("Not implemented yet!"); } @Override public void removeGlobalRoute(I routeId) { routingTableCache.remove(routeId); } @Override public Set getRoutes(I routeId) { //Note: currently works for global routes only wherein there is just single route Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!"); return (Set) routingTableCache.get(routeId); } @Override public R getARoute(I routeId) { throw new UnsupportedOperationException("Not implemented yet!"); } /** * Registers listener for sending any change notification * * @param listener */ @Override public void registerRouteChangeListener(RouteChangeListener listener) { routeChangeListeners.add(listener); } /** * Returning the list of route change listeners for Unit testing * Note: the package scope is default * @return List of registered RouteChangeListener listeners */ List getRegisteredRouteChangeListeners(){ return routeChangeListeners; } public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) { this.clusterGlobalServices = clusterGlobalServices; } public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) { if(cacheAwareRegistration != null) { cacheAwareRegistration.unregister(); } this.clusterGlobalServices = null; } /** * Creates the Routing Table clustered global services cache * @throws CacheExistException -- cluster global services exception when cache exist * @throws CacheConfigException -- cluster global services exception during cache config * @throws CacheListenerAddException -- cluster global services exception during adding of listener */ void createRoutingTableCache() throws CacheExistException, CacheConfigException, CacheListenerAddException { // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it // should be caching? // let us check here if the cache already exists -- if so don't create if (!clusterGlobalServices.existCache( ROUTING_TABLE_GLOBAL_CACHE)) { if(log.isDebugEnabled()){ log.debug("createRoutingTableCache: creating a new routing table cache "+ROUTING_TABLE_GLOBAL_CACHE ); } routingTableCache = clusterGlobalServices.createCache( ROUTING_TABLE_GLOBAL_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); } else { if(log.isDebugEnabled()){ log.debug("createRoutingTableCache: found existing routing table cache "+ROUTING_TABLE_GLOBAL_CACHE ); } routingTableCache = clusterGlobalServices.getCache( ROUTING_TABLE_GLOBAL_CACHE); } } /** * Function called by the dependency manager when all the required * dependencies are satisfied * */ void init(Component c) { try { createRoutingTableCache(); } catch (CacheExistException e) { throw new IllegalStateException("could not construct routing table cache"); } catch (CacheConfigException e) { throw new IllegalStateException("could not construct routing table cache"); } catch (CacheListenerAddException e) { throw new IllegalStateException("could not construct routing table cache"); } } /** * Get routing table method is useful for unit testing * It has package scope */ ConcurrentMap getRoutingTableCache(){ return this.routingTableCache; } /** * Invoked when a new entry is available in the cache, the key is * only provided, the value will come as an entryUpdate invocation * * @param key Key for the entry just created * @param cacheName name of the cache for which update has been * received * @param originLocal true if the event is generated from this * node */ @Override public void entryCreated(I key, String cacheName, boolean originLocal) { //TBD: do we require this. if(log.isDebugEnabled()){ log.debug("RoutingTableUpdates: entryCreated routeId = "+key + " cacheName="+cacheName ); } } /** * Called anytime a given entry is updated * * @param key Key for the entry modified * @param new_value the new value the key will have * @param cacheName name of the cache for which update has been * received * @param originLocal true if the event is generated from this * node */ @Override public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) { if(log.isDebugEnabled()){ log.debug("RoutingTableUpdates: entryUpdated routeId = "+key + ",value = "+ new_value + " ,cacheName="+cacheName ); } for(RouteChangeListener rcl:routeChangeListeners){ rcl.onRouteUpdated(key, new_value); } } /** * Called anytime a given key is removed from the * ConcurrentHashMap we are listening to. * * @param key Key of the entry removed * @param cacheName name of the cache for which update has been * received * @param originLocal true if the event is generated from this * node */ @Override public void entryDeleted(I key, String cacheName, boolean originLocal) { if(log.isDebugEnabled()){ log.debug("RoutingTableUpdates: entryUpdated routeId = "+key + " local = "+ originLocal + " cacheName="+cacheName ); } for(RouteChangeListener rcl:routeChangeListeners){ rcl.onRouteDeleted(key); } } }