/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.aries.util.tracker; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.service.framework.CompositeBundle; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTrackerCustomizer; /** * A BundleTracker which will track bundles in the given context, and also * bundles in any child contexts. This should be used instead of the * normal non-recursive BundleTracker when registering bundle tracker * customizers. */ public class InternalRecursiveBundleTracker extends BundleTracker { private final int mask; private final ConcurrentMap<String,String> alreadyRecursedContexts = new ConcurrentHashMap<String, String>(); private final BundleTrackerCustomizer customizer; private final boolean nested; public InternalRecursiveBundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer, boolean nested) { super(context, stateMask, null); mask = stateMask; this.customizer = customizer; this.nested = nested; } /* * (non-Javadoc) * @see org.osgi.util.tracker.BundleTracker#addingBundle(org.osgi.framework.Bundle, org.osgi.framework.BundleEvent) */ @Override public Object addingBundle(Bundle b, BundleEvent event) { Object o = null; if (b instanceof CompositeBundle) { customizedProcessBundle(this, b, event, false); o = b; } else if (nested) { // Delegate to our customizer for normal bundles if (customizer != null) { o = customizer.addingBundle(b, event); } } return o; } /* * (non-Javadoc) * @see org.osgi.util.tracker.BundleTracker#modifiedBundle(org.osgi.framework.Bundle, org.osgi.framework.BundleEvent, java.lang.Object) */ @Override public void modifiedBundle(Bundle b, BundleEvent event, Object object) { if (b instanceof CompositeBundle) { customizedProcessBundle(this, b, event, false); } else { // Delegate to our customizer for normal bundles if (customizer != null) { customizer.modifiedBundle(b, event, object); } } } /* * (non-Javadoc) * @see org.osgi.util.tracker.BundleTracker#removedBundle(org.osgi.framework.Bundle, org.osgi.framework.BundleEvent, java.lang.Object) */ @Override public void removedBundle(Bundle b, BundleEvent event, Object object) { if (b instanceof CompositeBundle) { customizedProcessBundle(this, b, event, true); } else { if (customizer != null) { customizer.removedBundle(b, event, object); } } } protected void customizedProcessBundle(BundleTrackerCustomizer btc, Bundle b, BundleEvent event, boolean removing) { if (b instanceof CompositeBundle) { CompositeBundle cb = (CompositeBundle) b; // check if the compositeBundle is already tracked in the // BundleTrackerFactory String bundleScope = cb.getSymbolicName() + "_" + cb.getVersion().toString(); List<BundleTracker> btList = BundleTrackerFactory.getBundleTrackerList(bundleScope); // bundle is already active and there is no event associated // this can happen when bundle is first time added to the tracker // or when the tracker is being closed. if (event == null && !!!removing) { if (cb.getState() == Bundle.INSTALLED || cb.getState() == Bundle.RESOLVED || cb.getState() == Bundle.STARTING || cb.getState() == Bundle.ACTIVE) { openTracker(btc, cb, bundleScope, mask); } } else { // if we are removing, or the event is of the right type then we need to shutdown. if (removing || event.getType() == BundleEvent.STOPPED || event.getType() == BundleEvent.UNRESOLVED || event.getType() == BundleEvent.UNINSTALLED) { // if CompositeBundle is being stopped, let's remove the bundle // tracker(s) associated with the composite bundle String bundleId = b.getSymbolicName()+"/"+b.getVersion(); alreadyRecursedContexts.remove(bundleId); if (btList != null) { // unregister the bundlescope off the factory and close // bundle trackers BundleTrackerFactory.unregisterAndCloseBundleTracker(bundleScope); } } else if (event.getType() == BundleEvent.INSTALLED || event.getType() == BundleEvent.RESOLVED || event.getType() == BundleEvent.STARTING) { openTracker(btc, cb, bundleScope, mask); } } } } private synchronized void openTracker(BundleTrackerCustomizer btc, CompositeBundle cb, String bundleScope, int stateMask) { // let's process each of the bundle in the CompositeBundle BundleContext compositeBundleContext = cb.getCompositeFramework().getBundleContext(); String bundleId = cb.getSymbolicName()+"/"+cb.getVersion(); if (alreadyRecursedContexts.putIfAbsent(bundleId, bundleId) == null) { // let's track each of the bundle in the CompositeBundle BundleTracker bt = new InternalRecursiveBundleTracker(compositeBundleContext, stateMask, customizer, true); bt.open(); BundleTrackerFactory.registerBundleTracker(bundleScope, bt); } } }