| /******************************************************************************* |
| * Copyright (c) 2008, 2010 VMware Inc. |
| * 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 |
| * |
| * Contributors: |
| * VMware Inc. - initial contribution |
| *******************************************************************************/ |
| |
| package org.eclipse.virgo.kernel.userregion.internal.importexpansion; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.osgi.framework.internal.core.BundleRepository; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Version; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import org.eclipse.virgo.kernel.osgi.framework.ImportExpander; |
| import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException; |
| import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException; |
| import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException; |
| |
| import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge; |
| import org.eclipse.virgo.kernel.artifact.library.LibraryDefinition; |
| import org.eclipse.virgo.nano.serviceability.Assert; |
| import org.eclipse.virgo.kernel.userregion.internal.UserRegionLogEvents; |
| import org.eclipse.virgo.medic.eventlog.EventLogger; |
| import org.eclipse.virgo.repository.ArtifactDescriptor; |
| import org.eclipse.virgo.repository.Attribute; |
| import org.eclipse.virgo.repository.Repository; |
| import org.eclipse.virgo.util.math.OrderedPair; |
| import org.eclipse.virgo.util.osgi.manifest.VersionRange; |
| import org.eclipse.virgo.util.osgi.manifest.BundleManifest; |
| import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory; |
| import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName; |
| import org.eclipse.virgo.util.osgi.manifest.ExportedPackage; |
| import org.eclipse.virgo.util.osgi.manifest.ImportedBundle; |
| import org.eclipse.virgo.util.osgi.manifest.ImportedLibrary; |
| import org.eclipse.virgo.util.osgi.manifest.ImportedPackage; |
| import org.eclipse.virgo.util.osgi.manifest.Resolution; |
| |
| /** |
| * A helper class for handling the expansion of <code>Import-Library</code> and <code>Import-Bundle</code> headers in a |
| * bundle manifest into <code>Import-Package</code> header entries. |
| * <p/> |
| * |
| * <strong>Concurrent Semantics</strong><br /> |
| * This class is <strong>thread-safe</strong>. |
| * |
| */ |
| public final class ImportExpansionHandler implements ImportExpander { |
| |
| private static final String IMPORT_SCOPE_APPLICATION = "application"; |
| |
| private static final String IMPORT_SCOPE_DIRECTIVE = "import-scope"; |
| |
| private static final String MISSING_BUNDLE_SYMBOLIC_NAME = "<bundle symbolic name not present>"; |
| |
| private static final String INSTRUMENTED_SUFFIX = ".instrumented"; |
| |
| private static final String SYNTHETIC_CONTEXT_SUFFIX = "-synthetic.context"; |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| private final Repository repository; |
| |
| private final TrackedPackageImportsFactory trackedPackageImportsFactory = new StandardTrackedPackageImportsFactory(); |
| |
| private final BundleContext bundleContext; |
| |
| private final Set<String> packagesExportedBySystemBundle; |
| |
| private final EventLogger eventLogger; |
| |
| ImportExpansionHandler(Repository repository, Set<String> packagesExportedBySystemBundle, EventLogger eventLogger) { |
| this(repository, null, packagesExportedBySystemBundle, eventLogger); |
| } |
| |
| public ImportExpansionHandler(Repository repository, BundleContext bundleContext, Set<String> packagesExportedBySystemBundle, |
| EventLogger eventLogger) { |
| this.repository = repository; |
| this.bundleContext = bundleContext; |
| this.packagesExportedBySystemBundle = packagesExportedBySystemBundle; |
| this.eventLogger = eventLogger; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @throws ImportMergeException |
| */ |
| public ImportPromotionVector expandImports(List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException, |
| ImportMergeException { |
| StandardImportPromotionVector importPromotionVector = new StandardImportPromotionVector(this.trackedPackageImportsFactory); |
| TrackedPackageImports packageImportsToBePromoted = this.trackedPackageImportsFactory.createCollector(); |
| |
| for (BundleManifest bundleManifest : bundleManifests) { |
| TrackedPackageImports bundlePackageImportsToBePromoted = this.trackedPackageImportsFactory.createCollector(); |
| |
| detectPromotedPackageImports(bundleManifest, bundlePackageImportsToBePromoted); |
| |
| expandImportsIfNecessary(bundleManifest, bundlePackageImportsToBePromoted, bundleManifests); |
| if (!bundlePackageImportsToBePromoted.isEmpty()) { |
| packageImportsToBePromoted.merge(bundlePackageImportsToBePromoted); |
| BundleSymbolicName bundleSymbolicNameHeader = bundleManifest.getBundleSymbolicName(); |
| Assert.notNull(bundleSymbolicNameHeader, "Bundle-SymbolicName must be present for import promotion tracking"); |
| importPromotionVector.put(bundleSymbolicNameHeader.getSymbolicName(), bundlePackageImportsToBePromoted); |
| } |
| } |
| |
| mergePromotedImports(packageImportsToBePromoted, bundleManifests); |
| return importPromotionVector; |
| } |
| |
| /** |
| * Detect package imports with application import scope and add these to the package imports to be promoted. |
| */ |
| private void detectPromotedPackageImports(BundleManifest bundleManifest, TrackedPackageImports bundlePackageImportsToBePromoted) { |
| List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages(); |
| List<ImportedPackage> importedPackagesToPromote = new ArrayList<>(); |
| for (ImportedPackage importedPackage : importedPackages) { |
| if (IMPORT_SCOPE_APPLICATION.equals(importedPackage.getDirectives().get(IMPORT_SCOPE_DIRECTIVE))) { |
| importedPackagesToPromote.add(importedPackage); |
| } |
| } |
| TrackedPackageImports trackedPackageImportsToPromote = this.trackedPackageImportsFactory.create(importedPackagesToPromote, |
| "Import-Package in '" + bundleManifest.getBundleSymbolicName().getSymbolicName() + "' version '" + bundleManifest.getBundleVersion() |
| + "'"); |
| bundlePackageImportsToBePromoted.merge(trackedPackageImportsToPromote); |
| } |
| |
| private void mergePromotedImports(TrackedPackageImports importsToBemerged, List<BundleManifest> bundleManifests) throws ImportMergeException { |
| for (BundleManifest bundleManifest : bundleManifests) { |
| mergePromotedImports(importsToBemerged, bundleManifest); |
| } |
| } |
| |
| private void mergePromotedImports(TrackedPackageImports importsToBemerged, BundleManifest bundleManifest) throws ImportMergeException { |
| BundleSymbolicName bundleSymbolicNameHeader = bundleManifest.getBundleSymbolicName(); |
| if (bundleSymbolicNameHeader.getSymbolicName() == null || !bundleSymbolicNameHeader.getSymbolicName().endsWith(SYNTHETIC_CONTEXT_SUFFIX)) { |
| mergeImports(importsToBemerged, bundleManifest); |
| } |
| } |
| |
| private void mergeImports(TrackedPackageImports importsToBemerged, BundleManifest bundleManifest) throws ImportMergeException { |
| TrackedPackageImports bundleTrackedPackageImports = this.trackedPackageImportsFactory.create(bundleManifest); |
| bundleTrackedPackageImports.merge(importsToBemerged); |
| setMergedImports(bundleManifest, bundleTrackedPackageImports); |
| } |
| |
| // TODO either write test to check if imports have been expanded or remove return value? |
| private boolean expandImportsIfNecessary(BundleManifest manifest, TrackedPackageImports packageImportsToBePromoted, |
| List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException { |
| boolean expanded = false; |
| |
| List<ImportedBundle> directlyImportedBundles = manifest.getImportBundle().getImportedBundles(); |
| List<ImportedLibrary> importedLibraries = manifest.getImportLibrary().getImportedLibraries(); |
| |
| if (directlyImportedBundles.size() > 0 || importedLibraries.size() > 0) { |
| this.logger.info("Import-Library and/or Import-Bundle header found. Original manifest: \n{}", manifest); |
| expandImports(importedLibraries, directlyImportedBundles, manifest, packageImportsToBePromoted, bundleManifests); |
| this.logger.info("Updated manifest: \n{}", manifest); |
| expanded = true; |
| } |
| return expanded; |
| } |
| |
| private UnableToSatisfyBundleDependenciesException createExceptionForMissingLibrary(String name, VersionRange versionRange, |
| BundleManifest bundleManifest) { |
| String description = String.format("A library with the name '%s' and a version within the range '%s' could not be found", name, versionRange); |
| BundleSymbolicName bundleSymbolicName = bundleManifest.getBundleSymbolicName(); |
| return new UnableToSatisfyBundleDependenciesException(bundleSymbolicName != null ? bundleSymbolicName.getSymbolicName() |
| : MISSING_BUNDLE_SYMBOLIC_NAME, bundleManifest.getBundleVersion(), description); |
| } |
| |
| void expandImports(List<ImportedLibrary> libraryImports, List<ImportedBundle> directlyImportedBundles, BundleManifest bundleManifest) |
| throws UnableToSatisfyDependenciesException { |
| expandImports(libraryImports, directlyImportedBundles, bundleManifest, this.trackedPackageImportsFactory.createCollector(), |
| Collections.EMPTY_LIST); |
| } |
| |
| private void expandImports(List<ImportedLibrary> libraryImports, List<ImportedBundle> directlyImportedBundles, BundleManifest bundleManifest, |
| TrackedPackageImports packageImportsToBePromoted, List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException { |
| Assert.notNull(libraryImports, "Library imports must be non-null"); |
| Assert.notNull(directlyImportedBundles, "Direct bundle imports must be non-null"); |
| |
| mergeImports( |
| getAdditionalPackageImports(libraryImports, directlyImportedBundles, bundleManifest, packageImportsToBePromoted, bundleManifests), |
| bundleManifest); |
| |
| bundleManifest.getImportBundle().getImportedBundles().clear(); |
| bundleManifest.getImportLibrary().getImportedLibraries().clear(); |
| } |
| |
| /** |
| * The bundle with the given bundle manifest imports the given list of libraries and the given list of bundles. |
| * Dereference these imports and return a collection of the corresponding {@link TrackedPackageImports}. Any |
| * promoted imports are added to the given <code>TrackedPackageImports</code> of imports to be promoted. |
| */ |
| private TrackedPackageImports getAdditionalPackageImports(List<ImportedLibrary> importedLibraries, List<ImportedBundle> directlyImportedBundles, |
| BundleManifest bundleManifest, TrackedPackageImports packageImportsToBePromoted, List<BundleManifest> additionalManifests) |
| throws UnableToSatisfyDependenciesException { |
| TrackedPackageImports additionalPackageImports = this.trackedPackageImportsFactory.createCollector(); |
| TrackedPackageImports libraryPackageImports = getLibraryPackageImports(importedLibraries, packageImportsToBePromoted, bundleManifest, |
| additionalManifests); |
| additionalPackageImports.merge(libraryPackageImports); |
| for (ImportedBundle directlyImportedBundle : directlyImportedBundles) { |
| additionalPackageImports.merge(getBundlePackageImports(directlyImportedBundle, packageImportsToBePromoted, bundleManifest, |
| additionalManifests)); |
| } |
| return additionalPackageImports; |
| } |
| |
| /** |
| * Get a {@link TrackedPackageImports} instance representing the package imports that correspond to the given bundle |
| * import. The imported bundle is looked up in the given {@link BundleRepository} and, if it is not found, |
| * {@link UnableToSatisfyBundleDependenciesException} is thrown. If the bundle import is to be promoted, then the |
| * result is also merged into the given <code>TrackedPackageImports</code> instance representing the package imports |
| * to be promoted. |
| */ |
| private TrackedPackageImports getBundlePackageImports(ImportedBundle importedBundle, TrackedPackageImports packageImportsToBePromoted, |
| BundleManifest importingBundle, List<BundleManifest> additionalManifests) throws UnableToSatisfyBundleDependenciesException { |
| String bundleSymbolicName = importedBundle.getBundleSymbolicName(); |
| VersionRange importVersionRange = importedBundle.getVersion(); |
| boolean mandatory = importedBundle.getResolution().equals(Resolution.MANDATORY); |
| if (bundleSymbolicName.equals(importingBundle.getBundleSymbolicName().getSymbolicName()) |
| && importVersionRange.includes(importingBundle.getBundleVersion())) { |
| throw new UnableToSatisfyBundleDependenciesException(importingBundle.getBundleSymbolicName().getSymbolicName(), |
| importingBundle.getBundleVersion(), "Import-Bundle must not import the importing bundle"); |
| } |
| |
| OrderedPair<BundleManifest, Boolean> bundleManifestHolder = findBundle(bundleSymbolicName, importVersionRange, additionalManifests); |
| if (bundleManifestHolder.getFirst() != null) { |
| return createTrackedPackageImportsFromImportedBundle(bundleManifestHolder, importedBundle.isApplicationImportScope(), |
| packageImportsToBePromoted); |
| } else if (mandatory) { |
| throw new UnableToSatisfyBundleDependenciesException( |
| importingBundle.getBundleSymbolicName() != null ? importingBundle.getBundleSymbolicName().getSymbolicName() |
| : MISSING_BUNDLE_SYMBOLIC_NAME, importingBundle.getBundleVersion(), "Import-Bundle with symbolic name '" + bundleSymbolicName |
| + "' in version range '" + importVersionRange + "' could not be satisfied"); |
| } else { |
| return this.trackedPackageImportsFactory.createEmpty(); |
| } |
| } |
| |
| /** |
| * Return the {@link TrackedPackageImports} corresponding to importing the given {@link BundleManifest} and, if |
| * appropriate, merge them into the given imports to be promoted. |
| */ |
| private TrackedPackageImports createTrackedPackageImportsFromImportedBundle(OrderedPair<BundleManifest, Boolean> bundleManifest, |
| boolean promoteExports, TrackedPackageImports packageImportsToBePromoted) { |
| TrackedPackageImports bundlePackageImports = createPackageImportsFromPackageExports(bundleManifest, promoteExports); |
| if (promoteExports) { |
| packageImportsToBePromoted.merge(bundlePackageImports); |
| } |
| return bundlePackageImports; |
| } |
| |
| /** |
| * Create a {@link TrackedPackageImports} instance corresponding to the given package exports from the given bundle |
| * manifest. |
| * <p /> |
| * Importing fragment bundles is not encouraged but sometimes it's the only way out when reusing poorly packaged |
| * bundles. Packages exported by a fragment are imported with the host's bundle symbolic name, an exact range |
| * matching only the exported package version, and a bundle version range corresponding to that of the fragment host |
| * header. Since the generated imports do not include a bundle version matching attribute with a value of the form |
| * [v,v], there is a risk that the package import will wire to the wrong version of the host. This risk seems very |
| * small and not worth the extra complexity of matching up hosts and fragments (some of which would need searching |
| * for in the repository) during import expansion. |
| * |
| */ |
| private TrackedPackageImports createPackageImportsFromPackageExports(OrderedPair<BundleManifest, Boolean> bundleManifestHolder, |
| boolean promoteExports) { |
| BundleManifest bundleManifest = bundleManifestHolder.getFirst(); |
| List<ExportedPackage> exportedPackages = bundleManifest.getExportPackage().getExportedPackages(); |
| if (exportedPackages.isEmpty()) { |
| return this.trackedPackageImportsFactory.createEmpty(); |
| } |
| String bundleVersion = bundleManifest.getBundleVersion().toString(); |
| String bundleSymbolicName = bundleManifest.getBundleSymbolicName().getSymbolicName(); |
| |
| List<ImportedPackage> packageImports; |
| |
| if (bundleManifest.getFragmentHost().getBundleSymbolicName() != null) { |
| bundleSymbolicName = bundleManifest.getFragmentHost().getBundleSymbolicName(); |
| packageImports = BundleManifestProcessor.createImportedPackageForEachExportedPackageOfFragment(exportedPackages, bundleSymbolicName, |
| bundleManifest.getFragmentHost().getBundleVersion()); |
| } else { |
| packageImports = BundleManifestProcessor.createImportedPackageForEachExportedPackage(exportedPackages, bundleSymbolicName, bundleVersion); |
| } |
| |
| if (promoteExports) { |
| tagImportsAsPromoted(packageImports); |
| } |
| |
| if (bundleManifestHolder.getSecond()) { |
| diagnoseSystemBundleOverlap(packageImports, bundleSymbolicName, bundleVersion); |
| } |
| |
| return this.trackedPackageImportsFactory.create(packageImports, "Import-Bundle '" + bundleManifest.getBundleSymbolicName().getSymbolicName() |
| + "' version '" + bundleManifest.getBundleVersion() + "'"); |
| } |
| |
| private void tagImportsAsPromoted(List<ImportedPackage> packageImports) { |
| for (ImportedPackage importedPackage : packageImports) { |
| importedPackage.getDirectives().put(IMPORT_SCOPE_DIRECTIVE, IMPORT_SCOPE_APPLICATION); |
| } |
| } |
| |
| /** |
| * Check whether the packages imported by importing a bundle are exported by the system bundle and, if they are, |
| * issue a warning. |
| * |
| * @param importedPackages the imported packages |
| * @param bundleSymbolicNameString the symbolic name of the imported bundle |
| * @param bundleVersion the version of the imported bundle |
| */ |
| private void diagnoseSystemBundleOverlap(List<ImportedPackage> importedPackages, String bundleSymbolicNameString, String bundleVersion) { |
| Set<String> overlap = new HashSet<>(); |
| for (ImportedPackage importedPackage : importedPackages) { |
| String packageName = importedPackage.getPackageName(); |
| if (this.packagesExportedBySystemBundle.contains(packageName)) { |
| overlap.add(packageName); |
| } |
| } |
| if (!overlap.isEmpty()) { |
| StringBuilder imports = new StringBuilder(); |
| boolean first = true; |
| for (ImportedPackage packageImport : importedPackages) { |
| if (!first) { |
| imports.append(","); |
| } |
| first = false; |
| imports.append(packageImport.getPackageName()); |
| } |
| this.eventLogger.log(UserRegionLogEvents.SYSTEM_BUNDLE_OVERLAP, bundleSymbolicNameString, bundleVersion, overlap.toString(), |
| imports.toString()); |
| } |
| } |
| |
| /** |
| * Get a {@link TrackedPackageImports} instance representing the package imports that correspond to the given |
| * library imports. Each imported library is looked up in the given {@link BundleRepository} and, if it is not |
| * found, {@link UnableToSatisfyBundleDependenciesException} is thrown. If any package imports are to be promoted, |
| * then the result is also merged into the given <code>TrackedPackageImports</code> instance representing the |
| * package imports to be promoted. |
| */ |
| private TrackedPackageImports getLibraryPackageImports(List<ImportedLibrary> importedLibraries, TrackedPackageImports packageImportsToBePromoted, |
| BundleManifest importingBundle, List<BundleManifest> additionalManifests) throws UnableToSatisfyBundleDependenciesException { |
| TrackedPackageImports allLibraryPackageImports = this.trackedPackageImportsFactory.createCollector(); |
| for (ImportedLibrary importedLibrary : importedLibraries) { |
| VersionRange libraryVersionRange = importedLibrary.getVersion(); |
| String libraryName = importedLibrary.getLibrarySymbolicName(); |
| |
| ArtifactDescriptor libraryArtefact = findArtifactDescriptorForLibrary(libraryName, libraryVersionRange); |
| |
| if (libraryArtefact != null) { |
| |
| if (!libraryName.endsWith(INSTRUMENTED_SUFFIX) && libraryArtefact.getName().endsWith(INSTRUMENTED_SUFFIX)) { |
| this.eventLogger.log(UserRegionLogEvents.ALTERNATE_INSTRUMENTED_LIBRARY_FOUND, importingBundle.getBundleSymbolicName(), |
| libraryName, libraryVersionRange.toString(), libraryArtefact.getName()); |
| } |
| |
| Version libraryVersion = libraryArtefact.getVersion(); |
| TrackedPackageImports libraryPackageImports = this.trackedPackageImportsFactory.createContainer("Import-Library '" |
| + importedLibrary.getLibrarySymbolicName() + "' version '" + libraryVersion + "'"); |
| |
| Set<Attribute> importedBundles = libraryArtefact.getAttribute("Import-Bundle"); |
| |
| for (Attribute importedBundle : importedBundles) { |
| String bundleSymbolicName = importedBundle.getValue(); |
| Map<String, Set<String>> properties = importedBundle.getProperties(); |
| Set<String> versionSet = properties.get("version"); |
| VersionRange bundleVersionRange; |
| if (versionSet != null && !versionSet.isEmpty()) { |
| bundleVersionRange = new VersionRange(versionSet.iterator().next()); |
| } else { |
| bundleVersionRange = VersionRange.NATURAL_NUMBER_RANGE; |
| } |
| OrderedPair<BundleManifest, Boolean> bundleManifest = findBundle(bundleSymbolicName, bundleVersionRange, additionalManifests); |
| if (bundleManifest.getFirst() != null) { |
| boolean applicationImportScope = false; |
| Set<String> importScopeSet = properties.get(IMPORT_SCOPE_DIRECTIVE); |
| if (importScopeSet != null && !importScopeSet.isEmpty()) { |
| applicationImportScope = IMPORT_SCOPE_APPLICATION.equals(importScopeSet.iterator().next()); |
| } |
| libraryPackageImports.merge(createTrackedPackageImportsFromImportedBundle(bundleManifest, applicationImportScope, |
| packageImportsToBePromoted)); |
| } else { |
| Resolution importedBundleResolution = Resolution.MANDATORY; |
| Set<String> resolutionSet = properties.get("resolution"); |
| if (resolutionSet != null && !resolutionSet.isEmpty()) { |
| importedBundleResolution = Resolution.valueOf(resolutionSet.iterator().next().toUpperCase(Locale.ENGLISH)); |
| } |
| |
| if (importedBundleResolution.equals(Resolution.MANDATORY)) { |
| throw new UnableToSatisfyBundleDependenciesException( |
| importingBundle.getBundleSymbolicName() != null ? importingBundle.getBundleSymbolicName().getSymbolicName() |
| : MISSING_BUNDLE_SYMBOLIC_NAME, importingBundle.getBundleVersion(), "Imported library '" + libraryName |
| + "' version '" + libraryVersion + "' contains Import-Bundle for bundle '" + bundleSymbolicName |
| + "' in version range '" + bundleVersionRange + "' which could not be satisfied"); |
| } |
| } |
| } |
| allLibraryPackageImports.merge(libraryPackageImports); |
| } else if (importedLibrary.getResolution().equals(Resolution.MANDATORY)) { |
| throw createExceptionForMissingLibrary(libraryName, libraryVersionRange, importingBundle); |
| } |
| } |
| return allLibraryPackageImports; |
| } |
| |
| /** |
| * Set the package imports of the given {@link BundleManifest} to the merged imports of the given |
| * {@link TrackedPackageImports}. |
| * |
| * @param bundleManifest the bundle manifest to be modified |
| * @param bundleTrackedPackageImports the <code>TrackedPackageImports</code> containing the merged imports |
| */ |
| private void setMergedImports(BundleManifest bundleManifest, TrackedPackageImports bundleTrackedPackageImports) { |
| bundleManifest.getImportPackage().getImportedPackages().clear(); |
| bundleManifest.getImportPackage().getImportedPackages().addAll(bundleTrackedPackageImports.getMergedImports()); |
| } |
| |
| /** |
| * Find the bundle with the given symbolic name and version range in the given bundle repository. If the given |
| * scoper is non-null, scope the symbolic name before searching the repository. Return the {@link BundleManifest} |
| * of the bundle if it was found. |
| */ |
| private OrderedPair<BundleManifest, Boolean> findBundle(String bundleSymbolicName, VersionRange versionRange, |
| List<BundleManifest> additionalManifests) { |
| |
| boolean diagnose = false; |
| |
| // prefer bundles from the supplied list |
| BundleManifest bundleManifest = findMatchingManifest(bundleSymbolicName, versionRange, additionalManifests); |
| |
| if (bundleManifest == null && this.bundleContext != null) { |
| Bundle[] installedBundles = this.bundleContext.getBundles(); |
| for (Bundle bundle : installedBundles) { |
| if (bundleSymbolicName.equals(bundle.getSymbolicName()) && versionRange.includes(bundle.getVersion())) { |
| bundleManifest = getBundleManifest(bundle); |
| diagnose = true; |
| break; |
| } |
| } |
| } |
| |
| if (bundleManifest == null) { |
| ArtifactDescriptor artefact = findArtifactDescriptorForBundle(bundleSymbolicName, versionRange); |
| if (artefact != null) { |
| diagnose = true; |
| bundleManifest = BundleManifestFactory.createBundleManifest(BundleBridge.convertToDictionary(artefact)); |
| } |
| } |
| |
| if (bundleManifest != null) { |
| this.logger.info("Found definition for bundle with symbolic name '{}' and version range '{}': {}", bundleSymbolicName, versionRange, |
| bundleManifest); |
| } else { |
| this.logger.info("Could not find definition for bundle with symbolic name '{}' and version range '{}'", bundleSymbolicName, versionRange); |
| } |
| |
| return new OrderedPair<>(bundleManifest, diagnose); |
| } |
| |
| /** |
| * Finds the manifest in the supplied list that matches the given bundle symbolic name and version range. If no |
| * match is found, null is returned. If many matches are found, the one with the highest version range is selected. |
| */ |
| private BundleManifest findMatchingManifest(String bundleSymbolicName, VersionRange versionRange, List<BundleManifest> additionalManifests) { |
| Version selectedVersion = null; |
| BundleManifest selectedManifest = null; |
| |
| for (final BundleManifest manifest : additionalManifests) { |
| BundleSymbolicName bsn = manifest.getBundleSymbolicName(); |
| if (bsn != null && bundleSymbolicName.equals(bsn.getSymbolicName())) { |
| Version version = manifest.getBundleVersion(); |
| if (versionRange.includes(version)) { |
| if (selectedVersion == null || version.compareTo(selectedVersion) > 0) { |
| selectedVersion = version; |
| selectedManifest = manifest; |
| } |
| } |
| } |
| } |
| return selectedManifest; |
| } |
| |
| private ArtifactDescriptor findArtifactDescriptorForBundle(String bundleSymbolicName, VersionRange versionRange) { |
| return this.repository.get(BundleBridge.BRIDGE_TYPE, bundleSymbolicName, versionRange); |
| } |
| |
| private ArtifactDescriptor findArtifactDescriptorForLibrary(String librarySymbolicName, VersionRange versionRange) { |
| return this.repository.get(LibraryDefinition.LIBRARY_TYPE, librarySymbolicName, versionRange); |
| } |
| |
| /** |
| * Get a {@link BundleManifest} for the given {@link Bundle}. If a definition cannot be created, return |
| * <code>null</code>. |
| * |
| * @param bundle the bundle whose definition is required |
| * @return the bundle definition or <code>null</code> if no definition can be created |
| */ |
| private BundleManifest getBundleManifest(Bundle bundle) { |
| return BundleManifestFactory.createBundleManifest(bundle.getHeaders()); |
| } |
| |
| } |