Simplify method isMutualExclusive in Subnet.
[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                 Set<R> routes = new HashSet<R>();
76                 routes.add(route);
77                 routingTableCache.put(routeId, routes);
78                 clusterGlobalServices.tcommit();
79             } else {
80                 throw new DuplicateRouteException(" There is already existing route " + existingRoute);
81             }
82
83         } catch (NotSupportedException e) {
84             throw new RoutingTableException("Transaction error - while trying to create route id="
85                     + routeId + "with route" + route, e);
86         } catch (HeuristicRollbackException e) {
87             throw new RoutingTableException("Transaction error - while trying to create route id="
88                     + routeId + "with route" + route, e);
89         } catch (RollbackException e) {
90             throw new RoutingTableException("Transaction error - while trying to create route id="
91                     + routeId + "with route" + route, e);
92         } catch (HeuristicMixedException e) {
93             throw new RoutingTableException("Transaction error - while trying to create route id="
94                     + routeId + "with route" + route, e);
95         } catch (javax.transaction.SystemException e) {
96             throw new SystemException("System error occurred - while trying to create with value", e);
97         }
98
99     }
100
101     @Override
102     public void removeRoute(I routeId, R route) {
103         throw new UnsupportedOperationException("Not implemented yet!");
104     }
105
106     @Override
107     public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
108         Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
109         try {
110             if (log.isDebugEnabled()) {
111                 log.debug("removeGlobalRoute: removing  a new route with id" + routeId);
112             }
113             // lets start a transaction
114             clusterGlobalServices.tbegin();
115
116             routingTableCache.remove(routeId);
117             clusterGlobalServices.tcommit();
118
119         } catch (NotSupportedException e) {
120             throw new RoutingTableException("Transaction error - while trying to remove route id="
121                     + routeId, e);
122         } catch (HeuristicRollbackException e) {
123             throw new RoutingTableException("Transaction error - while trying to remove route id="
124                     + routeId, e);
125         } catch (RollbackException e) {
126             throw new RoutingTableException("Transaction error - while trying to remove route id="
127                     + routeId, e);
128         } catch (HeuristicMixedException e) {
129             throw new RoutingTableException("Transaction error - while trying to remove route id="
130                     + routeId, e);
131         } catch (javax.transaction.SystemException e) {
132             throw new SystemException("System error occurred - while trying to remove with value", e);
133         }
134     }
135
136     @Override
137     public Set<R> getRoutes(I routeId) {
138
139         // Note: currently works for global routes only wherein there is just single
140         // route
141         Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!");
142         return (Set<R>) routingTableCache.get(routeId);
143     }
144
145     @Override
146     public R getARoute(I routeId) {
147         throw new UnsupportedOperationException("Not implemented yet!");
148     }
149
150     /**
151      * @deprecated doesn't do anything will be removed once listeners used
152      *             whiteboard pattern Registers listener for sending any change
153      *             notification
154      * @param listener
155      */
156     @Override
157     public void registerRouteChangeListener(RouteChangeListener listener) {
158
159     }
160
161     public void setRouteChangeListener(RouteChangeListener rcl) {
162         if(rcl != null){
163             routeChangeListeners.add(rcl);
164         }else{
165             log.warn("setRouteChangeListener called with null listener");
166         }
167     }
168
169     public void unSetRouteChangeListener(RouteChangeListener rcl) {
170         if(rcl != null){
171          routeChangeListeners.remove(rcl);
172         }else{
173             log.warn("unSetRouteChangeListener called with null listener");
174         }
175     }
176
177     /**
178      * Returning the set of route change listeners for Unit testing Note: the
179      * package scope is default
180      *
181      * @return List of registered RouteChangeListener<I,R> listeners
182      */
183     Set<RouteChangeListener> getRegisteredRouteChangeListeners() {
184         return routeChangeListeners;
185     }
186
187     public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
188         this.clusterGlobalServices = clusterGlobalServices;
189     }
190
191     public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
192         if((clusterGlobalServices != null ) &&  (this.clusterGlobalServices.equals(clusterGlobalServices))){
193             this.clusterGlobalServices = null;
194         }
195     }
196
197     /**
198      * Creates the Routing Table clustered global services cache
199      *
200      * @throws CacheExistException
201      *           -- cluster global services exception when cache exist
202      * @throws CacheConfigException
203      *           -- cluster global services exception during cache config
204      * @throws CacheListenerAddException
205      *           -- cluster global services exception during adding of listener
206      */
207
208     void createRoutingTableCache() throws CacheExistException, CacheConfigException,
209             CacheListenerAddException {
210         // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
211         // should be caching?
212
213         // let us check here if the cache already exists -- if so don't create
214         if (!clusterGlobalServices.existCache(ROUTING_TABLE_GLOBAL_CACHE)) {
215
216             if (log.isDebugEnabled()) {
217                 log.debug("createRoutingTableCache: creating a new routing table cache "
218                         + ROUTING_TABLE_GLOBAL_CACHE);
219             }
220             routingTableCache = clusterGlobalServices.createCache(ROUTING_TABLE_GLOBAL_CACHE,
221                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
222         } else {
223             if (log.isDebugEnabled()) {
224                 log.debug("createRoutingTableCache: found existing routing table cache "
225                         + ROUTING_TABLE_GLOBAL_CACHE);
226             }
227             routingTableCache = clusterGlobalServices.getCache(ROUTING_TABLE_GLOBAL_CACHE);
228         }
229
230     }
231
232     /**
233      * Function called by the dependency manager when all the required
234      * dependencies are satisfied
235      *
236      */
237     void init(Component c) {
238         try {
239
240             createRoutingTableCache();
241         } catch (CacheExistException e) {
242             throw new IllegalStateException("could not construct routing table cache");
243         } catch (CacheConfigException e) {
244             throw new IllegalStateException("could not construct routing table cache");
245         } catch (CacheListenerAddException e) {
246             throw new IllegalStateException("could not construct routing table cache");
247         }
248     }
249
250     /**
251      * Get routing table method is useful for unit testing <note>It has package
252      * scope</note>
253      */
254     ConcurrentMap getRoutingTableCache() {
255         return this.routingTableCache;
256     }
257
258     /**
259      * This is used from integration test NP rest API to check out the result of the
260      * cache population
261      * <Note> For testing purpose only-- use it wisely</Note>
262      * @return
263      */
264     public String dumpRoutingTableCache(){
265        Set<Map.Entry<I, R>> cacheEntrySet = this.routingTableCache.entrySet();
266        StringBuilder sb = new StringBuilder();
267        for(Map.Entry<I,R> entry:cacheEntrySet){
268            sb.append("Key:").append(entry.getKey()).append("---->Value:")
269                    .append((entry.getValue() != null)?entry.getValue():"null")
270                    .append("\n");
271        }
272        return sb.toString();
273     }
274
275     /**
276      * Invoked when a new entry is available in the cache, the key is only
277      * provided, the value will come as an entryUpdate invocation
278      *
279      * @param key
280      *          Key for the entry just created
281      * @param cacheName
282      *          name of the cache for which update has been received
283      * @param originLocal
284      *          true if the event is generated from this node
285      */
286     @Override
287     public void entryCreated(I key, String cacheName, boolean originLocal) {
288         // TBD: do we require this.
289         if (log.isDebugEnabled()) {
290             log.debug("RoutingTableUpdates: entryCreated  routeId = " + key + " cacheName=" + cacheName);
291         }
292     }
293
294     /**
295      * Called anytime a given entry is updated
296      *
297      * @param key
298      *          Key for the entry modified
299      * @param new_value
300      *          the new value the key will have
301      * @param cacheName
302      *          name of the cache for which update has been received
303      * @param originLocal
304      *          true if the event is generated from this node
305      */
306     @Override
307     public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) {
308         if (log.isDebugEnabled()) {
309             log.debug("RoutingTableUpdates: entryUpdated  routeId = " + key + ",value = " + new_value
310                     + " ,cacheName=" + cacheName + " originLocal="+originLocal);
311         }
312         if (!originLocal) {
313             for (RouteChangeListener rcl : routeChangeListeners) {
314                 rcl.onRouteUpdated(key, new_value);
315             }
316         }
317     }
318
319     /**
320      * Called anytime a given key is removed from the ConcurrentHashMap we are
321      * listening to.
322      *
323      * @param key
324      *          Key of the entry removed
325      * @param cacheName
326      *          name of the cache for which update has been received
327      * @param originLocal
328      *          true if the event is generated from this node
329      */
330     @Override
331     public void entryDeleted(I key, String cacheName, boolean originLocal) {
332         if (log.isDebugEnabled()) {
333             log.debug("RoutingTableUpdates: entryUpdated  routeId = " + key + " local = " + originLocal
334                     + " cacheName=" + cacheName + " originLocal="+originLocal);
335         }
336         if (!originLocal) {
337             for (RouteChangeListener rcl : routeChangeListeners) {
338                 rcl.onRouteDeleted(key);
339             }
340         }
341     }
342 }