2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.client.mdsal.spi;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.HashMultimap;
14 import com.google.common.collect.Multimap;
15 import java.util.Arrays;
16 import java.util.Collection;
18 import java.util.Objects;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.dom.api.DOMNotification;
21 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
22 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
23 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
24 import org.opendaylight.yangtools.concepts.AbstractRegistration;
25 import org.opendaylight.yangtools.concepts.ListenerRegistration;
26 import org.opendaylight.yangtools.concepts.Registration;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 public final class NetconfDeviceNotificationService implements DOMNotificationService {
32 private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceNotificationService.class);
34 private final Multimap<Absolute, DOMNotificationListener> listeners = HashMultimap.create();
36 // Notification publish is very simple and hijacks the thread of the caller
37 // TODO: should we not reuse the implementation for notification router from mdsal-dom-broker ?
38 @SuppressWarnings("checkstyle:IllegalCatch")
39 public synchronized void publishNotification(final DOMNotification notification) {
40 for (var listener : listeners.get(notification.getType())) {
42 listener.onNotification(notification);
43 } catch (Exception e) {
44 LOG.warn("Listener {} threw an uncaught exception during processing notification {}", listener,
51 public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
52 final Collection<Absolute> types) {
53 final var lsnr = requireNonNull(listener);
54 final var typesArray = types.stream().map(Objects::requireNonNull).distinct().toArray(Absolute[]::new);
55 return switch (typesArray.length) {
56 case 0 -> new AbstractListenerRegistration<>(lsnr) {
58 protected void removeRegistration() {
62 case 1 -> registerOne(lsnr, typesArray[0]);
63 default -> registerMultiple(lsnr, typesArray);
68 public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
69 final Absolute... types) {
70 return registerNotificationListener(listener, Arrays.asList(types));
74 public Registration registerNotificationListeners(final Map<Absolute, DOMNotificationListener> typeToListener) {
75 final var copy = Map.copyOf(typeToListener);
76 return switch (copy.size()) {
81 final var entry = copy.entrySet().iterator().next();
82 yield registerOne(entry.getValue(), entry.getKey());
84 default -> registerMultiple(copy);
89 synchronized int size() {
90 return listeners.size();
93 private synchronized <T extends DOMNotificationListener> @NonNull ListenerRegistration<T> registerOne(
94 final @NonNull T listener, final Absolute type) {
95 listeners.put(type, listener);
96 return new AbstractListenerRegistration<>(listener) {
98 protected void removeRegistration() {
99 synchronized (NetconfDeviceNotificationService.this) {
100 listeners.remove(type, getInstance());
106 private synchronized <T extends DOMNotificationListener> @NonNull ListenerRegistration<T> registerMultiple(
107 final @NonNull T listener, final Absolute[] types) {
108 for (var type : types) {
109 listeners.put(type, listener);
111 return new AbstractListenerRegistration<>(listener) {
113 protected void removeRegistration() {
114 synchronized (NetconfDeviceNotificationService.this) {
115 for (var type : types) {
116 listeners.remove(type, getInstance());
123 private synchronized @NonNull Registration registerMultiple(final Map<Absolute, DOMNotificationListener> toReg) {
124 // we have at least two entries, which we will save as an array of 4 objects
126 final var array = new Object[toReg.size() * 2];
127 for (var entry : toReg.entrySet()) {
128 final var type = entry.getKey();
129 final var listener = entry.getValue();
131 listeners.put(type, listener);
133 array[idx++] = listener;
136 return new AbstractRegistration() {
138 protected void removeRegistration() {
139 synchronized (NetconfDeviceNotificationService.this) {
140 for (int i = 0, length = array.length; i < length; ) {
141 final var type = array[i++];
142 final var listener = array[i++];
143 if (!listeners.remove(type, listener)) {
144 LOG.warn("Failed to remove {} listener {}, very weird", type, listener, new Throwable());