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.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;
27 * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
28 * propagates bundle events to all of them.
30 * Primary customizer may return tracking object,
31 * which will be passed to it during invocation of
32 * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Future)}
35 * This extender modifies behaviour to not leak platform thread
36 * in {@link BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)}
37 * but deliver this event from its own single threaded executor.
39 * If bundle is removed before event for adding bundle was executed,
40 * that event is cancelled. If addingBundle event is currently in progress
41 * or was already executed, platform thread is block untill addingBundle
42 * finishes so bundle could be removed correctly in platform thread.
45 * Method {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
46 * is never invoked on registered trackers.
50 public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
51 private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
52 .setNameFormat("config-bundle-tracker-%d").build();
53 private final ExecutorService eventExecutor;
54 private final BundleTrackerCustomizer<T> primaryTracker;
55 private final BundleTrackerCustomizer<?>[] additionalTrackers;
57 private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
59 public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
60 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
61 this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
64 public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
65 final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
66 final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
67 super(context, bundleState, null);
68 this.primaryTracker = primaryBundleTrackerCustomizer;
69 this.additionalTrackers = additionalBundleTrackerCustomizers;
70 eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
71 LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
75 public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
76 LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously",bundle,event);
77 Future<T> future = eventExecutor.submit(new Callable<T>() {
79 public T call() throws Exception {
81 T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
83 forEachAdditionalBundle(new BundleStrategy() {
85 public void execute(final BundleTrackerCustomizer<?> tracker) {
86 tracker.addingBundle(bundle, event);
89 LOG.trace("AddingBundle for {} and event {} finished successfully",bundle,event);
90 return primaryTrackerRetVal;
91 } catch (Exception e) {
92 LOG.error("Failed to add bundle {}", bundle, e);
101 public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
102 // Intentionally NOOP
107 public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
108 if(!object.isDone() && object.cancel(false)) {
109 // We canceled adding event before it was processed
110 // so it is safe to return
111 LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.",bundle);
115 LOG.trace("Invoking removedBundle event for {}",bundle);
116 primaryTracker.removedBundle(bundle, event, object.get());
117 forEachAdditionalBundle(new BundleStrategy() {
119 public void execute(final BundleTrackerCustomizer<?> tracker) {
120 tracker.removedBundle(bundle, event, null);
123 LOG.trace("Removed bundle event for {} finished successfully.",bundle);
124 } catch (Exception e) {
125 LOG.error("Failed to remove bundle {}", bundle, e);
129 private void forEachAdditionalBundle(final BundleStrategy lambda) {
130 for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
131 lambda.execute(trac);
135 private interface BundleStrategy {
136 void execute(BundleTrackerCustomizer<?> tracker);