package org.bundlemaker.core.osgi.manifest; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bundlemaker.core.analysis.AnalysisModelQueries; import org.bundlemaker.core.analysis.IBundleMakerArtifact; import org.bundlemaker.core.analysis.IDependency; import org.bundlemaker.core.analysis.IModuleArtifact; import org.bundlemaker.core.analysis.IPackageArtifact; import org.bundlemaker.core.common.collections.GenericCache; import org.bundlemaker.core.osgi.utils.ArtifactUtils; import org.bundlemaker.core.osgi.utils.ManifestUtils; import org.bundlemaker.core.resource.IModule; import org.eclipse.core.runtime.Assert; import org.eclipse.virgo.util.osgi.manifest.ExportedPackage; import org.eclipse.virgo.util.osgi.manifest.ImportedPackage; import org.eclipse.virgo.util.osgi.manifest.RequiredBundle; import org.eclipse.virgo.util.osgi.manifest.RequiredBundle.Visibility; import org.eclipse.virgo.util.osgi.manifest.Resolution; import org.eclipse.virgo.util.osgi.manifest.VersionRange; import org.eclipse.virgo.util.osgi.manifest.parse.HeaderDeclaration; import org.osgi.framework.Constants; import org.osgi.framework.Version; /** * <p> * </p> * * @author Gerd Wütherich (gerd@gerd-wuetherich.de) */ public class DefaultManifestCreator extends AbstractManifestCreator { /** - */ private DuplicatePackagesVisitor _duplicatePackagesVisitor; // TODO boolean _specifyBundleNameIfMultipleExporterExist = true; boolean _useOptionalOnMissingImports = true; /** * {@inheritDoc} */ @Override protected void onCreateManifest() { setBundleManifestVersion(); setBundleSymbolicName(); setBundleName(); setBundleVersion(); setFragmentHost(); setEclipseExtensibleAPI(); setExportPackage(); setImportPackageAndRequireBundle(); // disabled due to BM-208 (ManifestCreator: Handling of importing exported packages is not mature) // importExportedPackages(); setSpiProviderHeader(); // setTransitiveClosure(); copyOriginalManifestHeader(); copyTemplateManifestHeader(); } /** * <p> * Determines if the specified package should be imported using Import-Package. * </p> * * @param packageName * The package name that is required by the bundle * @param exportingModules * the modules that the required package provide * @return true if the package should be imported or false if a Require-Bundle header should be added to the Manifest */ protected boolean useImportPackage(String packageName, List<IModuleArtifact> exportingModules) { switch (getManifestPreferences().getDependencyStyle()) { case STRICT_IMPORT_PACKAGE: // always use Import-Package return true; case STRICT_REQUIRE_BUNDLE: // force Require-Bundle return false; case PREFER_IMPORT_PACKAGE: // use import-package if at most one exporter exists, otherwise use // Require-Bundle return exportingModules.size() <= 1; default: throw new IllegalArgumentException("Unknown DependencyStyle enum value '" + getManifestPreferences().getDependencyStyle() + "'"); } } /** * <p> * Determine if the reference to the given package should be skipped, i.e. if it should <b>not</b> be added to * manifest via Import-Package or Require-Bundle at all. * </p> * * <p> * This implementation does <b>not</b> skip any references but keeps them all. Override this method to provide * different behavior, for example to skip references to packages from the boot classloader (e.g. sun.*) * * @param packageName * The package name that is required by the bundle * @param value * modules that the required package provide * @return true to skip the reference (don't add it to the manifest) or true to include it via Import-Package or * Require-Bundle) */ protected boolean skipReferencedPackage(String packageName, List<IModuleArtifact> exportingModules) { return false; } /** * <p> * Sets the value of the <code>Bundle-ManifestVersion</code> header to '2'. * </p> */ protected void setBundleManifestVersion() { getBundleManifest().setBundleManifestVersion(2); } /** * <p> * Sets the bundle's symbolic name to the name of the associated {@link IResourceModule}. * </p> */ protected void setBundleSymbolicName() { getBundleManifest().getBundleSymbolicName().setSymbolicName(getResourceModule().getModuleIdentifier().getName()); } /** * <p> * Sets the bundle's name to the name of the associated {@link IResourceModule}. * </p> */ protected void setBundleName() { getBundleManifest().setBundleName(getResourceModule().getModuleIdentifier().getName()); } /** * <p> * Sets the bundle's version to the version of the associated {@link IResourceModule}, or '0.0.0' if the module's * version is not a valid OSGi version. * </p> */ protected void setBundleVersion() { if (ManifestUtils.isValidOSGiVersion(getResourceModule().getModuleIdentifier().getVersion())) { getBundleManifest().setBundleVersion(new Version(getResourceModule().getModuleIdentifier().getVersion())); } else { getBundleManifest().setBundleVersion(new Version("0.0.0")); } } /** * <p> * Sets the fragment host for the bundle if the associated module is tagged as a fragment module. * </p> */ protected void setFragmentHost() { if (ManifestUtils.isFragment(getResourceModule())) { IModule hostModule = ManifestUtils.getFragmentHost(getResourceModule()); if (hostModule != null) { getBundleManifest().getFragmentHost().setBundleSymbolicName(hostModule.getModuleIdentifier().getName()); } else { // TODO } } } /** * <p> * Sets the header 'Eclipse-ExtensibleAPI' to 'true'. * </p> */ protected void setEclipseExtensibleAPI() { if (!ManifestUtils.isFragment(getResourceModule())) { getBundleManifest().setHeader("Eclipse-ExtensibleAPI", "true"); } } /** * <p> * Copies all headers from the original manifest except the headers listed in * {@link IManifestConstants#ORIGINAL_HEADERS_NOT_TO_COPY}. * </p> */ protected void copyOriginalManifestHeader() { for (String key : ManifestUtils.getHeaders(getOriginalManifest())) { if (!containsIgnoreCase(key, IManifestConstants.ORIGINAL_HEADERS_NOT_TO_COPY)) { getBundleManifest().setHeader(key, getOriginalManifest().getHeader(key)); } } } /** * <p> * Copies all headers from the original manifest except the headers listed in * {@link IManifestConstants#TEMPLATE_HEADERS_NOT_TO_COPY}. * </p> */ protected void copyTemplateManifestHeader() { for (String key : ManifestUtils.getHeaders(getManifestTemplate())) { if (!containsIgnoreCase(key, IManifestConstants.TEMPLATE_HEADERS_NOT_TO_COPY)) { getBundleManifest().setHeader(key, getManifestTemplate().getHeader(key)); } } } /** * <p> * </p> */ protected void setExportPackage() { // iterate over all the contained packages for (IPackageArtifact packageArtifact : ArtifactUtils.getAllChildPackages(getModuleArtifact())) { // only export packages that contain types - do NOT export the // default package if (packageArtifact.containsTypes() && packageArtifact.getQualifiedName().trim().length() > 0) { // if (hasMostSpecificPackageTemplate(IManifestConstants.HEADER_EXCLUDED_EXPORTS, packageArtifact.getQualifiedName())) { continue; } // ExportedPackage exportedPackage = getBundleManifest().getExportPackage().addExportedPackage( packageArtifact.getQualifiedName()); // RULE: set export package version if (exportedPackage.getVersion().toString() != getResourceModule().getModuleIdentifier().getVersion() && !getManifestPreferences().noExportPackageVersion() && ManifestUtils.isValidOSGiVersion(getResourceModule().getModuleIdentifier().getVersion())) { // exportedPackage.setVersion(new Version(getResourceModule().getModuleIdentifier().getVersion())); } // rule "set attributes and directives from template manifest" if (hasMostSpecificPackageTemplate(IManifestConstants.HEADER_EXPORT_TEMPLATE, exportedPackage.getPackageName())) { HeaderDeclaration header = getMostSpecificPackageTemplate(IManifestConstants.HEADER_EXPORT_TEMPLATE, exportedPackage.getPackageName()); exportedPackage.getAttributes().putAll(header.getAttributes()); exportedPackage.getDirectives().putAll(header.getDirectives()); } } } } /** * <p> * </p> * */ protected void setImportPackageAndRequireBundle() { // step 1: get the map of all required packages (with the associated // modules) GenericCache<String, List<IModuleArtifact>> requiredPackages = createPackageToExportingModulesCache(); // List<IModule> requiredBundles = new LinkedList<IModule>(); List<Entry<String, List<IModuleArtifact>>> entrySet = new LinkedList<Map.Entry<String, List<IModuleArtifact>>>( requiredPackages.entrySet()); // Sort list to make output better readable Collections.sort(entrySet, new Comparator<Entry<String, List<IModuleArtifact>>>() { @Override public int compare(Entry<String, List<IModuleArtifact>> arg0, Entry<String, List<IModuleArtifact>> arg1) { return arg0.getKey().compareTo(arg1.getKey()); } }); // step 2: iterate over all the packages for (Entry<String, List<IModuleArtifact>> packageEntry : entrySet) { // Check if this reference really should be added to the manifest if (skipReferencedPackage(packageEntry.getKey(), packageEntry.getValue())) { continue; } // step 2a: if the package is contained in exactly one module, we // can use 'Import-Package' if (useImportPackage(packageEntry.getKey(), packageEntry.getValue())) { // importWithImportPackage(packageEntry.getKey(), packageEntry.getValue()); } // step 2b: if we have a split package, we have to use // 'Require-Bundle' instead else { // importWithRequiredBundle(packageEntry.getKey(), packageEntry.getValue(), requiredBundles); } } // for (ImportedPackage importedPackage : getManifestTemplate().getImportPackage().getImportedPackages()) { if (!containsImportedPackage(importedPackage.getPackageName())) { getBundleManifest().getImportPackage().getImportedPackages().add(importedPackage); } } // for (RequiredBundle requiredBundle : getManifestTemplate().getRequireBundle().getRequiredBundles()) { if (!containsBundle(requiredBundle.getBundleSymbolicName())) { getBundleManifest().getRequireBundle().getRequiredBundles().add(requiredBundle); } } } /** * <p> * </p> * * @param moduleArtifacts * @param packageName */ protected void importWithImportPackage(String packageName, List<IModuleArtifact> moduleArtifacts) { Assert.isNotNull(packageName); Assert.isNotNull(moduleArtifacts); // if (moduleArtifacts.size() > 1) { System.out.println(" ********************************** "); System.out.println(); System.out.println(" Split-packages can not be imported with Import-Package! " + packageName + " : " + moduleArtifacts); System.out.println(); System.out.println(" ********************************** "); } // Rule: filter self-hosted packages for (IModuleArtifact moduleArtifact : moduleArtifacts) { if (moduleArtifact.getAssociatedModule() != null && (moduleArtifact.getAssociatedModule().equals(getResourceModule()) || ManifestUtils .isHostForResourceModule(moduleArtifact.getAssociatedModule(), getResourceModule()))) { // TODO: Error? System.out.println(" **** filter self-hosted packages: " + packageName + " : " + moduleArtifacts); return; } } // Rule: filter excluded imports if (hasMostSpecificPackageTemplate(IManifestConstants.HEADER_EXCLUDED_IMPORTS, packageName)) { return; } // Rule: import the required package ImportedPackage importedPackage = getBundleManifest().getImportPackage().addImportedPackage(packageName); // get the first moduleArtifact IModuleArtifact moduleArtifact = moduleArtifacts.get(0); // Rule: add Bundle-SymbolicName to imported package if multiple // exporters exist if (moduleArtifact.getAssociatedModule() != null) { Collection<IPackageArtifact> duplicatePackageProvider = getDuplicatePackageProvider(packageName); if (duplicatePackageProvider != null) { int hostModules = 0; for (IPackageArtifact iPackageArtifact : duplicatePackageProvider) { IBundleMakerArtifact exportingModuleArtifact = iPackageArtifact.getParent(IModuleArtifact.class); IModule associatedModule = ((IModuleArtifact) exportingModuleArtifact).getAssociatedModule(); if (associatedModule == null || !associatedModule.getUserAttributes().containsKey(IManifestConstants.OSGI_FRAGMENT_HOST)) { hostModules++; } } if (hostModules > 1 && _specifyBundleNameIfMultipleExporterExist) { // get the module IModule module = moduleArtifact.getAssociatedModule(); // get the module name String moduleName = getModularizedSystem().getExecutionEnvironment().equals(module) ? Constants.SYSTEM_BUNDLE_SYMBOLICNAME : module.getModuleIdentifier().getName(); // set the module name importedPackage.setBundleSymbolicName(moduleName); } } } // Rule: set missing types 'optional' // TODO "<< Missing Types >>" if ((moduleArtifact.getName().equals("<< Missing Types >>") && isUseOptionalOnMissingImports()) || forceOptionalImportPackage(packageName, moduleArtifacts)) { importedPackage.setResolution(Resolution.OPTIONAL); } // Rule: add templates attributes and parameter HeaderDeclaration headerDeclaration = getMostSpecificPackageTemplate(IManifestConstants.HEADER_IMPORT_TEMPLATE, packageName); if (headerDeclaration != null) { importedPackage.getAttributes().putAll(headerDeclaration.getAttributes()); importedPackage.getDirectives().putAll(headerDeclaration.getDirectives()); } } /** * <p> * </p> * * @param packageName * @param moduleArtifacts * @return */ protected boolean forceOptionalImportPackage(String packageName, List<IModuleArtifact> moduleArtifacts) { return false; } protected Collection<IPackageArtifact> getDuplicatePackageProvider(String packageName) { if (_duplicatePackagesVisitor == null) { _duplicatePackagesVisitor = new DuplicatePackagesVisitor(); getRootArtifact().accept(_duplicatePackagesVisitor); } return _duplicatePackagesVisitor.getDuplicatePackageProvider(packageName); } /** * <p> * </p> * * @param packageName * @param modules * @param requiredBundles */ protected void importWithRequiredBundle(String packageName, List<IModuleArtifact> modules, List<IModule> requiredBundles) { for (IModuleArtifact exportingModuleArtifact : modules) { // rule: filter 'self-requires' if (exportingModuleArtifact.isVirtual()) { continue; } // get the module IModule exportingModule = exportingModuleArtifact.getAssociatedModule(); // rule: filter 'self-requires' if (exportingModule.equals(getModuleArtifact().getAssociatedModule())) { continue; } // get the host module IModule exportingHostModule = ManifestUtils.getFragmentHost(exportingModule); // filter 'self-requires' if (exportingHostModule.equals(getModuleArtifact().getAssociatedModule())) { continue; } // if (requiredBundles.contains(exportingHostModule)) { continue; } if (isExcludedRequiredBundle(exportingHostModule)) { continue; } if (ManifestUtils.isHostForResourceModule(exportingModuleArtifact.getAssociatedModule(), getResourceModule())) { continue; } // create a required bundle String symbolicName = getModularizedSystem().getExecutionEnvironment().equals(exportingHostModule) ? Constants.SYSTEM_BUNDLE_SYMBOLICNAME : exportingHostModule.getModuleIdentifier().getName(); // RequiredBundle requiredBundle = getBundleManifest().getRequireBundle().addRequiredBundle(symbolicName); // set a version range if multiple versions exist if (getModularizedSystem().getModules(exportingHostModule.getModuleIdentifier().getName()).size() > 1) { requiredBundle.setBundleVersion(new VersionRange("[" + exportingHostModule.getModuleIdentifier().getVersion() + "," + exportingHostModule.getModuleIdentifier().getVersion() + "]")); } // set the visibility to reexport if (getManifestPreferences().isReexportRequiredBundles()) { requiredBundle.setVisibility(Visibility.REEXPORT); } // Rule: add templates attributes and parameter HeaderDeclaration headerDeclaration = getMostSpecificPackageTemplate( IManifestConstants.HEADER_REQUIRE_BUNDLE_TEMPLATE, exportingHostModule.getModuleIdentifier().getName()); if (headerDeclaration != null) { requiredBundle.getAttributes().putAll(headerDeclaration.getAttributes()); requiredBundle.getDirectives().putAll(headerDeclaration.getDirectives()); } // add to memory requiredBundles.add(exportingHostModule); } } // /** // * <p> // * Add a list of all transitive required bundles. // * </p> // */ // protected void setTransitiveClosure() { // // List<String> transitiveClosure = new LinkedList<String>(); // // // for (IModule module : getModularizedSystem().getTransitiveReferencedModules(getResourceModule()) // .getReferencedModules()) { // // // // if (!containsBundle(module.getModuleIdentifier().getName()) // && !module.equals(getModularizedSystem().getExecutionEnvironment())) { // // // // transitiveClosure.add(module.getModuleIdentifier().getName()); // } // } // // // // if (!transitiveClosure.isEmpty()) { // // Collections.sort(transitiveClosure); // // // // StringBuilder stringBuilder = new StringBuilder(); // for (Iterator<String> iterator = transitiveClosure.iterator(); iterator.hasNext();) { // stringBuilder.append(iterator.next()); // if (iterator.hasNext()) { // stringBuilder.append(", "); // } // } // getBundleManifest().setHeader(IManifestConstants.TRANSITIVE_CLOSURE, stringBuilder.toString()); // } // } /** * <p> * </p> * * @return */ protected final GenericCache<String, List<IModuleArtifact>> createPackageToExportingModulesCache() { // required packages GenericCache<String, List<IModuleArtifact>> requiredPackages = new GenericCache<String, List<IModuleArtifact>>() { /** - */ private static final long serialVersionUID = 1L; @Override protected List<IModuleArtifact> create(String key) { return new LinkedList<IModuleArtifact>(); } }; // for (IDependency dependency : getModuleArtifact().getDependenciesTo()) { // TODO IBundleMakerArtifact bundleMakerArtifact = (IBundleMakerArtifact) dependency.getTo(); // TODO IPackageArtifact packageArtifact = (IPackageArtifact) bundleMakerArtifact.getParent(IPackageArtifact.class); IModuleArtifact moduleArtifact = (IModuleArtifact) packageArtifact.getParent(IModuleArtifact.class); // List<IModuleArtifact> modules = requiredPackages.getOrCreate(packageArtifact.getQualifiedName()); if (!modules.contains(moduleArtifact)) { modules.add(moduleArtifact); } } return requiredPackages; } /** * <p> * </p> * * @param templateManifest * @param headerKey * @param packageName * @return */ public boolean hasMostSpecificPackageTemplate(String headerKey, String packageName) { // return getMostSpecificPackageTemplate(headerKey, packageName) != null; } /** * <p> * </p> */ protected void setSpiProviderHeader() { // TODO if (AnalysisModelQueries.findChild(getModuleArtifact(), "META-INF.services", IPackageArtifact.class) != null) { getBundleManifest().setHeader("SPI-Provider", "*"); } } /** * <p> * </p> * * @param templateManifest * @param headerKey * @param packageName * @return */ protected HeaderDeclaration getMostSpecificPackageTemplate(String headerKey, String packageName) { // get the import package template String templateHeader = getManifestTemplate().getHeader(headerKey); // List<HeaderDeclaration> packageTemplates = ManifestUtils.parseManifestValue(templateHeader); // get the template return ManifestUtils.findMostSpecificDeclaration(packageTemplates, packageName); } /** * <p> * </p> * * @param templateManifest * @param bundleSymbolicName * @return */ protected boolean isExcludedRequiredBundle(IModule module) { // Assert.isNotNull(module); // if (getManifestTemplate() == null) { return false; } // get the import package template String templateHeader = getManifestTemplate().getHeader( IManifestConstants.HEADER_EXCLUDED_REQUIRED_BUNDLES_TEMPLATE); if (templateHeader == null) { return false; } // List<HeaderDeclaration> excludedRequiredBundleTemplates = ManifestUtils.parseManifestValue(templateHeader); // get the template HeaderDeclaration declaration = ManifestUtils.findMostSpecificDeclaration(excludedRequiredBundleTemplates, module .getModuleIdentifier().getName()); // return the result return declaration != null; } /** * <p> * </p> */ protected void importExportedPackages() { // for (ExportedPackage exportedPackage : getBundleManifest().getExportPackage().getExportedPackages()) { // if (!containsImportedPackage(exportedPackage.getPackageName())) { // TODO: Attributes? getBundleManifest().getImportPackage().addImportedPackage(exportedPackage.getPackageName()); } } } /** * <p> * </p> * * @param symbolicName * @param requiredBundles * @return */ private boolean containsBundle(String requiredBundleName) { // for (RequiredBundle alreadyRequiredBundle : getBundleManifest().getRequireBundle().getRequiredBundles()) { // if (alreadyRequiredBundle.getBundleSymbolicName().equalsIgnoreCase(requiredBundleName)) { return true; } } // return false; } /** * <p> * </p> * * @param importedPackage * @param importedPackages * @return */ private boolean containsImportedPackage(String importedPackageName) { // for (ImportedPackage alreadyImportedPackage : getBundleManifest().getImportPackage().getImportedPackages()) { // if (alreadyImportedPackage.getPackageName().equalsIgnoreCase(importedPackageName)) { return true; } } // return false; } /** * <p> * </p> * * @param value * @param values * @return */ private boolean containsIgnoreCase(String value, Collection<String> values) { // if (value == null) { return false; } // for (String v : values) { // if (value.equalsIgnoreCase(v)) { return true; } } // return false; } /** * @param useOptionalOnMissingImports the useOptionalOnMissingImports to set */ public void setUseOptionalOnMissingImports(boolean useOptionalOnMissingImports) { _useOptionalOnMissingImports = useOptionalOnMissingImports; } /** * @return the useOptionalOnMissingImports */ public boolean isUseOptionalOnMissingImports() { return _useOptionalOnMissingImports; } }