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