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.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;
26 * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
27 * propagates bundle events to all of them.
29 * Primary customizer may return tracking object,
30 * which will be passed to it during invocation of
31 * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Future)}
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.
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.
44 * Method {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
45 * is never invoked on registered trackers.
49 public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
51 private final ExecutorService eventExecutor;
52 private final BundleTrackerCustomizer<T> primaryTracker;
53 private final BundleTrackerCustomizer<?>[] additionalTrackers;
55 private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
57 public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
58 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
59 this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
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);
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>() {
77 public T call() throws Exception {
79 T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
81 forEachAdditionalBundle(new BundleStrategy() {
83 public void execute(final BundleTrackerCustomizer<?> tracker) {
84 tracker.addingBundle(bundle, event);
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);
99 public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
100 // Intentionally NOOP
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);
113 LOG.trace("Invoking removedBundle event for {}",bundle);
114 primaryTracker.removedBundle(bundle, event, object.get());
115 forEachAdditionalBundle(new BundleStrategy() {
117 public void execute(final BundleTrackerCustomizer<?> tracker) {
118 tracker.removedBundle(bundle, event, null);
121 LOG.trace("Removed bundle event for {} finished successfully.",bundle);
122 } catch (InterruptedException | ExecutionException e) {
123 LOG.error("Addition of bundle failed, ", e);
127 private void forEachAdditionalBundle(final BundleStrategy lambda) {
128 for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
129 lambda.execute(trac);
133 private static interface BundleStrategy {
134 void execute(BundleTrackerCustomizer<?> tracker);