BUG-1120: optimize notification delivery fast path
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / impl / ListenerMapGeneration.java
1 /**
2  * Copyright (c) 2014 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.controller.sal.binding.impl;
9
10 import java.util.Arrays;
11 import java.util.Collection;
12 import java.util.HashSet;
13 import java.util.Set;
14
15 import org.opendaylight.yangtools.yang.binding.Notification;
16
17 import com.google.common.base.Predicate;
18 import com.google.common.cache.CacheBuilder;
19 import com.google.common.cache.CacheLoader;
20 import com.google.common.cache.LoadingCache;
21 import com.google.common.collect.ImmutableMultimap;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Multimap;
25
26 /**
27  * An immutable view of the current generation of listeners.
28  */
29 final class ListenerMapGeneration {
30     private static final int CACHE_MAX_ENTRIES = 1000;
31
32     /**
33      * Constant map of notification type to subscribed listeners.
34      */
35     private final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> typeToListeners;
36
37     /**
38      * Dynamic cache of notification implementation to matching listeners. This cache loads entries based on
39      * the contents of the {@link #typeToListeners} map.
40      */
41     private final LoadingCache<Class<?>, Iterable<NotificationListenerRegistration<?>>> implementationToListeners =
42             CacheBuilder.newBuilder()
43             .weakKeys()
44             .maximumSize(CACHE_MAX_ENTRIES)
45             .build(new CacheLoader<Class<?>, Iterable<NotificationListenerRegistration<?>>>() {
46                 @Override
47                 public Iterable<NotificationListenerRegistration<?>> load(final Class<?> key) {
48                     final Set<NotificationListenerRegistration<?>> regs = new HashSet<>();
49
50                     for (final Class<?> type : getNotificationTypes(key)) {
51                         @SuppressWarnings("unchecked")
52                         final Collection<NotificationListenerRegistration<?>> l = typeToListeners.get((Class<? extends Notification>) type);
53                         if (l != null) {
54                             regs.addAll(l);
55                         }
56                     }
57
58                     return ImmutableSet.copyOf(regs);
59                 }
60             });
61
62     ListenerMapGeneration() {
63         typeToListeners = ImmutableMultimap.of();
64     }
65
66     ListenerMapGeneration(final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> listeners) {
67         this.typeToListeners = ImmutableMultimap.copyOf(listeners);
68     }
69
70     /**
71      * Current listeners. Exposed for creating the next generation.
72      *
73      * @return Current type-to-listener map.
74      */
75     Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> getListeners() {
76         return typeToListeners;
77     }
78
79     /**
80      * Look up the listeners which need to see this notification delivered.
81      *
82      * @param notification Notification object
83      * @return Iterable of listeners, guaranteed to be nonnull.
84      */
85     public Iterable<NotificationListenerRegistration<?>> listenersFor(final Notification notification) {
86         // Safe to use, as our loader does not throw checked exceptions
87         return implementationToListeners.getUnchecked(notification.getClass());
88     }
89
90     public Iterable<Class<? extends Notification>> getKnownTypes() {
91         return typeToListeners.keySet();
92     }
93
94     private static Iterable<Class<?>> getNotificationTypes(final Class<?> cls) {
95         final Class<?>[] ifaces = cls.getInterfaces();
96         return Iterables.filter(Arrays.asList(ifaces), new Predicate<Class<?>>() {
97             @Override
98             public boolean apply(final Class<?> input) {
99                 if (Notification.class.equals(input)) {
100                     return false;
101                 }
102                 return Notification.class.isAssignableFrom(input);
103             }
104         });
105     }
106 }