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