2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.manager.impl.osgi;
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;
28 * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
29 * propagates bundle events to all of them.
31 * Primary customizer may return tracking object,
32 * which will be passed to it during invocation of
33 * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Future)}
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.
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.
46 * Method {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
47 * is never invoked on registered trackers.
51 public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
53 private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
54 .setNameFormat("config-bundle-tracker-%d")
56 private final ExecutorService eventExecutor;
57 private final BundleTrackerCustomizer<T> primaryTracker;
58 private final BundleTrackerCustomizer<?>[] additionalTrackers;
60 private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
62 public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
63 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
64 this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
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);
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>() {
82 public T call() throws Exception {
84 T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
86 forEachAdditionalBundle(new BundleStrategy() {
88 public void execute(final BundleTrackerCustomizer<?> tracker) {
89 tracker.addingBundle(bundle, event);
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);
104 public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
105 // Intentionally NOOP
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);
118 LOG.trace("Invoking removedBundle event for {}",bundle);
119 primaryTracker.removedBundle(bundle, event, object.get());
120 forEachAdditionalBundle(new BundleStrategy() {
122 public void execute(final BundleTrackerCustomizer<?> tracker) {
123 tracker.removedBundle(bundle, event, null);
126 LOG.trace("Removed bundle event for {} finished successfully.",bundle);
127 } catch (InterruptedException | ExecutionException e) {
128 LOG.error("Addition of bundle failed, ", e);
132 private void forEachAdditionalBundle(final BundleStrategy lambda) {
133 for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
134 lambda.execute(trac);
138 private static interface BundleStrategy {
139 void execute(BundleTrackerCustomizer<?> tracker);