BUG-865: remove reference to URLSchemaContextResolver
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / GlobalBundleScanningSchemaServiceImpl.java
index 60a7e81c4c046183e6ee59e0f27d48e5a15e90bc..4ba62e4fc40fe62a6c7c4a68fe1d77b12e1f4475 100644 (file)
@@ -8,23 +8,32 @@
 package org.opendaylight.controller.sal.dom.broker;
 
 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.YangTextSchemaContextResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
@@ -36,42 +45,46 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
 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 YangTextSchemaContextResolver contextResolver = YangTextSchemaContextResolver.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() {
@@ -80,13 +93,27 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
 
     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
@@ -115,29 +142,35 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
     }
 
     @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) {
@@ -146,7 +179,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
         }
         if (services != null) {
             for (Object rawListener : services) {
-                SchemaServiceListener listener = (SchemaServiceListener) rawListener;
+                final SchemaContextListener listener = (SchemaContextListener) rawListener;
                 try {
                     listener.onGlobalContextUpdated(snapshot);
                 } catch (Exception e) {
@@ -156,9 +189,15 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
         }
     }
 
-    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();
@@ -169,7 +208,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
                 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 {
@@ -181,7 +220,8 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
             }
 
             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();
             }
 
@@ -189,8 +229,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
         }
 
         @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) {
         }
 
         /**
@@ -200,22 +239,30 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
          */
 
         @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);
@@ -223,23 +270,30 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
         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);
     }
 }