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

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.