Issue Details (XML | Word | Printable)

Key: OSGI-583
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Critical Critical
Assignee: Costin Leau
Reporter: Sam Brannen
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
Spring OSGi

OsgiBundleResourcePatternResolver.findBundleClassPathMatchingFolders(List, Bundle, String, String) fails prematurely

Created: 18/Jul/08 07:43 AM   Updated: 28/Jul/08 12:53 PM
Component/s: CORE
Affects Version/s: 1.1
Fix Version/s: 1.1.1

Time Tracking:
Not Specified


 Description  « Hide
The findBundleClassPathMatchingFolders() method processes candidate resources for classpath matching; however,
if a candidate is encountered which does not exist, the method fails the search prematurely by allowing an IOException
to propagate. The code causing this problem is:

for (int i = 0; i < resources.length; i++) {
list.add(resources[i].getURL().getPath());
}

If the candidate resource does not exist, per the Resource API, the call to getURL() will throw an IOException.
Thus the call to getURL() should be wrapped in a try-catch block, and only existing candidates should be
added to the list.

To reproduce this, create a bundle with the following Bundle-ClassPath: .,foo,bar
The bundle should contain META-INF/test.xml in the root of the bundle.
Then if you execute a getResources() call for classpath*:META-INF/test.xml, the call will fail
because foo/META-INF/test.xml does not exist.

 All   Comments   Work Log   Change History   FishEye   Builds      Sort Order: Ascending order - Click to sort in descending order
Costin Leau added a comment - 23/Jul/08 08:12 AM
I recall this case is handled and tested so I added more invalid entries to the existing tests and I am unable to reproduce the bug. I've tested this on Equinox and Knopflerfish just to make sure.
I've tried your example and with logging on, the invalid entries are filtered out early. Here is a snippet:

main TRACE [io.OsgiBundleResourcePatternResolver] - Analyzing Bundle-ClassPath entries for bundle [20|org.springframework.osgi.iandt.io.functional.fragments]
main TRACE [io.OsgiBundleResourcePatternResolver] - Found Bundle-ClassPath entries {., bundleclasspath/folder, bundleclasspath/simple.jar, foo}
main TRACE [io.OsgiBundleResourcePatternResolver] - Classpath entry [bundleclasspath/folder] resolves to [bundleentry://20/bundleclasspath/folder/]
main TRACE [io.OsgiBundleResourcePatternResolver] - Resolved location pattern [bundleclasspath/folder/*.file] to resources []
main TRACE [io.OsgiBundleResourcePatternResolver] - Classpath entry [bundleclasspath/simple.jar] resolves to [bundleentry://20/bundleclasspath/simple.jar]
main TRACE [io.OsgiBundleResourcePatternResolver] - Classpath entry [foo] resolves to [null]

Notice that since foo resolves to null, any search for it will be discarded.
Do you have a stracktrace for this bug?

Sam Brannen added a comment - 25/Jul/08 08:02 AM
Hi Costin,

This issue still exists. Consider the following code from a Spring managed component which implements ResourceLoaderAware:

        String path = "classpath*:META-INF/resource-1.xml";
        String result = "From ResourceLoader's InputStream via '" + path + "':\n";

        if (this.resourceLoader instanceof ResourcePatternResolver) {
            ResourcePatternResolver resourcePatternResolver = (ResourcePatternResolver) this.resourceLoader;
            Resource[] resources = resourcePatternResolver.getResources(path);
            for (Resource resource : resources) {
                InputStream inputStream = null;
                try {
                    inputStream = resource.getInputStream();
                    result += FileCopyUtils.copyToString(new BufferedReader(new InputStreamReader(inputStream)));
                    result += "\n";
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }
            }
        }

In my test case, Bundle-Classpath = .,MODULE-INF, and the root of the bundle does in fact contain the file META-INF/resource-1.xml. As I mentioned before, this fails on the call to resourcePatternResolver.getResources(path) as follows:

java.io.FileNotFoundException: OSGi resource[MODULE-INF/META-INF/resource-1.xml|bnd.id=85|bnd.sym=web_module_resource_loading-1.0.0.BUILD-20080725124936-web_module_resource_loading.web] cannot be resolved to URL because it does not exist
at org.springframework.osgi.io.OsgiBundleResource.getURL(OsgiBundleResource.java:224)
at org.springframework.osgi.io.OsgiBundleResourcePatternResolver.findBundleClassPathMatchingFolders(OsgiBundleResourcePatternResolver.java:461)

Note that if I remove MODULE-INF from the bundle classpath, the code executes as expected and properly locates the classpath*:META-INF/resource-1.xml resource.

Thanks for looking into this!

  Sam

Costin Leau added a comment - 25/Jul/08 10:22 AM
I've found the problem and committed a fix. The problem aren't invalid bundle classpath entries but missing resources in these entries (which trigger an exception when doing a search for classpath*:).

Sam Brannen added a comment - 28/Jul/08 06:41 AM
Hi Costin,

I've not yet run any code against your fix, but based on visual inspection, I can't imagine that the current
code solves the problem I initially reported. Specifically, the for-loop remains unchanged. So it appears
that you have only solved the issue when the returned list of candidate resources is of length one.

When the returned list of candidate resources is greater than one, however, the chance that one of the
candidate resources does not exist still remains. Thus, the only robust solution I can envision is one
similar to what I laid out in the description of this issue:

// untested code:
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
if (resource.exists()) {
list.add(resource.getURL().getPath());
}
}

Please take another look at this issue.

Costin Leau added a comment - 28/Jul/08 06:54 AM
Sam, please test the code and if you run into the problem again let me know. I appreciate the 'visual inspection' but there are integration tests in place that show things are running fine as they are : the resources returned by the resource loader always exist (they are tested during discovery, after all since this is a pattern based matching, it's hard to find/detect resources that do not exist) - the case with the array of fixed length takes care case where no resources are found.



Sam Brannen added a comment - 28/Jul/08 12:52 PM
Costin, you are indeed correct: you only have to call resource.exists() if the size of the candidate resource array is larger than one.
I guess I just wasn't "imaginative" enough with my visual inspection. ;)

Our tests pass, and as a result I'll re-close this issue.

Thanks,

 Sam

Sam Brannen added a comment - 28/Jul/08 12:53 PM
Changes have been successfully tested.