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
9 package org.opendaylight.controller.config.facade.xml.osgi;
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 java.lang.ref.SoftReference;
16 import java.util.Collections;
17 import java.util.HashSet;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.ThreadFactory;
23 import java.util.concurrent.atomic.AtomicReference;
24 import org.opendaylight.controller.config.util.capability.Capability;
25 import org.opendaylight.controller.config.util.capability.ModuleListener;
26 import org.opendaylight.controller.config.util.capability.YangModuleCapability;
27 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
28 import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.model.api.Module;
31 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
33 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
34 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class YangStoreService implements YangStoreContext {
40 private static final Logger LOG = LoggerFactory.getLogger(YangStoreService.class);
43 * This is a rather interesting locking model. We need to guard against both the
44 * cache expiring from GC and being invalidated by schema context change. The
45 * context can change while we are doing processing, so we do not want to block
46 * it, so no synchronization can happen on the methods.
48 * So what we are doing is the following:
50 * We synchronize with GC as usual, using a SoftReference.
52 * The atomic reference is used to synchronize with {@link #refresh(org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext)}, e.g. when
53 * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
54 * that may happen while the getter is already busy acting on the old schema context,
55 * so it needs to understand that a refresh has happened and retry. To do that, it
56 * attempts a CAS operation -- if it fails, in knows that the SoftReference has
57 * been replaced and thus it needs to retry.
59 * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally
60 * to stop multiple threads doing the same work.
62 private final AtomicReference<SoftReference<YangStoreSnapshot>> ref =
63 new AtomicReference<>(new SoftReference<YangStoreSnapshot>(null));
65 private final AtomicReference<SoftReference<BindingRuntimeContext>> refBindingContext =
66 new AtomicReference<>(new SoftReference<BindingRuntimeContext>(null));
68 private final SchemaContextProvider schemaContextProvider;
69 private final SchemaSourceProvider<YangTextSchemaSource> sourceProvider;
71 private final ExecutorService notificationExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
73 public Thread newThread(final Runnable r) {
74 return new Thread(r, "yangstore-capability-notifications");
78 private final Set<ModuleListener> listeners = Collections.synchronizedSet(new HashSet<ModuleListener>());
80 public YangStoreService(final SchemaContextProvider schemaContextProvider,
81 final SchemaSourceProvider<YangTextSchemaSource> sourceProvider) {
82 this.schemaContextProvider = schemaContextProvider;
83 this.sourceProvider = sourceProvider;
86 synchronized YangStoreContext getYangStoreSnapshot() {
87 SoftReference<YangStoreSnapshot> r = ref.get();
88 YangStoreSnapshot ret = r.get();
91 // We need to be compute a new value
92 // TODO sourceProvider is not a snapshot
93 ret = new YangStoreSnapshot(schemaContextProvider.getSchemaContext(), refBindingContext.get().get(), sourceProvider);
95 if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
96 LOG.debug("Concurrent refresh detected, recomputing snapshot");
105 public YangStoreContext getCurrentSnapshot() {
106 return getYangStoreSnapshot();
111 public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
112 return getYangStoreSnapshot().getModuleMXBeanEntryMap();
116 public Map<QName, Map<String, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries() {
117 return getYangStoreSnapshot().getQNamesToIdentitiesToModuleMXBeanEntries();
121 public Set<Module> getModules() {
122 return getYangStoreSnapshot().getModules();
126 public String getModuleSource(final ModuleIdentifier moduleIdentifier) {
127 return getYangStoreSnapshot().getModuleSource(moduleIdentifier);
131 public EnumResolver getEnumResolver() {
132 return getYangStoreSnapshot().getEnumResolver();
135 public void refresh(final BindingRuntimeContext runtimeContext) {
136 final YangStoreSnapshot previous = ref.get().get();
137 ref.set(new SoftReference<YangStoreSnapshot>(null));
138 refBindingContext.set(new SoftReference<>(runtimeContext));
139 notificationExecutor.submit(new CapabilityChangeNotifier(previous));
142 public AutoCloseable registerModuleListener(final ModuleListener listener) {
143 YangStoreContext context = ref.get().get();
145 if (context == null) {
146 context = getYangStoreSnapshot();
149 this.listeners.add(listener);
150 listener.onCapabilitiesChanged(toCapabilities(context.getModules(), context), Collections.<Capability>emptySet());
152 return new AutoCloseable() {
154 public void close() {
155 YangStoreService.this.listeners.remove(listener);
160 private static Set<Capability> toCapabilities(final Set<Module> modules, final YangStoreContext current) {
161 return ImmutableSet.copyOf(Collections2.transform(modules, new Function<Module, Capability>() {
163 public Capability apply(final Module input) {
164 return new YangModuleCapability(input, current.getModuleSource(input));
169 private final class CapabilityChangeNotifier implements Runnable {
171 private final YangStoreSnapshot previous;
173 public CapabilityChangeNotifier(final YangStoreSnapshot previous) {
174 this.previous = previous;
179 final YangStoreContext current = getYangStoreSnapshot();
181 if (!current.equals(previous)) {
182 final Set<Module> prevModules = previous.getModules();
183 final Set<Module> currModules = current.getModules();
184 final Set<Module> removed = Sets.difference(prevModules, currModules);
185 final Set<Module> added = Sets.difference(currModules, prevModules);
187 final Set<Capability> addedCaps = toCapabilities(added, current);
188 final Set<Capability> removedCaps = toCapabilities(removed, current);
190 for (final ModuleListener listener : listeners) {
191 listener.onCapabilitiesChanged(addedCaps, removedCaps);