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