/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.config.yang.store.impl; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; import java.util.Enumeration; import java.util.Set; import org.opendaylight.controller.config.yang.store.api.YangStoreException; import org.opendaylight.controller.config.yang.store.api.YangStoreService; import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.util.tracker.BundleTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; public class ExtenderYangTracker extends BundleTracker implements YangStoreService { private static final Logger logger = LoggerFactory .getLogger(ExtenderYangTracker.class); private final Multimap bundlesToYangURLs = HashMultimap .create(); private final YangStoreCache cache = new YangStoreCache(); private final MbeParser mbeParser; public ExtenderYangTracker(BundleContext context) { this(context, new MbeParser()); } @VisibleForTesting ExtenderYangTracker(BundleContext context, MbeParser mbeParser) { super(context, Bundle.ACTIVE, null); this.mbeParser = mbeParser; logger.trace("Registered as extender with context {}", context); } @Override public Object addingBundle(Bundle bundle, BundleEvent event) { // Ignore system bundle // // system bundle has config-api on classpath && // config-api contains yang files => // system bundle contains yang files from that bundle if (bundle.getBundleId() == 0) return bundle; Enumeration yangURLs = bundle.findEntries("META-INF/yang", "*.yang", false); if (yangURLs == null) return bundle; synchronized (this) { while (yangURLs.hasMoreElements()) { URL yang = yangURLs.nextElement(); logger.debug("Bundle {} found yang file {}", bundle, yang); bundlesToYangURLs.put(bundle, yang); } } return bundle; } @Override public void removedBundle(Bundle bundle, BundleEvent event, Object object) { synchronized (this) { Collection urls = bundlesToYangURLs.removeAll(bundle); logger.debug( "Removed following yang URLs {} because of removed bundle {}", urls, bundle); } } @Override public synchronized YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException { Optional yangStoreOpt = cache .getCachedYangStore(bundlesToYangURLs); if (yangStoreOpt.isPresent()) { logger.debug("Returning cached yang store {}", yangStoreOpt.get()); return yangStoreOpt.get(); } try { YangStoreSnapshot yangStoreSnapshot = mbeParser .parseYangFiles(fromUrlsToInputStreams()); logger.debug( "{} module entries parsed successfully from {} yang files", yangStoreSnapshot.countModuleMXBeanEntries(), bundlesToYangURLs.values().size()); cache.cacheYangStore(bundlesToYangURLs, yangStoreSnapshot); return yangStoreSnapshot; } catch (RuntimeException e) { logger.warn( "Unable to parse yang files, yang files that were picked up so far: {}", bundlesToYangURLs, e); throw new YangStoreException("Unable to parse yang files", e); } } private Collection fromUrlsToInputStreams() { return Collections2.transform(bundlesToYangURLs.values(), new Function() { @Override public InputStream apply(URL url) { try { return url.openStream(); } catch (IOException e) { logger.warn("Unable to open stream from {}", url); throw new IllegalStateException( "Unable to open stream from " + url, e); } } }); } private static final class YangStoreCache { Set cachedUrls; YangStoreSnapshot cachedYangStoreSnapshot; Optional getCachedYangStore( Multimap bundlesToYangURLs) { Set urls = setFromMultimapValues(bundlesToYangURLs); if (cachedUrls != null && cachedUrls.equals(urls)) { Preconditions.checkState(cachedYangStoreSnapshot != null); return Optional.of(cachedYangStoreSnapshot); } return Optional.absent(); } private static Set setFromMultimapValues( Multimap bundlesToYangURLs) { Set urls = Sets.newHashSet(bundlesToYangURLs.values()); Preconditions.checkState(bundlesToYangURLs.size() == urls.size()); return urls; } void cacheYangStore(Multimap urls, YangStoreSnapshot yangStoreSnapshot) { this.cachedUrls = setFromMultimapValues(urls); this.cachedYangStoreSnapshot = yangStoreSnapshot; } } }