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