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;
/**
* 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);
}