<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>org.apache.karaf.features.core</artifactId>
+ <scope>provided</scope>
+ <!-- Needed to generate optional OSGi import -->
+ <optional>true</optional>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
--- /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.apache.karaf.features.FeaturesService;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Optional support for Karaf's FeaturesService. This class centralizes wrapping based on bundle resolution state. If
+ * FeaturesService interface is not resolved, this class ends up reusing RegularYangModuleInfoRegistry. If the interface
+ * is resolved, we use it to locate the appropriate service whenever we are asked to activate.
+ */
+@NonNullByDefault
+final class KarafFeaturesSupport {
+ @FunctionalInterface
+ private interface WrapperFunction {
+ YangModuleInfoRegistry wrap(BundleContext ctx, RegularYangModuleInfoRegistry delegate);
+ }
+
+ private static final class NoopWrapperFunction implements WrapperFunction {
+ @Override
+ public YangModuleInfoRegistry wrap(final BundleContext ctx, final RegularYangModuleInfoRegistry delegate) {
+ return delegate;
+ }
+ }
+
+ private static final class KarafWrapperFunction implements WrapperFunction {
+ // Forces FeaturesService to be resolved
+ private static final Class<FeaturesService> FEATURES_SERVICE = FeaturesService.class;
+
+ @Override
+ public YangModuleInfoRegistry wrap(final BundleContext ctx, final RegularYangModuleInfoRegistry delegate) {
+ final ServiceReference<FeaturesService> ref = ctx.getServiceReference(FEATURES_SERVICE);
+ if (ref != null) {
+ final FeaturesService features = ctx.getService(ref);
+ if (features != null) {
+ LOG.debug("Integrating with Karaf's FeaturesService");
+ return KarafYangModuleInfoRegistry.create(features, delegate);
+ }
+ }
+
+ return delegate;
+ }
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(KarafFeaturesSupport.class);
+ private static final WrapperFunction WRAPPER = staticInit();
+
+ private KarafFeaturesSupport() {
+ // Hidden on purpose
+ }
+
+ static YangModuleInfoRegistry wrap(final BundleContext ctx, final RegularYangModuleInfoRegistry regular) {
+ return WRAPPER.wrap(ctx, regular);
+ }
+
+ private static WrapperFunction staticInit() {
+ try {
+ final WrapperFunction karaf = new KarafWrapperFunction();
+ LOG.info("Will attempt to integrate with Karaf FeaturesService");
+ return karaf;
+ } catch (NoClassDefFoundError e) {
+ LOG.trace("Failed to initialize Karaf support", e);
+ LOG.info("Karaf FeaturesService integration disabled");
+ return new NoopWrapperFunction();
+ }
+ }
+}
--- /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 java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.apache.karaf.features.DeploymentEvent;
+import org.apache.karaf.features.DeploymentListener;
+import org.apache.karaf.features.FeaturesService;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class KarafYangModuleInfoRegistry extends YangModuleInfoRegistry implements DeploymentListener {
+ private static final Logger LOG = LoggerFactory.getLogger(KarafYangModuleInfoRegistry.class);
+
+ private final RegularYangModuleInfoRegistry delegate;
+
+ private boolean scannerEnabled = false;
+ private boolean updatesEnabled = false;
+
+ private KarafYangModuleInfoRegistry(final RegularYangModuleInfoRegistry delegate) {
+ this.delegate = requireNonNull(delegate);
+ }
+
+ static @NonNull KarafYangModuleInfoRegistry create(final FeaturesService features,
+ final RegularYangModuleInfoRegistry delegate) {
+ final KarafYangModuleInfoRegistry ret = new KarafYangModuleInfoRegistry(delegate);
+ features.registerListener(ret);
+ return ret;
+ }
+
+ @Override
+ public synchronized void deploymentEvent(final DeploymentEvent event) {
+ LOG.debug("Features service indicates {}", event);
+ switch (event) {
+ case DEPLOYMENT_STARTED:
+ case BUNDLES_INSTALLED:
+ updatesEnabled = false;
+ LOG.debug("BindingRuntimeContext updates disabled");
+ break;
+ case BUNDLES_RESOLVED:
+ case DEPLOYMENT_FINISHED:
+ default:
+ updatesEnabled = true;
+ LOG.debug("BindingRuntimeContext updates enabled");
+ if (scannerEnabled) {
+ delegate.enableScannerAndUpdate();
+ }
+ }
+ }
+
+ @Override
+ synchronized void scannerUpdate() {
+ if (updatesEnabled) {
+ delegate.scannerUpdate();
+ }
+ }
+
+ @Override
+ synchronized void enableScannerAndUpdate() {
+ scannerEnabled = true;
+ if (updatesEnabled) {
+ delegate.enableScannerAndUpdate();
+
+ }
+ }
+
+ @Override
+ List<ObjectRegistration<YangModuleInfo>> registerInfos(final List<YangModuleInfo> infos) {
+ return delegate.registerInfos(infos);
+ }
+
+ @Override
+ synchronized void close() {
+ delegate.close();
+ }
+
+ @Override
+ synchronized void scannerShutdown() {
+ scannerEnabled = false;
+ delegate.scannerShutdown();
+ }
+}
@Activate
void activate(final BundleContext ctx) {
LOG.info("Model Runtime starting");
- moduleRegistry = new YangModuleInfoRegistry(contextFactory, parserFactory);
+ moduleRegistry = YangModuleInfoRegistry.create(ctx, contextFactory, parserFactory);
bundleTracker = new YangModuleInfoScanner(ctx, moduleRegistry);
bundleTracker.open();
moduleRegistry.enableScannerAndUpdate();
--- /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 RegularYangModuleInfoRegistry extends YangModuleInfoRegistry {
+ private static final Logger LOG = LoggerFactory.getLogger(RegularYangModuleInfoRegistry.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;
+
+ RegularYangModuleInfoRegistry(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
+ @Override
+ void scannerUpdate() {
+ if (!ignoreScanner) {
+ synchronized (this) {
+ updateService();
+ }
+ }
+ }
+
+ @Override
+ synchronized void scannerShutdown() {
+ ignoreScanner = true;
+ }
+
+ @Override
+ synchronized void enableScannerAndUpdate() {
+ ignoreScanner = false;
+ updateService();
+ }
+
+ @Override
+ synchronized void close() {
+ ignoreScanner = true;
+ if (currentInstance != null) {
+ currentInstance.dispose();
+ currentInstance = null;
+ }
+ }
+
+ @Override
+ 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;
+ }
+}
*/
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.eclipse.jdt.annotation.NonNull;
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.framework.BundleContext;
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);
+abstract class YangModuleInfoRegistry {
+ static @NonNull YangModuleInfoRegistry create(final BundleContext ctx, final ComponentFactory contextFactory,
+ final YangParserFactory factory) {
+ return KarafFeaturesSupport.wrap(ctx, new RegularYangModuleInfoRegistry(contextFactory, 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;
- }
+ abstract void scannerUpdate();
- 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;
- }
+ abstract void scannerShutdown();
+ abstract void enableScannerAndUpdate();
- final ComponentInstance newInstance = contextFactory.newInstance(
- OSGiEffectiveModelImpl.props(nextGeneration(), newSnapshot));
- if (currentInstance != null) {
- currentInstance.dispose();
- }
- currentInstance = newInstance;
- currentSnapshot = newSnapshot;
- }
+ abstract void close();
- @Holding("this")
- private long nextGeneration() {
- return generation == -1 ? -1 : ++generation;
- }
+ abstract List<ObjectRegistration<YangModuleInfo>> registerInfos(List<YangModuleInfo> infos);
}
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import org.apache.karaf.features.FeaturesService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
target = new OSGiModelRuntime();
target.parserFactory = parserFactory;
target.contextFactory = contextFactory;
+ doReturn(null).when(bundleContext).getServiceReference(FeaturesService.class);
}
@After