Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / dom / mdsal-dom-schema-osgi / src / main / java / org / opendaylight / mdsal / dom / schema / osgi / impl / YangModuleInfoScanner.java
1 /*
2  * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
9
10 import static java.util.Objects.requireNonNull;
11
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;
17 import java.net.URL;
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;
32
33 /**
34  * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry.
35  */
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/";
40
41     private static final String YANG_MODLE_BINDING_PROVIDER_SERVICE = MODULE_INFO_PROVIDER_PATH_PREFIX
42             + YangModelBindingProvider.class.getName();
43
44     private final YangModuleInfoRegistry moduleInfoRegistry;
45
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);
49     }
50
51     @Override
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();
57         }
58
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();
63         }
64
65         LOG.debug("Got addingBundle({}) with YangModelBindingProvider resource {}", bundle, resource);
66         final List<String> lines;
67         try {
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();
72         }
73
74         if (lines.isEmpty()) {
75             LOG.debug("Bundle {} has empty services for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
76             return ImmutableList.of();
77         }
78
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;
83             try {
84                 moduleInfo = retrieveModuleInfo(moduleInfoName, bundle);
85             } catch (ScanningException e) {
86                 LOG.warn("Failed to acquire {} from bundle {}, ignoring it", moduleInfoName, bundle, e);
87                 continue;
88             }
89
90             infos.add(moduleInfo);
91         }
92
93         final List<ObjectRegistration<YangModuleInfo>> registrations = moduleInfoRegistry.registerInfos(infos);
94         LOG.trace("Bundle {} resulted in registrations {}", bundle, registrations);
95         moduleInfoRegistry.scannerUpdate();
96         return registrations;
97     }
98
99     @Override
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();
107             }
108         }
109     }
110
111     @Override
112     public void removedBundle(final Bundle bundle, final BundleEvent event,
113             final List<ObjectRegistration<YangModuleInfo>> regs) {
114         regs.forEach(ObjectRegistration::close);
115         moduleInfoRegistry.scannerUpdate();
116     }
117
118     private static YangModuleInfo retrieveModuleInfo(final String className, final Bundle bundle)
119             throws ScanningException {
120         final Class<?> loadedClass;
121         try {
122             loadedClass = bundle.loadClass(className);
123         } catch (ClassNotFoundException e) {
124             throw new ScanningException(e, "Failed to load class %s", className);
125         }
126
127         final Class<? extends YangModelBindingProvider> providerClass;
128         try {
129             providerClass = loadedClass.asSubclass(YangModelBindingProvider.class);
130         } catch (ClassCastException e) {
131             throw new ScanningException(e, "Failed to validate %s", loadedClass);
132         }
133
134         final Constructor<? extends YangModelBindingProvider> ctor;
135         try {
136             ctor = providerClass.getDeclaredConstructor();
137         } catch (NoSuchMethodException e) {
138             throw new ScanningException(e, "%s does not have a no-argument constructor", providerClass);
139         } catch (SecurityException e) {
140             throw new ScanningException(e, "Failed to reflect on %s", providerClass);
141         }
142
143         YangModelBindingProvider instance;
144         try {
145             instance = ctor.newInstance();
146         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
147                 | InvocationTargetException e) {
148             throw new ScanningException(e, "Failed to instantiate %s", providerClass);
149         }
150
151         return instance.getModuleInfo();
152     }
153
154     @NonNullByDefault
155     private static final class ScanningException extends Exception {
156         private static final long serialVersionUID = 1L;
157
158         ScanningException(final Exception cause, final String format, final Object... args) {
159             super(String.format(format, args), cause);
160         }
161     }
162 }