Add common entity ownership interfaces 20/27620/15
authorTom Pantelis <tpanteli@brocade.com>
Mon, 28 Sep 2015 04:41:37 +0000 (00:41 -0400)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 18 Nov 2015 20:04:03 +0000 (20:04 +0000)
Added genericized entity ownership interfaces that are common between
the DOM and binding interfaces.

The instance identifier type in Entity is what differs between DOM and binding
but it's referenced in most the classes. For the classes that were
genericized, I prefixed then with "Generic" for lack of another/better
name. The intent is to keep the previous class names for the binding
versions (eg Entity, EntityOwnershipService etc), as they will be used most
commonly, and prefix the DOM versions with "DOM" to be consistent with other
md-sal APIs (eg DataBroker). The common classes for the data broker are
prefixed with "Async" but that doesn't really mean anything here so I went
with "Generic".

The other option is to keep the previous class names for the common API
and prefix the derived classes with "Binding" and "DOM" appropriately.
But we've tried to hide the binding concept as mauch as possible so
prefixing with "Binding" would add noise.

Change-Id: I00013f61f7c848c99fdc30740c8ab323448cc976
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
common/mdsal-common-api/pom.xml
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/CandidateAlreadyRegisteredException.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeState.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipState.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntity.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipCandidateRegistration.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipChange.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListener.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListenerRegistration.java [new file with mode: 0644]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipService.java [new file with mode: 0644]
common/mdsal-common-api/src/test/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeStateTest.java [new file with mode: 0644]

index cf8a2c15d5e41ebdb5a415146fe52e80ad005d8a..11e1ee5a7b0d0ded08b7eed82aca858a9b086056 100644 (file)
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-common</artifactId>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <scm>
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/CandidateAlreadyRegisteredException.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/CandidateAlreadyRegisteredException.java
new file mode 100644 (file)
index 0000000..7dfb407
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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.mdsal.common.api.clustering;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Thrown when a Candidate has already been registered for a given Entity. This could be due to a component doing a
+ * duplicate registration or two different components within the same process trying to register a Candidate.
+ */
+public class CandidateAlreadyRegisteredException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    private final GenericEntity<?> entity;
+
+    public <T extends Path<T>> CandidateAlreadyRegisteredException(@Nonnull GenericEntity<T> entity) {
+        super(String.format("Candidate has already been registered for %s",
+                Preconditions.checkNotNull(entity, "entity should not be null")));
+        this.entity = entity;
+    }
+
+    /**
+     * @return the entity for which a Candidate has already been registered in the current process.
+     *
+     * @param <T> the instance identifier path type
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull
+    public <T extends Path<T>> GenericEntity<T> getEntity() {
+        return (GenericEntity<T>) entity;
+    }
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeState.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeState.java
new file mode 100644 (file)
index 0000000..4dbb629
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Map;
+
+/**
+ * Enumerates the ownership change states for an entity.
+ *
+ * @author Thomas Pantelis
+ */
+public enum EntityOwnershipChangeState {
+    /**
+     * The local process instance has been granted ownership.
+     */
+    LOCAL_OWNERSHIP_GRANTED(false, true, true),
+
+    /**
+     * The local process instance has lost ownership and another process instance is now the owner.
+     */
+    LOCAL_OWNERSHIP_LOST_NEW_OWNER(true, false, true),
+
+    /**
+     * The local process instance has lost ownership and there are no longer any candidates for the entity and
+     * thus has no owner.
+     */
+    LOCAL_OWNERSHIP_LOST_NO_OWNER(true, false, false),
+
+    /**
+     * Entity ownership has transitioned to another process instance and this instance was not the previous owner.
+     */
+    REMOTE_OWNERSHIP_CHANGED(false, false, true),
+
+    /**
+     * A remote process instance has lost ownership and there are no longer any candidates for the entity and
+     * thus has no owner.
+     */
+    REMOTE_OWNERSHIP_LOST_NO_OWNER(false, false, false);
+
+    private static final Map<Key, EntityOwnershipChangeState> BY_KEY;
+    static {
+        Builder<Key, EntityOwnershipChangeState> builder = ImmutableMap.<Key, EntityOwnershipChangeState>builder();
+        for(EntityOwnershipChangeState e: values()) {
+            builder.put(new Key(e.wasOwner, e.isOwner, e.hasOwner), e);
+        }
+
+        BY_KEY = builder.build();
+    }
+
+    private final boolean wasOwner;
+    private final boolean isOwner;
+    private final boolean hasOwner;
+
+    private EntityOwnershipChangeState(boolean wasOwner, boolean isOwner, boolean hasOwner) {
+        this.wasOwner = wasOwner;
+        this.isOwner = isOwner;
+        this.hasOwner = hasOwner;
+    }
+
+    /**
+     * Returns the previous ownership status of the entity for this process instance.
+     * @return true if this process was the owner of the entity at the time this notification was generated
+     */
+    public boolean wasOwner() {
+        return wasOwner;
+    }
+
+    /**
+     * Returns the current ownership status of the entity for this process instance.
+     * @return true if this process is now the owner of the entity
+     */
+    public boolean isOwner() {
+        return isOwner;
+    }
+
+    /**
+     * Returns the current ownership status of the entity across all process instances.
+     * @return true if the entity has an owner which may or may not be this process. If false, then
+     *         the entity has no candidates and thus no owner.
+     */
+    public boolean hasOwner() {
+        return hasOwner;
+    }
+
+    @Override
+    public String toString() {
+        return name() + " [wasOwner=" + wasOwner + ", isOwner=" + isOwner + ", hasOwner=" + hasOwner + "]";
+    }
+
+    public static EntityOwnershipChangeState from(boolean wasOwner, boolean isOwner, boolean hasOwner) {
+        EntityOwnershipChangeState state = BY_KEY.get(new Key(wasOwner, isOwner, hasOwner));
+        Preconditions.checkArgument(state != null, "Invalid combination of wasOwner: %s, isOwner: %s, hasOwner: %s",
+                wasOwner, isOwner, hasOwner);
+        return state;
+    }
+
+    private static final class Key {
+        private final boolean wasOwner;
+        private final boolean isOwner;
+        private final boolean hasOwner;
+
+        Key(boolean wasOwner, boolean isOwner, boolean hasOwner) {
+            this.wasOwner = wasOwner;
+            this.isOwner = isOwner;
+            this.hasOwner = hasOwner;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + (hasOwner ? 1231 : 1237);
+            result = prime * result + (isOwner ? 1231 : 1237);
+            result = prime * result + (wasOwner ? 1231 : 1237);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            Key other = (Key) obj;
+            return hasOwner == other.hasOwner && isOwner == other.isOwner && wasOwner == other.wasOwner;
+        }
+    }
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipState.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipState.java
new file mode 100644 (file)
index 0000000..fcd7d84
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+/**
+ * Enumerates the current ownership state for an entity.
+ *
+ * @author Thomas Pantelis
+ */
+public enum EntityOwnershipState {
+    /**
+     * The local process instance is the owner of the entity.
+     */
+    IS_OWNER,
+
+    /**
+     * A remote process instance is the owner of the entity.
+     */
+    OWNED_BY_OTHER,
+
+    /**
+     * The entity has no owner and thus no candidates.
+     */
+    NO_OWNER;
+
+    public static EntityOwnershipState from(boolean isOwner, boolean hasOwner) {
+        if(isOwner) {
+            return IS_OWNER;
+        } else if(hasOwner) {
+            return OWNED_BY_OTHER;
+        } else {
+            return NO_OWNER;
+        }
+    }
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntity.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntity.java
new file mode 100644 (file)
index 0000000..9cd856d
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A clustered Entity is something which is shared by multiple applications across a cluster. An Entity has a type
+ * and an identifier.
+ * <p>
+ * The type describes the type of the Entity where examples of a type maybe "openflow" or "netconf"
+ * etc. An Entity type could be tied to how exactly an application shares and "owns" an entity. For example we may want
+ * an application which deals with the openflow entity to be assigned ownership of that entity based on a first come
+ * first served basis. On the other hand for netconf entity types we may want applications to gain ownership based on
+ * a load balancing approach. While this mechanism of assigning a ownership acquisition strategy is not finalized the
+ * intention is that the entity type will play a role in determining the strategy and thus should be put in place.
+ * <p>
+ * The identifier is an instance identifier path. The reason for the choice of instance identifier path is because it
+ * can easily be used to represent a data node. For example an inventory node represents a shared entity and it is best
+ * referenced by its instance identifier path if the inventory node is stored in the data store.
+ * <p>
+ * Note that an entity identifier must conform to a valid yang schema. If there is no existing yang schema to
+ * represent an entity, the general-entity yang model can be used.
+ * <p>
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <T> the entity identifier type
+ */
+public class GenericEntity<T extends Path<T>> implements Serializable, Identifiable<T> {
+    private static final long serialVersionUID = 1L;
+
+    private final String type;
+    private final T id;
+
+    protected GenericEntity(@Nonnull String type, @Nonnull T id) {
+        this.type = Preconditions.checkNotNull(type, "type should not be null");
+        this.id = Preconditions.checkNotNull(id, "id should not be null");
+    }
+
+    /**
+     * @return the id of entity.
+     */
+    @Nonnull
+    @Override
+    public final T getIdentifier() {
+        return id;
+    }
+
+    /**
+     * @return the type of entity.
+     */
+    @Nonnull
+    public final String getType(){
+        return type;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        GenericEntity<T> entity = (GenericEntity<T>) o;
+
+        if (!id.equals(entity.id)) {
+            return false;
+        }
+
+        if (!type.equals(entity.type)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * type.hashCode() + id.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [type=" + type + ", id=" + id + "]";
+    }
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipCandidateRegistration.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipCandidateRegistration.java
new file mode 100644 (file)
index 0000000..669d1f9
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * An interface that records a request to register a Candidate for a given Entity. Calling close on the
+ * registration will remove the Candidate from any future ownership considerations for that Entity.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <P> the instance identifier type
+ * @param <E> the GenericEntity type
+ */
+public interface GenericEntityOwnershipCandidateRegistration<P extends Path<P>, E extends GenericEntity<P>>
+        extends ObjectRegistration<E> {
+
+   /**
+    * Unregister the candidate
+    */
+   @Override
+   void close();
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipChange.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipChange.java
new file mode 100644 (file)
index 0000000..e70aa5d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A DTO that encapsulates an ownership change for an entity.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <P> the instance identifier path type
+ * @param <E> the GenericEntity type
+ */
+public class GenericEntityOwnershipChange<P extends Path<P>, E extends GenericEntity<P>> {
+    private final E entity;
+    private final EntityOwnershipChangeState state;
+
+    public GenericEntityOwnershipChange(@Nonnull E entity, @Nonnull EntityOwnershipChangeState state) {
+        this.entity = Preconditions.checkNotNull(entity, "entity can't be null");
+        this.state = Preconditions.checkNotNull(state, "state can't be null");
+    }
+
+    /**
+     * Returns the entity whose ownership status changed.
+     * @return the entity
+     */
+    @Nonnull public E getEntity() {
+        return entity;
+    }
+
+    /**
+     * Returns the ownership change state.
+     * @return an EntityOwnershipChangeState enum
+     */
+    @Nonnull public EntityOwnershipChangeState getState() {
+        return state;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [entity=" + entity + ", state=" + state + "]";
+    }
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListener.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListener.java
new file mode 100644 (file)
index 0000000..e1b10d1
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * An interface for a class that listens for entity ownership changes.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <P> the instance identifier path type
+ * @param <C> the GenericEntityOwnershipChange type
+ */
+public interface GenericEntityOwnershipListener<P extends Path<P>,
+        C extends GenericEntityOwnershipChange<P, ? extends GenericEntity<P>>> {
+
+    /**
+     * A notification that is generated when the ownership status of an entity changes.
+     *
+     * @param ownershipChange contains the entity and its ownership change state
+     */
+    void ownershipChanged(C ownershipChange);
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListenerRegistration.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipListenerRegistration.java
new file mode 100644 (file)
index 0000000..b7671b7
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * An interface that records a request to register a ownership status change listener for a given Entity.
+ * Calling close on the registration will unregister listeners and future ownership changes will not
+ * be delivered to the listener.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <P> the instance identifier path type
+ */
+public interface GenericEntityOwnershipListenerRegistration<P extends Path<P>,
+        L extends GenericEntityOwnershipListener<P, ? extends GenericEntityOwnershipChange<P, ? extends GenericEntity<P>>>>
+            extends ObjectRegistration<L> {
+
+    /**
+     * @return the entity type that the listener was registered for
+     */
+    @Nonnull String getEntityType();
+
+    /**
+     * Unregister the listener
+     */
+    @Override
+    void close();
+}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipService.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/clustering/GenericEntityOwnershipService.java
new file mode 100644 (file)
index 0000000..81bed04
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * <p>
+ * An interface that provides the means for a component/application to request ownership for a given
+ * Entity on the current cluster member. Entity ownership is always tied to a process and two components on the same
+ * process cannot register a candidate for a given Entity.
+ * </p>
+ * <p>
+ * A component/application may also register interest in the ownership status of an Entity. The listener would be
+ * notified whenever the ownership status changes.
+ * </p>
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <P> the instance identifier path type
+ * @param <E> the GenericEntity type
+ */
+public interface GenericEntityOwnershipService<P extends Path<P>, E extends GenericEntity<P>,
+        L extends GenericEntityOwnershipListener<P, ? extends GenericEntityOwnershipChange<P, E>>> {
+
+    /**
+     * Registers a candidate for ownership of the given entity. Only one such request can be made per entity
+     * per process. If multiple requests for registering a candidate for a given entity are received in the
+     * current process a CandidateAlreadyRegisteredException will be thrown.
+     * <p>
+     * The registration is performed asynchronously and any registered entity ownership listener is
+     * notified of ownership status changes for the entity.
+     *
+     * @param entity the entity which the Candidate wants to own
+     * @return a registration object that can be used to unregister the Candidate
+     * @throws CandidateAlreadyRegisteredException if the candidate was already registered
+     */
+    GenericEntityOwnershipCandidateRegistration<P, E> registerCandidate(@Nonnull E entity)
+            throws CandidateAlreadyRegisteredException;
+
+    /**
+     * Registers a listener that is interested in ownership changes for entities of the given entity type. The
+     * listener is notified whenever its process instance is granted ownership of the entity and also whenever
+     * it loses ownership. On registration the listener will be notified of all entities its process instance
+     * currently owns at the time of registration.
+     *
+     * @param entityType the type of entities whose ownership status the Listener is interested in
+     * @param listener the listener that is interested in the entities
+     * @return a registration object that can be used to unregister the Listener
+     */
+    GenericEntityOwnershipListenerRegistration<P, L> registerListener(@Nonnull String entityType, @Nonnull L listener);
+
+    /**
+     * Gets the current ownership state information for an entity.
+     *
+     * @param forEntity the entity to query.
+     * @return an Optional EntityOwnershipState whose instance is present if the entity is found
+     */
+    Optional<EntityOwnershipState> getOwnershipState(@Nonnull E forEntity);
+}
diff --git a/common/mdsal-common-api/src/test/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeStateTest.java b/common/mdsal-common-api/src/test/java/org/opendaylight/mdsal/common/api/clustering/EntityOwnershipChangeStateTest.java
new file mode 100644 (file)
index 0000000..3ad043c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.mdsal.common.api.clustering;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Unit tests for EntityOwnershipChangeState.
+ *
+ * @author Thomas Pantelis
+ */
+public class EntityOwnershipChangeStateTest {
+
+    @Test
+    public void testFromWithValid() {
+        assertEquals("from(false, true, true)", EntityOwnershipChangeState.LOCAL_OWNERSHIP_GRANTED,
+                EntityOwnershipChangeState.from(false, true, true));
+        assertEquals("from(true, false, true)", EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NEW_OWNER,
+                EntityOwnershipChangeState.from(true, false, true));
+        assertEquals("from(true, false, false)", EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NO_OWNER,
+                EntityOwnershipChangeState.from(true, false, false));
+        assertEquals("from(false, false, true)", EntityOwnershipChangeState.REMOTE_OWNERSHIP_CHANGED,
+                EntityOwnershipChangeState.from(false, false, true));
+        assertEquals("from(false, false, false)", EntityOwnershipChangeState.REMOTE_OWNERSHIP_LOST_NO_OWNER,
+                EntityOwnershipChangeState.from(false, false, false));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFromWithInvalidFalseTrueFalse() {
+        EntityOwnershipChangeState.from(false, true, false);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFromWithInvalidTrueTrueFalse() {
+        EntityOwnershipChangeState.from(true, true, false);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFromWithInvalidTrueTrueTrue() {
+        EntityOwnershipChangeState.from(true, true, true);
+    }
+}
\ No newline at end of file