import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.CheckedFuture;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaServiceExtension;
+import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
-import org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver;
+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.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OsgiBundleScanningSchemaService implements SchemaContextProvider, DOMSchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
+public class OsgiBundleScanningSchemaService implements SchemaContextProvider, DOMSchemaService,
+ ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, DOMYangTextSourceProvider,
+ AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(OsgiBundleScanningSchemaService.class);
+ private static final AtomicReference<OsgiBundleScanningSchemaService> GLOBAL_INSTANCE = new AtomicReference<>();
+
+ private static final long FRAMEWORK_BUNDLE_ID = 0;
+
+ @GuardedBy("lock")
private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
- private final URLSchemaContextResolver contextResolver = URLSchemaContextResolver.create("global-bundle");
+ private final YangTextSchemaContextResolver contextResolver = YangTextSchemaContextResolver.create("global-bundle");
private final BundleScanner scanner = new BundleScanner();
+ private final Object lock = new Object();
private final BundleContext context;
private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
private BundleTracker<Iterable<Registration>> bundleTracker;
private boolean starting = true;
- private static OsgiBundleScanningSchemaService instance;
+
+ private volatile boolean stopping;
private OsgiBundleScanningSchemaService(final BundleContext context) {
this.context = Preconditions.checkNotNull(context);
}
- public synchronized static OsgiBundleScanningSchemaService createInstance(final BundleContext ctx) {
- Preconditions.checkState(instance == null);
- instance = new OsgiBundleScanningSchemaService(ctx);
+ public static @Nonnull OsgiBundleScanningSchemaService createInstance(final BundleContext ctx) {
+ OsgiBundleScanningSchemaService instance = new OsgiBundleScanningSchemaService(ctx);
+ Preconditions.checkState(GLOBAL_INSTANCE.compareAndSet(null, instance));
instance.start();
return instance;
}
- public synchronized static OsgiBundleScanningSchemaService getInstance() {
+ public static OsgiBundleScanningSchemaService getInstance() {
+ OsgiBundleScanningSchemaService instance = GLOBAL_INSTANCE.get();
Preconditions.checkState(instance != null, "Global Instance was not instantiated");
return instance;
}
@VisibleForTesting
- public static synchronized void destroyInstance() {
- try {
+ public static void destroyInstance() {
+ OsgiBundleScanningSchemaService instance = GLOBAL_INSTANCE.getAndSet(null);
+ if (instance != null) {
instance.close();
- } finally {
- instance = null;
}
}
return context;
}
- public void start() {
+ private void start() {
checkState(context != null);
LOG.debug("start() starting");
- listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, OsgiBundleScanningSchemaService.this);
- bundleTracker = new BundleTracker<>(context, Bundle.RESOLVED | Bundle.STARTING |
- Bundle.STOPPING | Bundle.ACTIVE, scanner);
- bundleTracker.open();
+ 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");
+ LOG.debug("BundleTracker.open() complete");
+
+ if (Iterables.size(listeners.getListeners()) > 0) {
+ tryToUpdateSchemaContext();
+ }
+ }
listenerTracker.open();
starting = false;
- tryToUpdateSchemaContext();
LOG.debug("start() complete");
}
}
@Override
- public synchronized ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
- final Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
- if(potentialCtx.isPresent()) {
- listener.onGlobalContextUpdated(potentialCtx.get());
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+ final SchemaContextListener listener) {
+ synchronized (lock) {
+ final Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+ if (potentialCtx.isPresent()) {
+ listener.onGlobalContextUpdated(potentialCtx.get());
+ }
+ return listeners.register(listener);
}
- return listeners.register(listener);
}
@Override
public void close() {
- if (bundleTracker != null) {
- bundleTracker.close();
- }
- if (listenerTracker != null) {
- listenerTracker.close();
- }
+ synchronized (lock) {
+ stopping = true;
+ if (bundleTracker != null) {
+ bundleTracker.close();
+ bundleTracker = null;
+ }
+ if (listenerTracker != null) {
+ listenerTracker.close();
+ listenerTracker = null;
+ }
- for (final ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
- l.close();
+ for (final ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
+ l.close();
+ }
}
}
- private synchronized void updateContext(final SchemaContext snapshot) {
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ @VisibleForTesting
+ @GuardedBy("lock")
+ void notifyListeners(final SchemaContext snapshot) {
final Object[] services = listenerTracker.getServices();
for (final ListenerRegistration<SchemaContextListener> listener : listeners) {
try {
}
}
+ @SuppressWarnings("checkstyle:IllegalCatch")
private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
@Override
public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
- if (bundle.getBundleId() == 0) {
+ if (bundle.getBundleId() == FRAMEWORK_BUNDLE_ID) {
return Collections.emptyList();
}
@Override
public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
+ if (bundle.getBundleId() == FRAMEWORK_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;
+ }
+ }
}
/**
* {@link #getYangStoreSnapshot()} will throw exception. There is no
* rollback.
*/
-
+ @SuppressWarnings("checkstyle:IllegalCatch")
@Override
- public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
+ public void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
for (final Registration url : urls) {
try {
url.close();
}
final int numUrls = Iterables.size(urls);
- if(numUrls > 0 ) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(), bundle.getState(), numUrls);
+ if (numUrls > 0 ) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(),
+ bundle.getState(), numUrls);
}
tryToUpdateSchemaContext();
}
@Override
- public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
+ public SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
final SchemaContextListener listener = context.getService(reference);
final SchemaContext _ctxContext = getGlobalContext();
return listener;
}
- public synchronized void tryToUpdateSchemaContext() {
- if (starting) {
+ public void tryToUpdateSchemaContext() {
+ if (starting || stopping) {
return;
}
- final Optional<SchemaContext> schema = contextResolver.getSchemaContext();
- if(schema.isPresent()) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Got new SchemaContext: # of modules {}", schema.get().getAllModuleIdentifiers().size());
- }
- updateContext(schema.get());
+ synchronized (lock) {
+ final Optional<SchemaContext> schema = contextResolver.getSchemaContext();
+ if (schema.isPresent()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Got new SchemaContext: # of modules {}", schema.get().getAllModuleIdentifiers().size());
+ }
+
+ notifyListeners(schema.get());
+ }
}
}
@Override
- public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+ public void modifiedService(final ServiceReference<SchemaContextListener> reference,
+ final SchemaContextListener service) {
// NOOP
}
@Override
- public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+ public void removedService(final ServiceReference<SchemaContextListener> reference,
+ final SchemaContextListener service) {
context.ungetService(reference);
}
+
+ @Override
+ public Map<Class<? extends DOMSchemaServiceExtension>, DOMSchemaServiceExtension> getSupportedExtensions() {
+ return ImmutableMap.of(DOMYangTextSourceProvider.class, this);
+ }
+
+ @Override
+ public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(
+ final SourceIdentifier sourceIdentifier) {
+ return contextResolver.getSource(sourceIdentifier);
+ }
}