d55f6a2f123e93319f07c25472c756a3f787029e
[mdsal.git] / dom / mdsal-dom-schema-osgi / src / main / java / org / opendaylight / mdsal / dom / schema / osgi / impl / OSGiDOMSchemaService.java
1 /*
2  * Copyright (c) 2017, 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.dom.schema.osgi.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.util.List;
15 import java.util.concurrent.CopyOnWriteArrayList;
16 import java.util.concurrent.atomic.AtomicReference;
17 import java.util.function.Consumer;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.binding.runtime.api.ModuleInfoSnapshot;
20 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
21 import org.opendaylight.mdsal.dom.schema.osgi.OSGiModuleInfoSnapshot;
22 import org.opendaylight.yangtools.concepts.AbstractRegistration;
23 import org.opendaylight.yangtools.concepts.Registration;
24 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
25 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
26 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
27 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
28 import org.osgi.service.component.ComponentFactory;
29 import org.osgi.service.component.annotations.Activate;
30 import org.osgi.service.component.annotations.Component;
31 import org.osgi.service.component.annotations.Deactivate;
32 import org.osgi.service.component.annotations.Reference;
33 import org.osgi.service.component.annotations.ReferenceCardinality;
34 import org.osgi.service.component.annotations.ReferencePolicy;
35 import org.osgi.service.component.annotations.ReferencePolicyOption;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * OSGi Service Registry-backed implementation of {@link DOMSchemaService}.
41  */
42 @Component(immediate = true)
43 public final class OSGiDOMSchemaService implements DOMSchemaService, DOMSchemaService.YangTextSourceExtension {
44     private static final Logger LOG = LoggerFactory.getLogger(OSGiDOMSchemaService.class);
45
46     private final List<ModelContextListener> listeners = new CopyOnWriteArrayList<>();
47     private final AtomicReference<ModuleInfoSnapshot> currentSnapshot = new AtomicReference<>();
48     private final ComponentFactory<ModelContextListener> listenerFactory;
49
50     private boolean deactivated;
51
52     @Activate
53     public OSGiDOMSchemaService(
54             @Reference(target = "(component.factory=" + ModelContextListener.FACTORY_NAME + ")")
55             final ComponentFactory<ModelContextListener> listenerFactory) {
56         this.listenerFactory = requireNonNull(listenerFactory);
57         LOG.info("DOM Schema services activated");
58     }
59
60     @Override
61     public List<Extension> supportedExtensions() {
62         return List.of(this);
63     }
64
65     @Deactivate
66     void deactivate() {
67         LOG.info("DOM Schema services deactivated");
68         deactivated = true;
69     }
70
71     @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
72     void bindSnapshot(final OSGiModuleInfoSnapshot newContext) {
73         LOG.info("Updating context to generation {}", newContext.getGeneration());
74         final var snapshot = newContext.getService();
75         final var modelContext = snapshot.modelContext();
76         final var previous = currentSnapshot.getAndSet(snapshot);
77         LOG.debug("Snapshot updated from {} to {}", previous, snapshot);
78
79         listeners.forEach(listener -> notifyListener(modelContext, listener));
80     }
81
82     void unbindSnapshot(final OSGiModuleInfoSnapshot oldContext) {
83         final var snapshot = oldContext.getService();
84         if (currentSnapshot.compareAndSet(snapshot, null) && !deactivated) {
85             LOG.info("Lost final generation {}", oldContext.getGeneration());
86         }
87     }
88
89     @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC,
90             policyOption = ReferencePolicyOption.GREEDY)
91     void addListener(final ModelContextListener listener) {
92         LOG.trace("Adding listener {}", listener);
93         listeners.add(listener);
94         listener.onModelContextUpdated(getGlobalContext());
95     }
96
97     void removeListener(final ModelContextListener listener) {
98         LOG.trace("Removing listener {}", listener);
99         listeners.remove(listener);
100     }
101
102     @Override
103     public @NonNull EffectiveModelContext getGlobalContext() {
104         return currentSnapshot.get().modelContext();
105     }
106
107     @Override
108     public Registration registerSchemaContextListener(final Consumer<EffectiveModelContext> listener) {
109         final var reg = listenerFactory.newInstance(ModelContextListener.props(listener));
110         return new AbstractRegistration() {
111             @Override
112             protected void removeRegistration() {
113                 reg.dispose();
114             }
115         };
116     }
117
118     @Override
119     public ListenableFuture<YangTextSource> getYangTexttSource(final SourceIdentifier sourceId) {
120         try {
121             return Futures.immediateFuture(currentSnapshot.get().getYangTextSource(sourceId));
122         } catch (MissingSchemaSourceException e) {
123             return Futures.immediateFailedFuture(e);
124         }
125     }
126
127     @SuppressWarnings("checkstyle:illegalCatch")
128     private static void notifyListener(final @NonNull EffectiveModelContext modelContext,
129             final ModelContextListener listener) {
130         try {
131             listener.onModelContextUpdated(modelContext);
132         } catch (RuntimeException e) {
133             LOG.warn("Failed to notify listener {}", listener, e);
134         }
135     }
136 }