Bug 125471 - Unable to load resource using buddy classloading
Summary: Unable to load resource using buddy classloading
Status: RESOLVED FIXED
Alias: None
Product: Equinox
Classification: Eclipse Project
Component: Framework (show other bugs)
Version: 3.2   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 3.2 M6   Edit
Assignee: Thomas Watson CLA
QA Contact:
URL:
Whiteboard:
Keywords: contributed
: 110712 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-01-27 09:13 EST by Paul Webster CLA
Modified: 2006-08-23 15:20 EDT (History)
6 users (show)

See Also:


Attachments
3 plugins that won't load a resource (8.74 KB, application/octet-stream)
2006-01-27 09:15 EST, Paul Webster CLA
no flags Details
GlobalPolicy#loadResources(String) Patch (2.21 KB, patch)
2006-01-28 19:45 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
GlobalPolicy.java Patch (R3_1_maintenance) (2.49 KB, patch)
2006-01-30 12:25 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
DependentPolicy.java Patch (R3_1_maintenance) (4.63 KB, patch)
2006-01-30 12:31 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
RegisteredPolicy.java Patch (R3_1_maintenance) (5.66 KB, patch)
2006-01-30 12:36 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
BundleLoader.java Patch (R3_1_maintenance) (4.37 KB, patch)
2006-01-30 12:42 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
PolicyHandler.java Patch (R3_1_maintenance) (1.77 KB, patch)
2006-01-30 12:44 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
PolicyHandler.java Patch (R3_1_maintenance) (1.75 KB, patch)
2006-02-03 15:27 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
BundleLoader.java Patch (R3_1_maintenance) (2.64 KB, patch)
2006-02-04 19:10 EST, Nathan Beyer (Cerner) CLA
no flags Details | Diff
Combined patch to all files (12.81 KB, patch)
2006-02-09 15:37 EST, Thomas Watson CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Paul Webster CLA 2006-01-27 09:13:56 EST
I have plugin com.ex.A with a class LoadStuff with a static method that does:
class.getClassLoader().getResourceAsStream(resource) and has Eclipse-BuddyPolicy: registered.

I have plugin com.ex.B with a Worker class that calls into LoadStuff to load a resource.  It depends on com.ex.A.

I have a UI plugin com.ex.C that depends on com.ex.B, instantiates Worker and passes in the resource /properties/stuff.properties, which is contained in C, and has Eclipse-RegisterBuddy: com.ex.A

It won't load the resource unless I make com.ex.C depend on com.ex.A.  It seems that you not only need the buddy registration but also a direct dependancy.  Then it works.

But then if I change the policy from registered to global, it stops working.

I'll attach 3 project example.

PW
Comment 1 Paul Webster CLA 2006-01-27 09:15:24 EST
Created attachment 33700 [details]
3 plugins that won't load a resource
Comment 2 Nathan Beyer (Cerner) CLA 2006-01-28 19:45:56 EST
Created attachment 33758 [details]
GlobalPolicy#loadResources(String) Patch

This patch should resolve this bug, as well as fix some more of the issues in bug 110712, when a budy policy of "global" is used.

This patch only affects the class org.eclipse.osgi.framework.internal.core.GlobalPolicy. The patch was made against the R3_1_maintenance branch/tag. I don't know how how different the 3.2 code is, but it should be easy to port.

The issues I found are that the loadResource and loadResources implementations were using the utility method BundleLoader.getPackageName, when they should have been using the BundleLoader.getResourcePackageName. The second issue is that the loadResources method was only looking up one ExportedPackage, the first that matched, instead of all that matched. This makes sense for loadClass and loadResource, but not for loadResources.
Comment 3 Jeff McAffer CLA 2006-01-28 20:36:14 EST
thanks for the patch.  We'll take a look
Comment 4 Pascal Rapicault CLA 2006-01-30 10:52:22 EST
The patch looks good. I would favor the usage of Vector instead of ArrayList when collecting the results, so that you get a enumeration immediatly.

Could you please provide a patch to the other policies provided since they all suffer from the same problem?
Comment 5 Nathan Beyer (Cerner) CLA 2006-01-30 11:26:11 EST
There are pieces of this fix for RegisteredPolicy in bug 110712. I can post those fixes here as well. I can work out another patch for the DependentPolicy fairly easily.

Should the patch be against HEAD or the R3_1_maintenance branch? I know there's a refactoring with Equinox, but I didn't know that has affected this particular piece of code too much. Additionally, assuming there is going to be another 3.1 maintenance release, I personally need this in that branch.

As for the ArrayList vs. Vector - I can convert it to use Vectors without issue. As a habit, I prefer to use ArrayLists instead since Vector's are internally synchronized, but I can convert it to keep it consistent.

Thanks.
Comment 6 Nathan Beyer (Cerner) CLA 2006-01-30 12:25:58 EST
Created attachment 33797 [details]
GlobalPolicy.java Patch (R3_1_maintenance)

Patch for GlobalPolicy to retrieve all resources and use Vectors.
Comment 7 Nathan Beyer (Cerner) CLA 2006-01-30 12:31:25 EST
Created attachment 33798 [details]
DependentPolicy.java Patch (R3_1_maintenance)

Patch for DependentPolicy.java to retrieve all resources properly. Additionally, I've cleaned up this class a little bit, so that the thread synchronization is correct and safe. Previously, there was only one method that was synchronized, but this method alone wouldn't protect the data in all scenarios. I've just synchronized the public methods to make it more straight-forward. If this is seen as a bottle neck, then a read/write mutex or something similar could be used. The other way to resolve this would be to eliminate all synchronization and convert the lazy loading to upfront loading in the constructor.
Comment 8 Nathan Beyer (Cerner) CLA 2006-01-30 12:36:27 EST
Created attachment 33800 [details]
RegisteredPolicy.java Patch (R3_1_maintenance)

A patch for RegisteredPolicy.java to load all resources. I've reworked this class slightly. Originally it was extending DependentPolicy, but this seemed awkward and not altogether appropriate (it didn't seem to pass the "is-a" test for extension). As such, I just remove the extension and implemented IBuddyPolicy directly and pulled up the small portion of initialization code that it was borrowing. I believe this makes the initialization (constructor) a bit cleaner. Also, I eliminated the cast to AbstractBundle and just used the methods that were available, so as to preserve some isolation.
Comment 9 Nathan Beyer (Cerner) CLA 2006-01-30 12:42:54 EST
Created attachment 33801 [details]
BundleLoader.java Patch (R3_1_maintenance)

A patch for BundleLoader to load all resources. This was also posted on bug 110712, but seems most appropriate here now.
Comment 10 Nathan Beyer (Cerner) CLA 2006-01-30 12:44:00 EST
Created attachment 33802 [details]
PolicyHandler.java Patch (R3_1_maintenance)

A patch for PolicyHandler to load all resources. This was also posted on bug 110712, but seems more appropriate here now.
Comment 11 John Arthorne CLA 2006-01-31 16:42:04 EST
*** Bug 110712 has been marked as a duplicate of this bug. ***
Comment 12 Nathan Beyer (Cerner) CLA 2006-02-03 12:14:40 EST
As an aside, but somewhat related, question, how are plug-in fragments supposed to affect the Buddy Loading policies, if at all?

I ran a test with a host plugin that has no buddy policy and then put a buddy policy of global in a fragment. The fragment code was unable to load an exported class initially. When I moved the global policy from the fragment to the host, the fragment was able to load the class.

According to the documentation, it seems that you can have multiple policy values, so I was wondering if policies declared in a fragment should be appended to the policies of the host.
Comment 13 Pascal Rapicault CLA 2006-02-03 13:08:23 EST
Given the way fragments are handled by osgi, that would make sense. Do you want to prototype?
Tom can you think of any particular problem that this could cause?
Comment 14 Thomas Watson CLA 2006-02-03 13:34:36 EST
Can buddy policies collide with each other?  For example, if one fragment specifies one policy and another fragment specifies another policy?  I don't think this is really an issue because the host can specify all supported policies in a list today.  Fragments should just add to the list of policies specified by the host and we should ensure that duplicate policies are not added to the host list.  

This should be isolated to the BundleLoader class if someone is willing to prototype.  I suggest a separate bug report be opened for this.
Comment 15 Pascal Rapicault CLA 2006-02-03 15:07:16 EST
On the patches:
- The BundleLoader#getResources can't be changed since this behavior is specified. The patch won't be applied.
- PolicyHandler should provide use Vector
- I don't see the point of the complete reorg of DependentPolicy and RegisteredPolicy. I was expecting change only in getResources().
Comment 16 Nathan Beyer (Cerner) CLA 2006-02-03 15:27:21 EST
Created attachment 34120 [details]
PolicyHandler.java Patch (R3_1_maintenance)

Converted PolicyHandler to use Vector instead of ArrayList.

Any reason for using Vectors instead of ArrayLists? I'm assuming it's not because the Collections APIs are trying to be avoided, as this class uses Sets and HashSets already. Vectors are so much slower than ArrayLists, due to synchronization.
Comment 17 Nathan Beyer (Cerner) CLA 2006-02-03 15:50:30 EST
(In reply to comment #15)
> - I don't see the point of the complete reorg of DependentPolicy and
> RegisteredPolicy. I was expecting change only in getResources().
> 

Several reasons actually, most of which I mentioned in comment #7 and comment #8.

The major reason for the changes to DependentPolicy are because it was using a lazy load mechanism and since there was at least one piece of syncrhonization and because this is a utility for class loading I assumed it must be thread safe. The current implementation was only synchronizing a single method ('addDependent'), but this wasn't completely protecting all of the fields that were being read and written. It looked like there could be conditions where a read/write race might occur. Perhaps my analysis was incorrect.

As for RegisteredPolicy, the implementation was extending from DependentPolicy. This seemed awkward as it doesn't seem to pass the "is-a" test; "RegisteredPolicy is-a DependentPolicy". There is no mention in the documention that for a registered bundle to be in the class loader, it must make a dependency as well. If it doesn't need to have a dependency, then further changes probably need to be made to look at all bundles, not just dependents.

Additionally, and the better reason, the only thing that RegisteredPolicy was really using from DependentPolicy was the loading of the BundleDescriptions, but even after it utilized this it looped back through all of the descriptions to eliminate all dependents that weren't registered. This seemed liked a waste of cycles. Another small change was to eliminate the need to cast the Bundle to an AbstractBundle to get the manifest data; I changed it to use the public interface.

I apologize if I over stepped some bounds, but I thought I could provide some additional cleanup for stability and performance. If you'd like, I can easily provide a different set of patches for these files that maintains the original implementations.
Comment 18 Nathan Beyer (Cerner) CLA 2006-02-03 16:17:15 EST
(In reply to comment #15)
> On the patches:
> - The BundleLoader#getResources can't be changed since this behavior is
> specified. The patch won't be applied.

The change is actually to "findResources", correct? I just want to make sure we're discussing the same thing.

Without the changes to BundleLoader, the other patches won't really do much at all. The problem is that findResources is currently acts as through we're only finding a single resource or a single class, meaning that you won't need to find the very first resource you encounter. This does not comply with the definition of the ClassLoader#getResources [1], nor what I would consider the spirit of the buddy class loading.

The specific case that I've run into is that the plugin that declares a buddy policy of any type and exposes a package that is also exposed by another plugin. With the current implementation, the exposed packaged in the first plugin will be found and no other bundles will be searched.

Here's a layout of what I'm discussing.

Plug-in A
 - Export-Package: org.ex.StuffLoader, properties
 - Eclipse-BuddyPolicy: registered
The class StuffLoader loads ALL resources using ClassLoader.getResources("properties/stuff.properties"). This project contains the resource "properties/stuff.properties" with default data.

Plug-in B
 - Export-Package: properties
 - Eclipse-RegisterBuddy: A
This plug-in uses StuffLoader and contains a resource "properties/stuff.properties" with some overriding data.

Given the current implementation of BundeLoader, the search would stop as soon as the exported package in plug-in A was found. Additionally, this would fail even if plug-in A didn't have a file with the same URI, but still exported that package because it has other resources in their as well.

If this is the defined behavior of BundleLoader or more specifically the ClassLoaderDelegate, then I would argue that the defined behavior is incorrect and should be fixed. The ClassLoader#getResources() [1] must return ALL resources available for a given URI.


[1] http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html#getResources(java.lang.String)
Comment 19 Pascal Rapicault CLA 2006-02-03 17:35:16 EST
Changing bundleLoader would probably make us fail the conformance tests since this is the class that implements the lookup defined in osgi.
Buddy loading comes as an extension of the standard osgi lookup when none of the osgi lookup have found a class/resource. Therefore we have the liberty of changing it to return all the resources.

For questions on the osgi spec I recommend you to post your questions on the osgi-dev mailing list from the osgi consortium: http://bundles.osgi.org/mailman/listinfo/osgi-dev 
Comment 20 Nathan Beyer (Cerner) CLA 2006-02-04 19:10:14 EST
Created attachment 34153 [details]
BundleLoader.java Patch (R3_1_maintenance)

I reviewed my changes and they did go over board. This patch should keep the defined OSGi behavior intact and enable the buddy loading extension to still work.

This patch maintains the original algorithm, but always checks for a buddy policy and adds any resources to the original results.

During my tests, this resolves the issues that I've mentioned earlier.
Comment 21 Nathan Beyer (Cerner) CLA 2006-02-06 15:08:20 EST
Any thoughts or comments on the latest patch?

I don't want to be a bother, but I have some project designs on hold that depend on the finalization of this functionality.

Thanks.
Comment 22 Thomas Watson CLA 2006-02-06 15:33:53 EST
We will try to have something for you in M5 ... I have not looked at the patches myself yet.
Comment 23 Thomas Watson CLA 2006-02-09 14:13:56 EST
Ok, the original problem reported by Paul is completely different than the one trying to be addressed by the various patches attached to this bug report.

The original problem that can be recreated by the project in attachment 33700 [details] is a problem with loading a single resource by using getResource not getResoures (plural).

Here the registered policy is actually working as designed but it is probably not documented clearly what the registered policy does.  In the comment of the RegisteredPolicy class we have the following:

Registered policy is an implementation of a buddy policy. 
It is responsible for looking up a class in the bundles (registrant) that declare interest in the bundle that require the buddy loading.
Note that the registrants must have a direct dependency on the bundle needing buddy.

So you must not only register with the bundle needing the buddy but you must also be dependent on the bundle directly.  The sentence "Note that the registrants must have a direct dependency on the bundle needing buddy." should probably be added to the documentation of "registered" buddy policy.

If this was the only thing contained in this bug report then I would close this as invalid.  But starting in comment 2 Nathan raised a separate issue having to do with delegating to the buddy policy all the time for getResources (plural) calls even when resources were found in the normal delegation.  It is debatable if this is the correct coarse of action because buddies are only supposed to be delegated to if the class/resource could not be found under normal delegation.  

In this case the patches are changing this behavior for getResources to always delegate to buddies even when resources are available under normal delegation.  The latest patch to BundleLoader still changes the defined OSGi behavior for Import-Package.  If a package is imported we should not look anywhere else except the exporter of the package.  We MUST return the result directly from the exporter if the package is imported.  The current patch changes the behavior to always look locally and in buddies when the package is imported, neither should be asked if the package is imported.

I do not have a particular problem with the other changes provided by the patches.  In a way it does seem to make sense to search all places possible (including buddies) when getting an exhaustive list of URLs to a particular resource.  I will attach a new patch that I think is acceptable (does not change Import-Package behavior), but given the relative sensitivity of this change I will need at least two other committers to approve before I will consider releasing.  Therefore I'm slipping the Milestone to M6.

You made your patches against 3.1_maintenance.  This type of change is not a good candidate for a point release (i.e. for 3.1.3).
Comment 24 Thomas Watson CLA 2006-02-09 15:37:42 EST
Created attachment 34446 [details]
Combined patch to all files

I combined all changes into a single patch.  I reverted RegisteredPolicy back to extend DependentPolicy to limit the code changes just to the issue at hand.

I cleaned up a few things in the patch.  One functional thing that I found to be a problem was in DependentPolicy#loadResources.  The original patch only seemed to add all dependents only if the result was not null from one of the dependents.  This would seem to leave some resources out if the full depenency list was not completely built during the call the getResources.  The current patch will ensure all dependents (immediate and recursive) are search during the getResources call.
Comment 25 Thomas Watson CLA 2006-02-09 15:40:29 EST
DJ and Pascal.  Please give the patch a thorough review.  I'm slipping to M6 unless you both give a +1 in time for M5.

Nathan, please make sure the DependentPolicy change still works in your env given the changes I made to ensure all dependents are searched.  Thanks.
Comment 26 Nathan Beyer (Cerner) CLA 2006-02-10 14:06:36 EST
(In reply to comment #25)
> Nathan, please make sure the DependentPolicy change still works in your env
> given the changes I made to ensure all dependents are searched.  Thanks.
> 

Yes, this patch still still works for me and since I'm not using any import package or dynamic import statements, the functionality is exactly what I needed.
Comment 27 Thomas Watson CLA 2006-03-27 14:36:35 EST
Pascal, can you review the latest patch.  Need to decide if this is required for M6.
Comment 28 Pascal Rapicault CLA 2006-03-27 16:57:50 EST
I'm a bit worried by the change in bundleloader by which we always consult the buddy loading, but aside from that the patch looks good and should be tried out for M6.
Comment 29 Thomas Watson CLA 2006-03-27 17:55:17 EST
Patch released for M6.
Comment 30 DJ Houghton CLA 2006-04-18 14:53:18 EDT
[contributed patch applied]
Comment 31 Thomas Watson CLA 2006-08-23 15:20:26 EDT
adding "contributed" keyword to patches contributed by the community.