Bump to odlparent-6.0.0-SNAPSHOT
[mdsal.git] / entityownership / mdsal-eos-dom-simple / src / main / java / org / opendaylight / mdsal / eos / dom / simple / SimpleDOMEntityOwnershipService.java
1 /*
2  * Copyright (c) 2017 Cisco 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.mdsal.eos.dom.simple;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState.LOCAL_OWNERSHIP_GRANTED;
12 import static org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NO_OWNER;
13
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.base.MoreObjects;
16 import com.google.common.base.MoreObjects.ToStringHelper;
17 import com.google.common.collect.ArrayListMultimap;
18 import com.google.common.collect.HashBasedTable;
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.Multimap;
21 import com.google.common.collect.Table;
22 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 import java.util.Collection;
24 import java.util.Optional;
25 import java.util.UUID;
26 import org.checkerframework.checker.lock.qual.GuardedBy;
27 import org.kohsuke.MetaInfServices;
28 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
29 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState;
30 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
31 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
32 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
33 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange;
34 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
35 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
36 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
37 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * Simple {@link DOMEntityOwnershipService} operating as an isolated island. It has no awareness of the world outside
44  * of itself.
45  *
46  * @author Robert Varga
47  */
48 @MetaInfServices
49 public final class SimpleDOMEntityOwnershipService implements DOMEntityOwnershipService {
50     private static final Logger LOG = LoggerFactory.getLogger(SimpleDOMEntityOwnershipService.class);
51
52     @GuardedBy("entities")
53     private final Table<String, YangInstanceIdentifier, DOMEntity> entities = HashBasedTable.create();
54
55     @GuardedBy("listeners")
56     private final Multimap<String, DOMEntityOwnershipListener> listeners = ArrayListMultimap.create(0, 1);
57
58     private final UUID uuid;
59
60     @VisibleForTesting
61     SimpleDOMEntityOwnershipService(final UUID uuid) {
62         this.uuid = requireNonNull(uuid);
63     }
64
65     public SimpleDOMEntityOwnershipService() {
66         this(UUID.randomUUID());
67     }
68
69     @Override
70     public DOMEntityOwnershipCandidateRegistration registerCandidate(final DOMEntity entity)
71             throws CandidateAlreadyRegisteredException {
72         synchronized (entities) {
73             final DOMEntity prev = entities.get(entity.getType(), entity.getIdentifier());
74             if (prev != null) {
75                 throw new CandidateAlreadyRegisteredException(prev);
76             }
77
78             entities.put(entity.getType(), entity.getIdentifier(), entity);
79             LOG.debug("{}: registered candidate {}", uuid, entity);
80         }
81
82         notifyListeners(entity, LOCAL_OWNERSHIP_GRANTED);
83         return new EntityRegistration(entity);
84     }
85
86     @Override
87     public DOMEntityOwnershipListenerRegistration registerListener(final String entityType,
88             final DOMEntityOwnershipListener listener) {
89
90         final Collection<DOMEntity> owned;
91         synchronized (entities) {
92             owned = ImmutableList.copyOf(entities.row(entityType).values());
93             LOG.trace("{}: acquired candidates {} for new listener {}", uuid, owned, listener);
94         }
95
96         synchronized (listeners) {
97             listeners.put(entityType, listener);
98         }
99
100         for (DOMEntity entity : owned) {
101             notifyListener(listener, new DOMEntityOwnershipChange(entity, LOCAL_OWNERSHIP_GRANTED));
102         }
103         LOG.debug("{}: registered listener {}", uuid, listener);
104         return new ListenerRegistration(entityType, listener);
105     }
106
107     @Override
108     public Optional<EntityOwnershipState> getOwnershipState(final DOMEntity forEntity) {
109         return isCandidateRegistered(forEntity) ? Optional.of(EntityOwnershipState.IS_OWNER) : Optional.empty();
110     }
111
112     @Override
113     public boolean isCandidateRegistered(final DOMEntity forEntity) {
114         synchronized (entities) {
115             return entities.contains(forEntity.getType(), forEntity.getIdentifier());
116         }
117     }
118
119     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
120             justification = "https://github.com/spotbugs/spotbugs/issues/811")
121     private void removeEntity(final DOMEntity entity) {
122         synchronized (entities) {
123             entities.remove(entity.getType(), entity.getIdentifier());
124             LOG.debug("{}: unregistered candidate {}", uuid, entity);
125         }
126
127         notifyListeners(entity, LOCAL_OWNERSHIP_LOST_NO_OWNER);
128     }
129
130     @SuppressWarnings("checkstyle:illegalCatch")
131     private void notifyListener(final DOMEntityOwnershipListener listener, final DOMEntityOwnershipChange change) {
132         try {
133             LOG.trace("{} notifying listener {} change {}", uuid, listener, change);
134             listener.ownershipChanged(change);
135         } catch (RuntimeException e) {
136             LOG.warn("{}: Listener {} change {} failed", uuid, listener, change, e);
137         }
138     }
139
140     private void notifyListeners(final DOMEntity entity, final EntityOwnershipChangeState state) {
141         final DOMEntityOwnershipChange change = new DOMEntityOwnershipChange(entity, state);
142
143         final Collection<DOMEntityOwnershipListener> snap;
144
145         synchronized (listeners) {
146             snap = ImmutableList.copyOf(listeners.get(entity.getType()));
147         }
148
149         for (DOMEntityOwnershipListener listener : snap) {
150             notifyListener(listener, change);
151         }
152     }
153
154     void unregisterListener(final ListenerRegistration reg) {
155         synchronized (listeners) {
156             listeners.remove(reg.getEntityType(), reg.getInstance());
157             LOG.debug("{}: unregistered listener {}", uuid, reg.getInstance());
158         }
159     }
160
161     @Override
162     public String toString() {
163         final ToStringHelper h = MoreObjects.toStringHelper(SimpleDOMEntityOwnershipService.class).add("uuid", uuid);
164
165         synchronized (entities) {
166             h.add("entities", entities);
167         }
168         synchronized (listeners) {
169             h.add("listeners", listeners);
170         }
171
172         return h.toString();
173     }
174
175     private final class EntityRegistration extends AbstractObjectRegistration<DOMEntity> implements
176             DOMEntityOwnershipCandidateRegistration {
177         EntityRegistration(final DOMEntity entity) {
178             super(entity);
179         }
180
181         @Override
182         protected void removeRegistration() {
183             removeEntity(getInstance());
184         }
185     }
186
187     private final class ListenerRegistration extends AbstractObjectRegistration<DOMEntityOwnershipListener>
188             implements DOMEntityOwnershipListenerRegistration {
189         private final String entityType;
190
191         ListenerRegistration(final String entityType, final DOMEntityOwnershipListener listener) {
192             super(listener);
193             this.entityType = requireNonNull(entityType);
194         }
195
196         @Override
197         public String getEntityType() {
198             return entityType;
199         }
200
201         @Override
202         protected void removeRegistration() {
203             unregisterListener(this);
204         }
205     }
206 }