2 * Copyright (c) 2013 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.controller.config.yang.store.impl;
10 import java.io.IOException;
11 import java.io.InputStream;
15 import org.opendaylight.controller.config.yang.store.api.YangStoreException;
16 import org.opendaylight.controller.config.yang.store.api.YangStoreListenerRegistration;
17 import org.opendaylight.controller.config.yang.store.api.YangStoreService;
18 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
19 import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
20 import org.osgi.framework.Bundle;
21 import org.osgi.framework.BundleContext;
22 import org.osgi.framework.BundleEvent;
23 import org.osgi.util.tracker.BundleTracker;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import com.google.common.annotations.VisibleForTesting;
28 import com.google.common.base.Function;
29 import com.google.common.base.Optional;
30 import com.google.common.base.Preconditions;
31 import com.google.common.collect.Collections2;
32 import com.google.common.collect.HashMultimap;
33 import com.google.common.collect.Multimap;
34 import com.google.common.collect.Sets;
36 public class ExtenderYangTracker extends BundleTracker<Object> implements
39 private static final Logger logger = LoggerFactory
40 .getLogger(ExtenderYangTracker.class);
42 private final Multimap<Bundle, URL> bundlesToYangURLs = HashMultimap
44 private final YangStoreCache cache = new YangStoreCache();
45 private final MbeParser mbeParser;
46 private final List<YangStoreListener> listeners = new ArrayList<>();
48 public ExtenderYangTracker(BundleContext context) {
49 this(context, new MbeParser());
54 ExtenderYangTracker(BundleContext context, MbeParser mbeParser) {
55 super(context, Bundle.ACTIVE, null);
56 this.mbeParser = mbeParser;
57 logger.trace("Registered as extender with context {}", context);
61 public Object addingBundle(Bundle bundle, BundleEvent event) {
63 // Ignore system bundle:
64 // system bundle might have config-api on classpath &&
65 // config-api contains yang files =>
66 // system bundle might contain yang files from that bundle
67 if (bundle.getBundleId() == 0)
70 Enumeration<URL> yangURLs = bundle.findEntries("META-INF/yang", "*.yang", false);
71 if (yangURLs != null) {
73 while (yangURLs.hasMoreElements()) {
74 URL url = yangURLs.nextElement();
75 logger.debug("Bundle {} found yang file {}", bundle, url);
76 bundlesToYangURLs.put(bundle, url);
78 Collection<URL> urls = bundlesToYangURLs.get(bundle);
79 notifyListeners(urls, true);
85 private void notifyListeners(Collection<URL> urls, boolean adding) {
86 if (urls.size() > 0) {
87 RuntimeException potential = new RuntimeException("Error while notifying listeners");
88 for (YangStoreListener listener : listeners) {
91 listener.onAddedYangURL(urls);
93 listener.onRemovedYangURL(urls);
95 } catch(RuntimeException e) {
96 potential.addSuppressed(e);
99 if (potential.getSuppressed().length > 0) {
106 public synchronized void removedBundle(Bundle bundle, BundleEvent event, Object object) {
107 Collection<URL> urls = bundlesToYangURLs.removeAll(bundle);
108 if (urls.size() > 0) {
109 logger.debug("Removed following yang URLs {} because of removed bundle {}", urls, bundle);
110 notifyListeners(urls, false);
115 public synchronized YangStoreSnapshot getYangStoreSnapshot()
116 throws YangStoreException {
117 Optional<YangStoreSnapshot> yangStoreOpt = cache
118 .getCachedYangStore(bundlesToYangURLs);
119 if (yangStoreOpt.isPresent()) {
120 logger.debug("Returning cached yang store {}", yangStoreOpt.get());
121 return yangStoreOpt.get();
125 YangStoreSnapshot yangStoreSnapshot = mbeParser
126 .parseYangFiles(fromUrlsToInputStreams());
128 "{} module entries parsed successfully from {} yang files",
129 yangStoreSnapshot.countModuleMXBeanEntries(),
130 bundlesToYangURLs.values().size());
131 cache.cacheYangStore(bundlesToYangURLs, yangStoreSnapshot);
133 return yangStoreSnapshot;
134 } catch (RuntimeException e) {
136 "Unable to parse yang files, yang files that were picked up so far: {}",
137 bundlesToYangURLs, e);
138 throw new YangStoreException("Unable to parse yang files", e);
142 private Collection<InputStream> fromUrlsToInputStreams() {
143 return Collections2.transform(bundlesToYangURLs.values(),
144 new Function<URL, InputStream>() {
147 public InputStream apply(URL url) {
149 return url.openStream();
150 } catch (IOException e) {
151 logger.warn("Unable to open stream from {}", url);
152 throw new IllegalStateException(
153 "Unable to open stream from " + url, e);
160 public synchronized YangStoreListenerRegistration registerListener(final YangStoreListener listener) {
161 listeners.add(listener);
162 return new YangStoreListenerRegistration() {
164 public void close() {
165 listeners.remove(listener);
170 private static final class YangStoreCache {
173 YangStoreSnapshot cachedYangStoreSnapshot;
175 Optional<YangStoreSnapshot> getCachedYangStore(
176 Multimap<Bundle, URL> bundlesToYangURLs) {
177 Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
178 if (cachedUrls != null && cachedUrls.equals(urls)) {
179 Preconditions.checkState(cachedYangStoreSnapshot != null);
180 return Optional.of(cachedYangStoreSnapshot);
182 return Optional.absent();
185 private static Set<URL> setFromMultimapValues(
186 Multimap<Bundle, URL> bundlesToYangURLs) {
187 Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
188 Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
192 void cacheYangStore(Multimap<Bundle, URL> urls,
193 YangStoreSnapshot yangStoreSnapshot) {
194 this.cachedUrls = setFromMultimapValues(urls);
195 this.cachedYangStoreSnapshot = yangStoreSnapshot;