checkStyleViolationSeverity=error implemented for mdsal-dom-broker
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / osgi / OsgiBundleScanningSchemaService.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.broker.osgi;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.Iterables;
17 import java.net.URL;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Enumeration;
21 import java.util.List;
22 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
23 import org.opendaylight.yangtools.concepts.ListenerRegistration;
24 import org.opendaylight.yangtools.concepts.Registration;
25 import org.opendaylight.yangtools.util.ListenerRegistry;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
29 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
30 import org.osgi.framework.Bundle;
31 import org.osgi.framework.BundleContext;
32 import org.osgi.framework.BundleEvent;
33 import org.osgi.framework.ServiceReference;
34 import org.osgi.util.tracker.BundleTracker;
35 import org.osgi.util.tracker.BundleTrackerCustomizer;
36 import org.osgi.util.tracker.ServiceTracker;
37 import org.osgi.util.tracker.ServiceTrackerCustomizer;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public class OsgiBundleScanningSchemaService implements SchemaContextProvider, DOMSchemaService,
42         ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
43     private static final Logger LOG = LoggerFactory.getLogger(OsgiBundleScanningSchemaService.class);
44
45     private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
46     private final YangTextSchemaContextResolver contextResolver = YangTextSchemaContextResolver.create("global-bundle");
47     private final BundleScanner scanner = new BundleScanner();
48     private final BundleContext context;
49
50     private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
51     private BundleTracker<Iterable<Registration>> bundleTracker;
52     private boolean starting = true;
53     private static OsgiBundleScanningSchemaService instance;
54
55     private OsgiBundleScanningSchemaService(final BundleContext context) {
56         this.context = Preconditions.checkNotNull(context);
57     }
58
59     public static synchronized OsgiBundleScanningSchemaService createInstance(final BundleContext ctx) {
60         Preconditions.checkState(instance == null);
61         instance = new OsgiBundleScanningSchemaService(ctx);
62         instance.start();
63         return instance;
64     }
65
66     public static synchronized OsgiBundleScanningSchemaService getInstance() {
67         Preconditions.checkState(instance != null, "Global Instance was not instantiated");
68         return instance;
69     }
70
71     @VisibleForTesting
72     public static synchronized void destroyInstance() {
73         try {
74             instance.close();
75         } finally {
76             instance = null;
77         }
78     }
79
80     public BundleContext getContext() {
81         return context;
82     }
83
84     public void start() {
85         checkState(context != null);
86         LOG.debug("start() starting");
87
88         listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class,
89                 OsgiBundleScanningSchemaService.this);
90         bundleTracker = new BundleTracker<>(context, Bundle.RESOLVED | Bundle.STARTING
91                 |
92                 Bundle.STOPPING | Bundle.ACTIVE, scanner);
93         bundleTracker.open();
94
95         LOG.debug("BundleTracker.open() complete");
96
97         listenerTracker.open();
98         starting = false;
99         tryToUpdateSchemaContext();
100
101         LOG.debug("start() complete");
102     }
103
104     @Override
105     public SchemaContext getSchemaContext() {
106         return getGlobalContext();
107     }
108
109     @Override
110     public SchemaContext getGlobalContext() {
111         return contextResolver.getSchemaContext().orNull();
112     }
113
114     @Override
115     public SchemaContext getSessionContext() {
116         throw new UnsupportedOperationException();
117     }
118
119     @Override
120     public synchronized ListenerRegistration<SchemaContextListener>
121         registerSchemaContextListener(final SchemaContextListener listener) {
122         final Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
123         if (potentialCtx.isPresent()) {
124             listener.onGlobalContextUpdated(potentialCtx.get());
125         }
126         return listeners.register(listener);
127     }
128
129     @Override
130     public void close() {
131         if (bundleTracker != null) {
132             bundleTracker.close();
133         }
134         if (listenerTracker != null) {
135             listenerTracker.close();
136         }
137
138         for (final ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
139             l.close();
140         }
141     }
142
143     @SuppressWarnings("checkstyle:IllegalCatch")
144     private synchronized void updateContext(final SchemaContext snapshot) {
145         final Object[] services = listenerTracker.getServices();
146         for (final ListenerRegistration<SchemaContextListener> listener : listeners) {
147             try {
148                 listener.getInstance().onGlobalContextUpdated(snapshot);
149             } catch (final Exception e) {
150                 LOG.error("Exception occured during invoking listener", e);
151             }
152         }
153         if (services != null) {
154             for (final Object rawListener : services) {
155                 final SchemaContextListener listener = (SchemaContextListener) rawListener;
156                 try {
157                     listener.onGlobalContextUpdated(snapshot);
158                 } catch (final Exception e) {
159                     LOG.error("Exception occured during invoking listener {}", listener, e);
160                 }
161             }
162         }
163     }
164
165     @SuppressWarnings("checkstyle:IllegalCatch")
166     private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
167         @Override
168         public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
169
170             if (bundle.getBundleId() == 0) {
171                 return Collections.emptyList();
172             }
173
174             final Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
175             if (enumeration == null) {
176                 return Collections.emptyList();
177             }
178
179             final List<Registration> urls = new ArrayList<>();
180             while (enumeration.hasMoreElements()) {
181                 final URL u = enumeration.nextElement();
182                 try {
183                     urls.add(contextResolver.registerSource(u));
184                     LOG.debug("Registered {}", u);
185                 } catch (final Exception e) {
186                     LOG.warn("Failed to register {}, ignoring it", e);
187                 }
188             }
189
190             if (!urls.isEmpty()) {
191                 LOG.debug("Loaded {} new URLs from bundle {}, attempting to rebuild schema context",
192                         urls.size(), bundle.getSymbolicName());
193                 tryToUpdateSchemaContext();
194             }
195
196             return ImmutableList.copyOf(urls);
197         }
198
199         @Override
200         public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
201         }
202
203         /**
204          * If removing YANG files makes yang store inconsistent, method
205          * {@link #getYangStoreSnapshot()} will throw exception. There is no
206          * rollback.
207          */
208         @SuppressWarnings("checkstyle:IllegalCatch")
209         @Override
210         public synchronized void removedBundle(final Bundle bundle, final BundleEvent event,
211                 final Iterable<Registration> urls) {
212             for (final Registration url : urls) {
213                 try {
214                     url.close();
215                 } catch (final Exception e) {
216                     LOG.warn("Failed do unregister URL {}, proceeding", url, e);
217                 }
218             }
219
220             final int numUrls = Iterables.size(urls);
221             if (numUrls > 0 ) {
222                 if (LOG.isDebugEnabled()) {
223                     LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(),
224                             bundle.getState(), numUrls);
225                 }
226
227                 tryToUpdateSchemaContext();
228             }
229         }
230     }
231
232     @Override
233     public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
234
235         final SchemaContextListener listener = context.getService(reference);
236         final SchemaContext _ctxContext = getGlobalContext();
237         if (getContext() != null && _ctxContext != null) {
238             listener.onGlobalContextUpdated(_ctxContext);
239         }
240         return listener;
241     }
242
243     public synchronized void tryToUpdateSchemaContext() {
244         if (starting) {
245             return;
246         }
247         final Optional<SchemaContext> schema = contextResolver.getSchemaContext();
248         if (schema.isPresent()) {
249             if (LOG.isDebugEnabled()) {
250                 LOG.debug("Got new SchemaContext: # of modules {}", schema.get().getAllModuleIdentifiers().size());
251             }
252
253             updateContext(schema.get());
254         }
255     }
256
257     @Override
258     public void modifiedService(final ServiceReference<SchemaContextListener> reference,
259             final SchemaContextListener service) {
260         // NOOP
261     }
262
263     @Override
264     public void removedService(final ServiceReference<SchemaContextListener> reference,
265             final SchemaContextListener service) {
266         context.ungetService(reference);
267     }
268 }