Do not use ListenerRegistration
[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.EntityOwnershipStateChange.LOCAL_OWNERSHIP_GRANTED;
12 import static org.opendaylight.mdsal.eos.common.api.EntityOwnershipStateChange.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.collect.ArrayListMultimap;
17 import com.google.common.collect.HashBasedTable;
18 import com.google.common.collect.Multimap;
19 import com.google.common.collect.Table;
20 import java.util.List;
21 import java.util.Optional;
22 import java.util.UUID;
23 import org.checkerframework.checker.lock.qual.GuardedBy;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.kohsuke.MetaInfServices;
26 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
27 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
28 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipStateChange;
29 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
30 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
31 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
32 import org.opendaylight.mdsal.eos.dom.simple.di.LocalDOMEntityOwnershipService;
33 import org.opendaylight.yangtools.concepts.AbstractRegistration;
34 import org.opendaylight.yangtools.concepts.Registration;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.osgi.service.component.annotations.Activate;
37 import org.osgi.service.component.annotations.Component;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 @Component
42 @MetaInfServices
43 public sealed class SimpleDOMEntityOwnershipService implements DOMEntityOwnershipService
44         permits LocalDOMEntityOwnershipService {
45     private static final Logger LOG = LoggerFactory.getLogger(SimpleDOMEntityOwnershipService.class);
46
47     @GuardedBy("entities")
48     private final Table<String, YangInstanceIdentifier, DOMEntity> entities = HashBasedTable.create();
49
50     @GuardedBy("listeners")
51     private final Multimap<String, DOMEntityOwnershipListener> listeners = ArrayListMultimap.create(0, 1);
52
53     private final UUID uuid;
54
55     @Activate
56     public SimpleDOMEntityOwnershipService() {
57         this(UUID.randomUUID());
58     }
59
60     @VisibleForTesting
61     SimpleDOMEntityOwnershipService(final UUID uuid) {
62         this.uuid = requireNonNull(uuid);
63     }
64
65     @Override
66     public Registration registerCandidate(final DOMEntity entity) throws CandidateAlreadyRegisteredException {
67         synchronized (entities) {
68             final var prev = entities.get(entity.getType(), entity.getIdentifier());
69             if (prev != null) {
70                 throw new CandidateAlreadyRegisteredException(prev);
71             }
72
73             entities.put(entity.getType(), entity.getIdentifier(), entity);
74             LOG.debug("{}: registered candidate {}", uuid, entity);
75         }
76
77         notifyListeners(entity, LOCAL_OWNERSHIP_GRANTED);
78         return new AbstractRegistration() {
79             @Override
80             protected void removeRegistration() {
81                 removeEntity(entity);
82             }
83         };
84     }
85
86     @Override
87     public Registration registerListener(final String entityType, final DOMEntityOwnershipListener listener) {
88         final List<DOMEntity> owned;
89         synchronized (entities) {
90             owned = List.copyOf(entities.row(entityType).values());
91             LOG.trace("{}: acquired candidates {} for new listener {}", uuid, owned, listener);
92         }
93
94         synchronized (listeners) {
95             listeners.put(entityType, listener);
96         }
97
98         for (var entity : owned) {
99             notifyListener(listener, entity, LOCAL_OWNERSHIP_GRANTED);
100         }
101         LOG.debug("{}: registered listener {}", uuid, listener);
102         return new AbstractRegistration() {
103             @Override
104             protected void removeRegistration() {
105                 synchronized (listeners) {
106                     listeners.remove(entityType, listener);
107                     LOG.debug("{}: unregistered listener {}", uuid, listener);
108                 }
109             }
110         };
111     }
112
113     @Override
114     public Optional<EntityOwnershipState> getOwnershipState(final DOMEntity forEntity) {
115         return isCandidateRegistered(forEntity) ? Optional.of(EntityOwnershipState.IS_OWNER) : Optional.empty();
116     }
117
118     @Override
119     public boolean isCandidateRegistered(final DOMEntity forEntity) {
120         synchronized (entities) {
121             return entities.contains(forEntity.getType(), forEntity.getIdentifier());
122         }
123     }
124
125     private void removeEntity(final DOMEntity entity) {
126         synchronized (entities) {
127             entities.remove(entity.getType(), entity.getIdentifier());
128             LOG.debug("{}: unregistered candidate {}", uuid, entity);
129         }
130
131         notifyListeners(entity, LOCAL_OWNERSHIP_LOST_NO_OWNER);
132     }
133
134     @SuppressWarnings("checkstyle:illegalCatch")
135     private void notifyListener(final DOMEntityOwnershipListener listener, final @NonNull DOMEntity entity,
136             final @NonNull EntityOwnershipStateChange change) {
137         try {
138             LOG.trace("{} notifying listener {} change {}", uuid, listener, change);
139             listener.ownershipChanged(entity, change, false);
140         } catch (RuntimeException e) {
141             LOG.warn("{}: Listener {} failed on {} change {}", uuid, listener, entity, change, e);
142         }
143     }
144
145     private void notifyListeners(final DOMEntity entity, final EntityOwnershipStateChange state) {
146         final List<DOMEntityOwnershipListener> snap;
147         synchronized (listeners) {
148             snap = List.copyOf(listeners.get(entity.getType()));
149         }
150
151         for (var listener : snap) {
152             notifyListener(listener, entity, state);
153         }
154     }
155
156     @Override
157     public String toString() {
158         final var helper = MoreObjects.toStringHelper(SimpleDOMEntityOwnershipService.class).add("uuid", uuid);
159         synchronized (entities) {
160             helper.add("entities", entities);
161         }
162         synchronized (listeners) {
163             helper.add("listeners", listeners);
164         }
165         return helper.toString();
166     }
167 }