Merge "Changed codec for Identityref in JSON transformation"
[controller.git] / opendaylight / md-sal / remoterpc-routingtable / implementation / src / main / java / org / opendaylight / controller / sal / connector / remoterpc / impl / RoutingTableImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.sal.connector.remoterpc.impl;
10
11 import com.google.common.base.Preconditions;
12 import org.apache.felix.dm.Component;
13 import org.opendaylight.controller.clustering.services.CacheConfigException;
14 import org.opendaylight.controller.clustering.services.CacheExistException;
15 import org.opendaylight.controller.clustering.services.CacheListenerAddException;
16 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
17 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
18 import org.opendaylight.controller.clustering.services.IClusterServices;
19 import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
20 import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
21 import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
22 import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import javax.transaction.HeuristicMixedException;
27 import javax.transaction.HeuristicRollbackException;
28 import javax.transaction.NotSupportedException;
29 import javax.transaction.RollbackException;
30 import java.util.Collections;
31 import java.util.EnumSet;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.concurrent.ConcurrentMap;
36
37 /**
38  * @author: syedbahm
39  */
40 public class RoutingTableImpl<I, R> implements RoutingTable<I, R>, ICacheUpdateAware<I, R> {
41     public static final String ROUTING_TABLE_GLOBAL_CACHE = "routing_table_global_cache";
42
43     private Logger log = LoggerFactory.getLogger(RoutingTableImpl.class);
44
45     private IClusterGlobalServices clusterGlobalServices = null;
46     private RoutingTableImpl routingTableInstance = null;
47     private ConcurrentMap routingTableCache = null;
48     private Set<RouteChangeListener> routeChangeListeners = Collections
49             .synchronizedSet(new HashSet<RouteChangeListener>());
50
51     public RoutingTableImpl() {
52     }
53
54     @Override
55     public void addRoute(I routeId, R route) throws RoutingTableException {
56         throw new UnsupportedOperationException(" Not implemented yet!");
57     }
58
59     @Override
60     public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException {
61         Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!");
62         Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!");
63         try {
64
65             Set<R> existingRoute = null;
66             // ok does the global route is already registered ?
67             if ((existingRoute = getRoutes(routeId)) == null) {
68
69                 if (log.isDebugEnabled()) {
70                     log.debug("addGlobalRoute: adding  a new route with id" + routeId + " and value = "
71                             + route);
72                 }
73                 // lets start a transaction
74                 clusterGlobalServices.tbegin();
75
76                 routingTableCache.put(routeId, route);
77                 clusterGlobalServices.tcommit();
78             } else {
79                 throw new DuplicateRouteException(" There is already existing route " + existingRoute);
80             }
81
82         } catch (NotSupportedException|HeuristicRollbackException|RollbackException|HeuristicMixedException e) {
83             throw new RoutingTableException("Transaction error - while trying to create route id="
84                     + routeId + "with route" + route, e);
85         } catch (javax.transaction.SystemException e) {
86             throw new SystemException("System error occurred - while trying to create with value", e);
87         }
88
89     }
90
91     @Override
92     public void removeRoute(I routeId, R route) {
93         throw new UnsupportedOperationException("Not implemented yet!");
94     }
95
96     @Override
97     public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
98         Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
99         try {
100             if (log.isDebugEnabled()) {
101                 log.debug("removeGlobalRoute: removing  a new route with id" + routeId);
102             }
103             // lets start a transaction
104             clusterGlobalServices.tbegin();
105
106             routingTableCache.remove(routeId);
107             clusterGlobalServices.tcommit();
108
109         } catch (NotSupportedException|HeuristicRollbackException|RollbackException|HeuristicMixedException e) {
110             throw new RoutingTableException("Transaction error - while trying to remove route id="
111                     + routeId, e);
112         } catch (javax.transaction.SystemException e) {
113             throw new SystemException("System error occurred - while trying to remove with value", e);
114         }
115     }
116
117     @Override
118     public Set<R> getRoutes(I routeId) {
119
120         // Note: currently works for global routes only wherein there is just single
121         // route
122         Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!");
123         R route = (R)routingTableCache.get(routeId);
124         Set<R>routes = null;
125         if(route !=null){
126            routes = new HashSet<R>();
127            routes.add(route);
128         }
129
130         return routes;
131     }
132
133   @Override
134   public Set<Map.Entry> getAllRoutes() {
135     return routingTableCache.entrySet();
136   }
137
138   @Override
139     public R getARoute(I routeId) {
140         throw new UnsupportedOperationException("Not implemented yet!");
141     }
142
143     /**
144      * @deprecated doesn't do anything will be removed once listeners used
145      *             whiteboard pattern Registers listener for sending any change
146      *             notification
147      * @param listener
148      */
149     @Override
150     public void registerRouteChangeListener(RouteChangeListener listener) {
151
152     }
153
154     public void setRouteChangeListener(RouteChangeListener rcl) {
155         if(rcl != null){
156             routeChangeListeners.add(rcl);
157         }else{
158             log.warn("setRouteChangeListener called with null listener");
159         }
160     }
161
162     public void unSetRouteChangeListener(RouteChangeListener rcl) {
163         if(rcl != null){
164          routeChangeListeners.remove(rcl);
165         }else{
166             log.warn("unSetRouteChangeListener called with null listener");
167         }
168     }
169
170     /**
171      * Returning the set of route change listeners for Unit testing Note: the
172      * package scope is default
173      *
174      * @return List of registered RouteChangeListener<I,R> listeners
175      */
176     Set<RouteChangeListener> getRegisteredRouteChangeListeners() {
177         return routeChangeListeners;
178     }
179
180     public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
181         this.clusterGlobalServices = clusterGlobalServices;
182     }
183
184     public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
185         if((clusterGlobalServices != null ) &&  (this.clusterGlobalServices.equals(clusterGlobalServices))){
186             this.clusterGlobalServices = null;
187         }
188     }
189
190     /**
191      * Creates the Routing Table clustered global services cache
192      *
193      * @throws CacheExistException
194      *           -- cluster global services exception when cache exist
195      * @throws CacheConfigException
196      *           -- cluster global services exception during cache config
197      * @throws CacheListenerAddException
198      *           -- cluster global services exception during adding of listener
199      */
200
201     void createRoutingTableCache() throws CacheExistException, CacheConfigException,
202             CacheListenerAddException {
203         // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
204         // should be caching?
205
206         // let us check here if the cache already exists -- if so don't create
207         if (!clusterGlobalServices.existCache(ROUTING_TABLE_GLOBAL_CACHE)) {
208
209             if (log.isDebugEnabled()) {
210                 log.debug("createRoutingTableCache: creating a new routing table cache "
211                         + ROUTING_TABLE_GLOBAL_CACHE);
212             }
213             routingTableCache = clusterGlobalServices.createCache(ROUTING_TABLE_GLOBAL_CACHE,
214                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
215         } else {
216             if (log.isDebugEnabled()) {
217                 log.debug("createRoutingTableCache: found existing routing table cache "
218                         + ROUTING_TABLE_GLOBAL_CACHE);
219             }
220             routingTableCache = clusterGlobalServices.getCache(ROUTING_TABLE_GLOBAL_CACHE);
221         }
222
223     }
224
225     /**
226      * Function called by the dependency manager when all the required
227      * dependencies are satisfied
228      *
229      */
230     void init(Component c) {
231         try {
232
233             createRoutingTableCache();
234         } catch (CacheExistException e) {
235             throw new IllegalStateException("could not construct routing table cache");
236         } catch (CacheConfigException e) {
237             throw new IllegalStateException("could not construct routing table cache");
238         } catch (CacheListenerAddException e) {
239             throw new IllegalStateException("could not construct routing table cache");
240         }
241     }
242
243     /**
244      * Get routing table method is useful for unit testing <note>It has package
245      * scope</note>
246      */
247     ConcurrentMap getRoutingTableCache() {
248         return this.routingTableCache;
249     }
250
251     /**
252      * This is used from integration test NP rest API to check out the result of the
253      * cache population
254      * <Note> For testing purpose only-- use it wisely</Note>
255      * @return
256      */
257     public String dumpRoutingTableCache(){
258        Set<Map.Entry<I, R>> cacheEntrySet = this.routingTableCache.entrySet();
259        StringBuilder sb = new StringBuilder();
260        for(Map.Entry<I,R> entry:cacheEntrySet){
261            sb.append("Key:").append(entry.getKey()).append("---->Value:")
262                    .append((entry.getValue() != null)?entry.getValue():"null")
263                    .append("\n");
264        }
265        return sb.toString();
266     }
267
268     /**
269      * Invoked when a new entry is available in the cache, the key is only
270      * provided, the value will come as an entryUpdate invocation
271      *
272      * @param key
273      *          Key for the entry just created
274      * @param cacheName
275      *          name of the cache for which update has been received
276      * @param originLocal
277      *          true if the event is generated from this node
278      */
279     @Override
280     public void entryCreated(I key, String cacheName, boolean originLocal) {
281         // TBD: do we require this.
282         if (log.isDebugEnabled()) {
283             log.debug("RoutingTableUpdates: entryCreated  routeId = " + key + " cacheName=" + cacheName);
284         }
285     }
286
287     /**
288      * Called anytime a given entry is updated
289      *
290      * @param key
291      *          Key for the entry modified
292      * @param new_value
293      *          the new value the key will have
294      * @param cacheName
295      *          name of the cache for which update has been received
296      * @param originLocal
297      *          true if the event is generated from this node
298      */
299     @Override
300     public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) {
301         if (log.isDebugEnabled()) {
302             log.debug("RoutingTableUpdates: entryUpdated  routeId = " + key + ",value = " + new_value
303                     + " ,cacheName=" + cacheName + " originLocal="+originLocal);
304         }
305         if (!originLocal) {
306             for (RouteChangeListener rcl : routeChangeListeners) {
307                 rcl.onRouteUpdated(key, new_value);
308             }
309         }
310     }
311
312     /**
313      * Called anytime a given key is removed from the ConcurrentHashMap we are
314      * listening to.
315      *
316      * @param key
317      *          Key of the entry removed
318      * @param cacheName
319      *          name of the cache for which update has been received
320      * @param originLocal
321      *          true if the event is generated from this node
322      */
323     @Override
324     public void entryDeleted(I key, String cacheName, boolean originLocal) {
325         if (log.isDebugEnabled()) {
326             log.debug("RoutingTableUpdates: entryUpdated  routeId = " + key + " local = " + originLocal
327                     + " cacheName=" + cacheName + " originLocal="+originLocal);
328         }
329         if (!originLocal) {
330             for (RouteChangeListener rcl : routeChangeListeners) {
331                 rcl.onRouteDeleted(key);
332             }
333         }
334     }
335 }