Bug 4105: Add hasOwner param to EntityOwnershipListener#ownershipChanged
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / entityownership / EntityOwnershipListenerSupport.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.datastore.entityownership;
9
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.Multimap;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.IdentityHashMap;
18 import java.util.Map;
19 import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
20 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidate;
21 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
22 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard.
28  *
29  * @author Thomas Pantelis
30  */
31 class EntityOwnershipListenerSupport {
32     private static final Logger LOG = LoggerFactory.getLogger(EntityOwnershipListenerSupport.class);
33
34     private final String logId;
35     private final ActorContext actorContext;
36     private final Map<EntityOwnershipListener, ListenerActorRefEntry> listenerActorMap = new IdentityHashMap<>();
37     private final Multimap<Entity, EntityOwnershipListener> entityListenerMap = HashMultimap.create();
38     private final Multimap<String, EntityOwnershipListener> entityTypeListenerMap = HashMultimap.create();
39
40     EntityOwnershipListenerSupport(ActorContext actorContext, String logId) {
41         this.actorContext = actorContext;
42         this.logId = logId;
43     }
44
45     String getLogId() {
46         return logId;
47     }
48
49     boolean hasCandidateForEntity(Entity entity) {
50         for(EntityOwnershipListener listener: entityListenerMap.get(entity)) {
51             if(listener instanceof EntityOwnershipCandidate) {
52                 return true;
53             }
54         }
55
56         return false;
57     }
58
59     void addEntityOwnershipListener(Entity entity, EntityOwnershipListener listener) {
60         LOG.debug("{}: Adding EntityOwnershipListener {} for {}", logId, listener, entity);
61
62         addListener(listener, entity, entityListenerMap);
63     }
64
65     void addEntityOwnershipListener(String entityType, EntityOwnershipListener listener) {
66         LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, listener, entityType);
67
68         addListener(listener, entityType, entityTypeListenerMap);
69     }
70
71     void removeEntityOwnershipListener(Entity entity, EntityOwnershipListener listener) {
72         LOG.debug("{}: Removing EntityOwnershipListener {} for {}", logId, listener, entity);
73
74         removeListener(listener, entity, entityListenerMap);
75     }
76
77     void removeEntityOwnershipListener(String entityType, EntityOwnershipListener listener) {
78         LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType);
79
80         removeListener(listener, entityType, entityTypeListenerMap);
81     }
82
83     void notifyEntityOwnershipListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) {
84         notifyListeners(entity, entity, wasOwner, isOwner, hasOwner, entityListenerMap);
85         notifyListeners(entity, entity.getType(), wasOwner, isOwner, hasOwner, entityTypeListenerMap);
86     }
87
88     void notifyEntityOwnershipListener(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner,
89             EntityOwnershipListener listener) {
90         notifyListeners(entity, wasOwner, isOwner, hasOwner, Arrays.asList(listener));
91     }
92
93     private <T> void notifyListeners(Entity entity, T mapKey, boolean wasOwner, boolean isOwner, boolean hasOwner,
94             Multimap<T, EntityOwnershipListener> listenerMap) {
95         Collection<EntityOwnershipListener> listeners = listenerMap.get(mapKey);
96         if(!listeners.isEmpty()) {
97             notifyListeners(entity, wasOwner, isOwner, hasOwner, listeners);
98         }
99     }
100
101     private void notifyListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner,
102             Collection<EntityOwnershipListener> listeners) {
103         EntityOwnershipChange changed = new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner);
104         for(EntityOwnershipListener listener: listeners) {
105             ActorRef listenerActor = listenerActorFor(listener);
106
107             LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed);
108
109             listenerActor.tell(changed, ActorRef.noSender());
110         }
111     }
112
113     private <T> void addListener(EntityOwnershipListener listener, T mapKey,
114             Multimap<T, EntityOwnershipListener> toListenerMap) {
115         if(toListenerMap.put(mapKey, listener)) {
116             ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener);
117             if(listenerEntry == null) {
118                 listenerActorMap.put(listener, new ListenerActorRefEntry());
119             } else {
120                 listenerEntry.referenceCount++;
121             }
122         }
123     }
124
125     private <T> void removeListener(EntityOwnershipListener listener, T mapKey,
126             Multimap<T, EntityOwnershipListener> fromListenerMap) {
127         if(fromListenerMap.remove(mapKey, listener)) {
128             ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener);
129
130             LOG.debug("{}: Found {}", logId, listenerEntry);
131
132             listenerEntry.referenceCount--;
133             if(listenerEntry.referenceCount <= 0) {
134                 listenerActorMap.remove(listener);
135
136                 if(listenerEntry.actorRef != null) {
137                     LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef);
138                     listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender());
139                 }
140             }
141         }
142     }
143
144     private ActorRef listenerActorFor(EntityOwnershipListener listener) {
145         return listenerActorMap.get(listener).actorFor(listener);
146     }
147
148     private class ListenerActorRefEntry {
149         ActorRef actorRef;
150         int referenceCount = 1;
151
152         ActorRef actorFor(EntityOwnershipListener listener) {
153             if(actorRef == null) {
154                 actorRef = actorContext.actorOf(EntityOwnershipListenerActor.props(listener));
155
156                 LOG.debug("{}: Created EntityOwnershipListenerActor {} for listener {}", logId, actorRef, listener);
157             }
158
159             return actorRef;
160         }
161
162         @Override
163         public String toString() {
164             return "ListenerActorRefEntry [actorRef=" + actorRef + ", referenceCount=" + referenceCount + "]";
165         }
166     }
167 }