/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.core.common.osgi; import java.util.ArrayList; import java.util.List; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.SynchronousBundleListener; import org.osgi.util.tracker.BundleTracker; /** * The {@link ResolvedBundleTracker} tracks any bundles which have reached the "resolved" state * semantically. This means that not only a "resolved" bundle is tracked but also a bundle who * has reached the "started" or "starting" state. * <p> * Override the methods {@link #addingBundle(Bundle)} or {@link #removedBundle(Bundle)} to consume the events. * <p> * This class is a simple replacement for an <i>OSGi</i> {@link BundleTracker}, whose usage for monitoring semantic * states is more complex. * * @author Michael Grammling - Initial Contribution */ public abstract class ResolvedBundleTracker implements SynchronousBundleListener { private BundleContext bundleContext; private List<Bundle> trackedBundles; /** * Creates a new instance of this class with the specified parameter. * * @param bundleContext a bundle context to be used to track any bundles (must not be null) * @throws IllegalArgumentException if the bundle context is null */ public ResolvedBundleTracker(BundleContext bundleContext) throws IllegalArgumentException { if (bundleContext == null) { throw new IllegalArgumentException("The bundle context must not be null!"); } this.bundleContext = bundleContext; this.trackedBundles = new ArrayList<Bundle>(); } /** * Opens the tracker. * <p> * For each bundle which is already available and which has reached at least the "resolved" state, an * {@link #addingBundle(Bundle)} event is fired. */ public synchronized void open() { this.bundleContext.addBundleListener(this); initialize(); } /** * Closes the tracker. * <p> * For each tracked bundle a {@link #removedBundle(Bundle)} event is fired. */ public synchronized void close() { this.bundleContext.removeBundleListener(this); uninitialize(); } private synchronized void initialize() { Bundle[] bundles = this.bundleContext.getBundles(); for (Bundle bundle : bundles) { int state = bundle.getState(); boolean shouldNotBeTracked = (state & (Bundle.INSTALLED | Bundle.UNINSTALLED)) > 0; if (shouldNotBeTracked) { remove(bundle); } else { add(bundle); } } } private synchronized void uninitialize() { for (Bundle bundle : this.trackedBundles) { try { removedBundle(bundle); } catch (Exception ex) { // nothing to do } } this.trackedBundles.clear(); } @Override public synchronized void bundleChanged(BundleEvent event) { Bundle bundle = event.getBundle(); int type = event.getType(); boolean shouldBeTracked = (type & ((BundleEvent.STARTING | BundleEvent.STARTED | BundleEvent.RESOLVED))) > 0; boolean shouldNotBeTracked = (type & (BundleEvent.UNINSTALLED | BundleEvent.UNRESOLVED)) > 0; if (shouldBeTracked) { add(bundle); } else if (shouldNotBeTracked) { remove(bundle); } } private void add(Bundle bundle) { if (!this.trackedBundles.contains(bundle)) { try { if (addingBundle(bundle)) { this.trackedBundles.add(bundle); } } catch (Exception ex) { // nothing to do } } } private void remove(Bundle bundle) { if (this.trackedBundles.contains(bundle)) { try { removedBundle(bundle); } catch (Exception ex) { // nothing to do } finally { this.trackedBundles.remove(bundle); } } } /** * The callback method to be invoked when a bundle was detected which at least reached * the "resolved" state. * <p> * This method might be overridden. * * @param bundle the according bundle (not null) * @return true if the bundle should be tracked, otherwise false */ public boolean addingBundle(Bundle bundle) { // override this method if needed return false; } /** * The callback method to be invoked when a tracked bundle was detected to leave at * least the "resolved" state semantically (e.g. if it was "uninstalled", etc.). * <p> * This method might be overridden. * * @param bundle the according bundle (not null) */ public void removedBundle(Bundle bundle) { // override this method if needed } }