</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>
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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();
- }
- }
-}
--- /dev/null
+/*
+ * 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> {
+
+}
<?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>
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+}
}
@SuppressWarnings("checkstyle:IllegalCatch")
- static YangModuleInfo extractYangModuleInfo(final Class<?> clazz) {
+ static @NonNull YangModuleInfo extractYangModuleInfo(final Class<?> clazz) {
try {
return BindingReflections.getModuleInfo(clazz);
} catch (Exception e) {
private static ModuleInfoBackedContext prepareContext(final Iterable<? extends YangModuleInfo> moduleInfos) {
final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
- ctx.addModuleInfos(moduleInfos);
+ ctx.registerModuleInfos(moduleInfos);
return ctx;
}
}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
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,
@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
}
@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
// 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);
}
}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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();
+ }
+}
<!-- 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>
</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>
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);
<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>
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
* 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;
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-<?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>
+++ /dev/null
-/*
- * 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();
- }
-}
<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>
--- /dev/null
+<?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>
</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>
</dependency>
<dependency>
<groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-binding-dom-codec-osgi</artifactId>
+ <artifactId>mdsal-binding-runtime-osgi</artifactId>
</dependency>
</dependencies>
</project>
<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>
<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>