2 * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.cluster.entityownership;
10 import akka.actor.ActorContext;
11 import akka.actor.ActorRef;
12 import akka.actor.PoisonPill;
13 import com.google.common.collect.HashMultimap;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.Multimap;
16 import java.util.Collection;
17 import java.util.IdentityHashMap;
19 import java.util.concurrent.locks.ReadWriteLock;
20 import java.util.concurrent.locks.ReentrantReadWriteLock;
21 import java.util.stream.Collectors;
22 import org.checkerframework.checker.lock.qual.GuardedBy;
23 import org.checkerframework.checker.lock.qual.Holding;
24 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState;
25 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
26 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange;
27 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. This class is
35 * @author Thomas Pantelis
37 class EntityOwnershipListenerSupport extends EntityOwnershipChangePublisher {
38 private static final Logger LOG = LoggerFactory.getLogger(EntityOwnershipListenerSupport.class);
40 private final String logId;
41 private final ActorContext actorContext;
42 private final ReadWriteLock listenerLock = new ReentrantReadWriteLock();
44 @GuardedBy("listenerLock")
45 private final Map<DOMEntityOwnershipListener, ListenerActorRefEntry> listenerActorMap = new IdentityHashMap<>();
47 @GuardedBy("listenerLock")
48 private final Multimap<String, DOMEntityOwnershipListener> entityTypeListenerMap = HashMultimap.create();
50 private volatile boolean inJeopardy = false;
52 EntityOwnershipListenerSupport(final ActorContext actorContext, final String logId) {
53 this.actorContext = actorContext;
63 * Set the in-jeopardy flag and indicate its previous state.
65 * @param inJeopardy new value of the in-jeopardy flag
66 * @return Previous value of the flag.
68 @SuppressWarnings("checkstyle:hiddenField")
69 boolean setInJeopardy(final boolean inJeopardy) {
70 final boolean wasInJeopardy = this.inJeopardy;
71 this.inJeopardy = inJeopardy;
75 void addEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) {
76 LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, listener, entityType);
78 listenerLock.writeLock().lock();
80 if (entityTypeListenerMap.put(entityType, listener)) {
81 ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener);
82 if (listenerEntry == null) {
83 listenerActorMap.put(listener, new ListenerActorRefEntry(listener));
85 listenerEntry.referenceCount++;
89 listenerLock.writeLock().unlock();
93 void removeEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) {
94 LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType);
96 listenerLock.writeLock().lock();
98 if (entityTypeListenerMap.remove(entityType, listener)) {
99 ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener);
101 LOG.debug("{}: Found {}", logId, listenerEntry);
103 listenerEntry.referenceCount--;
104 if (listenerEntry.referenceCount <= 0) {
105 listenerActorMap.remove(listener);
107 if (listenerEntry.actorRef != null) {
108 LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef);
109 listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender());
114 listenerLock.writeLock().unlock();
119 void notifyEntityOwnershipListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner,
120 final boolean hasOwner) {
121 listenerLock.readLock().lock();
123 Collection<DOMEntityOwnershipListener> listeners = entityTypeListenerMap.get(entity.getType());
124 if (!listeners.isEmpty()) {
125 notifyListeners(entity, wasOwner, isOwner, hasOwner,
126 listeners.stream().map(listenerActorMap::get).collect(Collectors.toList()));
129 listenerLock.readLock().unlock();
133 void notifyEntityOwnershipListener(final DOMEntity entity, final boolean wasOwner, final boolean isOwner,
134 final boolean hasOwner, final DOMEntityOwnershipListener listener) {
135 listenerLock.readLock().lock();
137 notifyListeners(entity, wasOwner, isOwner, hasOwner, ImmutableList.of(listenerActorMap.get(listener)));
139 listenerLock.readLock().unlock();
143 @Holding("listenerLock")
144 private void notifyListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner,
145 final boolean hasOwner, final Collection<ListenerActorRefEntry> listenerEntries) {
146 DOMEntityOwnershipChange changed = new DOMEntityOwnershipChange(entity,
147 EntityOwnershipChangeState.from(wasOwner, isOwner, hasOwner), inJeopardy);
148 for (ListenerActorRefEntry entry: listenerEntries) {
149 ActorRef listenerActor = entry.actorFor();
151 LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed);
153 listenerActor.tell(changed, ActorRef.noSender());
157 private class ListenerActorRefEntry {
158 final DOMEntityOwnershipListener listener;
160 @GuardedBy("listenerLock")
163 @GuardedBy("listenerLock")
164 int referenceCount = 1;
166 ListenerActorRefEntry(final DOMEntityOwnershipListener listener) {
167 this.listener = listener;
170 ActorRef actorFor() {
171 if (actorRef == null) {
172 actorRef = actorContext.actorOf(EntityOwnershipListenerActor.props(listener));
174 LOG.debug("{}: Created EntityOwnershipListenerActor {} for listener {}", logId, actorRef, listener);
181 public String toString() {
182 return "ListenerActorRefEntry [actorRef=" + actorRef + ", referenceCount=" + referenceCount + "]";