Remove yang-test
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / osgi / ExtensibleBundleTracker.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.controller.config.manager.impl.osgi;
9
10 import com.google.common.util.concurrent.ThreadFactoryBuilder;
11 import java.util.concurrent.ExecutionException;
12 import java.util.concurrent.ExecutorService;
13 import java.util.concurrent.Executors;
14 import java.util.concurrent.Future;
15 import java.util.concurrent.ThreadFactory;
16 import org.osgi.framework.Bundle;
17 import org.osgi.framework.BundleContext;
18 import org.osgi.framework.BundleEvent;
19 import org.osgi.util.tracker.BundleTracker;
20 import org.osgi.util.tracker.BundleTrackerCustomizer;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
26  * propagates bundle events to all of them.
27  *
28  * <p>
29  * Primary customizer may return tracking object, which will be passed to it
30  * during invocation of
31  * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
32  *
33  * <p>
34  * This extender modifies behavior to not leak platform thread in
35  * {@link BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)} but deliver
36  * this event from its own single threaded executor.
37  *
38  * <p>
39  * If bundle is removed before event for adding bundle was executed, that event
40  * is cancelled. If addingBundle event is currently in progress or was already
41  * executed, platform thread is block until addingBundle finishes so bundle
42  * could be removed correctly in platform thread.
43  *
44  * <p>
45  * Method
46  * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)} is
47  * never invoked on registered trackers.
48  *
49  * @param <T> value
50  */
51 public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
52     private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
53             .setNameFormat("config-bundle-tracker-%d").build();
54     private final ExecutorService eventExecutor;
55     private final BundleTrackerCustomizer<T> primaryTracker;
56     private final BundleTrackerCustomizer<?>[] additionalTrackers;
57
58     private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
59
60     public ExtensibleBundleTracker(final BundleContext context,
61             final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
62             final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
63         this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
64     }
65
66     public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
67             final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
68             final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
69         super(context, bundleState, null);
70         this.primaryTracker = primaryBundleTrackerCustomizer;
71         this.additionalTrackers = additionalBundleTrackerCustomizers;
72         eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
73         LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
74     }
75
76     @Override
77     public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
78         LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously", bundle, event);
79         return eventExecutor.submit(() -> {
80             T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
81
82             forEachAdditionalBundle(tracker -> tracker.addingBundle(bundle, event));
83             LOG.trace("AddingBundle for {} and event {} finished successfully", bundle, event);
84             return primaryTrackerRetVal;
85         });
86     }
87
88     @Override
89     public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
90         // Intentionally NOOP
91     }
92
93     @Override
94     public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
95         if (!object.isDone() && object.cancel(false)) {
96             // We canceled adding event before it was processed
97             // so it is safe to return
98             LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.", bundle);
99             return;
100         }
101         try {
102             LOG.trace("Invoking removedBundle event for {}", bundle);
103             primaryTracker.removedBundle(bundle, event, object.get());
104             forEachAdditionalBundle(tracker -> tracker.removedBundle(bundle, event, null));
105             LOG.trace("Removed bundle event for {} finished successfully.", bundle);
106         } catch (final ExecutionException | InterruptedException e) {
107             LOG.error("Failed to remove bundle {}", bundle, e);
108         }
109     }
110
111     private void forEachAdditionalBundle(final BundleStrategy lambda) {
112         for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
113             lambda.execute(trac);
114         }
115     }
116
117     private interface BundleStrategy {
118         void execute(BundleTrackerCustomizer<?> tracker);
119     }
120 }