/******************************************************************************* * Copyright (c) 2009, 2010 SpringSource, a divison of 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: * SpringSource, a division of VMware, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.kernel.osgi.provisioning.tools; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.virgo.ide.manifest.core.BundleManifestCorePlugin; import org.eclipse.virgo.ide.manifest.core.dependencies.IDependencyLocator; import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge; import org.eclipse.virgo.kernel.artifact.library.LibraryBridge; import org.eclipse.virgo.kernel.repository.BundleDefinition; import org.eclipse.virgo.kernel.repository.BundleRepository; import org.eclipse.virgo.kernel.repository.LibraryDefinition; import org.eclipse.virgo.kernel.repository.RepositoryBackedBundleRepository; import org.eclipse.virgo.repository.ArtifactBridge; import org.eclipse.virgo.repository.ArtifactDescriptor; import org.eclipse.virgo.repository.HashGenerator; import org.eclipse.virgo.repository.Repository; import org.eclipse.virgo.repository.RepositoryCreationException; import org.eclipse.virgo.repository.RepositoryFactory; import org.eclipse.virgo.repository.builder.ArtifactDescriptorBuilder; import org.eclipse.virgo.repository.configuration.ExternalStorageRepositoryConfiguration; import org.eclipse.virgo.repository.configuration.RepositoryConfiguration; import org.eclipse.virgo.util.osgi.VersionRange; import org.eclipse.virgo.util.osgi.manifest.BundleManifest; import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory; 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.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.parse.DummyParserLogger; import org.eclipse.virgo.util.osgi.manifest.parse.HeaderDeclaration; import org.eclipse.virgo.util.osgi.manifest.parse.HeaderParserFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import com.springsource.json.parser.AntlrJSONParser; import com.springsource.json.parser.JSONParser; import com.springsource.json.parser.ListNode; import com.springsource.json.parser.Node; import com.springsource.json.parser.ScalarNode; import com.springsource.json.parser.ScalarNodeType; /** * A helper class for locating a bundle's dependencies. * <p /> * * <strong>Concurrent Semantics</strong><br /> * The class is <strong>thread-safe</strong> * * @author Andy Wilkinson * @author Christian Dupuis * @author Steffen Pingel * @since 1.0 */ public final class DependencyLocator10 implements IDependencyLocator { private static final String SYSYEM_PACKAGES_PROPERTY = "org.osgi.framework.system.packages"; private static final String BUNDLE_VERSION_ATTRIBUTE = "bundle-version"; private static final String VERSION_ATTRIBUTE = "version"; private static final String VERSION_ALTERNATE_ATTRIBUTE = "specification-version"; private final BundleRepository bundleRepository; private static final List<String> SEARCH_PATHS_PATH = Arrays.asList(new String[] { "provisioning", "searchPaths" }); private static final String[] DEFAULT_SEARCH_PATHS = new String[] { "repository/bundles/subsystems/{name}/{bundle}.jar", "repository/bundles/ext/{bundle}", "repository/libraries/ext/{library}", "repository/bundles/usr/{bundle}", "repository/libraries/usr/{library}" }; private static final String[] LIB_SEARCH_PATH = new String[] { "lib/{bundle}.jar" }; private static final String PLATFORM_CONFIG_PATH = File.separatorChar + "config" + File.separatorChar + "server.config"; private static final Pattern PROPERTY_PATTERN = Pattern.compile("(\\$\\{(([^\\}]+))\\})"); /** * Creates a new <code>DependencyLocator</code> that will search for dependencies within the Server instance located * at the supplied <code>serverHomePath</code>. To improve search performance, bundle and library locations are * cached. The cache files will be written to the directory identified by the supplied * <code>cacheDirectoryPath</code>. * * @param serverHomePath The path to the server installation from within which dependencies are to be located * @param cacheDirectoryPath The path of the directory to which cache files should be written * @param javaVersion The version of Java to be used to determine the appropriate OSGi profile to use for defining * system bundle exports * * @throws IOException if a problem occurs loading and parsing the configuration of the Server instance. */ public DependencyLocator10(String serverHomePath, String cacheDirectoryPath, JavaVersion javaVersion) throws IOException { this(serverHomePath, null, cacheDirectoryPath, javaVersion); } /** * Creates a new <code>DependencyLocator</code> that will search for dependencies within the Server instance located * at the supplied <code>serverHomePath</code>. The supplied <code>additionalSearchPaths</code> will also be * included in the search. Each search path is used to locate bundles and libraries. To improve search performance, * bundle and library locations are cached. The cache files will be written to the directory identified by the * supplied <code>cacheDirectoryPath</code>. * * @param serverHomePath The path to the server installation from within which dependencies are to be located * @param additionalSearchPaths The additional search paths to use to locate the bundles and libraries that can * satisfy dependencies * @param cacheDirectoryPath The path of the directory to which cache files should be written * @param javaVersion The version of Java to be used to determine the appropriate OSGi profile to use for defining * system bundle exports * * @throws IOException if a problem occurs loading and parsing the configuration of the Server instance. */ public DependencyLocator10(String serverHomePath, String[] additionalSearchPaths, String cacheDirectoryPath, JavaVersion javaVersion) throws IOException { if (Platform.getOS().equals(Platform.OS_WIN32)) { serverHomePath = serverHomePath.replace('/', '\\'); cacheDirectoryPath = cacheDirectoryPath.replace('/', '\\'); for (int i = 0; i < additionalSearchPaths.length; i++) { additionalSearchPaths[i] = additionalSearchPaths[i].replace('/', '\\'); } } this.bundleRepository = new SystemPackageFilteringCompositeBundleRepository(serverHomePath, additionalSearchPaths, cacheDirectoryPath, javaVersion); } private static String[] readSearchPathsFromConfig(String configPath) { if (configPath != null) { File configFile = new File(configPath); if (configFile.exists()) { JSONParser parser = new AntlrJSONParser(); try { Node root = parser.parse(configFile.toURI().toURL()); return readStringsFromListNode(root, SEARCH_PATHS_PATH, DEFAULT_SEARCH_PATHS); } catch (Exception e) { throw new RuntimeException("Config file '" + configPath + "' could not be parsed.", e); } } else { throw new IllegalArgumentException("Config file '" + configPath + "' does not exist."); } } return new String[0]; } private static String[] readStringsFromListNode(Node root, List<String> pathToNode, String[] defaultValues) { if (root != null) { Node searchPathsNode = root.getNode(pathToNode); if (searchPathsNode != null) { if (searchPathsNode instanceof ListNode) { List<String> searchPaths = new ArrayList<String>(); ListNode listNode = (ListNode) searchPathsNode; List<Node> nodes = listNode.getNodes(); for (Node node : nodes) { if (node instanceof ScalarNode && ScalarNodeType.String.equals(((ScalarNode) node).getType())) { String path = ((ScalarNode) node).getValue(); searchPaths.add(path); } } return searchPaths.toArray(new String[searchPaths.size()]); } else { throw new RuntimeException("The node at " + pathToNode + " must be a list node."); } } else { return defaultValues; } } else { return defaultValues; } } /** * Locates all of the dependencies defined in the supplied manifest. Dependencies are identified from the manifest's * <code>Import-Package</code>, <code>Import-Bundle</code> and <code>Import-Library</code> headers. The dependencies * are returned in the form of a <code>Map</code>. Each key in the <code>Map</code> is a <code>File</code> which * points to the location of a bundle that satisfies one or more of the manifest's dependencies. Each * <code>List<String></code> contains all of the packages within a particular bundle which are visible, i.e. * they are imported by the supplied manifest. If a dependency is found to be satisfied by the system bundle a * <code>null</code> key may be included in the returned <code>Map</code> if the location of the satisfying jar * could not be determined, e.g. because the dependency is upon a standard JRE package. * <p> * Processing of the <code>Import-Package</code> header will, for each package, search for a bundle that exports the * package at the specified version and include its location in the returned <code>Map</code> along with an entry in * the associated <code>List</code> for the package. If no such bundle is found and the import does not have a * resolution of optional it will be added to a list of those which cannot be satisfied and included in a * <code>DependencyLocationException</code> thrown once processing is complete. If more than one such bundle is * found they will all be added to the Map. * <p> * Processing of the <code>Import-Library</code> header will, for each library, search for a library with the * required name and version. The location of each bundle that is in the library will be included in the returned * <code>Map</code> along with entries in the associated <code>Lists</code> for every package that is exported from * the library's bundles. If no library with the required name and version can be found, and the import does not * have a resolution of optional, the import of the library will be added to a list of those which cannot be * satisfied and included in a <code>DependencyLocationException</code> thrown once processing is complete. * <p> * If a single bundle satisfies more than one import its location will only be included once in the returned * <code>Map</code>. * <p> * If the manifest has no dependencies an empty <code>Map</code> is returned. * * @param manifest * @return the locations of all of the given manifest's dependencies * @throws DependencyLocationException if any of the manifest's dependencies cannot be located */ public Map<File, List<String>> locateDependencies(BundleManifest manifest) throws DependencyLocationException { List<ImportDescriptor> unsatisfiablePackageImports = new ArrayList<ImportDescriptor>(); List<ImportDescriptor> unsatisfiableLibraryImports = new ArrayList<ImportDescriptor>(); List<ImportDescriptor> unsatisfiableBundleImports = new ArrayList<ImportDescriptor>(); List<ImportDescriptor> unsatisfiableRequireBundles = new ArrayList<ImportDescriptor>(); Map<File, List<String>> dependencyLocations = new HashMap<File, List<String>>(); processImportedPackages(manifest.getImportPackage().getImportedPackages(), dependencyLocations, unsatisfiablePackageImports); processImportedLibraries(manifest.getImportLibrary().getImportedLibraries(), dependencyLocations, unsatisfiableLibraryImports); processImportedBundles(manifest.getImportBundle().getImportedBundles(), dependencyLocations, unsatisfiableBundleImports); List<String> packageNames = createListOfAllPackagesThatHaveAlreadyBeenSatisfied(dependencyLocations); processRequiredBundles(manifest.getRequireBundle().getRequiredBundles(), dependencyLocations, unsatisfiableRequireBundles, packageNames, true); throwDependencyLocationExceptionIfNecessary(unsatisfiablePackageImports, unsatisfiableBundleImports, unsatisfiableLibraryImports, unsatisfiableRequireBundles, dependencyLocations); return dependencyLocations; } private static List<String> createListOfAllPackagesThatHaveAlreadyBeenSatisfied( Map<File, List<String>> dependencyLocations) { List<String> packagesThatHaveBeenSatisfied = new ArrayList<String>(); for (Entry<File, List<String>> dependencyLocation : dependencyLocations.entrySet()) { packagesThatHaveBeenSatisfied.addAll(dependencyLocation.getValue()); } return packagesThatHaveBeenSatisfied; } private void processImportedBundles(List<ImportedBundle> importedBundles, Map<File, List<String>> dependencyLocations, List<ImportDescriptor> unsatisfiableBundleImports) { for (ImportedBundle importedBundle : importedBundles) { processImportedBundle(importedBundle, dependencyLocations, unsatisfiableBundleImports); } } private void processImportedBundle(ImportedBundle importedBundle, Map<File, List<String>> dependencyLocations, List<ImportDescriptor> unsatisfiableBundleImports) { String symbolicName = importedBundle.getBundleSymbolicName(); VersionRange bundleVersionRange = importedBundle.getVersion(); BundleDefinition bundleDefinition = this.bundleRepository.findBySymbolicName(symbolicName, bundleVersionRange); if (bundleDefinition == null) { unsatisfiableBundleImports.add(new ImportDescriptor(symbolicName, bundleVersionRange.toString(), bundleVersionRange.toParseString())); } else { registerDependencyLocationAndPackageNameForEveryExportedPackage(dependencyLocations, bundleDefinition, null); } } private void processImportedLibraries(List<ImportedLibrary> importedLibraries, Map<File, List<String>> dependencyLocations, List<ImportDescriptor> unsatisfiableLibraryImports) { for (ImportedLibrary importedLibrary : importedLibraries) { String libraryName = importedLibrary.getLibrarySymbolicName(); VersionRange versionRange = importedLibrary.getVersion(); LibraryDefinition libraryDefinition = this.bundleRepository.findLibrary(libraryName, versionRange); if (libraryDefinition != null) { List<ImportedBundle> importedBundles = libraryDefinition.getLibraryBundles(); for (ImportedBundle libraryBundle : importedBundles) { String symbolicName = libraryBundle.getBundleSymbolicName(); VersionRange bundleVersionRange = libraryBundle.getVersion(); BundleDefinition bundleDefinition = this.bundleRepository.findBySymbolicName(symbolicName, bundleVersionRange); if (bundleDefinition == null) { unsatisfiableLibraryImports.add(new ImportDescriptor(libraryName, versionRange.toString(), versionRange.toParseString())); } else { registerDependencyLocationAndPackageNameForEveryExportedPackage(dependencyLocations, bundleDefinition, null); } } } else if (Resolution.MANDATORY.equals(importedLibrary.getResolution())) { unsatisfiableLibraryImports.add(new ImportDescriptor(libraryName, versionRange.toString(), versionRange .toParseString())); } } } private void registerDependencyLocationAndPackageNameForEveryExportedPackage( Map<File, List<String>> dependencyLocations, BundleDefinition bundleDefinition, List<String> packagesThatHaveAlreadyBeenSatisfied) { BundleManifest manifest = bundleDefinition.getManifest(); for (ExportedPackage exportedPackage : manifest.getExportPackage().getExportedPackages()) { if (packagesThatHaveAlreadyBeenSatisfied == null || !packagesThatHaveAlreadyBeenSatisfied.contains(exportedPackage.getPackageName())) { registerPackageNameAgainstDependencyLocation(bundleDefinition.getLocation(), exportedPackage .getPackageName(), dependencyLocations); } } } private void processImportedPackages(List<ImportedPackage> importedPackages, Map<File, List<String>> dependencyLocations, List<ImportDescriptor> unsatisfiablePackageImports) { for (ImportedPackage importedPackage : importedPackages) { VersionRange versionRange = importedPackage.getVersion(); String packageName = importedPackage.getPackageName(); Set<? extends BundleDefinition> bundleDefinitions = this.bundleRepository.findByExportedPackage( packageName, versionRange); if (bundleDefinitions.size() > 0) { for (BundleDefinition bundleDefinition : bundleDefinitions) { registerPackageNameAgainstDependencyLocation(bundleDefinition.getLocation(), packageName, dependencyLocations); } } else if (Resolution.MANDATORY.equals(importedPackage.getResolution())) { unsatisfiablePackageImports.add(new ImportDescriptor(packageName, versionRange.toString(), versionRange .toParseString())); } } } /** * Get the version attribute from a given attribute map. * * @param map the attribute map * @return the value of the version attribute */ private static String getVersionAttribute(Map<String, String> map) { String version = map.get(VERSION_ATTRIBUTE); if (version == null) { version = map.get(VERSION_ALTERNATE_ATTRIBUTE); } return version; } private void processRequiredBundles(List<RequiredBundle> requiredBundles, Map<File, List<String>> dependencyLocations, List<ImportDescriptor> unsatisfiableRequireBundles, List<String> packagesThatHaveAlreadyBeenSatisfied, boolean root) { for (RequiredBundle requiredBundle : requiredBundles) { if (root || Visibility.REEXPORT.equals(requiredBundle.getVisibility())) { String bundleSymbolicName = requiredBundle.getBundleSymbolicName(); String bundleVersion = requiredBundle.getAttributes().get(BUNDLE_VERSION_ATTRIBUTE); VersionRange versionRange; if (bundleVersion != null) { versionRange = new VersionRange(bundleVersion); } else { versionRange = VersionRange.NATURAL_NUMBER_RANGE; } BundleDefinition bundleDefinition = this.bundleRepository.findBySymbolicName(bundleSymbolicName, versionRange); if (bundleDefinition != null) { registerDependencyLocationAndPackageNameForEveryExportedPackage(dependencyLocations, bundleDefinition, packagesThatHaveAlreadyBeenSatisfied); List<RequiredBundle> dependencysRequiredBundles = bundleDefinition.getManifest().getRequireBundle() .getRequiredBundles(); processRequiredBundles(dependencysRequiredBundles, dependencyLocations, unsatisfiableRequireBundles, packagesThatHaveAlreadyBeenSatisfied, false); } else if (Resolution.MANDATORY.equals(requiredBundle.getResolution())) { unsatisfiableRequireBundles.add(new ImportDescriptor(requiredBundle.getBundleSymbolicName(), versionRange.toString(), versionRange.toParseString())); } } } } private void registerPackageNameAgainstDependencyLocation(URI location, String packageName, Map<File, List<String>> dependencyLocations) { File fileLocation; if (location != null) { fileLocation = new File(location); } else { fileLocation = null; } List<String> existingPackageNames = dependencyLocations.get(fileLocation); if (existingPackageNames == null) { existingPackageNames = new ArrayList<String>(); dependencyLocations.put(fileLocation, existingPackageNames); } existingPackageNames.add(packageName); } private void throwDependencyLocationExceptionIfNecessary(List<ImportDescriptor> unsatisfiablePackageImports, List<ImportDescriptor> unsatisfiableBundleImports, List<ImportDescriptor> unsatisfiableLibraryImports, List<ImportDescriptor> unsatisfiableRequireBundles, Map<File, List<String>> satisfiedDependencies) { if (!unsatisfiableLibraryImports.isEmpty() || !unsatisfiablePackageImports.isEmpty() || !unsatisfiableRequireBundles.isEmpty() || !unsatisfiableBundleImports.isEmpty()) { throw new DependencyLocationException(toArray(unsatisfiablePackageImports), toArray(unsatisfiableBundleImports), toArray(unsatisfiableLibraryImports), toArray(unsatisfiableRequireBundles), satisfiedDependencies); } } private static ImportDescriptor[] toArray(List<ImportDescriptor> importDescriptors) { return importDescriptors.toArray(new ImportDescriptor[importDescriptors.size()]); } private static class SystemPackageFilteringCompositeBundleRepository implements BundleRepository { private final Map<String, Version> systemPackages; private final BundleRepository mainRepository; private final BundleRepository systemPackageRepository; private final BundleManifest systemBundleManifest; private final Set<BundleDefinition> jreProvidedDependenciesDefinitions; private final BundleDefinition systemBundleDefinition; private static final String SYSTEM_BUNDLE_SYMBOLIC_NAME = "org.eclipse.osgi"; SystemPackageFilteringCompositeBundleRepository(String serverHomePath, String[] additionalSearchPaths, String cacheDirectoryPath, JavaVersion javaVersion) throws IOException { String serverConfigPath = null; String serverProfilePath = null; if (serverHomePath != null) { serverConfigPath = serverHomePath + PLATFORM_CONFIG_PATH; if (javaVersion == JavaVersion.Java6) { serverProfilePath = serverHomePath + File.separator + "lib" + File.separator + "java6-server.profile"; } else { serverProfilePath = serverHomePath + File.separator + "lib" + File.separator + "java5-server.profile"; } File serverProfile = new File(serverProfilePath); if (!serverProfile.exists()) { serverProfilePath = serverHomePath + File.separator + "lib" + File.separator + "server.profile"; } } String[] searchPathsFromConfig = readSearchPathsFromConfig(serverConfigPath); String[] searchPaths; if (additionalSearchPaths != null) { searchPaths = new String[searchPathsFromConfig.length + additionalSearchPaths.length]; System.arraycopy(searchPathsFromConfig, 0, searchPaths, 0, searchPathsFromConfig.length); System.arraycopy(additionalSearchPaths, 0, searchPaths, searchPathsFromConfig.length, additionalSearchPaths.length); } else { searchPaths = searchPathsFromConfig; } File mainCache = new File(cacheDirectoryPath, "main-cache"); File sysCache = new File(cacheDirectoryPath, "sys-cache"); try { this.mainRepository = createBundleRepository("dep-loc-main", Arrays.asList(searchPaths), mainCache, serverHomePath); } catch (RepositoryCreationException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, BundleManifestCorePlugin.PLUGIN_ID, "Error creating repository index path [" + mainCache + "], server [" + serverHomePath + "]", e)); //TODO Why not just rethrow the RCE? throw new IOException("A failure occurred during repository creation"); } try { this.systemPackageRepository = createBundleRepository("dep-loc-sys", Arrays.asList(LIB_SEARCH_PATH), sysCache, serverHomePath); } catch (RepositoryCreationException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, BundleManifestCorePlugin.PLUGIN_ID, "Error creating repository index path [" + sysCache + "], server [" + serverHomePath + "]", e)); //TODO Why not just rethrow the RCE? throw new IOException("A failure occurred during repository creation"); } systemPackages = parseProfile(serverProfilePath); systemPackages.putAll(findExportsFromOsgiImplementationBundle(this.systemPackageRepository)); systemBundleManifest = createManifestExportingPackages(systemPackages); jreProvidedDependenciesDefinitions = new HashSet<BundleDefinition>(); systemBundleDefinition = new SystemBundleDefinition(systemBundleManifest, null); jreProvidedDependenciesDefinitions.add(systemBundleDefinition); } private Map<String, Version> findExportsFromOsgiImplementationBundle(BundleRepository bundleRepository) { Map<String, Version> exports = new HashMap<String, Version>(); BundleDefinition definition = bundleRepository.findBySymbolicName(SYSTEM_BUNDLE_SYMBOLIC_NAME, VersionRange.NATURAL_NUMBER_RANGE); if (definition != null) { BundleManifest manifest = definition.getManifest(); for (ExportedPackage exportedPackage : manifest.getExportPackage().getExportedPackages()) { Version version = exportedPackage.getVersion(); exports.put(exportedPackage.getPackageName(), version); } } return exports; } /** * {@inheritDoc} */ public Set<? extends BundleDefinition> findByExportedPackage(String packageName, VersionRange versionRange) { Version version; if ((version = systemPackages.get(packageName)) != null) { if (versionRange.includes(version)) { Set<? extends BundleDefinition> definitionsFromLib = this.systemPackageRepository .findByExportedPackage(packageName, versionRange); if (definitionsFromLib.isEmpty()) { return jreProvidedDependenciesDefinitions; } else { Set<BundleDefinition> systemBundleDefinitions = new HashSet<BundleDefinition>(); for (BundleDefinition definition : definitionsFromLib) { systemBundleDefinitions.add(new SystemBundleDefinition(systemBundleManifest, definition .getLocation())); } return systemBundleDefinitions; } } } return this.mainRepository.findByExportedPackage(packageName, versionRange); } /** * {@inheritDoc} */ public Set<? extends BundleDefinition> findByFragmentHost(String bundleSymbolicName, Version version) { return this.mainRepository.findByFragmentHost(bundleSymbolicName, version); } /** * {@inheritDoc} */ public BundleDefinition findBySymbolicName(String symbolicName, VersionRange versionRange) { return this.mainRepository.findBySymbolicName(symbolicName, versionRange); } /** * {@inheritDoc} */ public LibraryDefinition findLibrary(String libraryName, VersionRange versionRange) { return this.mainRepository.findLibrary(libraryName, versionRange); } /** * {@inheritDoc} */ public Set<? extends BundleDefinition> getBundles() { Set<BundleDefinition> bundles = new HashSet<BundleDefinition>(); bundles.addAll(this.mainRepository.getBundles()); // expose jre packages for content assist bundles.addAll(this.jreProvidedDependenciesDefinitions); return bundles; } /** * {@inheritDoc} */ public Set<? extends LibraryDefinition> getLibraries() { return this.mainRepository.getLibraries(); } /** * {@inheritDoc} */ public void refresh() { this.mainRepository.refresh(); } public void shutdown() { if (this.mainRepository instanceof RepositoryBackedBundleRepository) { ((RepositoryBackedBundleRepository) this.mainRepository).shutdown(); } if (this.systemPackageRepository instanceof RepositoryBackedBundleRepository) { ((RepositoryBackedBundleRepository) this.systemPackageRepository).shutdown(); } } private static Map<String, Version> parseProfile(String serverProfilePath) throws IOException { Map<String, Version> systemPackages = new HashMap<String, Version>(); if (serverProfilePath != null) { Properties properties = new Properties(); FileInputStream propertiesStream = new FileInputStream(new File(serverProfilePath)); try { properties.load(propertiesStream); } finally { if (propertiesStream != null) { propertiesStream.close(); } } String systemPackagesString = properties.getProperty(SYSYEM_PACKAGES_PROPERTY); List<HeaderDeclaration> exportHeaders = HeaderParserFactory.newHeaderParser(new DummyParserLogger()) .parsePackageHeader(systemPackagesString, "Export-Package"); for (HeaderDeclaration exportHeader : exportHeaders) { String versionString = getVersionAttribute(exportHeader.getAttributes()); Version version = versionString != null ? new Version(versionString) : Version.emptyVersion; for (String packageName : exportHeader.getNames()) { systemPackages.put(packageName, version); } } } return systemPackages; } private static BundleManifest createManifestExportingPackages(Map<String, Version> packagesMap) { Dictionary<String, String> manifestContents = new Hashtable<String, String>(); manifestContents.put(Constants.BUNDLE_MANIFESTVERSION, Integer.toString(2)); manifestContents.put("Manifest-Version", "1.0"); if (packagesMap.size() > 0) { StringWriter writer = new StringWriter(); Set<Entry<String, Version>> packagesSet = packagesMap.entrySet(); Iterator<Entry<String, Version>> packages = packagesSet.iterator(); while (packages.hasNext()) { Entry<String, Version> pkg = packages.next(); Version version = pkg.getValue(); String packageName = pkg.getKey(); writer.append(packageName + ";version=\"" + version.toString() + "\""); if (packages.hasNext()) { writer.append(","); } } } return BundleManifestFactory.createBundleManifest(manifestContents); } public ArtifactDescriptor findSubsystem(String subsystemName) { return null; } } static class SystemBundleDefinition implements BundleDefinition { private final BundleManifest manifest; private final URI location; SystemBundleDefinition(BundleManifest manifest, URI location) { this.manifest = manifest; this.location = location; } /** * {@inheritDoc} */ public BundleManifest getManifest() { return this.manifest; } /** * {@inheritDoc} */ public URI getLocation() { return this.location; } } private static BundleRepository createBundleRepository(String name, List<String> searchPaths, File baseIndexLocation, String serverHome) throws RepositoryCreationException { Set<ArtifactBridge> artefactBridges = new HashSet<ArtifactBridge>(); artefactBridges.add(new BundleBridge(new HashGenerator() { public void generateHash(ArtifactDescriptorBuilder artifactDescriptorBuilder, File artifactFile) { // do nothing } })); artefactBridges.add(new LibraryBridge(new HashGenerator() { public void generateHash(ArtifactDescriptorBuilder artifactDescriptorBuilder, File artifactFile) { // do nothing } })); List<RepositoryConfiguration> chainedConfiguration = new ArrayList<RepositoryConfiguration>(); int index = 1; for (String searchPath : searchPaths) { String repoName = name + "-" + index++; File indexLocation = new File(baseIndexLocation.getAbsolutePath() + repoName + ".index"); chainedConfiguration.add(new ExternalStorageRepositoryConfiguration(repoName, indexLocation, artefactBridges, makeAbsoluteIfNecessary(convertPath(searchPath), serverHome), null)); } RepositoryFactory repositoryFactory = getRepositoryFactory(); Repository repository = repositoryFactory.createRepository(chainedConfiguration); return new RepositoryBackedBundleRepository(repository); } private static String convertToAntStylePath(String searchPath) { return searchPath.replaceAll("\\{[^\\}]+\\}", "*"); } private static String expandProperties(String value) { Pattern regex = PROPERTY_PATTERN; StringBuffer buffer = new StringBuffer(value.length()); Matcher matcher = regex.matcher(value); int propertyGroup = matcher.groupCount(); String key, property = ""; while (matcher.find()) { key = matcher.group(propertyGroup); property = ""; if (key.contains("::")) { String[] keyDefault = key.split("::"); property = System.getProperty(keyDefault[0]); if (property == null) { property = keyDefault[1]; } else { property = property.replace('\\', '/'); } } else { property = System.getProperty(matcher.group(propertyGroup)).replace('\\', '/'); } matcher.appendReplacement(buffer, property); } matcher.appendTail(buffer); return buffer.toString(); } private static String convertPath(String path) { return convertToAntStylePath(expandProperties(path)); } private static String makeAbsoluteIfNecessary(String antPathPattern, String absoluteRoot) { String absolutePathPattern; if (!antPathPattern.startsWith("/") && !(antPathPattern.indexOf(":") > 0)) { absolutePathPattern = absoluteRoot + File.separator + antPathPattern; } else { absolutePathPattern = antPathPattern; } if (File.separator.equals("/")) { return absolutePathPattern.replace('\\', '/'); } else { return absolutePathPattern.replace('/', '\\'); } } private static RepositoryFactory getRepositoryFactory() { BundleContext bundleContext = Platform.getBundle(BundleManifestCorePlugin.PLUGIN_ID).getBundleContext(); RepositoryFactory repositoryFactory = null; ServiceReference serviceReference = bundleContext.getServiceReference(RepositoryFactory.class.getName()); if (serviceReference != null) { repositoryFactory = (RepositoryFactory) bundleContext.getService(serviceReference); } if (repositoryFactory == null) { throw new IllegalStateException( "RepositoryFactory service was not available. Is the repository bundle installed and started?"); } return repositoryFactory; } public Set<? extends BundleDefinition> getBundles() { return this.bundleRepository.getBundles(); } public Set<? extends LibraryDefinition> getLibraries() { return this.bundleRepository.getLibraries(); } public void shutdown() { if (this.bundleRepository instanceof SystemPackageFilteringCompositeBundleRepository) { ((SystemPackageFilteringCompositeBundleRepository) this.bundleRepository).shutdown(); } } }