While hacking the demo, I had 'problems' creating this 'expected'
behavior:
I have foo.jar which contains FooBar service.
This FooBar service uses two other services - AlesService and ScottService.
| <bean name="FooBarService"
class="org.jboss.foo.FooBarService">
| <property name="alesService"><inject/></property>
| <property name="scottService"><inject/></property>
| </bean>
|
Then I have two similar jars - acme1 and acme2.
Containing AlesService(Impl) and ScottService(Impl).
acme1 has all classes in version 1.
Where acme2 has ales package in version 2 and scott package in version 3.
Then I have the following classloading metadata in foo.jar:
| <classloading xmlns="urn:jboss:classloading:1.0"
export-all="NON_EMPTY">
| <capabilities>
| <package name="org.jboss.acme.foo" version="1"/>
| </capabilities>
| <requirements>
| <package name="org.jboss.acme.ales" from="1"
to="2" from-inclusive="true"/>
| <package name="org.jboss.acme.scott" from="3"
to="4" from-inclusive="true"/>
| </requirements>
| </classloading>
|
Expecting that FooBar would use AlesService(Impl) from acme1 and ScottService(Impl) from
acme2.
But that's not what happened when using the bootstrap configuration similar to what we
use in AS5_trunk. :-(
Either I don't know how to properly configure this, or it's broken. ;-)
The problems that I've found:
When ClassLoaderPolicy returns non-null getExported delegate loader, this gets cached in
BaseClassLoaderDomain::classLoadersByPackageName
| if (packageNames != null && info.getExported() != null)
| {
| for (String packageName : packageNames)
| {
| List<ClassLoaderInformation> list =
classLoadersByPackageName.get(packageName);
| if (list == null)
| {
| list = new CopyOnWriteArrayList<ClassLoaderInformation>();
| classLoadersByPackageName.put(packageName, list);
| }
| list.add(info);
|
and this is then used when looking up the class in other bundles as well, but w/o knowing
the right version:
BaseClassLoaderDomain::findLoaderInExports
| List<ClassLoaderInformation> list =
classLoadersByPackageName.get(packageName);
| if (trace)
| log.trace(this + " trying to load " + name + " from all
exports of package " + packageName + " " + list);
| if (list != null && list.isEmpty() == false)
| {
| for (ClassLoaderInformation info : list)
| {
| BaseDelegateLoader exported = info.getExported();
|
| // See whether the policies allow caching/blacklisting
| BaseClassLoaderPolicy loaderPolicy = exported.getPolicy();
| if (loaderPolicy == null || loaderPolicy.isCacheable() == false)
| canCache = false;
| if (loaderPolicy == null || loaderPolicy.isBlackListable() == false)
| canBlackList = false;
|
| if (exported.getResource(name) != null)
| {
| if (canCache)
| globalClassCache.put(name, exported);
| return exported;
| }
| }
| }
|
So, when attempting to load class with the same name from foo.jar's classlaoder, but
expecting it in diff version, I still get the previous one.
OK, then I disabled this exported usage - either by returning null on getExported or null
getPackageNames.
Still didn't work as expected. :-(
Now the version-unaware delegates were kicking in:
BaseClassLoaderDomain::findLoaderInImports
| for (DelegateLoader delegate : delegates)
| {
| if (trace)
| log.trace(this + " trying to load " + name + " from import
" + delegate + " for " + info.getClassLoader());
| if (delegate.getResource(name) != null)
| {
| info.cacheLoader(name, delegate);
| return delegate;
| }
| }
|
where by default in CLPolicy we return exported, which is just filtered by package names -
no version awareness.
So again the previous classloader was picked.
What I had to do then - and I don't see why this isn't default behavior - is the
following piece of code:
| public DelegateLoader getDelegateLoader(Module module, Requirement
requirement)
| {
| List<Capability> capabilities = getCapabilities();
| if (capabilities != null && capabilities.isEmpty() == false)
| {
| for(Capability capability : capabilities)
| {
| if (capability.resolves(this, requirement))
| return new FilteredDelegateLoader(getPolicy(), new
CapabilityFilter(capability));
| }
| // none of the capabilities match - don't put it as delegate
| return null;
| }
| return super.getDelegateLoader(module, requirement);
| }
|
| public class CapabilityFilter implements ClassFilter
| {
| private Capability capability;
|
| public CapabilityFilter(Capability capability)
| {
| if (capability == null)
| throw new IllegalArgumentException("Null capability.");
|
| this.capability = capability;
| }
|
| public boolean matchesClassName(String className)
| {
| return matchesPackageName(ClassLoaderUtils.getClassPackageName(className));
| }
|
| public boolean matchesResourcePath(String resourcePath)
| {
| return
matchesPackageName(ClassLoaderUtils.getResourcePackageName(resourcePath));
| }
|
| public boolean matchesPackageName(String packageName)
| {
| if (capability instanceof ExportPackages)
| {
| ExportPackages ep = (ExportPackages)capability;
| Set<String> packages = ep.getPackageNames(null);
| if (packages != null && packages.contains(packageName))
| return true;
| }
|
| return false;
| }
| }
|
This finally got me to my expected behavior. :-)
All this code can be found here:
-
http://anonsvn.jboss.org/repos/jbossas/projects/demos/osgi/
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4148569#...
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&a...