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.ExecutionException;
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;
25 * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
26 * propagates bundle events to all of them.
29 * Primary customizer may return tracking object, which will be passed to it
30 * during invocation of
31 * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
34 * This extender modifies behavior to not leak platform thread in
35 * {@link BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)} but deliver
36 * this event from its own single threaded executor.
39 * If bundle is removed before event for adding bundle was executed, that event
40 * is cancelled. If addingBundle event is currently in progress or was already
41 * executed, platform thread is block until addingBundle finishes so bundle
42 * could be removed correctly in platform thread.
46 * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)} is
47 * never invoked on registered trackers.
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;
58 private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
60 public ExtensibleBundleTracker(final BundleContext context,
61 final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
62 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
63 this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
66 public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
67 final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
68 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
69 super(context, bundleState, null);
70 this.primaryTracker = primaryBundleTrackerCustomizer;
71 this.additionalTrackers = additionalBundleTrackerCustomizers;
72 eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
73 LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
77 public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
78 LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously", bundle, event);
79 return eventExecutor.submit(() -> {
80 T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
82 forEachAdditionalBundle(tracker -> tracker.addingBundle(bundle, event));
83 LOG.trace("AddingBundle for {} and event {} finished successfully", bundle, event);
84 return primaryTrackerRetVal;
89 public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
94 public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
95 if (!object.isDone() && object.cancel(false)) {
96 // We canceled adding event before it was processed
97 // so it is safe to return
98 LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.", bundle);
102 LOG.trace("Invoking removedBundle event for {}", bundle);
103 primaryTracker.removedBundle(bundle, event, object.get());
104 forEachAdditionalBundle(tracker -> tracker.removedBundle(bundle, event, null));
105 LOG.trace("Removed bundle event for {} finished successfully.", bundle);
106 } catch (final ExecutionException | InterruptedException e) {
107 LOG.error("Failed to remove bundle {}", bundle, e);
111 private void forEachAdditionalBundle(final BundleStrategy lambda) {
112 for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
113 lambda.execute(trac);
117 private interface BundleStrategy {
118 void execute(BundleTrackerCustomizer<?> tracker);