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=5220ea29e28e8e74d22d0e45f2727646a896349e;hpb=c38bb5d90ebdb8e867cecc5b0dea0bfafe8ae621;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 5220ea29e2..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,94 +11,167 @@ 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.IdentityHashMap; import java.util.Map; -import org.opendaylight.controller.cluster.datastore.entityownership.messages.EntityOwnershipChanged; -import org.opendaylight.controller.md.sal.common.api.clustering.Entity; -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 Multimap entityListenerMap = HashMultimap.create(); + private final ReadWriteLock listenerLock = new ReentrantReadWriteLock(); - EntityOwnershipListenerSupport(ActorContext actorContext) { + @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; } - void addEntityOwnershipListener(Entity entity, EntityOwnershipListener listener) { - LOG.debug("Adding EntityOwnershipListener {} for {}", listener, entity); + @Override + String getLogId() { + return logId; + } - if(entityListenerMap.put(entity, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - if(listenerEntry == null) { - listenerActorMap.put(listener, new ListenerActorRefEntry()); - } else { - listenerEntry.referenceCount++; + /** + * 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(final String entityType, final DOMEntityOwnershipListener listener) { + LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, 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(Entity entity, EntityOwnershipListener listener) { - LOG.debug("Removing EntityOwnershipListener {} for {}", listener, entity); + void removeEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { + LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - if(entityListenerMap.remove(entity, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); + listenerLock.writeLock().lock(); + try { + if (entityTypeListenerMap.remove(entityType, listener)) { + ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - LOG.debug("Found {}", listenerEntry); + LOG.debug("{}: Found {}", logId, listenerEntry); - listenerEntry.referenceCount--; - if(listenerEntry.referenceCount <= 0) { - listenerActorMap.remove(listener); + listenerEntry.referenceCount--; + if (listenerEntry.referenceCount <= 0) { + listenerActorMap.remove(listener); - if(listenerEntry.actorRef != null) { - LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); - listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); + if (listenerEntry.actorRef != null) { + LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); + listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); + } } } + } finally { + listenerLock.writeLock().unlock(); + } + } + + @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(); } } - void notifyEntityOwnershipListeners(Entity entity, boolean wasOwner, boolean isOwner) { - Collection listeners = entityListenerMap.get(entity); - if(listeners.isEmpty()) { - return; + 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(); } + } - EntityOwnershipChanged changed = new EntityOwnershipChanged(entity, wasOwner, isOwner); - for(EntityOwnershipListener listener: listeners) { - ActorRef listenerActor = listenerActorFor(listener); + @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(); - LOG.debug("Notifying EntityOwnershipListenerActor {} with {}", listenerActor,changed); + LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed); 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 {}", actorRef, listener); + LOG.debug("{}: Created EntityOwnershipListenerActor {} for listener {}", logId, actorRef, listener); } return actorRef;