--- /dev/null
+*.class
+**/target
+**/bin
+dist
+**/logs
+products
+repository
+workspace
+*~
+target
+.classpath
+.project
+.settings
--- /dev/null
+DIRECTORY ORGANIZATION
+======================
+- third-party: contains all the third-party artifacts than needed
+repackaging or needed code modifications.
+- third-party/commons: contains all the parent POM files for the
+projects under the third-party directory. Only one is expected, which
+is the one located under third-party/commons/third-party, but there is
+a directory just in case we need to host more with different
+variations.
+- opendaylight: contains all the artifacts that constitute the
+controller project.
+- opendaylight/distribution: contains all the distributions that can
+be generated by packaging the several artifact. In practice for now
+there are two:
+ - "opendaylight", which is the full distribution of the controller
+ - "sdk", which contains only the artifact needed to build an app
+ against the controller (beaware this is still incomplete)
+The idea of the distribution directory is that more distribution can
+be added at will, maybe just composing subsets of the whole controller
+artifact set.
+
+HOW TO BUILD
+============
+In order to build it's required to have JDK 1.7+ and Maven 3+, to get
+a build going it's needed to:
+1) Choose the distribution, from within opendaylight/distribution
+2) Go in the directory and run
+ "mvn clean install"
+3) On succesfull completion go in the target directory to pick the zip
+file of the distribution or controller can be executed right from
+there going into the distribution directory.
+
+Thanks!!
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ org.opendaylight.controller.clustering.services
+ </Export-Package>
+ <Import-Package>
+ javax.transaction
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file CacheConfigException.java
+ *
+ * @brief Describe an exception that is raised when the cache being
+ * allocated has configuration errors, like mismatch parameters are
+ * passed and so on.
+ *
+ *
+ */
+package org.opendaylight.controller.clustering.services;
+
+import java.lang.Exception;
+
+/**
+ * Describe an exception that is raised when the cache being
+ * allocated has configuration errors, like mismatch parameters are
+ * passed and so on.
+ */
+public class CacheConfigException extends Exception {
+
+ /**
+ * Instantiates a new cache config exception.
+ */
+ public CacheConfigException() {
+ super();
+ }
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file CacheExistException.java
+ *
+ * @brief Describe an exception that is raised when the cache being
+ * allocated already exists
+ *
+ *
+ */
+package org.opendaylight.controller.clustering.services;
+
+import java.lang.Exception;
+
+/**
+ * Describe an exception that is raised when the cache being
+ * allocated already exists
+ */
+public class CacheExistException extends Exception {
+
+ /**
+ * Instantiates a new cache exist exception.
+ */
+ public CacheExistException() {
+ super();
+ }
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file CacheListenerAddException.java
+ *
+ * @brief Describe an exception that is raised when the cache
+ * Listener being added fails for any reason
+ *
+ */
+package org.opendaylight.controller.clustering.services;
+
+import java.lang.Exception;
+
+/**
+ * Describe an exception that is raised when the cache
+ * Listener being added fails for any reason
+ */
+public class CacheListenerAddException extends Exception {
+
+ /**
+ * Instantiates a new cache listener add exception.
+ */
+ public CacheListenerAddException() {
+ super();
+ }
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file ICacheUpdateAware.java
+ *
+ * @brief Interface for getting clustered cache updates
+ *
+ * Interface that needs to be implemented by the components
+ * that want to be informed of an update in a Clustered Cache. The
+ * interface need to be registerd in the OSGi service registry with a
+ * property "cachenames" which will contains a PropertySet object
+ * listing all the caches for which their is interes.
+ */
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * Interface that needs to be implemented by the components
+ * that want to be informed of an update in a Clustered Cache. The
+ * interface need to be registerd in the OSGi service registry with a
+ * property "cachenames" which will contains a PropertySet object
+ * listing all the caches for which their is interes.
+ *
+ */
+public interface ICacheUpdateAware<K, V> {
+ /**
+ * 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
+ */
+ void entryCreated(K key, String cacheName, boolean originLocal);
+
+ /**
+ * 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
+ */
+ void entryUpdated(K key, V new_value, String cacheName, boolean originLocal);
+
+ /**
+ * 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
+ */
+ void entryDeleted(K key, String cacheName, boolean originLocal);
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IClusterContainerServices.java
+ *
+ * @brief : Set of services and application will expect from the
+ * clustering services provider. This interface is per-container and so
+ * the container parameter is implicitely known
+ *
+ * Contract between the applications and the clustering service
+ * providers. Container version
+ */
+
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * Set of services and application will expect from the
+ * clustering services provider. This interface is per-container and so
+ * the container parameter is implicitely known
+ *
+ */
+public interface IClusterContainerServices extends IClusterServicesCommon {
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IClusterGlobalServices.java
+ *
+ * @brief : Set of services and application will expect from the
+ * clustering services provider. This interface is supposed to have
+ * Global scope
+ *
+ * Contract between the applications and the clustering service
+ * providers. Global version
+ */
+
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * Set of services and application will expect from the
+ * clustering services provider. This interface is supposed to have
+ * Global scope
+ *
+ */
+public interface IClusterGlobalServices extends IClusterServicesCommon {
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IClusterServices.java
+ *
+ * @brief : Set of services and application will expect from the
+ * clustering services provider
+ *
+ * Contract between the applications and the clustering service
+ * providers.
+ */
+
+package org.opendaylight.controller.clustering.services;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+/**
+ * Set of services and application will expect from the
+ * clustering services provider
+ *
+ */
+public interface IClusterServices {
+
+ /**
+ * Enumeration of the several modality with which a
+ * ConcurrentHashMap cache can be requested to the clustering
+ * services. The property that can be requested can be multiple.
+ *
+ */
+ public enum cacheMode {
+ /**
+ * Set for a cache that supports transaction that implies that
+ * is a transaction is open on the current thread the data
+ * will not immediately be reflected in the cache but will be
+ * staged till commit or rollback. If the transaction if NOT
+ * open the data will immediately go in the cache without
+ * staging.
+ */
+ TRANSACTIONAL,
+ /**
+ * Set on a cache that doesn't want to support
+ * transaction, so irrespective of the fact that we are in
+ * the middle of a transaction or no data will be
+ * immediately committed in the cache.
+ *
+ */
+ NON_TRANSACTIONAL;
+ }
+
+ /**
+ * Enumeration of the several properties that a cache can carry
+ *
+ */
+ public enum cacheProps {
+ /**
+ * The property returned describe the caracteristics of the
+ * transaction setup for the cache it was retrieved.
+ */
+ TRANSACTION_PROP,
+ /**
+ * The property returned report the clustering
+ * caracteristics of the cache for which property was
+ * queried.
+ */
+ CLUSTERING_PROP,
+ /**
+ * The property returned reports the locking
+ * caracteristics of the cache for which the property was
+ * queried
+ */
+ LOCKING_PROP;
+ }
+
+ /**
+ * Method that will create a new named cache per-container. The data
+ * structure if already present will cause an exception to be
+ * thrown to the caller.
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap to create
+ * @param cMode Mode of the cache that need to be retrieved. This
+ * is a set such that more than one property can be provided, of
+ * course contrasting requirements will not be accepted and in
+ * that case an exception is thrown
+ *
+ * @return ConcurrentHashMap to be used to modify the data structure
+ */
+ ConcurrentMap<?, ?> createCache(String containerName, String cacheName,
+ Set<cacheMode> cMode) throws CacheExistException,
+ CacheConfigException;
+
+ /**
+ * Method that will retrieve and return the handle to modify a
+ * data structire distributed via clustering services. The
+ * datastructure shall already have been created else a null
+ * reference will be returned.
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap to retrieve
+ *
+ * @return ConcurrentHashMap to be used to modify the data structure
+ */
+ ConcurrentMap<?, ?> getCache(String containerName, String cacheName);
+
+ /**
+ * Destroy a cachename given containerName/cachename, if doesn't exist
+ * the function does nothing. If the datastructure exists, the
+ * whole cluster will destroy the instance
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap to destroy
+ */
+ void destroyCache(String containerName, String cacheName);
+
+ /**
+ * Function to test the existance of a cache with a given name already
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap to destroy
+ *
+ * @return true if exists already, false otherwise
+ */
+ boolean existCache(String containerName, String cacheName);
+
+ /**
+ * Return the list of all teh caches registered with a container
+ *
+ * @param containerName Container for which we want to list all the caches registered
+ *
+ * @return The set of names, expressed as strings
+ */
+ Set<String> getCacheList(String containerName);
+
+ /**
+ * Return a list of properties that caracterize the cache
+ *
+ * @param containerName Name of the container where data structure resides
+ * @param cacheName Name of the cache
+ *
+ * @return The list of properties related to the cache
+ */
+ Properties getCacheProperties(String containerName, String cacheName);
+
+ /**
+ * Register an update handler for a given containerName/cacheName
+ * shared data structure. Multiple listeners are possible.
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap for which we
+ * want to register the listener
+ * @param u Interface to invoke when the updates are received
+ */
+ void addListener(String containerName, String cacheName, IGetUpdates<?, ?> u)
+ throws CacheListenerAddException;
+
+ /**
+ * Return a set of interfaces that are interesteed to listen to
+ * updates coming for a given datastructure shared via clustering
+ * services.
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap for which we
+ * want to retrieve the listener
+ */
+ Set<IGetUpdates<?, ?>> getListeners(String containerName, String cacheName);
+
+ /**
+ * UN-Register an update handler for a given containerName/cacheName
+ * shared data structure. Multiple listeners are possible.
+ *
+ * @param containerName Container to which the datastructure is associated
+ * @param cacheName Name of the ConcurrentHashMap for which we
+ * want to un-register the listener
+ * @param u Interface to un-register
+ */
+ void removeListener(String containerName, String cacheName,
+ IGetUpdates<?, ?> u);
+
+ /**
+ * Begin a transaction covering with all the data structures/HW
+ * updates. One transaction per-thread can be opened at the
+ * most, that means if multiple thread are available, multiple
+ * transactions can be outstanding.
+ *
+ */
+ void tbegin() throws NotSupportedException, SystemException;
+
+ /**
+ * Commit a transaction covering all the data structures/HW updates.
+ */
+ void tcommit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, java.lang.SecurityException,
+ java.lang.IllegalStateException, SystemException;
+
+ /**
+ * Rollback a transaction covering all the data structures/HW updates
+ */
+ void trollback() throws java.lang.IllegalStateException,
+ java.lang.SecurityException, SystemException;
+
+ /**
+ * Return the javax.transaction.Transaction associated with this thread
+ *
+ *
+ * @return Return the current transaction associated with this thread
+ */
+ Transaction tgetTransaction() throws SystemException;
+
+ /**
+ * @deprecated
+ * Function that says if we are standby in the 1-1 redundancy with
+ * active/standby model. The API is not encouraged hence is
+ * deprecated. It is supposed to be used as a stop-gap till the
+ * active-standby goal is achieved. The only guys that are
+ * supposed to use are:
+ * - southbound layer, should not listen on the OF port if standby
+ * - jetty configuration, on standby jetty should redirect calls
+ * to the active.
+ *
+ * @return true if the role is the one of standby, else false
+ */
+ boolean amIStandby();
+
+ /**
+ * @deprecated
+ * Get the InetAddress of the active controller for the
+ * active-standby case, where the standby controller has to
+ * redirect the HTTP requests received from applications layer
+ *
+ * @return Address of the active controller
+ */
+ InetAddress getActiveAddress();
+
+ /**
+ * Get the InetAddress of the all the controllers that make up this
+ * Cluster
+ *
+ * @return List of InetAddress'es of all the controllers
+ */
+ List<InetAddress> getClusteredControllers();
+
+ /**
+ * Get the InetAddress of this Controller as seen by the Cluster Manager
+ *
+ * @return InetAddress of this Controller as seen by the Cluster Manager.
+ */
+ InetAddress getMyAddress();
+
+ /**
+ * @deprecated
+ * Register a listener to the event of ChangeRole, raised every
+ * time there is a change in the role of active or standby.
+ *
+ * @param i Interface that will be called when the Role Change happens
+ */
+ void listenRoleChange(IListenRoleChange i)
+ throws ListenRoleChangeAddException;
+
+ /**
+ * @deprecated
+ * UN-Register a listener to the event of ChangeRole, raised every
+ * time there is a change in the role of active or standby.
+ *
+ * @param i Interface that will be called when the Role Change happens
+ */
+ void unlistenRoleChange(IListenRoleChange i);
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IClusterServicesCommon.java
+ *
+ * @brief : Set of services and application will expect from the
+ * clustering services provider. This interface is going to be the
+ * base for per-container and Global services and so the container
+ * parameter is omitted but who uses knows about it
+ *
+ * Contract between the applications and the clustering service
+ * providers. Common version
+ */
+
+package org.opendaylight.controller.clustering.services;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+/**
+ * @deprecated for internal use
+ * Set of services and application will expect from the
+ * clustering services provider. This interface is going to be the
+ * base for per-container and Global services and so the container
+ * parameter is omitted but who uses knows about it
+ *
+ */
+public interface IClusterServicesCommon {
+ /**
+ * Method that will create a new named cache. The data
+ * structure if already present will cause an exception to be
+ * thrown to the caller.
+ *
+ * @param cacheName Name of the ConcurrentHashMap to create
+ * @param cMode Mode of the cache that need to be retrieved. This
+ * is a set such that more than one property can be provided, of
+ * course contrasting requirements will not be accepted and in
+ * that case an exception is thrown
+ *
+ * @return ConcurrentHashMap to be used to modify the data structure
+ */
+ ConcurrentMap<?, ?> createCache(String cacheName,
+ Set<IClusterServices.cacheMode> cMode) throws CacheExistException,
+ CacheConfigException;
+
+ /**
+ * Method that will retrieve and return the handle to modify a
+ * data structire distributed via clustering services. The
+ * datastructure shall already have been created else a null
+ * reference will be returned.
+ *
+ * @param cacheName Name of the ConcurrentHashMap to retrieve
+ *
+ * @return ConcurrentHashMap to be used to modify the data structure
+ */
+ ConcurrentMap<?, ?> getCache(String cacheName);
+
+ /**
+ * Destroy a cachename given cachename, if doesn't exist
+ * the function does nothing. If the datastructure exists, the
+ * whole cluster will destroy the instance
+ *
+ * @param cacheName Name of the ConcurrentHashMap to destroy
+ */
+ void destroyCache(String cacheName);
+
+ /**
+ * Function to test the existance of a cache with a given name already
+ *
+ * @param cacheName Name of the ConcurrentHashMap to destroy
+ *
+ * @return true if exists already, false otherwise
+ */
+ boolean existCache(String cacheName);
+
+ /**
+ * Return the list of all teh caches registered in the context of
+ * the called
+ *
+ *
+ * @return The set of names, expressed as strings
+ */
+ Set<String> getCacheList();
+
+ /**
+ * Return a list of properties that caracterize the cache
+ *
+ * @param cacheName Name of the cache
+ *
+ * @return The list of properties related to the cache
+ */
+ Properties getCacheProperties(String cacheName);
+
+ /**
+ * Begin a transaction covering with all the data structures/HW
+ * updates. One transaction per-thread can be opened at the
+ * most, that means if multiple thread are available, multiple
+ * transactions can be outstanding.
+ *
+ */
+ void tbegin() throws NotSupportedException, SystemException;
+
+ /**
+ * Commit a transaction covering all the data structures/HW updates.
+ */
+ void tcommit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, java.lang.SecurityException,
+ java.lang.IllegalStateException, SystemException;
+
+ /**
+ * Rollback a transaction covering all the data structures/HW updates
+ */
+ void trollback() throws java.lang.IllegalStateException,
+ java.lang.SecurityException, SystemException;
+
+ /**
+ * Return the javax.transaction.Transaction associated with this thread
+ *
+ *
+ * @return Return the current transaction associated with this thread
+ */
+ Transaction tgetTransaction() throws SystemException;
+
+ /**
+ *
+ * Get the InetAddress of the coordinator controller in the cluster
+ *
+ * @return Address of the coordinator controller
+ */
+ InetAddress getCoordinatorAddress();
+
+ /**
+ * Get the InetAddress of the all the controllers that make up this
+ * Cluster
+ *
+ * @return List of InetAddress'es of all the controllers
+ */
+ List<InetAddress> getClusteredControllers();
+
+ /**
+ * Get the InetAddress of this Controller as seen by the Cluster Manager
+ *
+ * @return InetAddress of this Controller as seen by the Cluster Manager.
+ */
+ InetAddress getMyAddress();
+
+ /**
+ * Function that is used to know if the node on which is called is
+ * the cluster coordinator. The API is useful in scenario where
+ * the same logic is not worthed to be replicated on multiple
+ * nodes in the cluster and one can cook it up for all the
+ * others. In this scenario running the logic on the coordinator
+ * make sense, this of course implies logics that are not heavy
+ * and don't need to be scaled out linearly with the size of the
+ * cluster.
+ *
+ * @return true if the node on which the API is called is the
+ * coordinator for the cluster
+ */
+ boolean amICoordinator();
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file ICoordinatorChangeAware.java
+ *
+ *
+ * @brief Interface that needs to be implemented by who wants to be
+ * notified of coordinator role change
+ *
+ */
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * Interface that needs to be implemented by who wants to be
+ * notified of coordinator role change
+ *
+ */
+public interface ICoordinatorChangeAware {
+
+ /**
+ * Function that will be called when there is the event of
+ * coordinator change in the cluster.
+ */
+ public void coordinatorChanged();
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IGetUpdates.java
+ *
+ * @brief Interface that needs to be implemented by the listeners of
+ * updates received on data structure shared in the cluster
+ *
+ * Interface that needs to be implemented by the listeners of updates
+ * received on data structure shared in the cluster
+ */
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * @deprecated for internal use
+ * Interface that needs to be implemented by the listeners of
+ * updates received on data structure shared in the cluster
+ */
+public interface IGetUpdates<K, V> {
+ /**
+ * 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 containerName container for which the update has been received
+ * @param cacheName name of the cache for which update has been received
+ */
+ void entryCreated(K key, String containerName, String cacheName,
+ boolean local);
+
+ /**
+ * 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 containerName container for which the update has been received
+ * @param cacheName name of the cache for which update has been received
+ */
+ void entryUpdated(K key, V new_value, String containerName,
+ String cacheName, boolean local);
+
+ /**
+ * Called anytime a given key is removed from the
+ * ConcurrentHashMap we are listening to.
+ *
+ * @param key Key of the entry removed
+ * @param containerName container for which the update has been received
+ * @param cacheName name of the cache for which update has been received
+ */
+ void entryDeleted(K key, String containerName, String cacheName,
+ boolean originLocal);
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file IListenRoleChange.java
+ *
+ *
+ * @brief Interface that needs to be implemented by who wants to be
+ * notified of new active change
+ *
+ * @deprecated
+ *
+ * Interface that needs to be implemented by who wants to be notified
+ * of newly active taking over. Interface that is supposed to be
+ * short-lived and will be removed as soon as active-standby goal is reached.
+ */
+package org.opendaylight.controller.clustering.services;
+
+/**
+ * Interface that needs to be implemented by who wants to be notified
+ * of newly active taking over. Interface that is supposed to be
+ * short-lived and will be removed as soon as active-standby goal is reached.
+ *
+ */
+public interface IListenRoleChange {
+
+ /**
+ * @deprecated
+ * Function that will be called when a new active is
+ * available. This function is supposed only to be of use till
+ * active-standby milestone is reached, after will be removed.
+ *
+ */
+ public void newActiveAvailable();
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file ListenRoleChangeAddException.java
+ *
+ * @brief Describe an exception that is raised when the cache
+ * Listener being added fails for any reason
+ *
+ */
+package org.opendaylight.controller.clustering.services;
+
+import java.lang.Exception;
+
+/**
+ * @deprecated for internal use
+ * The Class ListenRoleChangeAddException.
+ */
+public class ListenRoleChangeAddException extends Exception {
+
+ /**
+ * Instantiates a new listen role change add exception.
+ */
+ public ListenRoleChangeAddException() {
+ super();
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services-implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.slf4j,
+ !org.jboss.*,
+ !bsh*,
+ !net.jcip.*,
+ javax.transaction,
+ *,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.core
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.clustering.services_implementation.internal.Activator
+ </Bundle-Activator>
+ <!-- Add in the DynamicImport-Package ONLY the packages that -->
+ <!-- contains types that MUST be unmarshalled by the -->
+ <!-- infinispan. They need to be wired at runtime even during -->
+ <!-- the bundle is already RESOLVED, but at the same time if -->
+ <!-- those are missing the bundle will still come up, this is -->
+ <!-- why those dependencies ends in the DynamicImport-Package -->
+ <!-- rather in the Import-Package -->
+ <DynamicImport-Package>
+ *
+ </DynamicImport-Package>
+ <Embed-Dependency>
+ infinispan-core,jgroups,jboss-marshalling-river,jboss-marshalling,jboss-logging,staxmapper;type=!pom;inline=false
+ </Embed-Dependency>
+ <Embed-Transitive>
+ true
+ </Embed-Transitive>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.infinispan</groupId>
+ <artifactId>infinispan-core</artifactId>
+ <version>5.2.3.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.felix.dm.Component;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getGlobalImplementations() {
+ Object[] res = { ClusterManager.class, ClusterGlobalManager.class };
+ return res;
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ClusterContainerManager.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ClusterContainerManager.class)) {
+ c.setInterface(new String[] { IClusterContainerServices.class
+ .getName() }, null);
+
+ c.add(createServiceDependency().setService(IClusterServices.class)
+ .setCallbacks("setClusterService", "unsetClusterService")
+ .setRequired(true));
+
+ // CacheUpdate services will be none or many so the
+ // dependency is optional
+ c.add(createContainerServiceDependency(containerName).setService(
+ ICacheUpdateAware.class).setCallbacks(
+ "setCacheUpdateAware", "unsetCacheUpdateAware")
+ .setRequired(false));
+
+ // Coordinator change event can be one or many so
+ // dependency is optional
+ c.add(createContainerServiceDependency(containerName).setService(
+ ICoordinatorChangeAware.class).setCallbacks(
+ "setCoordinatorChangeAware", "unsetCoordinatorChangeAware")
+ .setRequired(false));
+ }
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ */
+ public void configureGlobalInstance(Component c, Object imp) {
+ if (imp.equals(ClusterManager.class)) {
+ // export the service for Apps and Plugins
+ c.setInterface(new String[] { IClusterServices.class.getName() },
+ null);
+ }
+
+ if (imp.equals(ClusterGlobalManager.class)) {
+ c.setInterface(new String[] { IClusterGlobalServices.class
+ .getName() }, null);
+
+ c.add(createServiceDependency().setService(IClusterServices.class)
+ .setCallbacks("setClusterService", "unsetClusterService")
+ .setRequired(true));
+
+ // CacheUpdate services will be none or many so the
+ // dependency is optional
+ c.add(createServiceDependency().setService(ICacheUpdateAware.class)
+ .setCallbacks("setCacheUpdateAware",
+ "unsetCacheUpdateAware").setRequired(false));
+
+ // Coordinator change event can be one or many so
+ // dependency is optional
+ c.add(createServiceDependency().setService(
+ ICoordinatorChangeAware.class).setCallbacks(
+ "setCoordinatorChangeAware", "unsetCoordinatorChangeAware")
+ .setRequired(false));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
+import org.opendaylight.controller.clustering.services.IGetUpdates;
+
+@Listener
+public class CacheListenerContainer {
+ private IGetUpdates toBeUpdated;
+ private String containerName;
+ private String cacheName;
+
+ public CacheListenerContainer(IGetUpdates i, String containerName,
+ String cacheName) {
+ this.toBeUpdated = i;
+ this.containerName = containerName;
+ this.cacheName = cacheName;
+ }
+
+ public IGetUpdates whichListener() {
+ return this.toBeUpdated;
+ }
+
+ @CacheEntryCreated
+ public void observeCreate(CacheEntryCreatedEvent<Object, Object> event) {
+ if (event.isPre()) {
+ return;
+ }
+
+ if (this.toBeUpdated != null) {
+ this.toBeUpdated.entryCreated(event.getKey(), this.containerName,
+ this.cacheName, event.isOriginLocal());
+ }
+ }
+
+ @CacheEntryModified
+ public void observeModify(CacheEntryModifiedEvent<Object, Object> event) {
+ if (event.isPre()) {
+ return;
+ }
+
+ if (this.toBeUpdated != null) {
+ this.toBeUpdated.entryUpdated(event.getKey(), event.getValue(),
+ this.containerName, this.cacheName, event.isOriginLocal());
+ }
+ }
+
+ @CacheEntryRemoved
+ public void observeRemove(CacheEntryRemovedEvent<Object, Object> event) {
+ if (event.isPre()) {
+ return;
+ }
+
+ if (this.toBeUpdated != null) {
+ this.toBeUpdated.entryDeleted(event.getKey(), this.containerName,
+ this.cacheName, event.isOriginLocal());
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+
+public class ClusterContainerManager extends ClusterManagerCommon implements
+ IClusterContainerServices {
+}
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+
+public class ClusterGlobalManager extends ClusterManagerCommon implements
+ IClusterGlobalServices {
+}
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged;
+import org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent;
+import org.infinispan.remoting.transport.Address;
+import org.infinispan.remoting.transport.Transport;
+import org.infinispan.remoting.transport.jgroups.JGroupsAddress;
+import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
+import org.jgroups.Channel;
+import org.jgroups.Event;
+import org.jgroups.stack.GossipRouter;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.CacheListenerAddException;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustering.services.IGetUpdates;
+import org.opendaylight.controller.clustering.services.IListenRoleChange;
+import org.opendaylight.controller.clustering.services.ListenRoleChangeAddException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClusterManager implements IClusterServices {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(ClusterManager.class);
+ private DefaultCacheManager cm;
+ GossipRouter gossiper;
+ private HashSet roleChangeListeners;
+ private ViewChangedListener cacheManagerListener;
+
+ private static String loopbackAddress = "127.0.0.1";
+
+ /**
+ * Start a JGroups GossipRouter if we are a supernode. The
+ * GosispRouter is nothing more than a simple
+ * rendevouz-pointer. All the nodes that wants to join the cluster
+ * will come to any of the rendevouz point and they introduce the
+ * nodes to all the others. Once the meet and greet phase if over,
+ * the nodes will open a full-mesh with the remaining n-1 nodes,
+ * so even if the GossipRouter goes down nothing is lost.
+ * NOTE: This function has the side effect to set some of the
+ * JGROUPS configurations, this because in this function already
+ * we try to retrieve some of the network capabilities of the
+ * device and so it's better not to do that again
+ *
+ *
+ * @return GossipRouter
+ */
+ private GossipRouter startGossiper() {
+ boolean amIGossipRouter = false;
+ Integer gossipRouterPortDefault = 12001;
+ Integer gossipRouterPort = gossipRouterPortDefault;
+ InetAddress gossipRouterAddress = null;
+ String supernodes_list = System.getProperty("supernodes",
+ loopbackAddress);
+ StringBuffer sanitized_supernodes_list = new StringBuffer();
+ List<InetAddress> myAddresses = new ArrayList<InetAddress>();
+
+ StringTokenizer supernodes = new StringTokenizer(supernodes_list, ":");
+ if (supernodes.hasMoreTokens()) {
+ // Populate the list of my addresses
+ try {
+ Enumeration e = NetworkInterface.getNetworkInterfaces();
+ while (e.hasMoreElements()) {
+ NetworkInterface n = (NetworkInterface) e.nextElement();
+ Enumeration ee = n.getInetAddresses();
+ while (ee.hasMoreElements()) {
+ InetAddress i = (InetAddress) ee.nextElement();
+ myAddresses.add(i);
+ }
+ }
+ } catch (SocketException se) {
+ logger.error("Cannot get the list of network interfaces");
+ return null;
+ }
+ }
+ while (supernodes.hasMoreTokens()) {
+ String curr_supernode = supernodes.nextToken();
+ logger.debug("Examining supernode " + curr_supernode);
+ StringTokenizer host_port = new StringTokenizer(curr_supernode,
+ "[]");
+ String host;
+ String port;
+ Integer port_num = gossipRouterPortDefault;
+ if (host_port.countTokens() > 2) {
+ logger.error("Error parsing supernode " + curr_supernode
+ + " proceed to the next one");
+ continue;
+ }
+ host = host_port.nextToken();
+ InetAddress hostAddr;
+ try {
+ hostAddr = InetAddress.getByName(host);
+ } catch (UnknownHostException ue) {
+ logger.error("Host not known");
+ continue;
+ }
+ if (host_port.hasMoreTokens()) {
+ port = host_port.nextToken();
+ try {
+ port_num = Integer.valueOf(port);
+ } catch (NumberFormatException ne) {
+ logger
+ .error("Supplied supernode gossiepr port is not recognized, using standard gossipport");
+ port_num = gossipRouterPortDefault;
+ }
+ if ((port_num > 65535) || (port_num < 0)) {
+ logger
+ .error("Supplied supernode gossip port is outside a valid TCP port range");
+ port_num = gossipRouterPortDefault;
+ }
+ }
+ if (!amIGossipRouter) {
+ if (host != null) {
+ for (InetAddress myAddr : myAddresses) {
+ if (myAddr.equals(hostAddr)) {
+ amIGossipRouter = true;
+ gossipRouterAddress = hostAddr;
+ gossipRouterPort = port_num;
+ break;
+ }
+ }
+ }
+ }
+ if (!sanitized_supernodes_list.toString().equals("")) {
+ sanitized_supernodes_list.append(",");
+ }
+ sanitized_supernodes_list.append(hostAddr.getHostAddress() + "["
+ + port_num + "]");
+ }
+
+ if (amIGossipRouter) {
+ // Set the Jgroups binding interface to the one we got
+ // from the supernodes attribute
+ if (gossipRouterAddress != null) {
+ System.setProperty("jgroups.tcp.address", gossipRouterAddress
+ .getHostAddress());
+ }
+ } else {
+ // Set the Jgroup binding interface to the one we are well
+ // known outside or else to the first with non-local
+ // scope.
+ try {
+ String myBind = InetAddress.getLocalHost().getHostAddress();
+ if (myBind == null
+ || InetAddress.getLocalHost().isLoopbackAddress()) {
+ for (InetAddress myAddr : myAddresses) {
+ if (myAddr.isLoopbackAddress()
+ || myAddr.isLinkLocalAddress()) {
+ logger.debug("Skipping local address "
+ + myAddr.getHostAddress());
+ continue;
+ } else {
+ // First non-local address
+ myBind = myAddr.getHostAddress();
+ logger.debug("First non-local address " + myBind);
+ break;
+ }
+ }
+ }
+ String jgroupAddress = System
+ .getProperty("jgroups.tcp.address");
+ if (jgroupAddress == null) {
+ if (myBind != null) {
+ logger.debug("Set bind address to be " + myBind);
+ System.setProperty("jgroups.tcp.address", myBind);
+ } else {
+ logger
+ .debug("Set bind address to be LOCALHOST=127.0.0.1");
+ System.setProperty("jgroups.tcp.address", "127.0.0.1");
+ }
+ } else {
+ logger.debug("jgroup.tcp.address already set to be "
+ + jgroupAddress);
+ }
+ } catch (UnknownHostException uhe) {
+ logger
+ .error("Met UnknownHostException while trying to get binding address for jgroups");
+ }
+ }
+
+ // The supernodes list constitute also the tcpgossip initial
+ // host list
+ System.setProperty("jgroups.tcpgossip.initial_hosts",
+ sanitized_supernodes_list.toString());
+ logger.debug("jgroups.tcp.address set to "
+ + System.getProperty("jgroups.tcp.address"));
+ logger.debug("jgroups.tcpgossip.initial_hosts set to "
+ + System.getProperty("jgroups.tcpgossip.initial_hosts"));
+ GossipRouter res = null;
+ if (amIGossipRouter) {
+ logger.info("I'm a GossipRouter will listen on port "
+ + gossipRouterPort);
+ res = new GossipRouter(gossipRouterPort);
+ }
+ return res;
+ }
+
+ public void start() {
+ this.gossiper = startGossiper();
+ if (this.gossiper != null) {
+ logger.debug("Trying to start Gossiper");
+ try {
+ this.gossiper.start();
+ logger.info("Started GossipRouter");
+ } catch (Exception e) {
+ logger.error("GossipRouter didn't start exception " + e
+ + " met");
+ StringWriter sw = new StringWriter();
+ logger.error("Stack Trace that raised the exception");
+ e.printStackTrace(new PrintWriter(sw));
+ logger.error(sw.toString());
+ }
+ }
+ logger.info("Starting the ClusterManager");
+ try {
+ //FIXME keeps throwing FileNotFoundException
+ this.cm = new DefaultCacheManager("/config/infinispan-config.xml");
+ logger.debug("Allocated ClusterManager");
+ if (this.cm != null) {
+ this.cm.start();
+ this.cm.startCache();
+ logger.debug("Started the ClusterManager");
+ }
+ } catch (Exception ioe) {
+ StringWriter sw = new StringWriter();
+ logger.error("Cannot configure infinispan .. bailing out ");
+ logger.error("Stack Trace that raised th exception");
+ ioe.printStackTrace(new PrintWriter(sw));
+ logger.error(sw.toString());
+ this.cm = null;
+ this.stop();
+ }
+ logger.debug("Cache Manager has value " + this.cm);
+ }
+
+ public void stop() {
+ logger.info("Stopping the ClusterManager");
+ if (this.cm != null) {
+ logger.info("Found a valid ClusterManager, now let it be stopped");
+ this.cm.stop();
+ this.cm = null;
+ }
+ if (this.gossiper != null) {
+ this.gossiper.stop();
+ this.gossiper = null;
+ }
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> createCache(String containerName,
+ String cacheName, Set<cacheMode> cMode) throws CacheExistException,
+ CacheConfigException {
+ EmbeddedCacheManager manager = this.cm;
+ Cache c;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return null;
+ }
+
+ if (manager.cacheExists(realCacheName)) {
+ throw new CacheExistException();
+ }
+
+ // Sanity check to avoid contrasting parameters
+ if (cMode.containsAll(EnumSet.of(
+ IClusterServices.cacheMode.NON_TRANSACTIONAL,
+ IClusterServices.cacheMode.TRANSACTIONAL))) {
+ throw new CacheConfigException();
+ }
+
+ if (cMode.contains(IClusterServices.cacheMode.NON_TRANSACTIONAL)) {
+ c = manager.getCache(realCacheName);
+ return c;
+ } else if (cMode.contains(IClusterServices.cacheMode.TRANSACTIONAL)) {
+ Configuration rc = manager
+ .getCacheConfiguration("transactional-type");
+ manager.defineConfiguration(realCacheName, rc);
+ c = manager.getCache(realCacheName);
+ return c;
+ }
+ return null;
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> getCache(String containerName, String cacheName) {
+ EmbeddedCacheManager manager = this.cm;
+ Cache c;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return null;
+ }
+
+ if (manager.cacheExists(realCacheName)) {
+ c = manager.getCache(realCacheName);
+ return c;
+ }
+ return null;
+ }
+
+ @Override
+ public void destroyCache(String containerName, String cacheName) {
+ EmbeddedCacheManager manager = this.cm;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return;
+ }
+ if (manager.cacheExists(realCacheName)) {
+ manager.removeCache(realCacheName);
+ }
+ }
+
+ @Override
+ public boolean existCache(String containerName, String cacheName) {
+ EmbeddedCacheManager manager = this.cm;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return false;
+ }
+ return manager.cacheExists(realCacheName);
+ }
+
+ @Override
+ public Set<String> getCacheList(String containerName) {
+ Set<String> perContainerCaches = new HashSet();
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ return null;
+ }
+ for (String cacheName : manager.getCacheNames()) {
+ if (cacheName.startsWith("{" + containerName + "}_")) {
+ String[] res = cacheName.split("[{}]");
+ if (res.length >= 4 && res[1].equals(containerName)
+ && res[2].equals("_")) {
+ perContainerCaches.add(res[3]);
+ }
+ }
+ }
+
+ return (perContainerCaches);
+ }
+
+ @Override
+ public Properties getCacheProperties(String containerName, String cacheName) {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ return null;
+ }
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (!manager.cacheExists(realCacheName)) {
+ return null;
+ }
+ Configuration conf = manager.getCache(realCacheName).getAdvancedCache()
+ .getCacheConfiguration();
+ Properties p = new Properties();
+ p.setProperty(IClusterServices.cacheProps.TRANSACTION_PROP.toString(),
+ conf.transaction().toString());
+ p.setProperty(IClusterServices.cacheProps.CLUSTERING_PROP.toString(),
+ conf.clustering().toString());
+ p.setProperty(IClusterServices.cacheProps.LOCKING_PROP.toString(), conf
+ .locking().toString());
+ return p;
+ }
+
+ @Override
+ public void addListener(String containerName, String cacheName,
+ IGetUpdates<?, ?> u) throws CacheListenerAddException {
+ EmbeddedCacheManager manager = this.cm;
+ Cache c;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return;
+ }
+
+ if (!manager.cacheExists(realCacheName)) {
+ throw new CacheListenerAddException();
+ }
+ c = manager.getCache(realCacheName);
+ CacheListenerContainer cl = new CacheListenerContainer(u,
+ containerName, cacheName);
+ if (cl == null) {
+ throw new CacheListenerAddException();
+ }
+ c.addListener(cl);
+ }
+
+ @Override
+ public Set<IGetUpdates<?, ?>> getListeners(String containerName,
+ String cacheName) {
+ EmbeddedCacheManager manager = this.cm;
+ Cache c;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return null;
+ }
+
+ if (!manager.cacheExists(realCacheName)) {
+ return null;
+ }
+ c = manager.getCache(realCacheName);
+
+ Set<IGetUpdates<?, ?>> res = new HashSet();
+ Set<Object> listeners = c.getListeners();
+ for (Object listener : listeners) {
+ if (listener instanceof CacheListenerContainer) {
+ CacheListenerContainer cl = (CacheListenerContainer) listener;
+ res.add(cl.whichListener());
+ }
+ }
+
+ return res;
+ }
+
+ @Override
+ public void removeListener(String containerName, String cacheName,
+ IGetUpdates<?, ?> u) {
+ EmbeddedCacheManager manager = this.cm;
+ Cache c;
+ String realCacheName = "{" + containerName + "}_{" + cacheName + "}";
+ if (manager == null) {
+ return;
+ }
+
+ if (!manager.cacheExists(realCacheName)) {
+ return;
+ }
+ c = manager.getCache(realCacheName);
+
+ Set<Object> listeners = c.getListeners();
+ for (Object listener : listeners) {
+ if (listener instanceof CacheListenerContainer) {
+ CacheListenerContainer cl = (CacheListenerContainer) listener;
+ if (cl.whichListener() == u) {
+ c.removeListener(listener);
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void tbegin() throws NotSupportedException, SystemException {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ throw new IllegalStateException();
+ }
+ TransactionManager tm = manager.getCache("transactional-type")
+ .getAdvancedCache().getTransactionManager();
+ if (tm == null) {
+ throw new IllegalStateException();
+ }
+ tm.begin();
+ }
+
+ @Override
+ public void tcommit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, java.lang.SecurityException,
+ java.lang.IllegalStateException, SystemException {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ throw new IllegalStateException();
+ }
+ TransactionManager tm = manager.getCache("transactional-type")
+ .getAdvancedCache().getTransactionManager();
+ if (tm == null) {
+ throw new IllegalStateException();
+ }
+ tm.commit();
+ }
+
+ @Override
+ public void trollback() throws java.lang.IllegalStateException,
+ java.lang.SecurityException, SystemException {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ throw new IllegalStateException();
+ }
+ TransactionManager tm = manager.getCache("transactional-type")
+ .getAdvancedCache().getTransactionManager();
+ if (tm == null) {
+ throw new IllegalStateException();
+ }
+ tm.rollback();
+ }
+
+ @Override
+ public Transaction tgetTransaction() throws SystemException {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ throw new IllegalStateException();
+ }
+ TransactionManager tm = manager.getCache("transactional-type")
+ .getAdvancedCache().getTransactionManager();
+ if (tm == null) {
+ return null;
+ }
+ return tm.getTransaction();
+ }
+
+ @Override
+ public boolean amIStandby() {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ // In case we cannot fetch the information, lets assume we
+ // are standby, so to have less responsability.
+ return true;
+ }
+ return (!manager.isCoordinator());
+ }
+
+ private InetAddress addressToInetAddress(Address a) {
+ EmbeddedCacheManager manager = this.cm;
+ if ((manager == null) || (a == null)) {
+ // In case we cannot fetch the information, lets assume we
+ // are standby, so to have less responsability.
+ return null;
+ }
+ Transport t = manager.getTransport();
+ if (t instanceof JGroupsTransport) {
+ JGroupsTransport jt = (JGroupsTransport) t;
+ Channel c = jt.getChannel();
+ if (a instanceof JGroupsAddress) {
+ JGroupsAddress ja = (JGroupsAddress) a;
+ org.jgroups.Address phys = (org.jgroups.Address) c
+ .down(new Event(Event.GET_PHYSICAL_ADDRESS, ja
+ .getJGroupsAddress()));
+ if (phys instanceof org.jgroups.stack.IpAddress) {
+ InetAddress bindAddress = ((org.jgroups.stack.IpAddress) phys)
+ .getIpAddress();
+ return bindAddress;
+ }
+ }
+ }
+ return null;
+ }
+
+ public List<InetAddress> getClusteredControllers() {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ return null;
+ }
+ List<Address> controllers = manager.getMembers();
+ if ((controllers == null) || controllers.size() == 0)
+ return null;
+
+ List<InetAddress> clusteredControllers = new ArrayList<InetAddress>();
+ for (Address a : controllers) {
+ InetAddress inetAddress = addressToInetAddress(a);
+ if (inetAddress != null
+ && !inetAddress.getHostAddress().equals(loopbackAddress))
+ clusteredControllers.add(inetAddress);
+ }
+ return clusteredControllers;
+ }
+
+ public InetAddress getMyAddress() {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ return null;
+ }
+ return addressToInetAddress(manager.getAddress());
+ }
+
+ @Override
+ public InetAddress getActiveAddress() {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ // In case we cannot fetch the information, lets assume we
+ // are standby, so to have less responsability.
+ return null;
+ }
+
+ return addressToInetAddress(manager.getCoordinator());
+ }
+
+ @Override
+ public void listenRoleChange(IListenRoleChange i)
+ throws ListenRoleChangeAddException {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ // In case we cannot fetch the information, lets assume we
+ // are standby, so to have less responsability.
+ throw new ListenRoleChangeAddException();
+ }
+
+ if (this.roleChangeListeners == null) {
+ this.roleChangeListeners = new HashSet();
+ this.cacheManagerListener = new ViewChangedListener(
+ this.roleChangeListeners);
+ manager.addListener(this.cacheManagerListener);
+ }
+
+ if (this.roleChangeListeners != null) {
+ this.roleChangeListeners.add(i);
+ }
+ }
+
+ @Override
+ public void unlistenRoleChange(IListenRoleChange i) {
+ EmbeddedCacheManager manager = this.cm;
+ if (manager == null) {
+ // In case we cannot fetch the information, lets assume we
+ // are standby, so to have less responsability.
+ return;
+ }
+
+ if (this.roleChangeListeners != null) {
+ this.roleChangeListeners.remove(i);
+ }
+
+ if ((this.roleChangeListeners != null && this.roleChangeListeners
+ .isEmpty())
+ && (this.cacheManagerListener != null)) {
+ manager.removeListener(this.cacheManagerListener);
+ this.cacheManagerListener = null;
+ this.roleChangeListeners = null;
+ }
+ }
+
+ @Listener
+ public class ViewChangedListener {
+ Set<IListenRoleChange> roleListeners;
+
+ public ViewChangedListener(Set<IListenRoleChange> s) {
+ this.roleListeners = s;
+ }
+
+ @ViewChanged
+ public void viewChanged(ViewChangedEvent e) {
+ for (IListenRoleChange i : this.roleListeners) {
+ i.newActiveAvailable();
+ }
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.services_implementation.internal;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.CacheListenerAddException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustering.services.IClusterServicesCommon;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.opendaylight.controller.clustering.services.IListenRoleChange;
+import org.opendaylight.controller.clustering.services.ListenRoleChangeAddException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.Collections;
+import java.util.HashSet;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract public class ClusterManagerCommon implements IClusterServicesCommon {
+ protected String containerName = null;
+ private IClusterServices clusterService = null;
+ protected static final Logger logger = LoggerFactory
+ .getLogger(ClusterManagerCommon.class);
+ private Set<ICacheUpdateAware> cacheUpdateAware = Collections
+ .synchronizedSet(new HashSet<ICacheUpdateAware>());
+ private Set<ICoordinatorChangeAware> coordinatorChangeAware = Collections
+ .synchronizedSet(new HashSet<ICoordinatorChangeAware>());
+ private ListenCoordinatorChange coordinatorChangeListener = null;
+
+ /**
+ * Class needed to listen to the role changes from the cluster
+ * manager and to pass it along to the other components that
+ * export the interface ICoordinatorChangeAware
+ */
+ class ListenCoordinatorChange implements IListenRoleChange {
+ public void newActiveAvailable() {
+ if (coordinatorChangeAware != null) {
+ // Make sure to look the set while walking it
+ synchronized (coordinatorChangeAware) {
+ for (ICoordinatorChangeAware s : coordinatorChangeAware) {
+ // Now walk every instance and signal that the
+ // coordinator has changed
+ s.coordinatorChanged();
+ }
+ }
+ }
+ }
+ }
+
+ void setCoordinatorChangeAware(ICoordinatorChangeAware s) {
+ if (this.coordinatorChangeAware != null) {
+ this.coordinatorChangeAware.add(s);
+ }
+ }
+
+ void unsetCoordinatorChangeAware(ICoordinatorChangeAware s) {
+ if (this.coordinatorChangeAware != null) {
+ this.coordinatorChangeAware.remove(s);
+ }
+ }
+
+ void setCacheUpdateAware(ICacheUpdateAware s) {
+ if (this.cacheUpdateAware != null) {
+ this.cacheUpdateAware.add(s);
+ }
+ }
+
+ void unsetCacheUpdateAware(ICacheUpdateAware s) {
+ if (this.cacheUpdateAware != null) {
+ this.cacheUpdateAware.remove(s);
+ }
+ }
+
+ public void setClusterService(IClusterServices s) {
+ this.clusterService = s;
+ }
+
+ public void unsetClusterServices(IClusterServices s) {
+ if (this.clusterService == s) {
+ this.clusterService = null;
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ Dictionary props = c.getServiceProperties();
+ if (props != null) {
+ this.containerName = (String) props.get("containerName");
+ logger.debug("Running containerName:" + this.containerName);
+ } else {
+ // In the Global instance case the containerName is empty
+ this.containerName = "";
+ }
+ if (this.clusterService != null) {
+ this.coordinatorChangeListener = new ListenCoordinatorChange();
+ try {
+ this.clusterService
+ .listenRoleChange(this.coordinatorChangeListener);
+ logger.debug("Coordinator change handler registered");
+ } catch (ListenRoleChangeAddException ex) {
+ logger.error("Could not register coordinator change");
+ }
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when any of the required
+ * dependencies are going away
+ *
+ */
+ void destroy() {
+ if (this.clusterService != null
+ && this.coordinatorChangeListener != null) {
+ this.clusterService
+ .unlistenRoleChange(this.coordinatorChangeListener);
+ this.coordinatorChangeListener = null;
+ logger.debug("Coordinator change handler UNregistered");
+ }
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> createCache(String cacheName,
+ Set<IClusterServices.cacheMode> cMode) throws CacheExistException,
+ CacheConfigException {
+ if (this.clusterService != null) {
+ return this.clusterService.createCache(this.containerName,
+ cacheName, cMode);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> getCache(String cacheName) {
+ if (this.clusterService != null) {
+ return this.clusterService.getCache(this.containerName, cacheName);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void destroyCache(String cacheName) {
+ if (this.clusterService != null) {
+ this.clusterService.destroyCache(this.containerName, cacheName);
+ }
+ }
+
+ @Override
+ public boolean existCache(String cacheName) {
+ if (this.clusterService != null) {
+ return this.clusterService
+ .existCache(this.containerName, cacheName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Set<String> getCacheList() {
+ if (this.clusterService != null) {
+ return this.clusterService.getCacheList(this.containerName);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Properties getCacheProperties(String cacheName) {
+ if (this.clusterService != null) {
+ return this.clusterService.getCacheProperties(this.containerName,
+ cacheName);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void tbegin() throws NotSupportedException, SystemException {
+ if (this.clusterService != null) {
+ this.clusterService.tbegin();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public void tcommit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, java.lang.SecurityException,
+ java.lang.IllegalStateException, SystemException {
+ if (this.clusterService != null) {
+ this.clusterService.tcommit();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public void trollback() throws java.lang.IllegalStateException,
+ java.lang.SecurityException, SystemException {
+ if (this.clusterService != null) {
+ this.clusterService.trollback();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public Transaction tgetTransaction() throws SystemException {
+ if (this.clusterService != null) {
+ return this.clusterService.tgetTransaction();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public List<InetAddress> getClusteredControllers() {
+ if (this.clusterService != null) {
+ return this.clusterService.getClusteredControllers();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public InetAddress getMyAddress() {
+ if (this.clusterService != null) {
+ return this.clusterService.getMyAddress();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public InetAddress getCoordinatorAddress() {
+ if (this.clusterService != null) {
+ return this.clusterService.getActiveAddress();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean amICoordinator() {
+ if (this.clusterService != null) {
+ return (!this.clusterService.amIStandby());
+ } else {
+ return false;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
+ activate="start"
+ deactivate="stop"
+ immediate="true"
+ name="org.opendaylight.controller.clustering.services_implementation.internal.ClusterManager">
+ <implementation class="org.opendaylight.controller.clustering.services_implementation.internal.ClusterManager"/>
+ <service>
+ <provide interface="org.opendaylight.controller.clustering.services.IClusterServices"/>
+ </service>
+</scr:component>
--- /dev/null
+<infinispan xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:infinispan:config:5.1">
+ <global>
+ <transport>
+ <properties>
+ <property name="configurationFile" value="/config/jgroups.xml"/>
+ </properties>
+ </transport>
+ <!-- Enable JMX statistics -->
+ <globalJmxStatistics
+ enabled="true"
+ jmxDomain="org.infinispan"
+ cacheManagerName="SampleCacheManager"/>
+ </global>
+ <default>
+ <!-- Configure a synchronous replication cache -->
+ <clustering mode="replication">
+ <sync/>
+ </clustering>
+ <!--
+ Used to register JMX statistics in any available MBean server
+ -->
+ <jmxStatistics enabled="true"/>
+ </default>
+ <!-- transactionManagerLookupClass="org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup" -->
+ <namedCache name="transactional-type">
+ <transaction
+ transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup"
+ syncRollbackPhase="false"
+ syncCommitPhase="false"
+ cacheStopTimeout="30000"
+ use1PcForAutoCommitTransactions="false"
+ autoCommit="true"
+ lockingMode="OPTIMISTIC"
+ useSynchronization="false"
+ transactionMode="TRANSACTIONAL"
+ />
+ </namedCache>
+</infinispan>
--- /dev/null
+<!--
+ TCP based stack, with flow control and message bundling. This is usually used when IP
+ multicasting cannot be used in a network, e.g. because it is disabled (routers discard multicast).
+ Note that TCP.bind_addr and TCPPING.initial_hosts should be set, possibly via system properties, e.g.
+ -Djgroups.bind_addr=192.168.5.2 and -Djgroups.tcpping.initial_hosts=192.168.5.2[7800]".
+ author: Bela Ban
+-->
+<config xmlns="urn:org:jgroups"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/JGroups-3.0.xsd">
+
+ <TCP loopback="true"
+ bind_addr="${jgroups.tcp.address:127.0.0.1}"
+ bind_port="${jgroups.tcp.port:7800}"
+ recv_buf_size="${tcp.recv_buf_size:20M}"
+ send_buf_size="${tcp.send_buf_size:640K}"
+ discard_incompatible_packets="true"
+ max_bundle_size="64K"
+ max_bundle_timeout="30"
+ enable_bundling="true"
+ use_send_queues="true"
+ sock_conn_timeout="300"
+ timer_type="new"
+ timer.min_threads="4"
+ timer.max_threads="10"
+ timer.keep_alive_time="3000"
+ timer.queue_max_size="500"
+ thread_pool.enabled="true"
+ thread_pool.min_threads="2"
+ thread_pool.max_threads="30"
+ thread_pool.keep_alive_time="60000"
+ thread_pool.queue_enabled="false"
+ thread_pool.queue_max_size="100"
+ thread_pool.rejection_policy="discard"
+ oob_thread_pool.enabled="true"
+ oob_thread_pool.min_threads="2"
+ oob_thread_pool.max_threads="30"
+ oob_thread_pool.keep_alive_time="60000"
+ oob_thread_pool.queue_enabled="false"
+ oob_thread_pool.queue_max_size="100"
+ oob_thread_pool.rejection_policy="discard"/>
+
+ <!-- <TCP_NIO -->
+ <!-- bind_port="7800" -->
+ <!-- bind_interface="${jgroups.tcp_nio.bind_interface:bond0}" -->
+ <!-- use_send_queues="true" -->
+ <!-- sock_conn_timeout="300" -->
+ <!-- reader_threads="3" -->
+ <!-- writer_threads="3" -->
+ <!-- processor_threads="0" -->
+ <!-- processor_minThreads="0" -->
+ <!-- processor_maxThreads="0" -->
+ <!-- processor_queueSize="100" -->
+ <!-- processor_keepAliveTime="9223372036854775807"/> -->
+ <TCPGOSSIP initial_hosts="${jgroups.tcpgossip.initial_hosts}"/>
+ <!-- <TCPPING initial_hosts="${jgroups.tcpping.initial_hosts}" -->
+ <!-- port_range="0" -->
+ <!-- timeout="3000" -->
+ <!-- /> -->
+ <MERGE2 max_interval="30000" min_interval="10000"/>
+ <FD_SOCK/>
+ <FD timeout="3000" max_tries="3"/>
+ <VERIFY_SUSPECT timeout="1500"/>
+ <pbcast.NAKACK
+ use_mcast_xmit="false"
+ retransmit_timeout="300,600,1200,2400,4800"
+ discard_delivered_msgs="false"/>
+ <UNICAST2 timeout="300,600,1200"
+ stable_interval="5000"
+ max_bytes="1m"/>
+ <pbcast.STABLE stability_delay="500" desired_avg_gossip="5000" max_bytes="1m"/>
+ <pbcast.GMS print_local_addr="false" join_timeout="3000" view_bundling="true"/>
+ <UFC max_credits="200k" min_threshold="0.20"/>
+ <MFC max_credits="200k" min_threshold="0.20"/>
+ <FRAG2 frag_size="60000"/>
+ <RSVP timeout="60000" resend_interval="500" ack_on_delivery="false" />
+</config>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.stub</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ javax.transaction,
+ org.apache.felix.dm,
+ org.slf4j,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.core
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.clustering.stub.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * 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.clustering.stub.internal;
+
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.felix.dm.Component;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getGlobalImplementations() {
+ Object[] res = { ClusterGlobalManager.class };
+ return res;
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ClusterContainerManager.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ClusterContainerManager.class)) {
+ c.setInterface(new String[] { IClusterContainerServices.class
+ .getName() }, null);
+ }
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ */
+ public void configureGlobalInstance(Component c, Object imp) {
+ if (imp.equals(ClusterGlobalManager.class)) {
+ c.setInterface(new String[] { IClusterGlobalServices.class
+ .getName() }, null);
+ }
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.stub.internal;
+
+import java.net.UnknownHostException;
+
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+
+public class ClusterContainerManager extends ClusterManagerCommon implements
+ IClusterContainerServices {
+ public ClusterContainerManager() throws UnknownHostException {
+ super();
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.stub.internal;
+
+import java.net.UnknownHostException;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+
+public class ClusterGlobalManager extends ClusterManagerCommon implements
+ IClusterGlobalServices {
+ public ClusterGlobalManager() throws UnknownHostException {
+ super();
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.stub.internal;
+
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.CacheListenerAddException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustering.services.IClusterServicesCommon;
+import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.opendaylight.controller.clustering.services.IListenRoleChange;
+import org.opendaylight.controller.clustering.services.ListenRoleChangeAddException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.Collections;
+import java.util.HashSet;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract public class ClusterManagerCommon implements IClusterServicesCommon {
+ protected String containerName = "";
+ protected static final Logger logger = LoggerFactory
+ .getLogger(ClusterManagerCommon.class);
+ private InetAddress loopbackAddress;
+ private ConcurrentMap<String, ConcurrentMap<?, ?>> caches = new ConcurrentHashMap<String, ConcurrentMap<?, ?>>();
+
+ protected ClusterManagerCommon() throws UnknownHostException {
+ loopbackAddress = InetAddress.getByName("127.0.0.1");
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ Dictionary props = c.getServiceProperties();
+ if (props != null) {
+ this.containerName = (String) props.get("containerName");
+ logger.debug("Running containerName:" + this.containerName);
+ } else {
+ // In the Global instance case the containerName is empty
+ this.containerName = "";
+ }
+ }
+
+ /**
+ * Function called by the dependency manager when any of the required
+ * dependencies are going away
+ *
+ */
+ void destroy() {
+ // Clear the caches, will restart on the new life
+ this.caches.clear();
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> createCache(String cacheName,
+ Set<IClusterServices.cacheMode> cMode) throws CacheExistException,
+ CacheConfigException {
+ ConcurrentMap<?, ?> res = this.caches.get(cacheName);
+ if (res == null) {
+ res = new ConcurrentHashMap();
+ this.caches.put(cacheName, res);
+ return res;
+ }
+ throw new CacheExistException();
+ }
+
+ @Override
+ public ConcurrentMap<?, ?> getCache(String cacheName) {
+ return this.caches.get(cacheName);
+ }
+
+ @Override
+ public void destroyCache(String cacheName) {
+ this.caches.remove(cacheName);
+ }
+
+ @Override
+ public boolean existCache(String cacheName) {
+ return (this.caches.get(cacheName) != null);
+ }
+
+ @Override
+ public Set<String> getCacheList() {
+ return this.caches.keySet();
+ }
+
+ @Override
+ public Properties getCacheProperties(String cacheName) {
+ return null;
+ }
+
+ @Override
+ public void tbegin() throws NotSupportedException, SystemException {
+ }
+
+ @Override
+ public void tcommit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, java.lang.SecurityException,
+ java.lang.IllegalStateException, SystemException {
+ }
+
+ @Override
+ public void trollback() throws java.lang.IllegalStateException,
+ java.lang.SecurityException, SystemException {
+ }
+
+ @Override
+ public Transaction tgetTransaction() throws SystemException {
+ return null;
+ }
+
+ @Override
+ public List<InetAddress> getClusteredControllers() {
+ List<InetAddress> res = new ArrayList<InetAddress>();
+ res.add(loopbackAddress);
+ return res;
+ }
+
+ @Override
+ public InetAddress getMyAddress() {
+ return loopbackAddress;
+ }
+
+ @Override
+ public InetAddress getCoordinatorAddress() {
+ return loopbackAddress;
+ }
+
+ @Override
+ public boolean amICoordinator() {
+ return true;
+ }
+}
--- /dev/null
+
+/*
+ * 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
+ */
+
+/**
+ * @file TestClusteringStub.java
+ *
+ * @brief Unit tests for the stub implementation of clustering,
+ * needed only to run the integration tests
+ *
+ * Unit tests for the stub implementation of clustering,
+ * needed only to run the integration tests
+ */
+package org.opendaylight.controller.clustering.stub.internal;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import java.net.UnknownHostException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.stub.internal.ClusterGlobalManager;
+
+public class TestClusteringStub {
+ @Test
+ public void testStub1() {
+ IClusterGlobalServices c = null;
+ ClusterGlobalManager cm = null;
+ try {
+ cm = new ClusterGlobalManager();
+ c = (IClusterGlobalServices) cm;
+ } catch (UnknownHostException un) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+
+ // Make sure the stub cluster manager is allocated
+ Assert.assertTrue(cm != null);
+ Assert.assertTrue(c != null);
+
+ // ========================================
+ // Now start testing the several aspects of it.
+ // ========================================
+
+ // Allocate few caches
+ ConcurrentMap<String, Integer> c1 = null;
+ ConcurrentMap<String, Integer> c2 = null;
+ ConcurrentMap<String, Integer> c3 = null;
+ try {
+ c1 = (ConcurrentMap<String, Integer>) c.createCache("c1", null);
+ } catch (CacheExistException cee) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ } catch (CacheConfigException cce) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+
+ // Put some data to it
+ c1.put("FOO", 1);
+ c1.put("BAZ", 2);
+ c1.put("BAR", 3);
+
+ try {
+ c1 = (ConcurrentMap<String, Integer>) c.createCache("c1", null);
+ } catch (CacheExistException cee) {
+ // This exception should be raised because the cache
+ // already exists
+ Assert.assertTrue(true);
+ } catch (CacheConfigException cce) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+
+ // Make sure this cache is retrieved
+ c1 = (ConcurrentMap<String, Integer>) c.getCache("c1");
+ Assert.assertTrue(c1 != null);
+
+ // Now make sure the data exists
+ Integer res = null;
+ res = c1.get("FOO");
+ Assert.assertTrue(res != null);
+ res = c1.get("BAR");
+ Assert.assertTrue(res != null);
+ res = c1.get("BAZ");
+ Assert.assertTrue(res != null);
+
+ // Now create yet another two caches
+ try {
+ c2 = (ConcurrentMap<String, Integer>) c.createCache("c2", null);
+ c3 = (ConcurrentMap<String, Integer>) c.createCache("c3", null);
+ } catch (CacheExistException cee) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ } catch (CacheConfigException cce) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+
+ // Make sure the caches exist
+ Assert.assertTrue(c2 != null);
+ Assert.assertTrue(c3 != null);
+
+ // Put some fake data
+ c2.put("FOO", 11);
+ c2.put("BAZ", 22);
+ c2.put("BAR", 33);
+
+ c3.put("FOOBAR", 110);
+
+ // Test for cache existance
+ Assert.assertTrue(c.existCache("c1"));
+ Assert.assertTrue(c.existCache("c2"));
+ Assert.assertTrue(c.existCache("c3"));
+
+ // Get the Cache List
+ Set<String> caches = c.getCacheList();
+ Assert.assertTrue(caches != null);
+
+ // Check if the cachelist is correct
+ System.out.println("cache size:" + caches.size());
+ Assert.assertTrue(caches.size() == 3);
+ Assert.assertTrue(caches.contains("c1"));
+ Assert.assertTrue(caches.contains("c2"));
+ Assert.assertTrue(caches.contains("c3"));
+
+ // Check that the utility API for the cluster are working too
+ Assert.assertTrue(c.getCoordinatorAddress() != null);
+ Assert.assertTrue(c.getClusteredControllers() != null);
+ // This a one man-show
+ Assert.assertTrue(c.getClusteredControllers().size() == 1);
+ Assert.assertTrue(c.getMyAddress() != null);
+ // Make sure i'm the coordinator
+ Assert.assertTrue(c.amICoordinator());
+
+ // Now destroy some caches make sure they are gone
+ c.destroyCache("c1");
+ Assert.assertTrue(!c.existCache("c1"));
+ caches = c.getCacheList();
+ Assert.assertTrue(caches.size() == 2);
+
+ // Now recreate the cache, make sure a different one is
+ // retrieved, which should be empty
+ try {
+ c1 = (ConcurrentMap<String, Integer>) c.createCache("c1", null);
+ } catch (CacheExistException cee) {
+ // This exception should be raised because the cache
+ // already exists
+ Assert.assertTrue(true);
+ } catch (CacheConfigException cce) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+ c1 = (ConcurrentMap<String, Integer>) c.getCache("c1");
+ Assert.assertTrue(c1 != null);
+ Assert.assertTrue(c1.keySet().size() == 0);
+ caches = c.getCacheList();
+ Assert.assertTrue(caches.size() == 3);
+
+ // Now destroy the cache manager and make sure things are
+ // clean
+ cm.destroy();
+ caches = c.getCacheList();
+ Assert.assertTrue(caches.size() == 0);
+
+ // Now to re-create two caches and make sure they exists, but
+ // are different than in previous life
+ try {
+ c2 = (ConcurrentMap<String, Integer>) c.createCache("c2", null);
+ c3 = (ConcurrentMap<String, Integer>) c.createCache("c3", null);
+ } catch (CacheExistException cee) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ } catch (CacheConfigException cce) {
+ // Don't expect this assertion, so if happens signal a
+ // failure in the testcase
+ Assert.assertTrue(false);
+ }
+ Assert.assertTrue(c2 != null);
+ Assert.assertTrue(c3 != null);
+ caches = c.getCacheList();
+ Assert.assertTrue(caches.size() == 2);
+ Assert.assertTrue(c2.keySet().size() == 0);
+ Assert.assertTrue(c3.keySet().size() == 0);
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.test</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.slf4j,
+ javax.transaction,
+ org.eclipse.osgi.framework.console,
+ ch.qos.logback.classic,
+ org.opendaylight.controller.clustering.services
+ </Import-Package>
+ <Service-Component>
+ OSGI-INF/component.xml
+ </Service-Component>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * 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.clustering.test.internal;
+
+import java.io.Serializable;
+
+public class ComplexClass implements IComplex, Serializable {
+ private String identity;
+
+ public ComplexClass(String i) {
+ this.identity = i;
+ }
+
+ @Override
+ public String whoAmI() {
+ return ("ComplexClass_" + this.identity);
+ }
+
+ @Override
+ public void IAm(String s) {
+ this.identity = s;
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.test.internal;
+
+import java.io.Serializable;
+
+public class ComplexClass1 implements IComplex, Serializable {
+ private String identity;
+
+ public ComplexClass1(String i) {
+ this.identity = i;
+ }
+
+ @Override
+ public String whoAmI() {
+ return ("ComplexClass1_" + this.identity);
+ }
+
+ @Override
+ public void IAm(String s) {
+ this.identity = s;
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.test.internal;
+
+import java.io.Serializable;
+
+public class ComplexContainer implements Serializable {
+ private IComplex f;
+ private IComplex f1;
+ private Integer state;
+
+ public ComplexContainer(String i, Integer s) {
+ this.state = s;
+ this.f = new ComplexClass(i);
+ this.f1 = new ComplexClass1(i);
+ }
+
+ public String getIdentity() {
+ if (this.f != null && this.f1 != null) {
+ return ("[" + f.whoAmI() + "]-[" + f1.whoAmI() + "]");
+ }
+ return "<NOTSET>";
+ }
+
+ public void setIdentity(String i) {
+ if (this.f != null) {
+ this.f.IAm(i);
+ }
+ if (this.f1 != null) {
+ this.f1.IAm(i);
+ }
+ }
+
+ public Integer getState() {
+ return this.state;
+ }
+
+ @Override
+ public String toString() {
+ return ("{ID:" + this.getIdentity() + ",STATE:" + this.state + "}");
+ }
+}
--- /dev/null
+
+/*
+ * 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.clustering.test.internal;
+
+public interface IComplex {
+ String whoAmI();
+
+ void IAm(String s);
+}