Modify ModuleInfoBundleTracker to track RESOLVED bundles
[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.MoreExecutors;
11 import java.util.concurrent.Callable;
12 import java.util.concurrent.ExecutionException;
13 import java.util.concurrent.ExecutorService;
14 import java.util.concurrent.Future;
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, Future)}
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
51     private final ExecutorService eventExecutor;
52     private final BundleTrackerCustomizer<T> primaryTracker;
53     private final BundleTrackerCustomizer<?>[] additionalTrackers;
54
55     private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
56
57     public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
58                                    final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
59         this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
60     }
61
62     public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
63                                    final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
64                                    final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
65         super(context, bundleState, null);
66         this.primaryTracker = primaryBundleTrackerCustomizer;
67         this.additionalTrackers = additionalBundleTrackerCustomizers;
68         eventExecutor = MoreExecutors.newDirectExecutorService();
69         LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
70     }
71
72     @Override
73     public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
74         LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously",bundle,event);
75         Future<T> future = eventExecutor.submit(new Callable<T>() {
76             @Override
77             public T call() throws Exception {
78                 try {
79                     T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
80
81                     forEachAdditionalBundle(new BundleStrategy() {
82                         @Override
83                         public void execute(final BundleTrackerCustomizer<?> tracker) {
84                             tracker.addingBundle(bundle, event);
85                         }
86                     });
87                     LOG.trace("AddingBundle for {} and event {} finished successfully",bundle,event);
88                     return primaryTrackerRetVal;
89                 } catch (Exception e) {
90                     LOG.error("Failed to add bundle ",e);
91                     throw e;
92                 }
93             }
94         });
95         return future;
96     }
97
98     @Override
99     public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
100         // Intentionally NOOP
101
102     }
103
104     @Override
105     public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
106         if(!object.isDone() && object.cancel(false)) {
107             // We canceled adding event before it was processed
108             // so it is safe to return
109             LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.",bundle);
110             return;
111         }
112         try {
113             LOG.trace("Invoking removedBundle event for {}",bundle);
114             primaryTracker.removedBundle(bundle, event, object.get());
115             forEachAdditionalBundle(new BundleStrategy() {
116                 @Override
117                 public void execute(final BundleTrackerCustomizer<?> tracker) {
118                     tracker.removedBundle(bundle, event, null);
119                 }
120             });
121             LOG.trace("Removed bundle event for {} finished successfully.",bundle);
122         } catch (InterruptedException | ExecutionException e) {
123             LOG.error("Addition of bundle failed, ", e);
124         }
125     }
126
127     private void forEachAdditionalBundle(final BundleStrategy lambda) {
128         for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
129             lambda.execute(trac);
130         }
131     }
132
133     private static interface BundleStrategy {
134         void execute(BundleTrackerCustomizer<?> tracker);
135     }
136
137 }