2 * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.io.Resources;
14 import java.io.IOException;
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
18 import java.nio.charset.StandardCharsets;
19 import java.util.ArrayList;
20 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.opendaylight.yangtools.concepts.ObjectRegistration;
23 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
24 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleContext;
27 import org.osgi.framework.BundleEvent;
28 import org.osgi.framework.Constants;
29 import org.osgi.util.tracker.BundleTracker;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry.
36 final class YangModuleInfoScanner extends BundleTracker<List<ObjectRegistration<YangModuleInfo>>> {
37 private static final Logger LOG = LoggerFactory.getLogger(YangModuleInfoScanner.class);
38 // FIXME: this should be in a place shared with maven-sal-api-gen-plugin
39 private static final String MODULE_INFO_PROVIDER_PATH_PREFIX = "META-INF/services/";
41 private static final String YANG_MODLE_BINDING_PROVIDER_SERVICE = MODULE_INFO_PROVIDER_PATH_PREFIX
42 + YangModelBindingProvider.class.getName();
44 private final YangModuleInfoRegistry moduleInfoRegistry;
46 YangModuleInfoScanner(final BundleContext context, final YangModuleInfoRegistry moduleInfoRegistry) {
47 super(context, Bundle.RESOLVED | Bundle.STARTING | Bundle.STOPPING | Bundle.ACTIVE, null);
48 this.moduleInfoRegistry = requireNonNull(moduleInfoRegistry);
52 @SuppressWarnings("checkstyle:illegalCatch")
53 public List<ObjectRegistration<YangModuleInfo>> addingBundle(final Bundle bundle, final BundleEvent event) {
54 if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
55 LOG.debug("Ignoring system bundle {}", bundle);
56 return ImmutableList.of();
59 final URL resource = bundle.getEntry(YANG_MODLE_BINDING_PROVIDER_SERVICE);
60 if (resource == null) {
61 LOG.debug("Bundle {} does not have an entry for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
62 return ImmutableList.of();
65 LOG.debug("Got addingBundle({}) with YangModelBindingProvider resource {}", bundle, resource);
66 final List<String> lines;
68 lines = Resources.readLines(resource, StandardCharsets.UTF_8);
69 } catch (IOException e) {
70 LOG.error("Error while reading {} from bundle {}", resource, bundle, e);
71 return ImmutableList.of();
74 if (lines.isEmpty()) {
75 LOG.debug("Bundle {} has empty services for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
76 return ImmutableList.of();
79 final List<YangModuleInfo> infos = new ArrayList<>(lines.size());
80 for (String moduleInfoName : lines) {
81 LOG.trace("Retrieve ModuleInfo({}, {})", moduleInfoName, bundle);
82 final YangModuleInfo moduleInfo;
84 moduleInfo = retrieveModuleInfo(moduleInfoName, bundle);
85 } catch (ScanningException e) {
86 LOG.warn("Failed to acquire {} from bundle {}, ignoring it", moduleInfoName, bundle, e);
90 infos.add(moduleInfo);
93 final List<ObjectRegistration<YangModuleInfo>> registrations = moduleInfoRegistry.registerInfos(infos);
94 LOG.trace("Bundle {} resulted in registrations {}", bundle, registrations);
95 moduleInfoRegistry.scannerUpdate();
100 public void modifiedBundle(final Bundle bundle, final BundleEvent event,
101 final List<ObjectRegistration<YangModuleInfo>> regs) {
102 if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
103 LOG.debug("Framework bundle {} got event {}", bundle, event.getType());
104 if ((event.getType() & BundleEvent.STOPPING) != 0) {
105 LOG.info("OSGi framework is being stopped, halting bundle scanning");
106 moduleInfoRegistry.scannerShutdown();
113 public void removedBundle(final Bundle bundle, final BundleEvent event,
114 final List<ObjectRegistration<YangModuleInfo>> regs) {
115 regs.forEach(ObjectRegistration::close);
116 moduleInfoRegistry.scannerUpdate();
119 private static YangModuleInfo retrieveModuleInfo(final String className, final Bundle bundle)
120 throws ScanningException {
121 final Class<?> loadedClass;
123 loadedClass = bundle.loadClass(className);
124 } catch (ClassNotFoundException e) {
125 throw new ScanningException(e, "Failed to load class %s", className);
128 final Class<? extends YangModelBindingProvider> providerClass;
130 providerClass = loadedClass.asSubclass(YangModelBindingProvider.class);
131 } catch (ClassCastException e) {
132 throw new ScanningException(e, "Failed to validate %s", loadedClass);
135 final Constructor<? extends YangModelBindingProvider> ctor;
137 ctor = providerClass.getDeclaredConstructor();
138 } catch (NoSuchMethodException e) {
139 throw new ScanningException(e, "%s does not have a no-argument constructor", providerClass);
140 } catch (SecurityException e) {
141 throw new ScanningException(e, "Failed to reflect on %s", providerClass);
144 YangModelBindingProvider instance;
146 instance = ctor.newInstance();
147 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
148 | InvocationTargetException e) {
149 throw new ScanningException(e, "Failed to instantiate %s", providerClass);
152 return instance.getModuleInfo();
156 private static final class ScanningException extends Exception {
157 private static final long serialVersionUID = 1L;
159 ScanningException(final Exception cause, final String format, final Object... args) {
160 super(String.format(format, args), cause);