/****************************************************************************** | |
* Copyright (c) 2006, 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 | |
* and Apache License v2.0 which accompanies this distribution. | |
* The Eclipse Public License is available at | |
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 | |
* is available at http://www.opensource.org/licenses/apache2.0.php. | |
* You may elect to redistribute this code under either of these licenses. | |
* | |
* Contributors: | |
* VMware Inc. | |
*****************************************************************************/ | |
package org.eclipse.gemini.blueprint.io; | |
import java.io.IOException; | |
import java.net.URL; | |
import java.security.AccessController; | |
import java.security.PrivilegedActionException; | |
import java.security.PrivilegedExceptionAction; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Enumeration; | |
import java.util.LinkedHashSet; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.jar.JarEntry; | |
import java.util.jar.JarInputStream; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.eclipse.gemini.blueprint.io.internal.OsgiHeaderUtils; | |
import org.eclipse.gemini.blueprint.io.internal.OsgiResourceUtils; | |
import org.eclipse.gemini.blueprint.io.internal.OsgiUtils; | |
import org.eclipse.gemini.blueprint.io.internal.resolver.DependencyResolver; | |
import org.eclipse.gemini.blueprint.io.internal.resolver.ImportedBundle; | |
import org.eclipse.gemini.blueprint.io.internal.resolver.PackageAdminResolver; | |
import org.osgi.framework.Bundle; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.Constants; | |
import org.springframework.core.io.ContextResource; | |
import org.springframework.core.io.Resource; | |
import org.springframework.core.io.ResourceLoader; | |
import org.springframework.core.io.UrlResource; | |
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; | |
import org.springframework.core.io.support.ResourcePatternResolver; | |
import org.springframework.util.Assert; | |
import org.springframework.util.CollectionUtils; | |
import org.springframework.util.ObjectUtils; | |
import org.springframework.util.PathMatcher; | |
import org.springframework.util.StringUtils; | |
/** | |
* OSGi-aware {@link ResourcePatternResolver}. | |
* | |
* Can find resources in the <em>bundle jar</em> and <em>bundle space</em>. See {@link OsgiBundleResource} for more | |
* information. | |
* | |
* <p/> <b>ClassPath support</b> | |
* | |
* <p/> As mentioned by {@link PathMatchingResourcePatternResolver}, class-path pattern matching needs to resolve the | |
* class-path structure to a file-system location (be it an actual folder or a jar). Inside the OSGi environment this is | |
* problematic as the bundles can be loaded in memory directly from input streams. To avoid relying on each platform | |
* bundle storage structure, this implementation tries to determine the bundles that assemble the given bundle | |
* class-path and analyze each of them individually. This involves the bundle archive (including special handling of the | |
* <code>Bundle-Classpath</code> as it is computed at runtime), the bundle required packages and its attached fragments. | |
* | |
* Depending on the configuration of running environment, this might cause significant IO activity which can affect | |
* performance. | |
* | |
* <p/> <b>Note:</b> Currently, <em>static</em> imports as well as <code>Bundle-Classpath</code> and | |
* <code>Required-Bundle</code> entries are supported. Support for <code>DynamicPackage-Import</code> depends on | |
* how/when the underlying platform does the wiring between the dynamically imported bundle and the given bundle. | |
* | |
* <p/> <b>Portability Note:</b> Since it relies only on the OSGi API, this implementation depends heavily on how | |
* closely the platform implements the OSGi spec. While significant tests have been made to ensure compatibility, one | |
* <em>might</em> experience different behaviour especially when dealing with jars with missing folder entries or | |
* boot-path delegation. It is strongly recommended that wildcard resolution be thoroughly tested before switching to a | |
* different platform before you rely on it. | |
* | |
* @see Bundle | |
* @see OsgiBundleResource | |
* @see PathMatchingResourcePatternResolver | |
* | |
* @author Costin Leau | |
* | |
* TODO: Rework to use WIRE Admin | |
*/ | |
public class OsgiBundleResourcePatternResolver extends PathMatchingResourcePatternResolver { | |
/** | |
* Our own logger to protect against incompatible class changes. | |
*/ | |
private static final Log logger = LogFactory.getLog(OsgiBundleResourcePatternResolver.class); | |
/** | |
* The bundle on which this resolver works on. | |
*/ | |
private final Bundle bundle; | |
/** | |
* The bundle context associated with this bundle. | |
*/ | |
private final BundleContext bundleContext; | |
private static final String FOLDER_SEPARATOR = "/"; | |
private static final String FOLDER_WILDCARD = "**"; | |
private static final String JAR_EXTENSION = ".jar"; | |
private static final String BUNDLE_DEFAULT_CP = "."; | |
private static final char SLASH = '/'; | |
private static final char DOT = '.'; | |
// use the default package admin version | |
private final DependencyResolver resolver; | |
public OsgiBundleResourcePatternResolver(Bundle bundle) { | |
this(new OsgiBundleResourceLoader(bundle)); | |
} | |
public OsgiBundleResourcePatternResolver(ResourceLoader resourceLoader) { | |
super(resourceLoader); | |
if (resourceLoader instanceof OsgiBundleResourceLoader) { | |
this.bundle = ((OsgiBundleResourceLoader) resourceLoader).getBundle(); | |
} else { | |
this.bundle = null; | |
} | |
this.bundleContext = (bundle != null ? OsgiUtils.getBundleContext(this.bundle) : null); | |
this.resolver = (bundleContext != null ? new PackageAdminResolver(bundleContext) : null); | |
} | |
/** | |
* Finds existing resources. This method returns the actual resources found w/o adding any extra decoration (such as | |
* non-existing resources). | |
* | |
* @param locationPattern location pattern | |
* @return found resources (w/o any decoration) | |
* @throws IOException in case of I/O errors | |
*/ | |
protected Resource[] findResources(String locationPattern) throws IOException { | |
Assert.notNull(locationPattern, "Location pattern must not be null"); | |
int type = OsgiResourceUtils.getSearchType(locationPattern); | |
// look for patterns (includes classpath*:) | |
if (getPathMatcher().isPattern(locationPattern)) { | |
// treat classpath as a special case | |
if (OsgiResourceUtils.isClassPathType(type)) | |
return findClassPathMatchingResources(locationPattern, type); | |
return findPathMatchingResources(locationPattern, type); | |
} | |
// even though we have no pattern | |
// the OSGi space can return multiple entries for the same resource name | |
// - treat this case below | |
else { | |
Resource[] result = null; | |
OsgiBundleResource resource = new OsgiBundleResource(bundle, locationPattern); | |
switch (type) { | |
// same as bundle space | |
case OsgiResourceUtils.PREFIX_TYPE_NOT_SPECIFIED: | |
// consider bundle-space which can return multiple URLs | |
case OsgiResourceUtils.PREFIX_TYPE_BUNDLE_SPACE: | |
result = resource.getAllUrlsFromBundleSpace(locationPattern); | |
break; | |
// for the rest go with the normal resolving | |
default: | |
if (!resource.exists()) | |
result = new Resource[] { resource }; | |
break; | |
} | |
return result; | |
} | |
} | |
// add a non-existing resource, if none was found and no pattern was specified | |
public Resource[] getResources(final String locationPattern) throws IOException { | |
Resource[] resources = findResources(locationPattern); | |
// check whether we found something or we should fall-back to a | |
// non-existing resource | |
if (ObjectUtils.isEmpty(resources) && (!getPathMatcher().isPattern(locationPattern))) { | |
return new Resource[] { getResourceLoader().getResource(locationPattern) }; | |
} | |
// return the original array | |
return resources; | |
} | |
/** | |
* Special classpath method. Will try to detect the imported bundles (which are part of the classpath) and look for | |
* resources in all of them. This implementation will try to determine the bundles that compose the current bundle | |
* classpath and then it will inspect the bundle space of each of them individually. | |
* | |
* <p/> Since the bundle space is considered, runtime classpath entries such as dynamic imports are not supported | |
* (yet). | |
* | |
* @param locationPattern | |
* @param type | |
* @return classpath resources | |
*/ | |
@SuppressWarnings("unchecked") | |
private Resource[] findClassPathMatchingResources(String locationPattern, int type) throws IOException { | |
if (resolver == null) | |
throw new IllegalArgumentException( | |
"PackageAdmin service/a started bundle is required for classpath matching"); | |
final ImportedBundle[] importedBundles = resolver.getImportedBundles(bundle); | |
// eliminate classpath path | |
final String path = OsgiResourceUtils.stripPrefix(locationPattern); | |
final Collection<String> foundPaths = new LinkedHashSet<String>(); | |
// 1. search the imported packages | |
// find folder path matching | |
final String rootDirPath = determineFolderPattern(path); | |
if (System.getSecurityManager() != null) { | |
try { | |
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { | |
public Object run() throws IOException { | |
for (int i = 0; i < importedBundles.length; i++) { | |
final ImportedBundle importedBundle = importedBundles[i]; | |
if (!bundle.equals(importedBundle.getBundle())) { | |
findImportedBundleMatchingResource(importedBundle, rootDirPath, path, foundPaths); | |
} | |
} | |
return null; | |
} | |
}); | |
} catch (PrivilegedActionException pe) { | |
throw (IOException) pe.getException(); | |
} | |
} else { | |
for (int i = 0; i < importedBundles.length; i++) { | |
final ImportedBundle importedBundle = importedBundles[i]; | |
if (!bundle.equals(importedBundle.getBundle())) { | |
findImportedBundleMatchingResource(importedBundle, rootDirPath, path, foundPaths); | |
} | |
} | |
} | |
// 2. search the target bundle | |
findSyntheticClassPathMatchingResource(bundle, path, foundPaths); | |
// 3. resolve the entries using the official class-path method (as some of them might be hidden) | |
List<Resource> resources = new ArrayList<Resource>(foundPaths.size()); | |
for (String resourcePath : foundPaths) { | |
// classpath*: -> getResources() | |
if (OsgiResourceUtils.PREFIX_TYPE_CLASS_ALL_SPACE == type) { | |
CollectionUtils.mergeArrayIntoCollection(convertURLEnumerationToResourceArray(bundle | |
.getResources(resourcePath), resourcePath), resources); | |
} | |
// classpath -> getResource() | |
else { | |
URL url = bundle.getResource(resourcePath); | |
if (url != null) | |
resources.add(new UrlContextResource(url, resourcePath)); | |
} | |
} | |
if (logger.isTraceEnabled()) { | |
logger.trace("Fitered " + foundPaths + " to " + resources); | |
} | |
return (Resource[]) resources.toArray(new Resource[resources.size()]); | |
} | |
private String determineFolderPattern(String path) { | |
int index = path.lastIndexOf(FOLDER_SEPARATOR); | |
return (index > 0 ? path.substring(0, index + 1) : ""); | |
} | |
private ContextResource[] convertURLEnumerationToResourceArray(Enumeration<URL> enm, String path) { | |
Set<ContextResource> resources = new LinkedHashSet<ContextResource>(4); | |
while (enm != null && enm.hasMoreElements()) { | |
resources.add(new UrlContextResource(enm.nextElement(), path)); | |
} | |
return (ContextResource[]) resources.toArray(new ContextResource[resources.size()]); | |
} | |
/** | |
* Searches for the given pattern inside the imported bundle. This translates to pattern matching on the imported | |
* packages. | |
* | |
* @param importedBundle imported bundle | |
* @param path path used for pattern matching | |
* @param foundPaths collection of found results | |
*/ | |
@SuppressWarnings("unchecked") | |
private void findImportedBundleMatchingResource(final ImportedBundle importedBundle, String rootPath, String path, | |
final Collection<String> foundPaths) throws IOException { | |
final boolean trace = logger.isTraceEnabled(); | |
String[] packages = importedBundle.getImportedPackages(); | |
if (trace) | |
logger.trace("Searching path [" + path + "] on imported pkgs " + ObjectUtils.nullSafeToString(packages) | |
+ "..."); | |
final boolean startsWithSlash = rootPath.startsWith(FOLDER_SEPARATOR); | |
for (int i = 0; i < packages.length; i++) { | |
// transform the package name into a path | |
String pkg = packages[i].replace(DOT, SLASH) + SLASH; | |
if (startsWithSlash) { | |
pkg = FOLDER_SEPARATOR + pkg; | |
} | |
final PathMatcher matcher = getPathMatcher(); | |
// if the imported package matches the path | |
if (matcher.matchStart(path, pkg)) { | |
Bundle bundle = importedBundle.getBundle(); | |
// 1. look at the Bundle jar root | |
Enumeration<String> entries = bundle.getEntryPaths(pkg); | |
while (entries != null && entries.hasMoreElements()) { | |
String entry = entries.nextElement(); | |
if (startsWithSlash) | |
entry = FOLDER_SEPARATOR + entry; | |
if (matcher.match(path, entry)) { | |
if (trace) | |
logger.trace("Found entry [" + entry + "]"); | |
foundPaths.add(entry); | |
} | |
} | |
// 2. Do a Bundle-Classpath lookup (since the jar might use a different classpath) | |
Collection<String> cpMatchingPaths = findBundleClassPathMatchingPaths(bundle, path); | |
foundPaths.addAll(cpMatchingPaths); | |
} | |
} | |
} | |
/** | |
* Applies synthetic class-path analysis. That is, search the bundle space and the bundle class-path for entries | |
* matching the given path. | |
* | |
* @param bundle | |
* @param path | |
* @param foundPaths | |
* @throws IOException | |
*/ | |
private void findSyntheticClassPathMatchingResource(Bundle bundle, String path, Collection<String> foundPaths) | |
throws IOException { | |
// 1. bundle space lookup | |
OsgiBundleResourcePatternResolver localPatternResolver = new OsgiBundleResourcePatternResolver(bundle); | |
Resource[] foundResources = localPatternResolver.findResources(path); | |
boolean trace = logger.isTraceEnabled(); | |
if (trace) | |
logger.trace("Found synthetic cp resources " + ObjectUtils.nullSafeToString(foundResources)); | |
for (int j = 0; j < foundResources.length; j++) { | |
// assemble only the OSGi paths | |
foundPaths.add(foundResources[j].getURL().getPath()); | |
} | |
// 2. Bundle-Classpath lookup (on the path stripped of the prefix) | |
Collection<String> cpMatchingPaths = findBundleClassPathMatchingPaths(bundle, path); | |
if (trace) | |
logger.trace("Found Bundle-ClassPath matches " + cpMatchingPaths); | |
foundPaths.addAll(cpMatchingPaths); | |
// 3. Required-Bundle is considered already by the dependency resolver | |
} | |
/** | |
* Searches the bundle classpath (Bundle-Classpath) entries for the given pattern. | |
* | |
* @param bundle | |
* @param pattern | |
* @return | |
* @throws IOException | |
*/ | |
private Collection<String> findBundleClassPathMatchingPaths(Bundle bundle, String pattern) throws IOException { | |
// list of strings pointing to the matching resources | |
List<String> list = new ArrayList<String>(4); | |
boolean trace = logger.isTraceEnabled(); | |
if (trace) | |
logger.trace("Analyzing " + Constants.BUNDLE_CLASSPATH + " entries for bundle [" + bundle.getBundleId() | |
+ "|" + bundle.getSymbolicName() + "]"); | |
// see if there is a bundle class-path defined | |
String[] entries = OsgiHeaderUtils.getBundleClassPath(bundle); | |
if (trace) | |
logger.trace("Found " + Constants.BUNDLE_CLASSPATH + " entries " + ObjectUtils.nullSafeToString(entries)); | |
// 1. if so, look at the entries | |
for (int i = 0; i < entries.length; i++) { | |
String entry = entries[i]; | |
// make sure to exclude the default entry | |
if (!entry.equals(BUNDLE_DEFAULT_CP)) { | |
// 2. locate resource first from the bundle space (since it might not exist) | |
OsgiBundleResource entryResource = new OsgiBundleResource(bundle, entry); | |
// call the internal method to avoid catching an exception | |
URL url = null; | |
ContextResource res = entryResource.getResourceFromBundleSpace(entry); | |
if (res != null) { | |
url = res.getURL(); | |
} | |
if (trace) | |
logger.trace("Classpath entry [" + entry + "] resolves to [" + url + "]"); | |
// we've got a valid entry so let's parse it | |
if (url != null) { | |
String cpEntryPath = url.getPath(); | |
// is it a jar ? | |
if (entry.endsWith(JAR_EXTENSION)) | |
findBundleClassPathMatchingJarEntries(list, url, pattern); | |
// no, so it must be a folder | |
else | |
findBundleClassPathMatchingFolders(list, bundle, cpEntryPath, pattern); | |
} | |
} | |
} | |
return list; | |
} | |
/** | |
* Checks the jar entries from the Bundle-Classpath for the given pattern. | |
*/ | |
private void findBundleClassPathMatchingJarEntries(List<String> list, URL url, String pattern) throws IOException { | |
// get the stream to the resource and read it as a jar | |
JarInputStream jis = new JarInputStream(url.openStream()); | |
Set<String> result = new LinkedHashSet<String>(8); | |
boolean patternWithFolderSlash = pattern.startsWith(FOLDER_SEPARATOR); | |
// parse the jar and do pattern matching | |
try { | |
while (jis.available() > 0) { | |
JarEntry jarEntry = jis.getNextJarEntry(); | |
// if the jar has ended, the entry can be null (on Sun JDK at least) | |
if (jarEntry != null) { | |
String entryPath = jarEntry.getName(); | |
// check if leading "/" is needed or not (it depends how the jar was created) | |
if (entryPath.startsWith(FOLDER_SEPARATOR)) { | |
if (!patternWithFolderSlash) { | |
entryPath = entryPath.substring(FOLDER_SEPARATOR.length()); | |
} | |
} else { | |
if (patternWithFolderSlash) { | |
entryPath = FOLDER_SEPARATOR.concat(entryPath); | |
} | |
} | |
if (getPathMatcher().match(pattern, entryPath)) { | |
result.add(entryPath); | |
} | |
} | |
} | |
} finally { | |
try { | |
jis.close(); | |
} catch (IOException io) { | |
// ignore it - nothing we can't do about it | |
} | |
} | |
if (logger.isTraceEnabled()) | |
logger.trace("Found in nested jar [" + url + "] matching entries " + result); | |
list.addAll(result); | |
} | |
/** | |
* Checks the folder entries from the Bundle-Classpath for the given pattern. | |
* | |
* @param list | |
* @param bundle | |
* @param cpEntryPath | |
* @param pattern | |
* @throws IOException | |
*/ | |
private void findBundleClassPathMatchingFolders(List<String> list, Bundle bundle, String cpEntryPath, String pattern) | |
throws IOException { | |
// append path to the pattern and do a normal search | |
// folder/<pattern> starts being applied | |
String bundlePathPattern; | |
boolean entryWithFolderSlash = cpEntryPath.endsWith(FOLDER_SEPARATOR); | |
boolean patternWithFolderSlash = pattern.startsWith(FOLDER_SEPARATOR); | |
// concatenate entry + pattern w/o double slashes | |
if (entryWithFolderSlash) { | |
if (patternWithFolderSlash) | |
bundlePathPattern = cpEntryPath + pattern.substring(1, pattern.length()); | |
else | |
bundlePathPattern = cpEntryPath + pattern; | |
} else { | |
if (patternWithFolderSlash) | |
bundlePathPattern = cpEntryPath + pattern; | |
else | |
bundlePathPattern = cpEntryPath + FOLDER_SEPARATOR + pattern; | |
} | |
// search the bundle space for the detected resource | |
OsgiBundleResourcePatternResolver localResolver = new OsgiBundleResourcePatternResolver(bundle); | |
Resource[] resources = localResolver.getResources(bundlePathPattern); | |
boolean trace = logger.isTraceEnabled(); | |
List<String> foundResources = (trace ? new ArrayList<String>(resources.length) : null); | |
try { | |
// skip when dealing with non-existing resources | |
if (resources.length == 1 && !resources[0].exists()) { | |
return; | |
} else { | |
int cutStartingIndex = cpEntryPath.length(); | |
// add the resource stripping the cp | |
for (int i = 0; i < resources.length; i++) { | |
String path = resources[i].getURL().getPath().substring(cutStartingIndex); | |
list.add(path); | |
if (trace) | |
foundResources.add(path); | |
} | |
} | |
} finally { | |
if (trace) | |
logger.trace("Searching for [" + bundlePathPattern + "] revealed resources (relative to the cp entry [" | |
+ cpEntryPath + "]): " + foundResources); | |
} | |
} | |
/** | |
* Replace the super class implementation to pass in the searchType parameter. | |
* | |
* @see PathMatchingResourcePatternResolver#findPathMatchingResources(String) | |
*/ | |
private Resource[] findPathMatchingResources(String locationPattern, int searchType) throws IOException { | |
String rootDirPath = determineRootDir(locationPattern); | |
String subPattern = locationPattern.substring(rootDirPath.length()); | |
Resource[] rootDirResources = getResources(rootDirPath); | |
boolean trace = logger.isTraceEnabled(); | |
if (trace) | |
logger.trace("Found root resources for [" + rootDirPath + "] :" | |
+ ObjectUtils.nullSafeToString(rootDirResources)); | |
Set<Resource> result = new LinkedHashSet<Resource>(); | |
for (int i = 0; i < rootDirResources.length; i++) { | |
Resource rootDirResource = rootDirResources[i]; | |
if (isJarResource(rootDirResource)) { | |
result.addAll(doFindPathMatchingJarResources(rootDirResource, new URL(""), subPattern)); | |
} else { | |
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern, searchType)); | |
} | |
} | |
if (logger.isTraceEnabled()) { | |
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result); | |
} | |
return result.toArray(new Resource[result.size()]); | |
} | |
/** | |
* {@inheritDoc} | |
* | |
* Overrides the default check up since computing the URL can be fairly expensive operation as there is no caching | |
* (due to the framework dynamic nature). | |
*/ | |
protected boolean isJarResource(Resource resource) throws IOException { | |
if (resource instanceof OsgiBundleResource) { | |
// check the resource type | |
OsgiBundleResource bundleResource = (OsgiBundleResource) resource; | |
// if it's known, then it's not a jar | |
if (bundleResource.getSearchType() != OsgiResourceUtils.PREFIX_TYPE_UNKNOWN) { | |
return false; | |
} | |
// otherwise the normal parsing occur | |
} | |
return super.isJarResource(resource); | |
} | |
/** | |
* Based on the search type, uses the appropriate searching method. | |
* | |
* @see OsgiBundleResource#BUNDLE_URL_PREFIX | |
* @see org.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources(java.lang.String) | |
*/ | |
private Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern, int searchType) | |
throws IOException { | |
String rootPath = null; | |
if (rootDirResource instanceof OsgiBundleResource) { | |
OsgiBundleResource bundleResource = (OsgiBundleResource) rootDirResource; | |
rootPath = bundleResource.getPath(); | |
searchType = bundleResource.getSearchType(); | |
} else if (rootDirResource instanceof UrlResource) { | |
rootPath = rootDirResource.getURL().getPath(); | |
} | |
if (rootPath != null) { | |
String cleanPath = OsgiResourceUtils.stripPrefix(rootPath); | |
// sanitize the root folder (since it's possible to not specify the root which fails any further matches) | |
if (!cleanPath.endsWith(FOLDER_SEPARATOR)) { | |
cleanPath = cleanPath + FOLDER_SEPARATOR; | |
} | |
String fullPattern = cleanPath + subPattern; | |
Set<Resource> result = new LinkedHashSet<Resource>(); | |
doRetrieveMatchingBundleEntries(bundle, fullPattern, cleanPath, result, searchType); | |
return result; | |
} else { | |
return super.doFindPathMatchingFileResources(rootDirResource, subPattern); | |
} | |
} | |
/** | |
* Searches each level inside the bundle for entries based on the search strategy chosen. | |
* | |
* @param bundle the bundle to do the lookup | |
* @param fullPattern matching pattern | |
* @param dir directory inside the bundle | |
* @param result set of results (used to concatenate matching sub dirs) | |
* @param searchType the search strategy to use | |
* @throws IOException | |
*/ | |
private void doRetrieveMatchingBundleEntries(Bundle bundle, String fullPattern, String dir, Set<Resource> result, | |
int searchType) throws IOException { | |
Enumeration<?> candidates; | |
switch (searchType) { | |
case OsgiResourceUtils.PREFIX_TYPE_NOT_SPECIFIED: | |
case OsgiResourceUtils.PREFIX_TYPE_BUNDLE_SPACE: | |
// returns an enumeration of URLs | |
candidates = bundle.findEntries(dir, null, false); | |
break; | |
case OsgiResourceUtils.PREFIX_TYPE_BUNDLE_JAR: | |
// returns an enumeration of Strings | |
candidates = bundle.getEntryPaths(dir); | |
break; | |
case OsgiResourceUtils.PREFIX_TYPE_CLASS_SPACE: | |
// returns an enumeration of URLs | |
throw new IllegalArgumentException("class space does not support pattern matching"); | |
default: | |
throw new IllegalArgumentException("unknown searchType " + searchType); | |
} | |
// entries are relative to the root path - miss the leading / | |
if (candidates != null) { | |
boolean dirDepthNotFixed = (fullPattern.indexOf(FOLDER_WILDCARD) != -1); | |
while (candidates.hasMoreElements()) { | |
Object path = candidates.nextElement(); | |
String currPath; | |
if (path instanceof String) | |
currPath = handleString((String) path); | |
else | |
currPath = handleURL((URL) path); | |
if (!currPath.startsWith(dir)) { | |
// Returned resource path does not start with relative | |
// directory: | |
// assuming absolute path returned -> strip absolute path. | |
int dirIndex = currPath.indexOf(dir); | |
if (dirIndex != -1) { | |
currPath = currPath.substring(dirIndex); | |
} | |
} | |
if (currPath.endsWith(FOLDER_SEPARATOR) | |
&& (dirDepthNotFixed || StringUtils.countOccurrencesOf(currPath, FOLDER_SEPARATOR) < StringUtils | |
.countOccurrencesOf(fullPattern, FOLDER_SEPARATOR))) { | |
// Search subdirectories recursively: we manually get the | |
// folders on only one level | |
doRetrieveMatchingBundleEntries(bundle, fullPattern, currPath, result, searchType); | |
} | |
if (getPathMatcher().match(fullPattern, currPath)) { | |
if (path instanceof URL) | |
result.add(new UrlContextResource((URL) path, currPath)); | |
else | |
result.add(new OsgiBundleResource(bundle, currPath)); | |
} | |
} | |
} | |
} | |
/** | |
* Handles candidates returned as URLs. | |
* | |
* @param path | |
* @return | |
*/ | |
private String handleURL(URL path) { | |
return path.getPath(); | |
} | |
/** | |
* Handles candidates returned as Strings. | |
* | |
* @param path | |
* @return | |
*/ | |
private String handleString(String path) { | |
return FOLDER_SEPARATOR.concat(path); | |
} | |
} |