import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+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.concurrent.atomic.AtomicReference;
+import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
+import org.opendaylight.controller.sal.core.api.model.YangTextSourceProvider;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
-import org.opendaylight.yangtools.yang.parser.impl.util.URLSchemaContextResolver;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+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.URLSchemaContextResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaServiceListener, SchemaServiceListener>, AutoCloseable {
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, YangTextSourceProvider, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
- private final ListenerRegistry<SchemaServiceListener> listeners = new ListenerRegistry<>();
- private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver();
+ private static AtomicReference<GlobalBundleScanningSchemaServiceImpl> globalInstance = new AtomicReference<>();
+
+ @GuardedBy(value = "lock")
+ private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
+ private final URLSchemaContextResolver contextResolver = URLSchemaContextResolver.create("global-bundle");
private final BundleScanner scanner = new BundleScanner();
private final BundleContext context;
- private ServiceTracker<SchemaServiceListener, SchemaServiceListener> listenerTracker;
- private BundleTracker<Iterable<Registration<URL>>> bundleTracker;
+ private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
+ private BundleTracker<Iterable<Registration>> bundleTracker;
private boolean starting = true;
- private static GlobalBundleScanningSchemaServiceImpl instance;
+ private volatile boolean stopping;
+ private final Object lock = new Object();
private GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
this.context = Preconditions.checkNotNull(context);
}
- public synchronized static GlobalBundleScanningSchemaServiceImpl createInstance(final BundleContext ctx) {
- Preconditions.checkState(instance == null);
- instance = new GlobalBundleScanningSchemaServiceImpl(ctx);
+ public static GlobalBundleScanningSchemaServiceImpl createInstance(final BundleContext ctx) {
+ GlobalBundleScanningSchemaServiceImpl instance = new GlobalBundleScanningSchemaServiceImpl(ctx);
+ Preconditions.checkState(globalInstance.compareAndSet(null, instance));
instance.start();
return instance;
}
- public synchronized static GlobalBundleScanningSchemaServiceImpl getInstance() {
+ public static GlobalBundleScanningSchemaServiceImpl getInstance() {
+ GlobalBundleScanningSchemaServiceImpl instance = globalInstance.get();
Preconditions.checkState(instance != null, "Global Instance was not instantiated");
return instance;
}
@VisibleForTesting
- public static synchronized void destroyInstance() {
- instance = null;
+ public static void destroyInstance() {
+ GlobalBundleScanningSchemaServiceImpl instance = globalInstance.getAndSet(null);
+ if(instance != null) {
+ instance.close();
+ }
}
public BundleContext getContext() {
public void start() {
checkState(context != null);
+ LOG.debug("start() starting");
+
+ listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+ bundleTracker = new BundleTracker<>(context, Bundle.RESOLVED | Bundle.STARTING |
+ Bundle.STOPPING | Bundle.ACTIVE, scanner);
+
+ synchronized(lock) {
+ bundleTracker.open();
+
+ LOG.debug("BundleTracker.open() complete");
+
+ boolean hasExistingListeners = Iterables.size(listeners.getListeners()) > 0;
+ if(hasExistingListeners) {
+ tryToUpdateSchemaContext();
+ }
+ }
- listenerTracker = new ServiceTracker<>(context, SchemaServiceListener.class, GlobalBundleScanningSchemaServiceImpl.this);
- bundleTracker = new BundleTracker<>(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, scanner);
- bundleTracker.open();
listenerTracker.open();
starting = false;
- tryToUpdateSchemaContext();
+
+ LOG.debug("start() complete");
}
@Override
}
@Override
- public synchronized ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
- Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
- if(potentialCtx.isPresent()) {
- listener.onGlobalContextUpdated(potentialCtx.get());
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
+ synchronized(lock) {
+ Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+ if(potentialCtx.isPresent()) {
+ listener.onGlobalContextUpdated(potentialCtx.get());
+ }
+ return listeners.register(listener);
}
- return listeners.register(listener);
}
@Override
- public void close() throws Exception {
+ public void close() {
+ stopping = true;
if (bundleTracker != null) {
bundleTracker.close();
}
if (listenerTracker != null) {
listenerTracker.close();
}
- // FIXME: Add listeners.close();
- }
+ for (ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
+ l.close();
+ }
+ }
- private synchronized void updateContext(final SchemaContext snapshot) {
+ @GuardedBy(value = "lock")
+ private void notifyListeners(final SchemaContext snapshot) {
Object[] services = listenerTracker.getServices();
- for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+ for (ListenerRegistration<SchemaContextListener> listener : listeners) {
try {
listener.getInstance().onGlobalContextUpdated(snapshot);
} catch (Exception e) {
}
if (services != null) {
for (Object rawListener : services) {
- SchemaServiceListener listener = (SchemaServiceListener) rawListener;
+ final SchemaContextListener listener = (SchemaContextListener) rawListener;
try {
listener.onGlobalContextUpdated(snapshot);
} catch (Exception e) {
}
}
- private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration<URL>>> {
+ @Override
+ public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+ return contextResolver.getSource(sourceIdentifier);
+
+ }
+
+ private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
@Override
- public Iterable<Registration<URL>> addingBundle(final Bundle bundle, final BundleEvent event) {
+ public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
if (bundle.getBundleId() == 0) {
return Collections.emptyList();
return Collections.emptyList();
}
- final List<Registration<URL>> urls = new ArrayList<>();
+ final List<Registration> urls = new ArrayList<>();
while (enumeration.hasMoreElements()) {
final URL u = enumeration.nextElement();
try {
}
if (!urls.isEmpty()) {
- LOG.debug("Loaded {} new URLs, rebuilding schema context", urls.size());
+ LOG.debug("Loaded {} new URLs from bundle {}, attempting to rebuild schema context",
+ urls.size(), bundle.getSymbolicName());
tryToUpdateSchemaContext();
}
}
@Override
- public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration<URL>> object) {
- LOG.debug("Modified bundle {} {} {}", bundle, event, object);
+ public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
}
/**
*/
@Override
- public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration<URL>> urls) {
- for (Registration<URL> url : urls) {
+ public void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
+ for (Registration url : urls) {
try {
url.close();
} catch (Exception e) {
LOG.warn("Failed do unregister URL {}, proceeding", url, e);
}
}
- tryToUpdateSchemaContext();
+
+ int numUrls = Iterables.size(urls);
+ if(numUrls > 0 ) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(), bundle.getState(), numUrls);
+ }
+
+ tryToUpdateSchemaContext();
+ }
}
}
@Override
- public synchronized SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
+ public SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
- SchemaServiceListener listener = context.getService(reference);
+ SchemaContextListener listener = context.getService(reference);
SchemaContext _ctxContext = getGlobalContext();
if (getContext() != null && _ctxContext != null) {
listener.onGlobalContextUpdated(_ctxContext);
return listener;
}
- public synchronized void tryToUpdateSchemaContext() {
- if (starting) {
+ public void tryToUpdateSchemaContext() {
+ if (starting || stopping) {
return;
}
- Optional<SchemaContext> schema = contextResolver.tryToUpdateSchemaContext();
- if(schema.isPresent()) {
- updateContext(schema.get());
+
+ synchronized(lock) {
+ 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<SchemaServiceListener> reference, final SchemaServiceListener service) {
+ public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
// NOOP
}
@Override
- public void removedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
+ public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
context.ungetService(reference);
}
}