| /******************************************************************************* |
| * Copyright (c) 2008, 2017 Martin Lippert and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Matthew Webster initial implementation |
| * Martin Lippert supplementing mechanism reworked |
| * Heiko Seeberger Enhancements for service dynamics |
| * Martin Lippert fragment handling fixed |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.weaving.hooks; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.equinox.service.weaving.ISupplementerRegistry; |
| import org.eclipse.equinox.service.weaving.Supplementer; |
| import org.eclipse.equinox.weaving.adaptors.IWeavingAdaptor; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| |
| /** |
| * The supplementer registry controls the set of installed supplementer bundles |
| * and calculates which other bundles are supplemented by them. |
| * |
| * @author mlippert |
| */ |
| public class SupplementerRegistry implements ISupplementerRegistry { |
| |
| /** |
| * Manifest header (named "Supplement-Bundle") identifying the |
| * names (and optionally, version numbers) of any bundles supplemented by |
| * this bundle. All supplemented bundles will have all the exported packages |
| * of this bundle added to their imports list |
| * |
| * <p> |
| * The attribute value may be retrieved from the <code>Dictionary</code> |
| * object returned by the <code>Bundle.getHeaders</code> method. |
| */ |
| public static final String SUPPLEMENT_BUNDLE = "Eclipse-SupplementBundle"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest header (named "Supplement-Exporter") identifying the |
| * names (and optionally, version numbers) of the packages that the bundle |
| * supplements. All exporters of one of these packages will have the |
| * exported packages of this bundle added to their imports list. |
| * |
| * <p> |
| * The attribute value may be retrieved from the <code>Dictionary</code> |
| * object returned by the <code>Bundle.getHeaders</code> method. |
| */ |
| public static final String SUPPLEMENT_EXPORTER = "Eclipse-SupplementExporter"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest header (named "Supplement-Importer") identifying the |
| * names (and optionally, version numbers) of the packages that the bundle |
| * supplements. All importers of one of these packages will have the |
| * exported packages of this bundle added to their imports in addition. |
| * |
| * <p> |
| * The attribute value may be retrieved from the <code>Dictionary</code> |
| * object returned by the <code>Bundle.getHeaders</code> method. |
| */ |
| public static final String SUPPLEMENT_IMPORTER = "Eclipse-SupplementImporter"; //$NON-NLS-1$ |
| |
| private final IAdaptorProvider adaptorProvider; |
| |
| private BundleContext context; |
| |
| private final Set<String> dontWeaveTheseBundles; // elements of type String (symbolic name of bundle) |
| |
| private PackageAdmin packageAdmin; |
| |
| private final Map<String, Supplementer> supplementers; // keys of type String (symbolic name of supplementer bundle) |
| |
| private final Map<Long, Supplementer[]> supplementersByBundle; |
| |
| public SupplementerRegistry(final IAdaptorProvider adaptorProvider) { |
| this.adaptorProvider = adaptorProvider; |
| |
| this.supplementers = new HashMap<String, Supplementer>(); |
| this.supplementersByBundle = new HashMap<Long, Supplementer[]>(); |
| this.dontWeaveTheseBundles = new HashSet<String>(); |
| |
| this.dontWeaveTheseBundles.add("org.eclipse.osgi"); |
| this.dontWeaveTheseBundles.add("org.eclipse.core.runtime"); |
| this.dontWeaveTheseBundles.add("org.eclipse.equinox.common"); |
| this.dontWeaveTheseBundles.add("org.eclipse.equinox.weaving.hook"); |
| this.dontWeaveTheseBundles.add("org.eclipse.equinox.weaving.aspectj"); |
| this.dontWeaveTheseBundles.add("org.eclipse.equinox.weaving.caching"); |
| this.dontWeaveTheseBundles |
| .add("org.eclipse.equinox.weaving.caching.j9"); |
| this.dontWeaveTheseBundles.add("org.aspectj.runtime"); |
| this.dontWeaveTheseBundles.add("org.aspectj.weaver"); |
| this.dontWeaveTheseBundles |
| .add("org.eclipse.equinox.simpleconfigurator"); |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#addBundle(org.osgi.framework.Bundle) |
| */ |
| @Override |
| public void addBundle(final Bundle bundle) { |
| // First analyze which supplementers already exists for this bundle |
| addSupplementedBundle(bundle); |
| |
| // Second analyze if this bundle itself is a supplementer |
| addSupplementer(bundle, true); |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#addSupplementedBundle(org.osgi.framework.Bundle) |
| */ |
| @Override |
| public void addSupplementedBundle(final Bundle bundle) { |
| try { |
| final Dictionary<?, ?> manifest = bundle.getHeaders(""); //$NON-NLS-1$ |
| final ManifestElement[] imports = ManifestElement.parseHeader( |
| Constants.IMPORT_PACKAGE, |
| (String) manifest.get(Constants.IMPORT_PACKAGE)); |
| final ManifestElement[] exports = ManifestElement.parseHeader( |
| Constants.EXPORT_PACKAGE, |
| (String) manifest.get(Constants.EXPORT_PACKAGE)); |
| final List<Supplementer> supplementers = getMatchingSupplementers( |
| bundle.getSymbolicName(), imports, exports); |
| if (supplementers.size() > 0) { |
| this.addSupplementedBundle(bundle, supplementers); |
| } |
| |
| this.supplementersByBundle.put(bundle.getBundleId(), supplementers |
| .toArray(new Supplementer[supplementers.size()])); |
| } catch (final BundleException e) { |
| } |
| } |
| |
| private void addSupplementedBundle(final Bundle supplementedBundle, |
| final List<Supplementer> supplementers) { |
| for (final Supplementer supplementer : supplementers) { |
| supplementer.addSupplementedBundle(supplementedBundle); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#addSupplementer(org.osgi.framework.Bundle, |
| * boolean) |
| */ |
| @Override |
| public void addSupplementer(final Bundle bundle, |
| final boolean updateBundles) { |
| try { |
| final Dictionary<?, ?> manifest = bundle.getHeaders(""); //$NON-NLS-1$ |
| final ManifestElement[] supplementBundle = ManifestElement |
| .parseHeader(SUPPLEMENT_BUNDLE, |
| (String) manifest.get(SUPPLEMENT_BUNDLE)); |
| final ManifestElement[] supplementImporter = ManifestElement |
| .parseHeader(SUPPLEMENT_IMPORTER, |
| (String) manifest.get(SUPPLEMENT_IMPORTER)); |
| final ManifestElement[] supplementExporter = ManifestElement |
| .parseHeader(SUPPLEMENT_EXPORTER, |
| (String) manifest.get(SUPPLEMENT_EXPORTER)); |
| |
| if (supplementBundle != null || supplementImporter != null |
| || supplementExporter != null) { |
| |
| final Bundle[] hosts = this.packageAdmin.getHosts(bundle); |
| final Bundle host = hosts != null && hosts.length == 1 |
| ? hosts[0] |
| : null; |
| |
| final Supplementer newSupplementer = new Supplementer(bundle, |
| host, supplementBundle, supplementImporter, |
| supplementExporter); |
| |
| this.supplementers.put(bundle.getSymbolicName(), |
| newSupplementer); |
| if (updateBundles) { |
| resupplementInstalledBundles(newSupplementer); |
| } |
| } |
| } catch (final BundleException e) { |
| } |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#getMatchingSupplementers(java.lang.String, |
| * org.eclipse.osgi.util.ManifestElement[], |
| * org.eclipse.osgi.util.ManifestElement[]) |
| */ |
| @Override |
| public List<Supplementer> getMatchingSupplementers( |
| final String symbolicName, final ManifestElement[] imports, |
| final ManifestElement[] exports) { |
| List<Supplementer> result = Collections.emptyList(); |
| |
| if (supplementers.size() > 0 |
| && !this.dontWeaveTheseBundles.contains(symbolicName)) { |
| result = new LinkedList<Supplementer>(); |
| for (Supplementer supplementer : supplementers.values()) { |
| if (isSupplementerMatching(symbolicName, imports, exports, |
| supplementer)) { |
| result.add(supplementer); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#getPackageAdmin() |
| */ |
| @Override |
| public PackageAdmin getPackageAdmin() { |
| return packageAdmin; |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#getSupplementers(org.osgi.framework.Bundle) |
| */ |
| @Override |
| public Supplementer[] getSupplementers(final Bundle bundle) { |
| return getSupplementers(bundle.getBundleId()); |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#getSupplementers(long) |
| */ |
| @Override |
| public Supplementer[] getSupplementers(final long bundleID) { |
| if (supplementersByBundle.containsKey(bundleID)) { |
| return supplementersByBundle.get(bundleID); |
| } else { |
| return new Supplementer[0]; |
| } |
| } |
| |
| private boolean isSupplementerMatching(final String symbolicName, |
| final ManifestElement[] imports, final ManifestElement[] exports, |
| final Supplementer supplementer) { |
| final String supplementerName = supplementer.getSymbolicName(); |
| if (!supplementerName.equals(symbolicName)) { |
| if (supplementer.matchSupplementer(symbolicName) |
| || (imports != null |
| && supplementer.matchesSupplementImporter(imports)) |
| || (exports != null && supplementer |
| .matchesSupplementExporter(exports))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Refreshes the given bundles |
| * |
| * @param bundles The bundles to refresh |
| */ |
| @Override |
| public void refreshBundles(final Bundle[] bundles) { |
| // if (this.packageAdmin != null) { |
| // if (AbstractWeavingHook.verbose) { |
| // for (int i = 0; i < bundles.length; i++) { |
| // System.out.println("refresh bundle: " |
| // + bundles[i].getSymbolicName()); |
| // } |
| // } |
| // |
| // this.packageAdmin.refreshPackages(bundles); |
| // } |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#removeBundle(org.osgi.framework.Bundle) |
| */ |
| @Override |
| public void removeBundle(final Bundle bundle) { |
| // if this bundle is itself supplemented by others, remove the bundle from those lists |
| removeSupplementedBundle(bundle); |
| this.supplementersByBundle.remove(bundle.getBundleId()); |
| |
| this.adaptorProvider.resetAdaptor(bundle.getBundleId()); |
| |
| // if this bundle supplements other bundles, remove this supplementer and update the other bundles |
| if (supplementers.containsKey(bundle.getSymbolicName())) { |
| |
| // remove the supplementer from the list of supplementers |
| final Supplementer supplementer = supplementers |
| .get(bundle.getSymbolicName()); |
| supplementers.remove(bundle.getSymbolicName()); |
| if (AbstractWeavingHook.verbose) System.err.println( |
| "[org.eclipse.equinox.weaving.hook] info removing supplementer " //$NON-NLS-1$ |
| + bundle.getSymbolicName()); |
| |
| // refresh bundles that where supplemented by this bundle |
| final Bundle[] supplementedBundles = supplementer |
| .getSupplementedBundles(); |
| if (supplementedBundles != null && supplementedBundles.length > 0) { |
| final List<Bundle> bundlesToRefresh = new ArrayList<Bundle>( |
| supplementedBundles.length); |
| for (final Bundle bundleToRefresh : supplementedBundles) { |
| if (this.adaptorProvider.getAdaptor( |
| bundleToRefresh.getBundleId()) != null) { |
| bundlesToRefresh.add(bundleToRefresh); |
| } |
| } |
| |
| if (bundlesToRefresh.size() > 0) { |
| refreshBundles(bundlesToRefresh |
| .toArray(new Bundle[bundlesToRefresh.size()])); |
| } |
| } |
| |
| // remove this supplementer from the list of supplementers per other bundle |
| for (Bundle supplementedBundle : supplementedBundles) { |
| final long bundleId = supplementedBundle.getBundleId(); |
| final List<Supplementer> supplementerList = new ArrayList<Supplementer>( |
| Arrays.asList( |
| this.supplementersByBundle.get(bundleId))); |
| supplementerList.remove(supplementer); |
| this.supplementersByBundle.put(bundleId, |
| supplementerList.toArray(new Supplementer[0])); |
| } |
| } |
| } |
| |
| private void removeSupplementedBundle(final Bundle bundle) { |
| for (final Supplementer supplementer : this.supplementers.values()) { |
| supplementer.removeSupplementedBundle(bundle); |
| } |
| } |
| |
| private void resupplementInstalledBundles(final Supplementer supplementer) { |
| final Bundle[] installedBundles = context.getBundles(); |
| |
| final List<Bundle> bundlesToRefresh = new ArrayList<Bundle>(); |
| |
| for (Bundle installedBundle : installedBundles) { |
| try { |
| final Bundle bundle = installedBundle; |
| // skip the bundle itself, just resupplement already installed bundles |
| if (bundle.getSymbolicName() |
| .equals(supplementer.getSymbolicName())) { |
| continue; |
| } |
| // skip bundles that should not be woven |
| if (dontWeaveTheseBundles.contains(bundle.getSymbolicName())) { |
| continue; |
| } |
| // find out which of the installed bundles matches the new supplementer |
| final Dictionary<?, ?> manifest = bundle.getHeaders(""); //$NON-NLS-1$ |
| final ManifestElement[] imports = ManifestElement.parseHeader( |
| Constants.IMPORT_PACKAGE, |
| (String) manifest.get(Constants.IMPORT_PACKAGE)); |
| final ManifestElement[] exports = ManifestElement.parseHeader( |
| Constants.EXPORT_PACKAGE, |
| (String) manifest.get(Constants.EXPORT_PACKAGE)); |
| if (isSupplementerMatching(bundle.getSymbolicName(), imports, |
| exports, supplementer)) { |
| final IWeavingAdaptor adaptor = this.adaptorProvider |
| .getAdaptor(bundle.getBundleId()); |
| if (adaptor != null && adaptor.isInitialized()) { |
| bundlesToRefresh.add(bundle); |
| } else { |
| supplementer.addSupplementedBundle(bundle); |
| final Supplementer[] existingSupplementers = supplementersByBundle |
| .get(bundle.getBundleId()); |
| List<Supplementer> enhancedSupplementerList = null; |
| if (existingSupplementers != null) { |
| enhancedSupplementerList = new ArrayList<Supplementer>( |
| Arrays.asList(existingSupplementers)); |
| } else { |
| enhancedSupplementerList = new ArrayList<Supplementer>(); |
| } |
| if (!enhancedSupplementerList.contains(supplementer)) { |
| enhancedSupplementerList.add(supplementer); |
| } |
| |
| this.supplementersByBundle.put(bundle.getBundleId(), |
| enhancedSupplementerList |
| .toArray(new Supplementer[0])); |
| } |
| } |
| } catch (final BundleException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| if (bundlesToRefresh.size() > 0) { |
| final Bundle[] bundles = bundlesToRefresh |
| .toArray(new Bundle[bundlesToRefresh.size()]); |
| |
| refreshBundles(bundles); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#setBundleContext(org.osgi.framework.BundleContext) |
| */ |
| @Override |
| public void setBundleContext(final BundleContext context) { |
| this.context = context; |
| } |
| |
| /** |
| * @see org.eclipse.equinox.service.weaving.ISupplementerRegistry#setPackageAdmin(org.osgi.service.packageadmin.PackageAdmin) |
| */ |
| @Override |
| public void setPackageAdmin(final PackageAdmin packageAdmin) { |
| this.packageAdmin = packageAdmin; |
| } |
| } |