Initial code drop of yang model driven configuration system
[controller.git] / opendaylight / config / yang-store-impl / src / main / java / org / opendaylight / controller / config / yang / store / impl / ExtenderYangTracker.java
1 /*
2  * Copyright (c) 2013 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.config.yang.store.impl;
9
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.URL;
13 import java.util.Collection;
14 import java.util.Enumeration;
15 import java.util.Set;
16
17 import org.opendaylight.controller.config.yang.store.api.YangStoreException;
18 import org.opendaylight.controller.config.yang.store.api.YangStoreService;
19 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
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;
26
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;
35
36 public class ExtenderYangTracker extends BundleTracker<Object> implements
37         YangStoreService {
38
39     private static final Logger logger = LoggerFactory
40             .getLogger(ExtenderYangTracker.class);
41
42     private final Multimap<Bundle, URL> bundlesToYangURLs = HashMultimap
43             .create();
44     private final YangStoreCache cache = new YangStoreCache();
45     private final MbeParser mbeParser;
46
47     public ExtenderYangTracker(BundleContext context) {
48         this(context, new MbeParser());
49
50     }
51
52     @VisibleForTesting
53     ExtenderYangTracker(BundleContext context, MbeParser mbeParser) {
54         super(context, Bundle.ACTIVE, null);
55         this.mbeParser = mbeParser;
56         logger.trace("Registered as extender with context {}", context);
57     }
58
59     @Override
60     public Object addingBundle(Bundle bundle, BundleEvent event) {
61
62         // Ignore system bundle
63         //
64         // system bundle has config-api on classpath &&
65         // config-api contains yang files =>
66         // system bundle contains yang files from that bundle
67         if (bundle.getBundleId() == 0)
68             return bundle;
69
70         Enumeration<URL> yangURLs = bundle.findEntries("META-INF/yang",
71                 "*.yang", false);
72
73         if (yangURLs == null)
74             return bundle;
75
76         synchronized (this) {
77             while (yangURLs.hasMoreElements()) {
78                 URL yang = yangURLs.nextElement();
79                 logger.debug("Bundle {} found yang file {}", bundle, yang);
80                 bundlesToYangURLs.put(bundle, yang);
81             }
82         }
83
84         return bundle;
85     }
86
87     @Override
88     public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
89         synchronized (this) {
90             Collection<URL> urls = bundlesToYangURLs.removeAll(bundle);
91             logger.debug(
92                     "Removed following yang URLs {} because of removed bundle {}",
93                     urls, bundle);
94         }
95     }
96
97     @Override
98     public synchronized YangStoreSnapshot getYangStoreSnapshot()
99             throws YangStoreException {
100         Optional<YangStoreSnapshot> yangStoreOpt = cache
101                 .getCachedYangStore(bundlesToYangURLs);
102         if (yangStoreOpt.isPresent()) {
103             logger.debug("Returning cached yang store {}", yangStoreOpt.get());
104             return yangStoreOpt.get();
105         }
106
107         try {
108             YangStoreSnapshot yangStoreSnapshot = mbeParser
109                     .parseYangFiles(fromUrlsToInputStreams());
110             logger.debug(
111                     "{} module entries parsed successfully from {} yang files",
112                     yangStoreSnapshot.countModuleMXBeanEntries(),
113                     bundlesToYangURLs.values().size());
114             cache.cacheYangStore(bundlesToYangURLs, yangStoreSnapshot);
115
116             return yangStoreSnapshot;
117         } catch (RuntimeException e) {
118             logger.warn(
119                     "Unable to parse yang files, yang files that were picked up so far: {}",
120                     bundlesToYangURLs, e);
121             throw new YangStoreException("Unable to parse yang files", e);
122         }
123     }
124
125     private Collection<InputStream> fromUrlsToInputStreams() {
126         return Collections2.transform(bundlesToYangURLs.values(),
127                 new Function<URL, InputStream>() {
128
129                     @Override
130                     public InputStream apply(URL url) {
131                         try {
132                             return url.openStream();
133                         } catch (IOException e) {
134                             logger.warn("Unable to open stream from {}", url);
135                             throw new IllegalStateException(
136                                     "Unable to open stream from " + url, e);
137                         }
138                     }
139                 });
140     }
141
142     private static final class YangStoreCache {
143
144         Set<URL> cachedUrls;
145         YangStoreSnapshot cachedYangStoreSnapshot;
146
147         Optional<YangStoreSnapshot> getCachedYangStore(
148                 Multimap<Bundle, URL> bundlesToYangURLs) {
149             Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
150             if (cachedUrls != null && cachedUrls.equals(urls)) {
151                 Preconditions.checkState(cachedYangStoreSnapshot != null);
152                 return Optional.of(cachedYangStoreSnapshot);
153             }
154             return Optional.absent();
155         }
156
157         private static Set<URL> setFromMultimapValues(
158                 Multimap<Bundle, URL> bundlesToYangURLs) {
159             Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
160             Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
161             return urls;
162         }
163
164         void cacheYangStore(Multimap<Bundle, URL> urls,
165                 YangStoreSnapshot yangStoreSnapshot) {
166             this.cachedUrls = setFromMultimapValues(urls);
167             this.cachedYangStoreSnapshot = yangStoreSnapshot;
168         }
169
170     }
171 }