969b1296d1bacf4d4ad5843ec4c929c7545439ea
[controller.git] / opendaylight / config / config-manager-facade-xml / src / main / java / org / opendaylight / controller / config / facade / xml / osgi / YangStoreService.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.controller.config.facade.xml.osgi;
10
11 import com.google.common.base.Function;
12 import com.google.common.collect.Collections2;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Sets;
15 import com.google.common.util.concurrent.ThreadFactoryBuilder;
16 import java.util.Collections;
17 import java.util.HashSet;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import javax.annotation.concurrent.GuardedBy;
23 import org.opendaylight.controller.config.util.capability.Capability;
24 import org.opendaylight.controller.config.util.capability.ModuleListener;
25 import org.opendaylight.controller.config.util.capability.YangModuleCapability;
26 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
27 import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.model.api.Module;
30 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
31 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
32 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
33 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
34
35 public class YangStoreService implements YangStoreContext {
36
37     private final SchemaSourceProvider<YangTextSchemaSource> sourceProvider;
38     private final ExecutorService notificationExecutor = Executors.newSingleThreadExecutor(
39         new ThreadFactoryBuilder().setDaemon(true).setNameFormat("yangstore-capability-notifications").build());
40
41     /**
42      * Guarded by explicit lock to allow for properly synchronizing the initial notification and modification
43      * of the listener set.
44      */
45     @GuardedBy("listeners")
46     private final Set<ModuleListener> listeners = new HashSet<ModuleListener>();
47
48     /**
49      * This is the latest snapshot. Some of its state is always initialized, but the MXBean maps potentially cause
50      * recomputation. Accessing those two specific methods needs to re-check whether the snapshot has changed
51      * asynchronously and retry if it didi.
52      */
53     private volatile YangStoreSnapshot snap;
54
55     public YangStoreService(final SchemaContextProvider schemaContextProvider,
56         final SchemaSourceProvider<YangTextSchemaSource> sourceProvider) {
57         this.sourceProvider = sourceProvider;
58     }
59
60     public YangStoreContext getCurrentSnapshot() {
61         return snap;
62     }
63
64     @Deprecated
65     @Override
66     public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
67         Map<String, Map<String, ModuleMXBeanEntry>> ret;
68         YangStoreSnapshot snapshot;
69
70         do {
71             snapshot = snap;
72             ret = snapshot.getModuleMXBeanEntryMap();
73         } while (!snapshot.equals(snap));
74
75         return ret;
76     }
77
78     @Override
79     public Map<QName, Map<String, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries() {
80         Map<QName, Map<String, ModuleMXBeanEntry>> ret;
81         YangStoreSnapshot snapshot;
82
83         do {
84             snapshot = snap;
85             ret = snapshot.getQNamesToIdentitiesToModuleMXBeanEntries();
86         } while (!snapshot.equals(snap));
87
88         return ret;
89     }
90
91     @Override
92     public Set<Module> getModules() {
93         return snap.getModules();
94     }
95
96     @Override
97     public String getModuleSource(final ModuleIdentifier moduleIdentifier) {
98         return snap.getModuleSource(moduleIdentifier);
99     }
100
101     @Override
102     public EnumResolver getEnumResolver() {
103         return snap.getEnumResolver();
104     }
105
106     public void refresh(final BindingRuntimeContext runtimeContext) {
107         final YangStoreSnapshot next = new YangStoreSnapshot(runtimeContext, sourceProvider);
108         final YangStoreSnapshot previous = snap;
109         snap = next;
110         notificationExecutor.submit(new Runnable() {
111             @Override
112             public void run() {
113                 notifyListeners(previous, next);
114             }
115         });
116     }
117
118     public AutoCloseable registerModuleListener(final ModuleListener listener) {
119         final YangStoreContext context = snap;
120
121         synchronized (listeners) {
122             if (context != null) {
123                 listener.onCapabilitiesChanged(toCapabilities(context.getModules(), context), Collections.<Capability>emptySet());
124             }
125             this.listeners.add(listener);
126         }
127
128         return new AutoCloseable() {
129             @Override
130             public void close() {
131                 synchronized (listeners) {
132                     listeners.remove(listener);
133                 }
134             }
135         };
136     }
137
138     void notifyListeners(final YangStoreSnapshot previous, final YangStoreSnapshot current) {
139         final Set<Module> prevModules = previous.getModules();
140         final Set<Module> currModules = current.getModules();
141         final Set<Module> removed = Sets.difference(prevModules, currModules);
142         final Set<Module> added = Sets.difference(currModules, prevModules);
143
144         final Set<Capability> addedCaps = toCapabilities(added, current);
145         final Set<Capability> removedCaps = toCapabilities(removed, current);
146
147         synchronized (listeners) {
148             for (final ModuleListener listener : listeners) {
149                 listener.onCapabilitiesChanged(addedCaps, removedCaps);
150             }
151         }
152     }
153
154     private static Set<Capability> toCapabilities(final Set<Module> modules, final YangStoreContext current) {
155         return ImmutableSet.copyOf(Collections2.transform(modules, new Function<Module, Capability>() {
156             @Override
157             public Capability apply(final Module input) {
158                 return new YangModuleCapability(input, current.getModuleSource(input));
159             }
160         }));
161     }
162 }