Fix OSGiDOMSchemaService
[mdsal.git] / dom / mdsal-dom-schema-osgi / src / main / java / org / opendaylight / mdsal / dom / schema / osgi / impl / OSGiDOMSchemaService.java
index 567f10a8621fec4a83f8fa400102138aac4b8fd2..d55f6a2f123e93319f07c25472c756a3f787029e 100644 (file)
@@ -9,25 +9,26 @@ package org.opendaylight.mdsal.dom.schema.osgi.impl;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.runtime.api.ModuleInfoSnapshot;
 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.concepts.AbstractRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 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;
@@ -38,88 +39,96 @@ import org.slf4j.LoggerFactory;
 /**
  * OSGi Service Registry-backed implementation of {@link DOMSchemaService}.
  */
-@Component(service = DOMSchemaService.class, immediate = true)
-public final class OSGiDOMSchemaService extends AbstractDOMSchemaService.WithYangTextSources {
+@Component(immediate = true)
+public final class OSGiDOMSchemaService implements DOMSchemaService, DOMSchemaService.YangTextSourceExtension {
     private static final Logger LOG = LoggerFactory.getLogger(OSGiDOMSchemaService.class);
 
-    @Reference(target = "(component.factory=" + EffectiveModelContextImpl.FACTORY_NAME + ")")
-    ComponentFactory listenerFactory = null;
+    private final List<ModelContextListener> listeners = new CopyOnWriteArrayList<>();
+    private final AtomicReference<ModuleInfoSnapshot> currentSnapshot = new AtomicReference<>();
+    private final ComponentFactory<ModelContextListener> listenerFactory;
 
-    private final List<EffectiveModelContextListener> listeners = new CopyOnWriteArrayList<>();
+    private boolean deactivated;
 
-    private volatile ModuleInfoSnapshot currentSnapshot;
-
-    @Override
-    public EffectiveModelContext getGlobalContext() {
-        return currentSnapshot.getEffectiveModelContext();
+    @Activate
+    public OSGiDOMSchemaService(
+            @Reference(target = "(component.factory=" + ModelContextListener.FACTORY_NAME + ")")
+            final ComponentFactory<ModelContextListener> listenerFactory) {
+        this.listenerFactory = requireNonNull(listenerFactory);
+        LOG.info("DOM Schema services activated");
     }
 
     @Override
-    public ListenerRegistration<EffectiveModelContextListener> registerSchemaContextListener(
-            final EffectiveModelContextListener listener) {
-        return registerListener(requireNonNull(listener));
+    public List<Extension> supportedExtensions() {
+        return List.of(this);
     }
 
-    @Override
-    public ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
-        return currentSnapshot.getSource(sourceIdentifier);
+    @Deactivate
+    void deactivate() {
+        LOG.info("DOM Schema services deactivated");
+        deactivated = true;
     }
 
-    @Reference(fieldOption = FieldOption.REPLACE)
+    @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
     void bindSnapshot(final OSGiModuleInfoSnapshot newContext) {
-        LOG.trace("Updating context to generation {}", newContext.getGeneration());
-        final ModuleInfoSnapshot snapshot = newContext.getService();
-        final EffectiveModelContext ctx = snapshot.getEffectiveModelContext();
-        currentSnapshot = snapshot;
-        listeners.forEach(listener -> notifyListener(ctx, listener));
+        LOG.info("Updating context to generation {}", newContext.getGeneration());
+        final var snapshot = newContext.getService();
+        final var modelContext = snapshot.modelContext();
+        final var previous = currentSnapshot.getAndSet(snapshot);
+        LOG.debug("Snapshot updated from {} to {}", previous, snapshot);
+
+        listeners.forEach(listener -> notifyListener(modelContext, listener));
+    }
+
+    void unbindSnapshot(final OSGiModuleInfoSnapshot oldContext) {
+        final var snapshot = oldContext.getService();
+        if (currentSnapshot.compareAndSet(snapshot, null) && !deactivated) {
+            LOG.info("Lost final generation {}", oldContext.getGeneration());
+        }
     }
 
     @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC,
             policyOption = ReferencePolicyOption.GREEDY)
-    void addListener(final EffectiveModelContextListener listener) {
+    void addListener(final ModelContextListener listener) {
         LOG.trace("Adding listener {}", listener);
         listeners.add(listener);
         listener.onModelContextUpdated(getGlobalContext());
     }
 
-    void removeListener(final EffectiveModelContextListener listener) {
+    void removeListener(final ModelContextListener 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");
+    @Override
+    public @NonNull EffectiveModelContext getGlobalContext() {
+        return currentSnapshot.get().modelContext();
     }
 
-    private @NonNull ListenerRegistration<EffectiveModelContextListener> registerListener(
-            final @NonNull EffectiveModelContextListener listener) {
-        final ComponentInstance reg = listenerFactory.newInstance(EffectiveModelContextImpl.props(listener));
-        return new ListenerRegistration<>() {
-            @Override
-            public EffectiveModelContextListener getInstance() {
-                return listener;
-            }
-
+    @Override
+    public Registration registerSchemaContextListener(final Consumer<EffectiveModelContext> listener) {
+        final var reg = listenerFactory.newInstance(ModelContextListener.props(listener));
+        return new AbstractRegistration() {
             @Override
-            public void close() {
+            protected void removeRegistration() {
                 reg.dispose();
             }
         };
     }
 
+    @Override
+    public ListenableFuture<YangTextSource> getYangTexttSource(final SourceIdentifier sourceId) {
+        try {
+            return Futures.immediateFuture(currentSnapshot.get().getYangTextSource(sourceId));
+        } catch (MissingSchemaSourceException e) {
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
     @SuppressWarnings("checkstyle:illegalCatch")
-    private static void notifyListener(final EffectiveModelContext context,
-            final EffectiveModelContextListener listener) {
+    private static void notifyListener(final @NonNull EffectiveModelContext modelContext,
+            final ModelContextListener listener) {
         try {
-            listener.onModelContextUpdated(context);
+            listener.onModelContextUpdated(modelContext);
         } catch (RuntimeException e) {
             LOG.warn("Failed to notify listener {}", listener, e);
         }