X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2Fentityownership%2FEntityOwnershipListenerSupport.java;h=0c4a646efef47b84cd22d37c24a50b5608c32cff;hb=refs%2Fchanges%2F28%2F81128%2F25;hp=dd83e65f1d169a74175ee65fc8eeaf823a012e66;hpb=f2d49be57f39eeaf5b645cf0d9312b44c72e52ba;p=controller.git diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java index dd83e65f1d..0c4a646efe 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java @@ -11,133 +11,164 @@ import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.actor.PoisonPill; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Map; -import java.util.Set; -import org.opendaylight.controller.md.sal.common.api.clustering.Entity; -import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; -import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; +import org.checkerframework.checker.lock.qual.GuardedBy; +import org.checkerframework.checker.lock.qual.Holding; +import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState; +import org.opendaylight.mdsal.eos.dom.api.DOMEntity; +import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; +import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. + * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. This class is + * thread-safe. * * @author Thomas Pantelis */ -class EntityOwnershipListenerSupport { +class EntityOwnershipListenerSupport extends EntityOwnershipChangePublisher { private static final Logger LOG = LoggerFactory.getLogger(EntityOwnershipListenerSupport.class); private final String logId; private final ActorContext actorContext; - private final Map listenerActorMap = new IdentityHashMap<>(); - private final Set entitiesWithCandidateSet = new HashSet<>(); - private final Multimap entityTypeListenerMap = HashMultimap.create(); + private final ReadWriteLock listenerLock = new ReentrantReadWriteLock(); - EntityOwnershipListenerSupport(ActorContext actorContext, String logId) { + @GuardedBy("listenerLock") + private final Map listenerActorMap = new IdentityHashMap<>(); + + @GuardedBy("listenerLock") + private final Multimap entityTypeListenerMap = HashMultimap.create(); + + private volatile boolean inJeopardy = false; + + EntityOwnershipListenerSupport(final ActorContext actorContext, final String logId) { this.actorContext = actorContext; this.logId = logId; } + @Override String getLogId() { return logId; } - boolean hasCandidateForEntity(Entity entity) { - return entitiesWithCandidateSet.contains(entity); - } - - void setHasCandidateForEntity(Entity entity) { - entitiesWithCandidateSet.add(entity); - } - - void unsetHasCandidateForEntity(Entity entity) { - entitiesWithCandidateSet.remove(entity); + /** + * Set the in-jeopardy flag and indicate its previous state. + * + * @param inJeopardy new value of the in-jeopardy flag + * @return Previous value of the flag. + */ + @SuppressWarnings("checkstyle:hiddenField") + boolean setInJeopardy(final boolean inJeopardy) { + final boolean wasInJeopardy = this.inJeopardy; + this.inJeopardy = inJeopardy; + return wasInJeopardy; } - void addEntityOwnershipListener(String entityType, EntityOwnershipListener listener) { + void addEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - addListener(listener, entityType); + listenerLock.writeLock().lock(); + try { + if (entityTypeListenerMap.put(entityType, listener)) { + ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); + if (listenerEntry == null) { + listenerActorMap.put(listener, new ListenerActorRefEntry(listener)); + } else { + listenerEntry.referenceCount++; + } + } + } finally { + listenerLock.writeLock().unlock(); + } } - void removeEntityOwnershipListener(String entityType, EntityOwnershipListener listener) { + void removeEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - removeListener(listener, entityType); - } + listenerLock.writeLock().lock(); + try { + if (entityTypeListenerMap.remove(entityType, listener)) { + ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - void notifyEntityOwnershipListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) { - notifyListeners(entity, entity.getType(), wasOwner, isOwner, hasOwner); - } + LOG.debug("{}: Found {}", logId, listenerEntry); - void notifyEntityOwnershipListener(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, - EntityOwnershipListener listener) { - notifyListeners(entity, wasOwner, isOwner, hasOwner, Collections.singleton(listener)); - } + listenerEntry.referenceCount--; + if (listenerEntry.referenceCount <= 0) { + listenerActorMap.remove(listener); - private void notifyListeners(Entity entity, String mapKey, boolean wasOwner, boolean isOwner, boolean hasOwner) { - Collection listeners = entityTypeListenerMap.get(mapKey); - if(!listeners.isEmpty()) { - notifyListeners(entity, wasOwner, isOwner, hasOwner, listeners); + if (listenerEntry.actorRef != null) { + LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); + listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); + } + } + } + } finally { + listenerLock.writeLock().unlock(); } } - private void notifyListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, - Collection listeners) { - EntityOwnershipChange changed = new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner); - for(EntityOwnershipListener listener: listeners) { - ActorRef listenerActor = listenerActorFor(listener); - - LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed); - - listenerActor.tell(changed, ActorRef.noSender()); + @Override + void notifyEntityOwnershipListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner) { + listenerLock.readLock().lock(); + try { + Collection listeners = entityTypeListenerMap.get(entity.getType()); + if (!listeners.isEmpty()) { + notifyListeners(entity, wasOwner, isOwner, hasOwner, + listeners.stream().map(listenerActorMap::get).collect(Collectors.toList())); + } + } finally { + listenerLock.readLock().unlock(); } } - private void addListener(EntityOwnershipListener listener, String mapKey) { - if (entityTypeListenerMap.put(mapKey, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - if(listenerEntry == null) { - listenerActorMap.put(listener, new ListenerActorRefEntry()); - } else { - listenerEntry.referenceCount++; - } + void notifyEntityOwnershipListener(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner, final DOMEntityOwnershipListener listener) { + listenerLock.readLock().lock(); + try { + notifyListeners(entity, wasOwner, isOwner, hasOwner, ImmutableList.of(listenerActorMap.get(listener))); + } finally { + listenerLock.readLock().unlock(); } } - private void removeListener(EntityOwnershipListener listener, String mapKey) { - if (entityTypeListenerMap.remove(mapKey, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - - LOG.debug("{}: Found {}", logId, listenerEntry); + @Holding("listenerLock") + private void notifyListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner, final Collection listenerEntries) { + DOMEntityOwnershipChange changed = new DOMEntityOwnershipChange(entity, + EntityOwnershipChangeState.from(wasOwner, isOwner, hasOwner), inJeopardy); + for (ListenerActorRefEntry entry: listenerEntries) { + ActorRef listenerActor = entry.actorFor(); - listenerEntry.referenceCount--; - if(listenerEntry.referenceCount <= 0) { - listenerActorMap.remove(listener); + LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed); - if(listenerEntry.actorRef != null) { - LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); - listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); - } - } + listenerActor.tell(changed, ActorRef.noSender()); } } - private ActorRef listenerActorFor(EntityOwnershipListener listener) { - return listenerActorMap.get(listener).actorFor(listener); - } - private class ListenerActorRefEntry { + final DOMEntityOwnershipListener listener; + + @GuardedBy("listenerLock") ActorRef actorRef; + + @GuardedBy("listenerLock") int referenceCount = 1; - ActorRef actorFor(EntityOwnershipListener listener) { - if(actorRef == null) { + ListenerActorRefEntry(final DOMEntityOwnershipListener listener) { + this.listener = listener; + } + + ActorRef actorFor() { + if (actorRef == null) { actorRef = actorContext.actorOf(EntityOwnershipListenerActor.props(listener)); LOG.debug("{}: Created EntityOwnershipListenerActor {} for listener {}", logId, actorRef, listener);