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