Rework binding component instantiation 28/88028/18
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 24 Feb 2020 11:40:56 +0000 (12:40 +0100)
committerRobert Varga <nite@hq.sk>
Fri, 28 Feb 2020 14:58:28 +0000 (14:58 +0000)
This reshuffles how DOM and Binding interoperate where schema is
concerned. Since Binding requires Classloader information available
at scanning time, we refactor mdsal-dom-schema-service-osgi to
actually perform Binding-aware scanning, so that this information
is available in ModuleInfoSnapshot.

These are published as OSGiModuleInfoSnapshot along with generation
number for easier reference by a new component, OSGiModelRuntime.

DOMSchemaService is realized on top of ServiceRegistry, where it
listens for OSGiModelRuntime and for SchemaContextListeners, driving
an OSGi whiteboard pattern.

This renders mdsal-binding-dom-codec-osgi completely superfluous,
as there is not enough magic going on to justify it.

mdsal-binding-runtime-osgi is also hooked onto OSGiModuleInfoSnapshot,
providing BindingRuntimeContexts as needed and retaining generation
inforation.

JIRA: MDSAL-392
Change-Id: I083caf3abbc3d7e2212d1df14e45d2745096b5f9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
35 files changed:
artifacts/pom.xml
binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/BindingClassLoadingStrategy.java [deleted file]
binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/ModuleInfoBundleTracker.java [deleted file]
binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/OsgiModuleInfoRegistry.java [deleted file]
binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/binding/runtime/api/ModuleInfoSnapshot.java [new file with mode: 0644]
binding/mdsal-binding-runtime-osgi/pom.xml [moved from binding/mdsal-binding-dom-codec-osgi/pom.xml with 75% similarity]
binding/mdsal-binding-runtime-osgi/src/main/java/org/opendaylight/mdsal/binding/runtime/osgi/impl/BindingRuntimeContextImpl.java [new file with mode: 0644]
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/AbstractModuleInfoTracker.java [new file with mode: 0644]
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/BindingRuntimeHelpers.java
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/DefaultModuleInfoSnapshot.java [new file with mode: 0644]
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoBackedContext.java
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoRegistry.java [deleted file]
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoSnapshotBuilder.java [new file with mode: 0644]
binding/pom.xml
docs/pom.xml
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/schema/ScanningSchemaServiceProvider.java
dom/mdsal-dom-schema-osgi/pom.xml [moved from dom/mdsal-dom-schema-service-osgi/pom.xml with 82% similarity]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/OSGiModuleInfoSnapshot.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiDOMSchemaService.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiEffectiveModelImpl.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntime.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/SchemaSchemaContextListenerImpl.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoRegistry.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoScanner.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntimeTest.java [new file with mode: 0644]
dom/mdsal-dom-schema-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/osgi/impl/TestModel.java [moved from dom/mdsal-dom-schema-service-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/service/osgi/util/TestModel.java with 98% similarity]
dom/mdsal-dom-schema-osgi/src/test/resources/odl-datastore-test.yang [moved from dom/mdsal-dom-schema-service-osgi/src/test/resources/odl-datastore-test.yang with 100% similarity]
dom/mdsal-dom-schema-service-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaService.java [deleted file]
dom/mdsal-dom-schema-service-osgi/src/main/resources/org/opendaylight/blueprint/dom-osgi-schema-service.xml [deleted file]
dom/mdsal-dom-schema-service-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaServiceTest.java [deleted file]
dom/pom.xml
features/odl-mdsal-binding-runtime-api/pom.xml [new file with mode: 0644]
features/odl-mdsal-binding-runtime/pom.xml
features/odl-mdsal-dom-broker/pom.xml
features/pom.xml

index 65f8d03883a502dbcad75da4e1897d98d05b3c9e..699cf8ff7cca5d0828502018abac23201a8713ef 100644 (file)
@@ -68,7 +68,7 @@
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
-                <artifactId>mdsal-dom-schema-service-osgi</artifactId>
+                <artifactId>mdsal-dom-schema-osgi</artifactId>
                 <version>6.0.0-SNAPSHOT</version>
             </dependency>
             <dependency>
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
-                <artifactId>mdsal-binding-dom-codec-osgi</artifactId>
+                <artifactId>mdsal-binding-runtime-api</artifactId>
                 <version>6.0.0-SNAPSHOT</version>
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
-                <artifactId>mdsal-binding-runtime-api</artifactId>
+                <artifactId>mdsal-binding-runtime-spi</artifactId>
                 <version>6.0.0-SNAPSHOT</version>
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
-                <artifactId>mdsal-binding-runtime-spi</artifactId>
+                <artifactId>mdsal-binding-runtime-osgi</artifactId>
                 <version>6.0.0-SNAPSHOT</version>
             </dependency>
             <dependency>
                 <classifier>features</classifier>
                 <type>xml</type>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>odl-mdsal-binding-runtime-api</artifactId>
+                <version>6.0.0-SNAPSHOT</version>
+                <classifier>features</classifier>
+                <type>xml</type>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>odl-mdsal-binding-runtime</artifactId>
diff --git a/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/BindingClassLoadingStrategy.java b/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/BindingClassLoadingStrategy.java
deleted file mode 100644 (file)
index d0ee2fa..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.osgi.impl;
-
-import org.opendaylight.binding.runtime.api.ClassLoadingStrategy;
-import org.opendaylight.binding.runtime.spi.GeneratedClassLoadingStrategy;
-import org.opendaylight.binding.runtime.spi.ModuleInfoBackedContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Component(immediate = true)
-public final class BindingClassLoadingStrategy implements ClassLoadingStrategy {
-    private static final Logger LOG = LoggerFactory.getLogger(BindingClassLoadingStrategy.class);
-
-    @Reference
-    YangParserFactory factory = null;
-
-    private ModuleInfoBundleTracker bundleTracker = null;
-    private ModuleInfoBackedContext moduleInfoBackedContext = null;
-
-    @Activate
-    void activate(final BundleContext ctx) {
-        LOG.info("Binding-DOM codec starting");
-
-        moduleInfoBackedContext = ModuleInfoBackedContext.create("binding-dom-codec", factory,
-            // FIXME: This is the fallback strategy, it should not be needed
-            GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
-
-        final OsgiModuleInfoRegistry registry = new OsgiModuleInfoRegistry(moduleInfoBackedContext,
-                moduleInfoBackedContext);
-
-        LOG.debug("Starting Binding-DOM codec bundle tracker");
-        bundleTracker = new ModuleInfoBundleTracker(ctx, registry);
-        bundleTracker.open();
-
-        LOG.info("Binding-DOM codec started");
-    }
-
-    @Deactivate
-    void deactivate() {
-        LOG.info("Binding-DOM codec stopping");
-        LOG.debug("Stopping Binding-DOM codec bundle tracker");
-        bundleTracker.close();
-        moduleInfoBackedContext = null;
-        bundleTracker = null;
-        LOG.info("Binding-DOM codec stopped");
-    }
-
-    @Override
-    public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
-        return moduleInfoBackedContext.loadClass(fullyQualifiedName);
-    }
-}
diff --git a/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/ModuleInfoBundleTracker.java b/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/ModuleInfoBundleTracker.java
deleted file mode 100644 (file)
index 4b39f10..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.mdsal.binding.dom.codec.osgi.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.Resources;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.io.IOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Pattern;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
-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;
-
-/**
- * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry.
- */
-final class ModuleInfoBundleTracker extends BundleTracker<Collection<ObjectRegistration<YangModuleInfo>>> {
-    private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBundleTracker.class);
-    // FIXME: this should be in a place shared with maven-sal-api-gen-plugin
-    private static final String MODULE_INFO_PROVIDER_PATH_PREFIX = "META-INF/services/";
-
-    private static final String YANG_MODLE_BINDING_PROVIDER_SERVICE = MODULE_INFO_PROVIDER_PATH_PREFIX
-            + YangModelBindingProvider.class.getName();
-
-    private static final Pattern BRACE_PATTERN = Pattern.compile("{}", Pattern.LITERAL);
-
-    private final OsgiModuleInfoRegistry moduleInfoRegistry;
-
-    private volatile boolean deferUpdates = true;
-
-    ModuleInfoBundleTracker(final BundleContext context, final OsgiModuleInfoRegistry moduleInfoRegistry) {
-        super(context, Bundle.RESOLVED | Bundle.STARTING | Bundle.STOPPING | Bundle.ACTIVE, null);
-        this.moduleInfoRegistry = requireNonNull(moduleInfoRegistry);
-    }
-
-    @Override
-    public void open() {
-        super.open();
-        deferUpdates = false;
-        moduleInfoRegistry.updateService();
-    }
-
-    @Override
-    public void close() {
-        deferUpdates = true;
-        super.close();
-    }
-
-    @Override
-    @SuppressWarnings("checkstyle:illegalCatch")
-    public Collection<ObjectRegistration<YangModuleInfo>> addingBundle(final Bundle bundle, final BundleEvent event) {
-        final URL resource = bundle.getEntry(YANG_MODLE_BINDING_PROVIDER_SERVICE);
-        if (resource == null) {
-            LOG.debug("Bundle {} does not have an entry for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
-            return ImmutableList.of();
-        }
-
-        LOG.debug("Got addingBundle({}) with YangModelBindingProvider resource {}", bundle, resource);
-        final List<String> lines;
-        try {
-            lines = Resources.readLines(resource, StandardCharsets.UTF_8);
-        } catch (IOException e) {
-            LOG.error("Error while reading {} from bundle {}", resource, bundle, e);
-            return ImmutableList.of();
-        }
-
-        if (lines.isEmpty()) {
-            LOG.debug("Bundle {} has empty services for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
-            return ImmutableList.of();
-        }
-
-        final List<ObjectRegistration<YangModuleInfo>> registrations = new ArrayList<>(lines.size());
-        for (String moduleInfoName : lines) {
-            LOG.trace("Retrieve ModuleInfo({}, {})", moduleInfoName, bundle);
-            final YangModuleInfo moduleInfo;
-            try {
-                moduleInfo = retrieveModuleInfo(moduleInfoName, bundle);
-            } catch (RuntimeException e) {
-                LOG.warn("Failed to acquire {} from bundle {}, ignoring it", moduleInfoName, bundle, e);
-                continue;
-            }
-
-            registrations.add(moduleInfoRegistry.registerInfo(moduleInfo));
-        }
-
-        if (!deferUpdates) {
-            moduleInfoRegistry.updateService();
-        }
-
-        LOG.trace("Bundle {} resulted in registrations {}", bundle, registrations);
-        return registrations;
-    }
-
-    @Override
-    public void modifiedBundle(final Bundle bundle, final BundleEvent event,
-            final Collection<ObjectRegistration<YangModuleInfo>> object) {
-        // No-op
-    }
-
-    @Override
-    @SuppressWarnings("checkstyle:illegalCatch")
-    public void removedBundle(final Bundle bundle, final BundleEvent event,
-            final Collection<ObjectRegistration<YangModuleInfo>> regs) {
-        if (regs == null) {
-            return;
-        }
-
-        try {
-            regs.forEach(reg -> {
-                try {
-                    reg.close();
-                } catch (Exception e) {
-                    LOG.warn("Unable to unregister YangModuleInfo {}", reg.getInstance(), e);
-                }
-            });
-        } finally {
-            if (!deferUpdates) {
-                moduleInfoRegistry.updateService();
-            }
-        }
-    }
-
-    private static YangModuleInfo retrieveModuleInfo(final String moduleInfoClass, final Bundle bundle) {
-        final Class<?> clazz = loadClass(moduleInfoClass, bundle);
-        if (!YangModelBindingProvider.class.isAssignableFrom(clazz)) {
-            String errorMessage = logMessage("Class {} does not implement {} in bundle {}", clazz,
-                YangModelBindingProvider.class, bundle);
-            throw new IllegalStateException(errorMessage);
-        }
-
-        final YangModelBindingProvider instance;
-        try {
-            Object instanceObj = clazz.newInstance();
-            instance = YangModelBindingProvider.class.cast(instanceObj);
-        } catch (InstantiationException e) {
-            String errorMessage = logMessage("Could not instantiate {} in bundle {}, reason {}", moduleInfoClass,
-                bundle, e);
-            throw new IllegalStateException(errorMessage, e);
-        } catch (IllegalAccessException e) {
-            String errorMessage = logMessage("Illegal access during instantiation of class {} in bundle {}, reason {}",
-                    moduleInfoClass, bundle, e);
-            throw new IllegalStateException(errorMessage, e);
-        }
-
-        try {
-            return instance.getModuleInfo();
-        } catch (NoClassDefFoundError | ExceptionInInitializerError e) {
-            throw new IllegalStateException("Error while executing getModuleInfo on " + instance, e);
-        }
-    }
-
-    private static Class<?> loadClass(final String moduleInfoClass, final Bundle bundle) {
-        try {
-            return bundle.loadClass(moduleInfoClass);
-        } catch (ClassNotFoundException e) {
-            String errorMessage = logMessage("Could not find class {} in bundle {}, reason {}", moduleInfoClass, bundle,
-                e);
-            throw new IllegalStateException(errorMessage);
-        }
-    }
-
-    @SuppressFBWarnings("SLF4J_UNKNOWN_ARRAY")
-    private static String logMessage(final String slfMessage, final Object... params) {
-        LOG.info(slfMessage, params);
-        return String.format(BRACE_PATTERN.matcher(slfMessage).replaceAll("%s"), params);
-    }
-}
diff --git a/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/OsgiModuleInfoRegistry.java b/binding/mdsal-binding-dom-codec-osgi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/osgi/impl/OsgiModuleInfoRegistry.java
deleted file mode 100644 (file)
index b001583..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.mdsal.binding.dom.codec.osgi.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.opendaylight.binding.runtime.spi.ModuleInfoBackedContext;
-import org.opendaylight.binding.runtime.spi.ModuleInfoRegistry;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
-import org.osgi.framework.ServiceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Update SchemaContext service in Service Registry each time new YangModuleInfo is added or removed.
- */
-final class OsgiModuleInfoRegistry implements ModuleInfoRegistry {
-    private static final Logger LOG = LoggerFactory.getLogger(OsgiModuleInfoRegistry.class);
-
-    private final SchemaContextProvider schemaContextProvider;
-    private final ModuleInfoBackedContext moduleInfoRegistry;
-
-    @GuardedBy("this")
-    private ServiceRegistration<?> registration;
-    @GuardedBy("this")
-    private long generation;
-
-    OsgiModuleInfoRegistry(final ModuleInfoBackedContext moduleInfoRegistry,
-        final SchemaContextProvider schemaContextProvider) {
-
-        this.moduleInfoRegistry = requireNonNull(moduleInfoRegistry);
-        this.schemaContextProvider = requireNonNull(schemaContextProvider);
-    }
-
-    @Override
-    public ObjectRegistration<YangModuleInfo> registerModuleInfo(final YangModuleInfo yangModuleInfo) {
-        return new ObjectRegistrationWrapper(registerInfo(yangModuleInfo));
-    }
-
-    @SuppressWarnings("checkstyle:illegalCatch")
-    synchronized void updateService() {
-        final SchemaContext context;
-        try {
-            context = schemaContextProvider.getSchemaContext();
-        } catch (final RuntimeException e) {
-            // The ModuleInfoBackedContext throws a RuntimeException if it can't create the schema context.
-            LOG.error("Error updating the schema context", e);
-            return;
-        }
-        LOG.trace("Assembled context {}", context);
-
-        //        // FIXME: MDSAL-392: UGH, this should be a snapshot
-        //        final BindingRuntimeContext next = DefaultBindingRuntimeContext.create(
-        //            new DefaultBindingRuntimeGenerator().generateTypeMapping(context), moduleInfoRegistry);
-
-        // FIXME: publish new the new context, remove the old one
-    }
-
-    ObjectRegistration<YangModuleInfo> registerInfo(final YangModuleInfo yangModuleInfo) {
-        return moduleInfoRegistry.registerModuleInfo(yangModuleInfo);
-    }
-
-    private class ObjectRegistrationWrapper implements ObjectRegistration<YangModuleInfo> {
-        private final ObjectRegistration<YangModuleInfo> inner;
-
-        ObjectRegistrationWrapper(final ObjectRegistration<YangModuleInfo> inner) {
-            this.inner = requireNonNull(inner);
-        }
-
-        @Override
-        public YangModuleInfo getInstance() {
-            return inner.getInstance();
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:illegalCatch")
-        public void close() {
-            try {
-                inner.close();
-            } finally {
-                // send modify event when a bundle disappears
-                updateService();
-            }
-        }
-
-        @Override
-        public String toString() {
-            return inner.toString();
-        }
-    }
-}
diff --git a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/binding/runtime/api/ModuleInfoSnapshot.java b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/binding/runtime/api/ModuleInfoSnapshot.java
new file mode 100644 (file)
index 0000000..5a5d1b2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.binding.runtime.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+
+@Beta
+public interface ModuleInfoSnapshot extends Immutable, ClassLoadingStrategy, EffectiveModelContextProvider,
+        SchemaSourceProvider<YangTextSchemaSource> {
+
+}
similarity index 75%
rename from binding/mdsal-binding-dom-codec-osgi/pom.xml
rename to binding/mdsal-binding-runtime-osgi/pom.xml
index 7311fa57614e68fa9921c95c274fcf10697cff26..3d941fc4f65208b52d652748afb891228c98912d 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- vi: set et smarttab sw=4 tabstop=4: -->
 <!--
- Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ Copyright (c) 2020 PANTHEON.tech, s.r.o. 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,
         <relativePath>../../dom/dom-parent</relativePath>
     </parent>
 
-    <artifactId>mdsal-binding-dom-codec-osgi</artifactId>
+    <artifactId>mdsal-binding-runtime-osgi</artifactId>
     <packaging>bundle</packaging>
 
     <dependencies>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-dom-codec</artifactId>
-        </dependency>
-
-        <!-- FIXME: MDSAL-392: this is ugly, we should be looking this up,
-                               but then we may want to end up doing something
-                               completely different in thes artifacts -->
-        <dependency>
-            <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-generator-impl</artifactId>
+            <artifactId>mdsal-binding-runtime-spi</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-runtime-spi</artifactId>
+            <artifactId>mdsal-dom-schema-osgi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>mockito-configuration</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>yang-test-util</artifactId>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-test-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-generator-impl</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
                 <configuration>
+                    <Automatic-Module-Name>org.opendaylight.mdsal.binding.runtime.osgi</Automatic-Module-Name>
                     <instructions>
-                        <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                        <!-- Karaf cannot handle Factory Component requirements, see https://issues.apache.org/jira/browse/KARAF-6625 -->
+                        <_dsannotations-options>norequirements</_dsannotations-options>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/binding/mdsal-binding-runtime-osgi/src/main/java/org/opendaylight/mdsal/binding/runtime/osgi/impl/BindingRuntimeContextImpl.java b/binding/mdsal-binding-runtime-osgi/src/main/java/org/opendaylight/mdsal/binding/runtime/osgi/impl/BindingRuntimeContextImpl.java
new file mode 100644 (file)
index 0000000..ddde762
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.runtime.osgi.impl;
+
+import org.opendaylight.binding.runtime.api.AbstractBindingRuntimeContext;
+import org.opendaylight.binding.runtime.api.BindingRuntimeContext;
+import org.opendaylight.binding.runtime.api.BindingRuntimeGenerator;
+import org.opendaylight.binding.runtime.api.BindingRuntimeTypes;
+import org.opendaylight.binding.runtime.api.ClassLoadingStrategy;
+import org.opendaylight.binding.runtime.api.DefaultBindingRuntimeContext;
+import org.opendaylight.mdsal.dom.schema.osgi.OSGiModuleInfoSnapshot;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Factory Component which implements {@link BindingRuntimeContext}.
+ */
+@Component(service = BindingRuntimeContext.class, immediate = true)
+public final class BindingRuntimeContextImpl extends AbstractBindingRuntimeContext {
+    private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeContextImpl.class);
+
+    @Reference
+    OSGiModuleInfoSnapshot effectiveModel = null;
+    @Reference
+    BindingRuntimeGenerator generator = null;
+
+    private BindingRuntimeContext delegate = null;
+    private long generation;
+
+    @Override
+    public ClassLoadingStrategy getStrategy() {
+        return delegate.getStrategy();
+    }
+
+    @Override
+    public BindingRuntimeTypes getTypes() {
+        return delegate.getTypes();
+    }
+
+    @Activate
+    void activate() {
+        generation = effectiveModel.getGeneration();
+        delegate = DefaultBindingRuntimeContext.create(
+            generator.generateTypeMapping(effectiveModel.getEffectiveModelContext()), effectiveModel);
+
+        LOG.debug("BindingRuntimeContext generation {} activated", generation);
+    }
+
+    @Deactivate
+    void deactivate() {
+        delegate = null;
+        LOG.debug("BindingRuntimeContext generation {} deactivated", generation);
+    }
+}
diff --git a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/AbstractModuleInfoTracker.java b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/AbstractModuleInfoTracker.java
new file mode 100644 (file)
index 0000000..8c999bf
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.binding.runtime.spi;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.util.concurrent.ListenableFuture;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
+import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaSourceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for things that create an EffectiveModuleContext or similar things from a (dynamic) set of
+ * YangModuleInfo objects.
+ *
+ * <p>
+ * Note this class has some locking quirks and may end up being further refactored.
+ */
+abstract class AbstractModuleInfoTracker implements Mutable {
+    abstract static class AbstractRegisteredModuleInfo {
+        final YangTextSchemaSourceRegistration reg;
+        final YangModuleInfo info;
+        final ClassLoader loader;
+
+        AbstractRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
+            final ClassLoader loader) {
+            this.info = requireNonNull(info);
+            this.reg = requireNonNull(reg);
+            this.loader = requireNonNull(loader);
+        }
+
+        @Override
+        public final String toString() {
+            return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+        }
+
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return helper.add("info", info).add("registration", reg).add("classLoader", loader);
+        }
+    }
+
+    private static final class ExplicitRegisteredModuleInfo extends AbstractRegisteredModuleInfo {
+        private int refcount = 1;
+
+        ExplicitRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
+                final ClassLoader loader) {
+            super(info, reg, loader);
+        }
+
+        void incRef() {
+            ++refcount;
+        }
+
+        boolean decRef() {
+            return --refcount == 0;
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("refCount", refcount);
+        }
+    }
+
+    private static final class ImplicitRegisteredModuleInfo extends AbstractRegisteredModuleInfo {
+        ImplicitRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
+                final ClassLoader loader) {
+            super(info, reg, loader);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractModuleInfoTracker.class);
+
+    private final YangTextSchemaContextResolver ctxResolver;
+
+    @GuardedBy("this")
+    private final ListMultimap<String, AbstractRegisteredModuleInfo> packageToInfoReg =
+            MultimapBuilder.hashKeys().arrayListValues().build();
+    @GuardedBy("this")
+    private final ListMultimap<SourceIdentifier, AbstractRegisteredModuleInfo> sourceToInfoReg =
+            MultimapBuilder.hashKeys().arrayListValues().build();
+    @GuardedBy("this")
+    private @Nullable ModuleInfoSnapshot currentSnapshot;
+
+    AbstractModuleInfoTracker(final YangTextSchemaContextResolver resolver) {
+        this.ctxResolver = requireNonNull(resolver);
+    }
+
+    public final synchronized List<ObjectRegistration<YangModuleInfo>> registerModuleInfos(
+            final Iterable<? extends YangModuleInfo> moduleInfos) {
+        final List<ObjectRegistration<YangModuleInfo>> ret = new ArrayList<>();
+        for (YangModuleInfo yangModuleInfo : moduleInfos) {
+            ret.add(register(requireNonNull(yangModuleInfo)));
+        }
+        return ret;
+    }
+
+    @Holding("this")
+    private ObjectRegistration<YangModuleInfo> register(final @NonNull YangModuleInfo moduleInfo) {
+        final Builder<ExplicitRegisteredModuleInfo> regBuilder = ImmutableList.builder();
+        for (YangModuleInfo info : flatDependencies(moduleInfo)) {
+            regBuilder.add(registerExplicitModuleInfo(info));
+        }
+        final ImmutableList<ExplicitRegisteredModuleInfo> regInfos = regBuilder.build();
+
+        return new AbstractObjectRegistration<>(moduleInfo) {
+            @Override
+            protected void removeRegistration() {
+                unregister(regInfos);
+            }
+        };
+    }
+
+    @Holding("this")
+    final void registerImplicitBindingClass(final Class<?> bindingClass) {
+        registerImplicitModuleInfo(BindingRuntimeHelpers.extractYangModuleInfo(bindingClass));
+    }
+
+    @Holding("this")
+    final @Nullable ClassLoader findClassLoader(final String fullyQualifiedName) {
+        // This performs an explicit check for binding classes
+        final String modulePackageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
+
+        // Try to find a loaded class loader
+        // FIXME: two-step process, try explicit registrations first
+        for (AbstractRegisteredModuleInfo reg : packageToInfoReg.get(modulePackageName)) {
+            return reg.loader;
+        }
+        return null;
+    }
+
+    /*
+     * Perform implicit registration of a YangModuleInfo and any of its dependencies. If there is a registration for
+     * a particular source, we do not create a duplicate registration.
+     */
+    @Holding("this")
+    private void registerImplicitModuleInfo(final @NonNull YangModuleInfo moduleInfo) {
+        for (YangModuleInfo info : flatDependencies(moduleInfo)) {
+            final Class<?> infoClass = info.getClass();
+            final SourceIdentifier sourceId = sourceIdentifierFrom(info);
+            if (sourceToInfoReg.containsKey(sourceId)) {
+                LOG.debug("Skipping implicit registration of {} as source {} is already registered", info, sourceId);
+                continue;
+            }
+
+            final YangTextSchemaSourceRegistration reg;
+            try {
+                reg = ctxResolver.registerSource(toYangTextSource(sourceId, info));
+            } catch (YangSyntaxErrorException | SchemaSourceException | IOException e) {
+                LOG.warn("Failed to register info {} source {}, ignoring it", info, sourceId, e);
+                continue;
+            }
+
+            final ImplicitRegisteredModuleInfo regInfo = new ImplicitRegisteredModuleInfo(info, reg,
+                infoClass.getClassLoader());
+            sourceToInfoReg.put(sourceId, regInfo);
+            packageToInfoReg.put(BindingReflections.getModelRootPackageName(infoClass.getPackage()), regInfo);
+        }
+    }
+
+    /*
+     * Perform explicit registration of a YangModuleInfo. This always results in a new explicit registration. In case
+     * there is a pre-existing implicit registration, it is removed just after the explicit registration is made.
+     */
+    @Holding("this")
+    private ExplicitRegisteredModuleInfo registerExplicitModuleInfo(final @NonNull YangModuleInfo info) {
+        // First search for an existing explicit registration
+        final SourceIdentifier sourceId = sourceIdentifierFrom(info);
+        for (AbstractRegisteredModuleInfo reg : sourceToInfoReg.get(sourceId)) {
+            if (reg instanceof ExplicitRegisteredModuleInfo && info.equals(reg.info)) {
+                final ExplicitRegisteredModuleInfo explicit = (ExplicitRegisteredModuleInfo) reg;
+                explicit.incRef();
+                LOG.debug("Reusing explicit registration {}", explicit);
+                return explicit;
+            }
+        }
+
+        // Create an explicit registration
+        final YangTextSchemaSourceRegistration reg;
+        try {
+            reg = ctxResolver.registerSource(toYangTextSource(sourceId, info));
+        } catch (YangSyntaxErrorException | SchemaSourceException | IOException e) {
+            throw new IllegalStateException("Failed to register info " + info, e);
+        }
+
+        final Class<?> infoClass = info.getClass();
+        final String packageName = BindingReflections.getModelRootPackageName(infoClass.getPackage());
+        final ExplicitRegisteredModuleInfo regInfo = new ExplicitRegisteredModuleInfo(info, reg,
+            infoClass.getClassLoader());
+        LOG.debug("Created new explicit registration {}", regInfo);
+
+        sourceToInfoReg.put(sourceId, regInfo);
+        removeImplicit(sourceToInfoReg.get(sourceId));
+        packageToInfoReg.put(packageName, regInfo);
+        removeImplicit(packageToInfoReg.get(packageName));
+
+        return regInfo;
+    }
+
+    // Reconsider utility of this
+    final Optional<? extends EffectiveModelContext> getResolverEffectiveModel() {
+        return ctxResolver.getEffectiveModelContext();
+    }
+
+    @Deprecated
+    final ListenableFuture<? extends YangTextSchemaSource> getResolverSource(final SourceIdentifier sourceIdentifier) {
+        return ctxResolver.getSource(sourceIdentifier);
+    }
+
+    @Holding("this")
+    final @NonNull ModuleInfoSnapshot updateSnapshot() {
+        final EffectiveModelContext effectiveModel = ctxResolver.getEffectiveModelContext().orElseThrow();
+        final ModuleInfoSnapshot local = currentSnapshot;
+        if (local != null && local.getEffectiveModelContext().equals(effectiveModel)) {
+            return local;
+        }
+
+        return updateSnapshot(effectiveModel);
+    }
+
+    @Holding("this")
+    private @NonNull ModuleInfoSnapshot updateSnapshot(final EffectiveModelContext effectiveModel) {
+        // Alright, now let's find out which sources got captured
+        final Set<SourceIdentifier> sources = new HashSet<>();
+        for (Entry<QNameModule, ModuleEffectiveStatement> entry : effectiveModel.getModuleStatements().entrySet()) {
+            final Optional<Revision> revision = entry.getKey().getRevision();
+            final ModuleEffectiveStatement module = entry.getValue();
+
+            sources.add(RevisionSourceIdentifier.create(module.argument(), revision));
+            module.streamEffectiveSubstatements(SubmoduleEffectiveStatement.class)
+                .map(submodule -> RevisionSourceIdentifier.create(submodule.argument(), revision))
+                .forEach(sources::add);
+        }
+
+        final Map<SourceIdentifier, YangModuleInfo> moduleInfos = new HashMap<>();
+        final Map<String, ClassLoader> classLoaders = new HashMap<>();
+        for (SourceIdentifier source : sources) {
+            final List<AbstractRegisteredModuleInfo> regs = sourceToInfoReg.get(source);
+            checkState(!regs.isEmpty(), "No registration for %s", source);
+
+            AbstractRegisteredModuleInfo reg = regs.stream()
+                    .filter(ExplicitRegisteredModuleInfo.class::isInstance).findFirst()
+                    .orElse(null);
+            if (reg == null) {
+                reg = regs.get(0);
+            }
+
+            final YangModuleInfo info = reg.info;
+            moduleInfos.put(source, info);
+            final Class<?> infoClass = info.getClass();
+            classLoaders.put(BindingReflections.getModelRootPackageName(infoClass.getPackage()),
+                infoClass.getClassLoader());
+        }
+
+        final ModuleInfoSnapshot next = new DefaultModuleInfoSnapshot(effectiveModel, moduleInfos, classLoaders);
+        currentSnapshot = next;
+        return next;
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+                justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private synchronized void unregister(final ImmutableList<ExplicitRegisteredModuleInfo> regInfos) {
+        for (ExplicitRegisteredModuleInfo regInfo : regInfos) {
+            if (!regInfo.decRef()) {
+                LOG.debug("Registration {} has references, not removing it", regInfo);
+                continue;
+            }
+
+            final SourceIdentifier sourceId = sourceIdentifierFrom(regInfo.info);
+            if (!sourceToInfoReg.remove(sourceId, regInfo)) {
+                LOG.warn("Failed to find {} registered under {}", regInfo, sourceId);
+            }
+
+            final String packageName = BindingReflections.getModelRootPackageName(regInfo.info.getClass().getPackage());
+            if (!packageToInfoReg.remove(packageName, regInfo)) {
+                LOG.warn("Failed to find {} registered under {}", regInfo, packageName);
+            }
+
+            regInfo.reg.close();
+        }
+    }
+
+    @Holding("this")
+    private static void removeImplicit(final List<AbstractRegisteredModuleInfo> regs) {
+        /*
+         * Search for implicit registration for a sourceId/packageName.
+         *
+         * Since we are called while an explicit registration is being created (and has already been inserted, we know
+         * there is at least one entry in the maps. We also know registrations retain the order in which they were
+         * created and that implicit registrations are not created if there already is a registration.
+         *
+         * This means that if an implicit registration exists, it will be the first entry in the list.
+         */
+        final AbstractRegisteredModuleInfo reg = regs.get(0);
+        if (reg instanceof ImplicitRegisteredModuleInfo) {
+            LOG.debug("Removing implicit registration {}", reg);
+            regs.remove(0);
+            reg.reg.close();
+        }
+    }
+
+    private static @NonNull YangTextSchemaSource toYangTextSource(final SourceIdentifier identifier,
+            final YangModuleInfo moduleInfo) {
+        return YangTextSchemaSource.delegateForByteSource(identifier, moduleInfo.getYangTextByteSource());
+    }
+
+    private static SourceIdentifier sourceIdentifierFrom(final YangModuleInfo moduleInfo) {
+        final QName name = moduleInfo.getName();
+        return RevisionSourceIdentifier.create(name.getLocalName(), name.getRevision());
+    }
+
+    private static @NonNull List<@NonNull YangModuleInfo> flatDependencies(final YangModuleInfo moduleInfo) {
+        // Flatten the modules being registered, with the triggering module being first...
+        final Set<YangModuleInfo> requiredInfos = new LinkedHashSet<>();
+        flatDependencies(requiredInfos, moduleInfo);
+
+        // ... now reverse the order in an effort to register dependencies first (triggering module last)
+        return ImmutableList.copyOf(requiredInfos).reverse();
+    }
+
+    private static void flatDependencies(final Set<YangModuleInfo> set, final YangModuleInfo moduleInfo) {
+        if (set.add(moduleInfo)) {
+            for (YangModuleInfo dep : moduleInfo.getImportedModules()) {
+                flatDependencies(set, dep);
+            }
+        }
+    }
+}
index b252b2e5cfd4dc52566b0942d387badd608caefc..c5ccd7483540de4eac04844f40abee7ffb8427e6 100644 (file)
@@ -56,7 +56,7 @@ public final class BindingRuntimeHelpers {
     }
 
     @SuppressWarnings("checkstyle:IllegalCatch")
-    static YangModuleInfo extractYangModuleInfo(final Class<?> clazz) {
+    static @NonNull YangModuleInfo extractYangModuleInfo(final Class<?> clazz) {
         try {
             return BindingReflections.getModuleInfo(clazz);
         } catch (Exception e) {
@@ -66,7 +66,7 @@ public final class BindingRuntimeHelpers {
 
     private static ModuleInfoBackedContext prepareContext(final Iterable<? extends YangModuleInfo> moduleInfos) {
         final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
-        ctx.addModuleInfos(moduleInfos);
+        ctx.registerModuleInfos(moduleInfos);
         return ctx;
     }
 }
diff --git a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/DefaultModuleInfoSnapshot.java b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/DefaultModuleInfoSnapshot.java
new file mode 100644 (file)
index 0000000..87c2e43
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.binding.runtime.spi;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+final class DefaultModuleInfoSnapshot extends GeneratedClassLoadingStrategy implements ModuleInfoSnapshot {
+    private final ImmutableMap<SourceIdentifier, YangModuleInfo> moduleInfos;
+    private final ImmutableMap<String, ClassLoader> classLoaders;
+    private final @NonNull EffectiveModelContext effectiveModel;
+
+    DefaultModuleInfoSnapshot(final EffectiveModelContext effectiveModel,
+            final Map<SourceIdentifier, YangModuleInfo> moduleInfos, final Map<String, ClassLoader> classLoaders) {
+        this.effectiveModel = requireNonNull(effectiveModel);
+        this.moduleInfos = ImmutableMap.copyOf(moduleInfos);
+        this.classLoaders = ImmutableMap.copyOf(classLoaders);
+    }
+
+    @Override
+    public EffectiveModelContext getEffectiveModelContext() {
+        return effectiveModel;
+    }
+
+    @Override
+    public ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
+        final YangModuleInfo info = moduleInfos.get(sourceIdentifier);
+        if (info == null) {
+            return Futures.immediateFailedFuture(
+                new MissingSchemaSourceException("No source registered", sourceIdentifier));
+        }
+        return Futures.immediateFuture(YangTextSchemaSource.delegateForByteSource(sourceIdentifier,
+                    info.getYangTextByteSource()));
+    }
+
+    @Override
+    public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
+        final String packageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
+        final ClassLoader loader = classLoaders.get(packageName);
+        if (loader == null) {
+            throw new ClassNotFoundException("Package " + packageName + " not found");
+        }
+        return loader.loadClass(fullyQualifiedName);
+    }
+}
index afb2afb3c2a19f71f82ea91c94aea020b7fb5996..31de1aac189b0acabf383db569e23d3c8815fb10 100644 (file)
@@ -11,54 +11,32 @@ import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import org.checkerframework.checker.lock.qual.GuardedBy;
 import org.checkerframework.checker.lock.qual.Holding;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.binding.runtime.api.BindingRuntimeGenerator;
 import org.opendaylight.binding.runtime.api.ClassLoadingStrategy;
 import org.opendaylight.binding.runtime.api.DefaultBindingRuntimeContext;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
 import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
-import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
-import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaSourceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 @Beta
-public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
-        implements ModuleInfoRegistry, EffectiveModelContextProvider, SchemaSourceProvider<YangTextSchemaSource> {
+public class ModuleInfoBackedContext extends AbstractModuleInfoTracker implements ClassLoadingStrategy,
+        EffectiveModelContextProvider, SchemaSourceProvider<YangTextSchemaSource> {
     private static final class WithFallback extends ModuleInfoBackedContext {
         private final @NonNull ClassLoadingStrategy fallback;
 
@@ -71,64 +49,11 @@ public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
         Class<?> loadUnknownClass(final String fullyQualifiedName) throws ClassNotFoundException {
             // We have not found a matching registration, consult the backing strategy
             final Class<?> cls = fallback.loadClass(fullyQualifiedName);
-            registerImplicitModuleInfo(BindingRuntimeHelpers.extractYangModuleInfo(cls));
+            registerImplicitBindingClass(cls);
             return cls;
         }
     }
 
-    private abstract static class AbstractRegisteredModuleInfo {
-        final YangTextSchemaSourceRegistration reg;
-        final YangModuleInfo info;
-        final ClassLoader loader;
-
-        AbstractRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
-            final ClassLoader loader) {
-            this.info = requireNonNull(info);
-            this.reg = requireNonNull(reg);
-            this.loader = requireNonNull(loader);
-        }
-
-        @Override
-        public final String toString() {
-            return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
-        }
-
-        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
-            return helper.add("info", info).add("registration", reg).add("classLoader", loader);
-        }
-    }
-
-    private static final class ExplicitRegisteredModuleInfo extends AbstractRegisteredModuleInfo {
-        private int refcount = 1;
-
-        ExplicitRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
-                final ClassLoader loader) {
-            super(info, reg, loader);
-        }
-
-        void incRef() {
-            ++refcount;
-        }
-
-        boolean decRef() {
-            return --refcount == 0;
-        }
-
-        @Override
-        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
-            return super.addToStringAttributes(helper).add("refCount", refcount);
-        }
-    }
-
-    private static final class ImplicitRegisteredModuleInfo extends AbstractRegisteredModuleInfo {
-        ImplicitRegisteredModuleInfo(final YangModuleInfo info, final YangTextSchemaSourceRegistration reg,
-                final ClassLoader loader) {
-            super(info, reg, loader);
-        }
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(ModuleInfoBackedContext.class);
-
     private static final LoadingCache<ClassLoadingStrategy,
         LoadingCache<ImmutableSet<YangModuleInfo>, ModuleInfoBackedContext>> CONTEXT_CACHES = CacheBuilder.newBuilder()
             .weakKeys().build(new CacheLoader<ClassLoadingStrategy,
@@ -141,24 +66,15 @@ public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
                                 @Override
                                 public ModuleInfoBackedContext load(final Set<YangModuleInfo> key) {
                                     final ModuleInfoBackedContext context = ModuleInfoBackedContext.create(strategy);
-                                    context.addModuleInfos(key);
+                                    context.registerModuleInfos(key);
                                     return context;
                                 }
                             });
                     }
             });
 
-    private final YangTextSchemaContextResolver ctxResolver;
-
-    @GuardedBy("this")
-    private final ListMultimap<String, AbstractRegisteredModuleInfo> packageToInfoReg =
-            MultimapBuilder.hashKeys().arrayListValues().build();
-    @GuardedBy("this")
-    private final ListMultimap<SourceIdentifier, AbstractRegisteredModuleInfo> sourceToInfoReg =
-            MultimapBuilder.hashKeys().arrayListValues().build();
-
     ModuleInfoBackedContext(final YangTextSchemaContextResolver resolver) {
-        this.ctxResolver = requireNonNull(resolver);
+        super(resolver);
     }
 
     @Beta
@@ -200,42 +116,15 @@ public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
     }
 
     @Override
-    @SuppressWarnings("checkstyle:illegalCatch")
-    public final Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
-        // This performs an explicit check for binding classes
-        final String modulePackageName = BindingReflections.getModelRootPackageName(fullyQualifiedName);
-
-        synchronized (this) {
-            // Try to find a loaded class loader
-            // FIXME: two-step process, try explicit registrations first
-            for (AbstractRegisteredModuleInfo reg : packageToInfoReg.get(modulePackageName)) {
-                return ClassLoaderUtils.loadClass(reg.loader, fullyQualifiedName);
-            }
-
-            return loadUnknownClass(fullyQualifiedName);
-        }
-    }
-
-    @Holding("this")
-    Class<?> loadUnknownClass(final String fullyQualifiedName) throws ClassNotFoundException {
-        throw new ClassNotFoundException(fullyQualifiedName);
-    }
-
-    @Override
-    public final synchronized ObjectRegistration<YangModuleInfo> registerModuleInfo(
-            final YangModuleInfo yangModuleInfo) {
-        return register(requireNonNull(yangModuleInfo));
+    public final synchronized Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
+        final ClassLoader loader = findClassLoader(fullyQualifiedName);
+        return loader != null ? ClassLoaderUtils.loadClass(loader, fullyQualifiedName)
+                : loadUnknownClass(fullyQualifiedName);
     }
 
     @Override
     public final ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
-        return ctxResolver.getSource(sourceIdentifier);
-    }
-
-    final synchronized void addModuleInfos(final Iterable<? extends YangModuleInfo> moduleInfos) {
-        for (YangModuleInfo yangModuleInfo : moduleInfos) {
-            register(requireNonNull(yangModuleInfo));
-        }
+        return getResolverSource(sourceIdentifier);
     }
 
     @Beta
@@ -248,160 +137,11 @@ public class ModuleInfoBackedContext extends GeneratedClassLoadingStrategy
     // Unite with current SchemaService
 
     public final Optional<? extends EffectiveModelContext> tryToCreateModelContext() {
-        return ctxResolver.getEffectiveModelContext();
+        return getResolverEffectiveModel();
     }
 
     @Holding("this")
-    private ObjectRegistration<YangModuleInfo> register(final @NonNull YangModuleInfo moduleInfo) {
-        final Builder<ExplicitRegisteredModuleInfo> regBuilder = ImmutableList.builder();
-        for (YangModuleInfo info : flatDependencies(moduleInfo)) {
-            regBuilder.add(registerExplicitModuleInfo(info));
-        }
-        final ImmutableList<ExplicitRegisteredModuleInfo> regInfos = regBuilder.build();
-
-        return new AbstractObjectRegistration<>(moduleInfo) {
-            @Override
-            protected void removeRegistration() {
-                unregister(regInfos);
-            }
-        };
-    }
-
-    /*
-     * Perform implicit registration of a YangModuleInfo and any of its dependencies. If there is a registration for
-     * a particular source, we do not create a duplicate registration.
-     */
-    @Holding("this")
-    final void registerImplicitModuleInfo(final @NonNull YangModuleInfo moduleInfo) {
-        for (YangModuleInfo info : flatDependencies(moduleInfo)) {
-            final Class<?> infoClass = info.getClass();
-            final SourceIdentifier sourceId = sourceIdentifierFrom(info);
-            if (sourceToInfoReg.containsKey(sourceId)) {
-                LOG.debug("Skipping implicit registration of {} as source {} is already registered", info, sourceId);
-                continue;
-            }
-
-            final YangTextSchemaSourceRegistration reg;
-            try {
-                reg = ctxResolver.registerSource(toYangTextSource(sourceId, info));
-            } catch (YangSyntaxErrorException | SchemaSourceException | IOException e) {
-                LOG.warn("Failed to register info {} source {}, ignoring it", info, sourceId, e);
-                continue;
-            }
-
-            final ImplicitRegisteredModuleInfo regInfo = new ImplicitRegisteredModuleInfo(info, reg,
-                infoClass.getClassLoader());
-            sourceToInfoReg.put(sourceId, regInfo);
-            packageToInfoReg.put(BindingReflections.getModelRootPackageName(infoClass.getPackage()), regInfo);
-        }
-    }
-
-    /*
-     * Perform explicit registration of a YangModuleInfo. This always results in a new explicit registration. In case
-     * there is a pre-existing implicit registration, it is removed just after the explicit registration is made.
-     */
-    @Holding("this")
-    private ExplicitRegisteredModuleInfo registerExplicitModuleInfo(final @NonNull YangModuleInfo info) {
-        // First search for an existing explicit registration
-        final SourceIdentifier sourceId = sourceIdentifierFrom(info);
-        for (AbstractRegisteredModuleInfo reg : sourceToInfoReg.get(sourceId)) {
-            if (reg instanceof ExplicitRegisteredModuleInfo && info.equals(reg.info)) {
-                final ExplicitRegisteredModuleInfo explicit = (ExplicitRegisteredModuleInfo) reg;
-                explicit.incRef();
-                LOG.debug("Reusing explicit registration {}", explicit);
-                return explicit;
-            }
-        }
-
-        // Create an explicit registration
-        final YangTextSchemaSourceRegistration reg;
-        try {
-            reg = ctxResolver.registerSource(toYangTextSource(sourceId, info));
-        } catch (YangSyntaxErrorException | SchemaSourceException | IOException e) {
-            throw new IllegalStateException("Failed to register info " + info, e);
-        }
-
-        final Class<?> infoClass = info.getClass();
-        final String packageName = BindingReflections.getModelRootPackageName(infoClass.getPackage());
-        final ExplicitRegisteredModuleInfo regInfo = new ExplicitRegisteredModuleInfo(info, reg,
-            infoClass.getClassLoader());
-        LOG.debug("Created new explicit registration {}", regInfo);
-
-        sourceToInfoReg.put(sourceId, regInfo);
-        removeImplicit(sourceToInfoReg.get(sourceId));
-        packageToInfoReg.put(packageName, regInfo);
-        removeImplicit(packageToInfoReg.get(packageName));
-
-        return regInfo;
-    }
-
-    final synchronized void unregister(final ImmutableList<ExplicitRegisteredModuleInfo> regInfos) {
-        for (ExplicitRegisteredModuleInfo regInfo : regInfos) {
-            if (!regInfo.decRef()) {
-                LOG.debug("Registration {} has references, not removing it", regInfo);
-                continue;
-            }
-
-            final SourceIdentifier sourceId = sourceIdentifierFrom(regInfo.info);
-            if (!sourceToInfoReg.remove(sourceId, regInfo)) {
-                LOG.warn("Failed to find {} registered under {}", regInfo, sourceId);
-            }
-
-            final String packageName = BindingReflections.getModelRootPackageName(regInfo.info.getClass().getPackage());
-            if (!packageToInfoReg.remove(packageName, regInfo)) {
-                LOG.warn("Failed to find {} registered under {}", regInfo, packageName);
-            }
-
-            regInfo.reg.close();
-        }
-    }
-
-    @Holding("this")
-    private static void removeImplicit(final List<AbstractRegisteredModuleInfo> regs) {
-        /*
-         * Search for implicit registration for a sourceId/packageName.
-         *
-         * Since we are called while an explicit registration is being created (and has already been inserted, we know
-         * there is at least one entry in the maps. We also know registrations retain the order in which they were
-         * created and that implicit registrations are not created if there already is a registration.
-         *
-         * This means that if an implicit registration exists, it will be the first entry in the list.
-         */
-        final AbstractRegisteredModuleInfo reg = regs.get(0);
-        if (reg instanceof ImplicitRegisteredModuleInfo) {
-            LOG.debug("Removing implicit registration {}", reg);
-            regs.remove(0);
-            reg.reg.close();
-        }
-    }
-
-    private static @NonNull YangTextSchemaSource toYangTextSource(final SourceIdentifier identifier,
-            final YangModuleInfo moduleInfo) {
-        return YangTextSchemaSource.delegateForByteSource(identifier, moduleInfo.getYangTextByteSource());
-    }
-
-    private static SourceIdentifier sourceIdentifierFrom(final YangModuleInfo moduleInfo) {
-        final QName name = moduleInfo.getName();
-        return RevisionSourceIdentifier.create(name.getLocalName(), name.getRevision());
-    }
-
-    private static List<YangModuleInfo> flatDependencies(final YangModuleInfo moduleInfo) {
-        // Flatten the modules being registered, with the triggering module being first...
-        final Set<YangModuleInfo> requiredInfos = new LinkedHashSet<>();
-        flatDependencies(requiredInfos, moduleInfo);
-
-        // ... now reverse the order in an effort to register dependencies first (triggering module last)
-        final List<YangModuleInfo> intendedOrder = new ArrayList<>(requiredInfos);
-        Collections.reverse(intendedOrder);
-
-        return intendedOrder;
-    }
-
-    private static void flatDependencies(final Set<YangModuleInfo> set, final YangModuleInfo moduleInfo) {
-        if (set.add(moduleInfo)) {
-            for (YangModuleInfo dep : moduleInfo.getImportedModules()) {
-                flatDependencies(set, dep);
-            }
-        }
+    Class<?> loadUnknownClass(final String fullyQualifiedName) throws ClassNotFoundException {
+        throw new ClassNotFoundException(fullyQualifiedName);
     }
 }
diff --git a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoRegistry.java b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoRegistry.java
deleted file mode 100644 (file)
index 8def478..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.binding.runtime.spi;
-
-import com.google.common.annotations.Beta;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
-
-@Beta
-public interface ModuleInfoRegistry {
-
-    ObjectRegistration<YangModuleInfo> registerModuleInfo(YangModuleInfo yangModuleInfo);
-}
diff --git a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoSnapshotBuilder.java b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/binding/runtime/spi/ModuleInfoSnapshotBuilder.java
new file mode 100644 (file)
index 0000000..96b38a2
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.binding.runtime.spi;
+
+import com.google.common.annotations.Beta;
+import java.util.NoSuchElementException;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.yangtools.concepts.CheckedBuilder;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
+
+@Beta
+public final class ModuleInfoSnapshotBuilder extends AbstractModuleInfoTracker
+        implements CheckedBuilder<ModuleInfoSnapshot, NoSuchElementException> {
+
+    public ModuleInfoSnapshotBuilder(final String name, final YangParserFactory parserFactory) {
+        super(YangTextSchemaContextResolver.create(name, parserFactory));
+    }
+
+    @Override
+    public synchronized ModuleInfoSnapshot build() {
+        return updateSnapshot();
+    }
+}
index bdcb0f4892ff24ca71d59ddd28cdee8dff5300c2..6c7e488cdcfefa27ea494b1151b654cd1b732117 100644 (file)
         <!-- Binding Runtime Services, dealing with class/schema mapping -->
         <module>mdsal-binding-runtime-api</module>
         <module>mdsal-binding-runtime-spi</module>
+        <module>mdsal-binding-runtime-osgi</module>
 
         <module>mdsal-binding-test-model</module>
         <module>mdsal-binding-dom-codec</module>
         <module>mdsal-binding-dom-codec-api</module>
         <module>mdsal-binding-dom-codec-spi</module>
-        <module>mdsal-binding-dom-codec-osgi</module>
 
         <module>mdsal-binding-api</module>
         <module>mdsal-binding-spi</module>
index 7f7b49e9afd1d0c55c6cce7857091f1be8c73852..fea9eda01a0375aefdc6a82603856b5186afe404 100644 (file)
@@ -44,7 +44,7 @@
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-dom-schema-service-osgi</artifactId>
+            <artifactId>mdsal-dom-schema-osgi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-dom-codec-osgi</artifactId>
+            <artifactId>mdsal-binding-runtime-api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-runtime-api</artifactId>
+            <artifactId>mdsal-binding-runtime-spi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-runtime-spi</artifactId>
+            <artifactId>mdsal-binding-runtime-osgi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
index f715cc8becf8cdac2c6bdcbb84e6671e84f6b2b7..029d354a639cda4e9d44f4887c92799fdebcbca6 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Deprecated
 public class ScanningSchemaServiceProvider extends AbstractDOMSchemaService.WithYangTextSources
         implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(ScanningSchemaServiceProvider.class);
similarity index 82%
rename from dom/mdsal-dom-schema-service-osgi/pom.xml
rename to dom/mdsal-dom-schema-osgi/pom.xml
index ef7a022ce0aba6d02f6b137d379af9b209819f5e..6bf2e1aa7cffe2f3fa89e676141fc6c0425289a4 100644 (file)
         <relativePath>../dom-parent</relativePath>
     </parent>
 
-    <artifactId>mdsal-dom-schema-service-osgi</artifactId>
+    <artifactId>mdsal-dom-schema-osgi</artifactId>
     <packaging>bundle</packaging>
 
     <dependencies>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-dom-api</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-dom-api</artifactId>
+            <artifactId>mdsal-binding-runtime-spi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-dom-broker</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>util</artifactId>
             <artifactId>yang-parser-impl</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>yang-test-util</artifactId>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
         </dependency>
 
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>mockito-configuration</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
                 <configuration>
+                    <Automatic-Module-Name>org.opendaylight.mdsal.dom.schema.osgi</Automatic-Module-Name>
                     <instructions>
-                        <Bundle-Name>osgiBundleScanningSchema</Bundle-Name>
-                        <Import-Package>
-                            *,
-                            org.opendaylight.mdsal.dom.api
-                        </Import-Package>
+                        <!-- Karaf cannot handle Factory Component requirements, see https://issues.apache.org/jira/browse/KARAF-6625 -->
+                        <_dsannotations-options>norequirements</_dsannotations-options>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/OSGiModuleInfoSnapshot.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/OSGiModuleInfoSnapshot.java
new file mode 100644 (file)
index 0000000..8f16b23
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+
+/**
+ * Combination of a {@link ModuleInfoSnapshot} with a linear generation.
+ */
+@Beta
+public interface OSGiModuleInfoSnapshot extends ModuleInfoSnapshot {
+
+    long getGeneration();
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiDOMSchemaService.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiDOMSchemaService.java
new file mode 100644 (file)
index 0000000..768ad5f
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.schema.osgi.OSGiModuleInfoSnapshot;
+import org.opendaylight.mdsal.dom.spi.AbstractDOMSchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.FieldOption;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OSGi Service Registry-backed implementation of {@link DOMSchemaService}.
+ */
+@Component(service = DOMSchemaService.class, immediate = true)
+public final class OSGiDOMSchemaService extends AbstractDOMSchemaService {
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiDOMSchemaService.class);
+
+    @Reference(target = "(component.factory=" + SchemaSchemaContextListenerImpl.FACTORY_NAME + ")")
+    ComponentFactory listenerFactory = null;
+
+    private final List<SchemaContextListener> listeners = new CopyOnWriteArrayList<>();
+
+    private volatile OSGiModuleInfoSnapshot currentContext;
+
+    @Override
+    public EffectiveModelContext getGlobalContext() {
+        return currentContext.getEffectiveModelContext();
+    }
+
+    @Override
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+            final SchemaContextListener listener) {
+        return registerListener(requireNonNull(listener));
+    }
+
+    @Reference(fieldOption = FieldOption.REPLACE)
+    void bindContext(final OSGiModuleInfoSnapshot newContext) {
+        final EffectiveModelContext ctx = newContext.getEffectiveModelContext();
+        LOG.trace("Updating context to {}", ctx);
+        currentContext = newContext;
+        listeners.forEach(listener -> notifyListener(ctx, listener));
+    }
+
+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC,
+            policyOption = ReferencePolicyOption.GREEDY)
+    void addListener(final SchemaContextListener listener) {
+        LOG.trace("Adding listener {}", listener);
+        listeners.add(listener);
+        listener.onGlobalContextUpdated(getGlobalContext());
+    }
+
+    void removeListener(final SchemaContextListener listener) {
+        LOG.trace("Removing listener {}", listener);
+        listeners.remove(listener);
+    }
+
+    @Activate
+    @SuppressWarnings("static-method")
+    void activate() {
+        LOG.info("DOM Schema services activated");
+    }
+
+    @Deactivate
+    @SuppressWarnings("static-method")
+    void deactivate() {
+        LOG.info("DOM Schema services deactivated");
+    }
+
+    private @NonNull ListenerRegistration<SchemaContextListener> registerListener(
+            final @NonNull SchemaContextListener listener) {
+        final ComponentInstance reg = listenerFactory.newInstance(SchemaSchemaContextListenerImpl.props(listener));
+        return new ListenerRegistration<>() {
+            @Override
+            public SchemaContextListener getInstance() {
+                return listener;
+            }
+
+            @Override
+            public void close() {
+                reg.dispose();
+            }
+        };
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private static void notifyListener(final SchemaContext context, final SchemaContextListener listener) {
+        try {
+            listener.onGlobalContextUpdated(context);
+        } catch (RuntimeException e) {
+            LOG.warn("Failed to notify listener {}", listener, e);
+        }
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiEffectiveModelImpl.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiEffectiveModelImpl.java
new file mode 100644 (file)
index 0000000..96b58a5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.mdsal.dom.schema.osgi.OSGiModuleInfoSnapshot;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+@Component(factory = OSGiEffectiveModelImpl.FACTORY_NAME,
+           service = { OSGiModuleInfoSnapshot.class, ModuleInfoSnapshot.class })
+public final class OSGiEffectiveModelImpl implements OSGiModuleInfoSnapshot {
+    // OSGi DS Component Factory name
+    static final String FACTORY_NAME = "org.opendaylight.mdsal.dom.schema.osgi.impl.OSGiEffectiveModelImpl";
+
+    // Keys to for activation properties
+    @VisibleForTesting
+    static final String GENERATION = "org.opendaylight.mdsal.dom.schema.osgi.impl.Generation";
+    @VisibleForTesting
+    static final String DELEGATE = "org.opendaylight.mdsal.dom.schema.osgi.impl.ModuleInfoSnapshot";
+
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiEffectiveModelImpl.class);
+
+    private ModuleInfoSnapshot delegate;
+    private long generation;
+
+    @Override
+    public long getGeneration() {
+        return generation;
+    }
+
+    @Override
+    public EffectiveModelContext getEffectiveModelContext() {
+        return delegate.getEffectiveModelContext();
+    }
+
+    @Override
+    public ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
+        return delegate.getSource(sourceIdentifier);
+    }
+
+    @Override
+    public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
+        return delegate.loadClass(fullyQualifiedName);
+    }
+
+    @Activate
+    void activate(final Map<String, ?> properties) {
+        generation = (Long) verifyNotNull(properties.get(GENERATION));
+        delegate = (ModuleInfoSnapshot) verifyNotNull(properties.get(DELEGATE));
+        LOG.debug("ClassLoadingEffectiveModelContext generation {} activated", generation);
+    }
+
+    @Deactivate
+    void deactivate() {
+        delegate = null;
+        LOG.debug("ClassLoadingEffectiveModelContext generation {} deactivated", generation);
+    }
+
+    @SuppressModernizer
+    static Dictionary<String, ?> props(final long generation, final ModuleInfoSnapshot delegate) {
+        final Dictionary<String, Object> ret = new Hashtable<>(4);
+        ret.put(Constants.SERVICE_RANKING, ranking(generation));
+        ret.put(GENERATION, generation);
+        ret.put(DELEGATE, requireNonNull(delegate));
+        return ret;
+    }
+
+    private static Integer ranking(final long generation) {
+        return generation >= 0 && generation <= Integer.MAX_VALUE ? (int) generation : Integer.MAX_VALUE;
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntime.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntime.java
new file mode 100644 (file)
index 0000000..3bea8c0
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(immediate = true)
+public final class OSGiModelRuntime {
+    private static final Logger LOG = LoggerFactory.getLogger(OSGiModelRuntime.class);
+
+    @Reference
+    YangParserFactory parserFactory = null;
+    @Reference(target = "(component.factory=" + OSGiEffectiveModelImpl.FACTORY_NAME + ")")
+    ComponentFactory contextFactory = null;
+
+    private YangModuleInfoScanner bundleTracker = null;
+    private YangModuleInfoRegistry moduleRegistry = null;
+
+    @Activate
+    void activate(final BundleContext ctx) {
+        LOG.info("Model Runtime starting");
+        moduleRegistry = new YangModuleInfoRegistry(contextFactory, parserFactory);
+        bundleTracker = new YangModuleInfoScanner(ctx, moduleRegistry);
+        bundleTracker.open();
+        moduleRegistry.enableScannerAndUpdate();
+        LOG.info("Model Runtime started");
+    }
+
+    @Deactivate
+    void deactivate() {
+        LOG.info("Model Runtime stopping");
+        moduleRegistry.close();
+        bundleTracker.close();
+        LOG.info("Model Runtime stopped");
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/SchemaSchemaContextListenerImpl.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/SchemaSchemaContextListenerImpl.java
new file mode 100644 (file)
index 0000000..6f4eeaa
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+
+/**
+ * A Factory Component which implements {@link SchemaContextListener}. Instances of this component are created through
+ * by {@link OSGiDOMSchemaService} each time a listener is registered.
+ */
+@Component(factory = SchemaSchemaContextListenerImpl.FACTORY_NAME, service = SchemaContextListener.class)
+public final class SchemaSchemaContextListenerImpl implements SchemaContextListener {
+    static final String FACTORY_NAME = "org.opendaylight.mdsal.dom.schema.osgi.impl.SchemaSchemaContextListener";
+
+    @VisibleForTesting
+    static final String DELEGATE = "org.opendaylight.mdsal.dom.schema.osgi.SchemaSchemaContextListener";
+
+    private SchemaContextListener delegate = null;
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext context) {
+        delegate.onGlobalContextUpdated(context);
+    }
+
+    @Activate
+    void activate(final Map<String, ?> properties) {
+        delegate = (SchemaContextListener) verifyNotNull(properties.get(DELEGATE));
+    }
+
+    @Deactivate
+    void deactivate() {
+        delegate = null;
+    }
+
+    @SuppressModernizer
+    static Dictionary<String, ?> props(final SchemaContextListener delegate) {
+        final Dictionary<String, Object> ret = new Hashtable<>(2);
+        ret.put(DELEGATE, requireNonNull(delegate));
+        return ret;
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoRegistry.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoRegistry.java
new file mode 100644 (file)
index 0000000..de1b4ad
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.opendaylight.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.binding.runtime.spi.ModuleInfoSnapshotBuilder;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.osgi.service.component.ComponentFactory;
+import org.osgi.service.component.ComponentInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Update SchemaContext service in Service Registry each time new YangModuleInfo is added or removed.
+ */
+final class YangModuleInfoRegistry {
+    private static final Logger LOG = LoggerFactory.getLogger(YangModuleInfoRegistry.class);
+
+    private final ComponentFactory contextFactory;
+    private final ModuleInfoSnapshotBuilder moduleInfoRegistry;
+
+    @GuardedBy("this")
+    private ComponentInstance currentInstance;
+    @GuardedBy("this")
+    private ModuleInfoSnapshot currentSnapshot;
+    @GuardedBy("this")
+    private int generation;
+
+    private volatile boolean ignoreScanner = true;
+
+    YangModuleInfoRegistry(final ComponentFactory contextFactory, final YangParserFactory factory) {
+        this.contextFactory = requireNonNull(contextFactory);
+        moduleInfoRegistry = new ModuleInfoSnapshotBuilder("binding-dom-codec", factory);
+    }
+
+    // Invocation from scanner, we may want to ignore this in order to not process partial updates
+    void scannerUpdate() {
+        if (!ignoreScanner) {
+            synchronized (this) {
+                updateService();
+            }
+        }
+    }
+
+    synchronized void scannerShutdown() {
+        ignoreScanner = true;
+    }
+
+    synchronized void enableScannerAndUpdate() {
+        ignoreScanner = false;
+        updateService();
+    }
+
+    synchronized void close() {
+        ignoreScanner = true;
+        if (currentInstance != null) {
+            currentInstance.dispose();
+            currentInstance = null;
+        }
+    }
+
+    List<ObjectRegistration<YangModuleInfo>> registerInfos(final List<YangModuleInfo> infos) {
+        return moduleInfoRegistry.registerModuleInfos(infos);
+    }
+
+    @Holding("this")
+    private void updateService() {
+        final ModuleInfoSnapshot newSnapshot;
+        try {
+            newSnapshot = moduleInfoRegistry.build();
+        } catch (NoSuchElementException e) {
+            LOG.debug("No snapshot available", e);
+            return;
+        }
+        if (newSnapshot.equals(currentSnapshot)) {
+            LOG.debug("No update to snapshot");
+            return;
+        }
+
+
+        final ComponentInstance newInstance = contextFactory.newInstance(
+            OSGiEffectiveModelImpl.props(nextGeneration(), newSnapshot));
+        if (currentInstance != null) {
+            currentInstance.dispose();
+        }
+        currentInstance = newInstance;
+        currentSnapshot = newSnapshot;
+    }
+
+    @Holding("this")
+    private long nextGeneration() {
+        return generation == -1 ? -1 : ++generation;
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoScanner.java b/dom/mdsal-dom-schema-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/osgi/impl/YangModuleInfoScanner.java
new file mode 100644 (file)
index 0000000..1033e4d
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Resources;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.util.tracker.BundleTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry.
+ */
+final class YangModuleInfoScanner extends BundleTracker<List<ObjectRegistration<YangModuleInfo>>> {
+    private static final Logger LOG = LoggerFactory.getLogger(YangModuleInfoScanner.class);
+    // FIXME: this should be in a place shared with maven-sal-api-gen-plugin
+    private static final String MODULE_INFO_PROVIDER_PATH_PREFIX = "META-INF/services/";
+
+    private static final String YANG_MODLE_BINDING_PROVIDER_SERVICE = MODULE_INFO_PROVIDER_PATH_PREFIX
+            + YangModelBindingProvider.class.getName();
+
+    private final YangModuleInfoRegistry moduleInfoRegistry;
+
+    YangModuleInfoScanner(final BundleContext context, final YangModuleInfoRegistry moduleInfoRegistry) {
+        super(context, Bundle.RESOLVED | Bundle.STARTING | Bundle.STOPPING | Bundle.ACTIVE, null);
+        this.moduleInfoRegistry = requireNonNull(moduleInfoRegistry);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public List<ObjectRegistration<YangModuleInfo>> addingBundle(final Bundle bundle, final BundleEvent event) {
+        if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
+            LOG.debug("Ignoring system bundle {}", bundle);
+            return ImmutableList.of();
+        }
+
+        final URL resource = bundle.getEntry(YANG_MODLE_BINDING_PROVIDER_SERVICE);
+        if (resource == null) {
+            LOG.debug("Bundle {} does not have an entry for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
+            return ImmutableList.of();
+        }
+
+        LOG.debug("Got addingBundle({}) with YangModelBindingProvider resource {}", bundle, resource);
+        final List<String> lines;
+        try {
+            lines = Resources.readLines(resource, StandardCharsets.UTF_8);
+        } catch (IOException e) {
+            LOG.error("Error while reading {} from bundle {}", resource, bundle, e);
+            return ImmutableList.of();
+        }
+
+        if (lines.isEmpty()) {
+            LOG.debug("Bundle {} has empty services for {}", bundle, YANG_MODLE_BINDING_PROVIDER_SERVICE);
+            return ImmutableList.of();
+        }
+
+        final List<YangModuleInfo> infos = new ArrayList<>(lines.size());
+        for (String moduleInfoName : lines) {
+            LOG.trace("Retrieve ModuleInfo({}, {})", moduleInfoName, bundle);
+            final YangModuleInfo moduleInfo;
+            try {
+                moduleInfo = retrieveModuleInfo(moduleInfoName, bundle);
+            } catch (ScanningException e) {
+                LOG.warn("Failed to acquire {} from bundle {}, ignoring it", moduleInfoName, bundle, e);
+                continue;
+            }
+
+            infos.add(moduleInfo);
+        }
+
+        final List<ObjectRegistration<YangModuleInfo>> registrations = moduleInfoRegistry.registerInfos(infos);
+        LOG.trace("Bundle {} resulted in registrations {}", bundle, registrations);
+        moduleInfoRegistry.scannerUpdate();
+        return registrations;
+    }
+
+    @Override
+    public void modifiedBundle(final Bundle bundle, final BundleEvent event,
+            final List<ObjectRegistration<YangModuleInfo>> regs) {
+        if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
+            LOG.debug("Framework bundle {} got event {}", bundle, event.getType());
+            if ((event.getType() & BundleEvent.STOPPING) != 0) {
+                LOG.info("OSGi framework is being stopped, halting bundle scanning");
+                moduleInfoRegistry.scannerShutdown();
+            }
+        }
+    }
+
+
+    @Override
+    public void removedBundle(final Bundle bundle, final BundleEvent event,
+            final List<ObjectRegistration<YangModuleInfo>> regs) {
+        regs.forEach(ObjectRegistration::close);
+        moduleInfoRegistry.scannerUpdate();
+    }
+
+    private static YangModuleInfo retrieveModuleInfo(final String className, final Bundle bundle)
+            throws ScanningException {
+        final Class<?> loadedClass;
+        try {
+            loadedClass = bundle.loadClass(className);
+        } catch (ClassNotFoundException e) {
+            throw new ScanningException(e, "Failed to load class %s", className);
+        }
+
+        final Class<? extends YangModelBindingProvider> providerClass;
+        try {
+            providerClass = loadedClass.asSubclass(YangModelBindingProvider.class);
+        } catch (ClassCastException e) {
+            throw new ScanningException(e, "Failed to validate %s", loadedClass);
+        }
+
+        final Constructor<? extends YangModelBindingProvider> ctor;
+        try {
+            ctor = providerClass.getDeclaredConstructor();
+        } catch (NoSuchMethodException e) {
+            throw new ScanningException(e, "%s does not have a no-argument constructor", providerClass);
+        } catch (SecurityException e) {
+            throw new ScanningException(e, "Failed to reflect on %s", providerClass);
+        }
+
+        YangModelBindingProvider instance;
+        try {
+            instance = ctor.newInstance();
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                | InvocationTargetException e) {
+            throw new ScanningException(e, "Failed to instantiate %s", providerClass);
+        }
+
+        return instance.getModuleInfo();
+    }
+
+    @NonNullByDefault
+    private static final class ScanningException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        ScanningException(final Exception cause, final String format, final Object... args) {
+            super(String.format(format, args), cause);
+        }
+    }
+}
diff --git a/dom/mdsal-dom-schema-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntimeTest.java b/dom/mdsal-dom-schema-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/osgi/impl/OSGiModelRuntimeTest.java
new file mode 100644 (file)
index 0000000..da2f543
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.schema.osgi.impl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentFactory;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class OSGiModelRuntimeTest {
+    @Mock
+    private YangParserFactory parserFactory;
+    @Mock
+    private ComponentFactory contextFactory;
+    @Mock
+    private BundleContext bundleContext;
+
+    private OSGiModelRuntime target;
+
+    @Before
+    public void before() {
+        target = new OSGiModelRuntime();
+        target.parserFactory = parserFactory;
+        target.contextFactory = contextFactory;
+    }
+
+    @After
+    public void after() {
+        verifyNoMoreInteractions(parserFactory);
+        verifyNoMoreInteractions(contextFactory);
+        verifyNoMoreInteractions(bundleContext);
+    }
+
+    @Test
+    public void testActivate() {
+        doReturn(new Bundle[0]).when(bundleContext).getBundles();
+        doNothing().when(bundleContext).addBundleListener(any());
+        target.activate(bundleContext);
+    }
+
+    @Test
+    public void testDeactivate() {
+        testActivate();
+        doNothing().when(bundleContext).removeBundleListener(any());
+        target.deactivate();
+    }
+}
similarity index 98%
rename from dom/mdsal-dom-schema-service-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/service/osgi/util/TestModel.java
rename to dom/mdsal-dom-schema-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/osgi/impl/TestModel.java
index 189e1357e8b88c272ed54f3ed9e0937083985c11..f136e4976ca7a36fdf072bfaa5134a20d1f4ca6a 100644 (file)
@@ -5,7 +5,7 @@
  * 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.mdsal.dom.schema.service.osgi.util;
+package org.opendaylight.mdsal.dom.schema.osgi.impl;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
diff --git a/dom/mdsal-dom-schema-service-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaService.java b/dom/mdsal-dom-schema-service-osgi/src/main/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaService.java
deleted file mode 100644 (file)
index 727d790..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.dom.schema.service.osgi;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.dom.broker.schema.ScanningSchemaServiceProvider;
-import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.BundleTracker;
-import org.osgi.util.tracker.BundleTrackerCustomizer;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public final class OsgiBundleScanningSchemaService extends ScanningSchemaServiceProvider
-        implements ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener> {
-
-    private static final Logger LOG = LoggerFactory.getLogger(OsgiBundleScanningSchemaService.class);
-    private static final AtomicReference<OsgiBundleScanningSchemaService> GLOBAL_INSTANCE = new AtomicReference<>();
-
-    private final BundleScanner scanner = new BundleScanner();
-    private final BundleContext context;
-
-    @GuardedBy("lock")
-    private BundleTracker<Iterable<Registration>> bundleTracker;
-    private final Object lock = new Object();
-
-    private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
-    private boolean starting = true;
-
-    private volatile boolean stopping;
-
-    private OsgiBundleScanningSchemaService(final BundleContext context) {
-        this.context = requireNonNull(context);
-    }
-
-    public static @NonNull OsgiBundleScanningSchemaService createInstance(final BundleContext ctx) {
-        final OsgiBundleScanningSchemaService instance = new OsgiBundleScanningSchemaService(ctx);
-        checkState(GLOBAL_INSTANCE.compareAndSet(null, instance));
-        instance.start();
-        return instance;
-    }
-
-    private void start() {
-        checkState(context != null);
-        LOG.debug("start() starting");
-
-        listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, this);
-        bundleTracker = new BundleTracker<>(context,
-                Bundle.RESOLVED | Bundle.STARTING | Bundle.STOPPING | Bundle.ACTIVE, scanner);
-
-        synchronized (lock) {
-            bundleTracker.open();
-
-            LOG.debug("BundleTracker.open() complete");
-
-            if (!hasListeners()) {
-                tryToUpdateSchemaContext();
-            }
-        }
-
-        listenerTracker.open();
-        starting = false;
-
-        LOG.debug("start() complete");
-    }
-
-    public static OsgiBundleScanningSchemaService getInstance() {
-        final OsgiBundleScanningSchemaService instance = GLOBAL_INSTANCE.get();
-        checkState(instance != null, "Global Instance was not instantiated");
-        return instance;
-    }
-
-    @VisibleForTesting
-    public static void destroyInstance() {
-        final OsgiBundleScanningSchemaService instance = GLOBAL_INSTANCE.getAndSet(null);
-        if (instance != null) {
-            instance.closeInstance();
-        }
-    }
-
-    private void closeInstance() {
-        stopping = true;
-        if (bundleTracker != null) {
-            bundleTracker.close();
-            bundleTracker = null;
-        }
-        if (listenerTracker != null) {
-            listenerTracker.close();
-            listenerTracker = null;
-        }
-        close();
-    }
-
-    public BundleContext getContext() {
-        return context;
-    }
-
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
-        @Override
-        public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
-
-            if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
-                return Collections.emptyList();
-            }
-
-            final Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
-            if (enumeration == null) {
-                return Collections.emptyList();
-            }
-
-            final List<URL> urls = new ArrayList<>();
-            while (enumeration.hasMoreElements()) {
-                final URL u = enumeration.nextElement();
-                try {
-                    urls.add(u);
-                    LOG.debug("Registered {}", u);
-                } catch (final Exception e) {
-                    LOG.warn("Failed to register {}, ignoring it", u, e);
-                }
-            }
-
-            final List<Registration> registrations = registerAvailableYangs(urls);
-            if (!registrations.isEmpty()) {
-                LOG.debug("Loaded {} new URLs from bundle {}, attempting to rebuild schema context",
-                        registrations.size(), bundle.getSymbolicName());
-                if (!starting && !stopping) {
-                    tryToUpdateSchemaContext();
-                }
-            }
-            return ImmutableList.copyOf(registrations);
-        }
-
-        @Override
-        public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
-            if (bundle.getBundleId() == Constants.SYSTEM_BUNDLE_ID) {
-                LOG.debug("Framework bundle {} got event {}", bundle, event.getType());
-                if ((event.getType() & BundleEvent.STOPPING) != 0) {
-                    LOG.info("OSGi framework is being stopped, halting bundle scanning");
-                    stopping = true;
-                }
-            }
-        }
-
-        /**
-         * If removing YANG files makes yang store inconsistent, method {@link #getYangStoreSnapshot()} will
-         * throw exception. There is no rollback.
-         */
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        @Override
-        public void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
-            for (final Registration url : urls) {
-                try {
-                    url.close();
-                } catch (final Exception e) {
-                    LOG.warn("Failed do unregister URL {}, proceeding", url, e);
-                }
-            }
-
-            final int numUrls = Iterables.size(urls);
-            if (numUrls > 0) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(), bundle.getState(),
-                            numUrls);
-                }
-                if (!starting && !stopping) {
-                    tryToUpdateSchemaContext();
-                }
-            }
-        }
-    }
-
-    @Override
-    public SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
-        final SchemaContextListener listener = context.getService(reference);
-        registerSchemaContextListener(listener);
-        return listener;
-    }
-
-    @Override
-    public void modifiedService(final ServiceReference<SchemaContextListener> reference,
-            final SchemaContextListener service) {
-        // NOOP
-    }
-
-    @Override
-    public void removedService(final ServiceReference<SchemaContextListener> reference,
-            final SchemaContextListener service) {
-        context.ungetService(reference);
-        removeListener(service);
-    }
-}
diff --git a/dom/mdsal-dom-schema-service-osgi/src/main/resources/org/opendaylight/blueprint/dom-osgi-schema-service.xml b/dom/mdsal-dom-schema-service-osgi/src/main/resources/org/opendaylight/blueprint/dom-osgi-schema-service.xml
deleted file mode 100644 (file)
index ae3f347..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0">
-
-  <bean id="osgiBundleScanningSchema" class="org.opendaylight.mdsal.dom.schema.service.osgi.OsgiBundleScanningSchemaService" factory-method="createInstance" destroy-method="close">
-    <argument ref="blueprintBundleContext" />
-  </bean>
-
-  <service ref="osgiBundleScanningSchema" interface="org.opendaylight.mdsal.dom.api.DOMSchemaService" odl:type="default" />
-
-</blueprint>
diff --git a/dom/mdsal-dom-schema-service-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaServiceTest.java b/dom/mdsal-dom-schema-service-osgi/src/test/java/org/opendaylight/mdsal/dom/schema/service/osgi/OsgiBundleScanningSchemaServiceTest.java
deleted file mode 100644 (file)
index 373a02a..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.dom.schema.service.osgi;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.schema.service.osgi.util.TestModel;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Filter;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-public class OsgiBundleScanningSchemaServiceTest {
-
-    private OsgiBundleScanningSchemaService osgiService;
-    private final BundleContext bundleContext = mock(BundleContext.class, "bundleContext");
-
-    @Before
-    public void setUp() throws InvalidSyntaxException {
-        destroyInstance();
-        doReturn(mock(Filter.class)).when(bundleContext).createFilter(any());
-        doNothing().when(bundleContext).addBundleListener(any());
-        doReturn(new Bundle[] {}).when(bundleContext).getBundles();
-        doNothing().when(bundleContext).addServiceListener(any(), any());
-        doReturn(new ServiceReference<?>[] {}).when(bundleContext).getServiceReferences(anyString(), any());
-        doNothing().when(bundleContext).removeBundleListener(any());
-        doNothing().when(bundleContext).removeServiceListener(any());
-        osgiService = OsgiBundleScanningSchemaService.createInstance(bundleContext);
-        assertEquals(osgiService, OsgiBundleScanningSchemaService.getInstance());
-        assertEquals(bundleContext, osgiService.getContext());
-    }
-
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    @After
-    public void destroyInstance() {
-        try {
-            OsgiBundleScanningSchemaService.getInstance();
-            OsgiBundleScanningSchemaService.destroyInstance();
-        } catch (final Exception e) {
-            assertTrue(e instanceof IllegalStateException);
-        }
-    }
-
-    @Test
-    public void basicTest() {
-        assertTrue(osgiService instanceof DOMSchemaService);
-
-        final SchemaContext schemaContext = TestModel.createTestContext();
-
-        final SchemaContextListener schemaContextListener = mock(SchemaContextListener.class);
-        doNothing().when(schemaContextListener).onGlobalContextUpdated(schemaContext);
-        osgiService.registerSchemaContextListener(schemaContextListener);
-
-        osgiService.notifyListeners(schemaContext);
-
-        doReturn(schemaContextListener).when(bundleContext).getService(null);
-        assertEquals(schemaContextListener, osgiService.addingService(null));
-
-        osgiService.registerSchemaContextListener(schemaContextListener);
-        assertNull(osgiService.getSchemaContext());
-
-        doReturn(false).when(bundleContext).ungetService(null);
-        osgiService.removedService(null, null);
-        verify(bundleContext).ungetService(any());
-
-        osgiService.close();
-    }
-
-    @Test(expected = UnsupportedOperationException.class)
-    public void sessionContextTest() {
-        osgiService.getSessionContext();
-    }
-}
index 7cc9f472f5c97c091f291f11427e3b3128ccf509..9231151804485ceac7dd7584a5effcbd6c0a7700 100644 (file)
@@ -29,7 +29,7 @@
         <module>mdsal-dom-spi</module>
         <module>mdsal-dom-broker</module>
         <module>mdsal-dom-inmemory-datastore</module>
-        <module>mdsal-dom-schema-service-osgi</module>
+        <module>mdsal-dom-schema-osgi</module>
     </modules>
 
     <properties>
diff --git a/features/odl-mdsal-binding-runtime-api/pom.xml b/features/odl-mdsal-binding-runtime-api/pom.xml
new file mode 100644 (file)
index 0000000..f84fe8b
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright © 2016, 2017 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.mdsal</groupId>
+        <artifactId>feature-parent</artifactId>
+        <version>6.0.0-SNAPSHOT</version>
+        <relativePath>../feature-parent</relativePath>
+    </parent>
+
+    <artifactId>odl-mdsal-binding-runtime-api</artifactId>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: MD-SAL :: Binding Runtime APIs</name>
+    <description>MD-SAL Java Binding runtime APIs</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>odl-mdsal-binding-base</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-generator-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-generator-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-runtime-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-runtime-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-codec-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-codec-spi</artifactId>
+        </dependency>
+    </dependencies>
+</project>
index 6618dd3cdc17e83d52c61bbe0ea2995fac9d1c17..3820001189c1bab506d3267cd166faf118fd219c 100644 (file)
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-generator-api</artifactId>
+            <artifactId>odl-mdsal-binding-runtime-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-generator-impl</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-generator-util</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-dom-codec-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-dom-codec-spi</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-dom-codec</artifactId>
@@ -73,7 +63,7 @@
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-binding-dom-codec-osgi</artifactId>
+            <artifactId>mdsal-binding-runtime-osgi</artifactId>
         </dependency>
     </dependencies>
 </project>
index e74b3e473359f7c722bf5710402df8736b934305..93b820ee3e807340648f301accc503a3a9af8e6b 100644 (file)
             <type>xml</type>
             <classifier>features</classifier>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>odl-mdsal-binding-runtime-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-dom-broker</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
-            <artifactId>mdsal-dom-schema-service-osgi</artifactId>
+            <artifactId>mdsal-dom-schema-osgi</artifactId>
         </dependency>
     </dependencies>
 </project>
index 6e54e76c338ba644d67a9d1c2497d3ea39b448ad..005a04c934f8cbe3969be2023e7106cb890cc305 100644 (file)
@@ -33,6 +33,7 @@
         <module>odl-mdsal-binding-base</module>
         <module>odl-mdsal-binding-dom-adapter</module>
         <module>odl-mdsal-binding-runtime</module>
+        <module>odl-mdsal-binding-runtime-api</module>
 
         <!-- Common -->
         <module>odl-mdsal-common</module>