Support for dynamic imports with wildcards
thomas.diesler May 14, 2010 4:52 AMRelates to
https://jira.jboss.org/jira/browse/JBOSGI-323
https://jira.jboss.org/jira/browse/JBCL-131
I added a ClassNotFoundHandler to the OSGiClassLoaderPolicy which kicks in if the policy has a PackageRequirement defined that is dynamic, uses wildcards and matches the requested resource.
// Iterate over the package requirements and check if // the requested class matches any of the filters for (Requirement req : osgiModule.getRequirements()) { if (req instanceof PackageRequirement) { PackageRequirement preq = (PackageRequirement)req; if (preq.isDynamic() && preq.isWildcard()) { ClassFilter filter = preq.toClassFilter(); if (filter.matchesPackageName(packageName)) { matchingReq = preq; break; } } } } if (matchingReq == null) return false;
If there is a matching requirement the hander iterates over all INSTALLED, RESOLVED, ACTIVE bundles to find a potential exporter for the class that could not be loaded. If an exporter could be found, the handler creates a LoaderDelegate and adds it to list of delegates defined on the policy.
DeploymentUnit unit = depBundle.getDeploymentUnit(); OSGiModule module = (OSGiModule)unit.getAttachment(Module.class); OSGiClassLoaderPolicy policy = (OSGiClassLoaderPolicy)module.getPolicy(); FilteredDelegateLoader delegate = new FilteredDelegateLoader(policy, matchingReq.toClassFilter()); delegate.setImportType(ImportType.AFTER); if (delegate.getResource(resourceName) != null) { List<DelegateLoader> delegates = (List<DelegateLoader>)getDelegates(); if (delegates == null) { delegates = new CopyOnWriteArrayList<DelegateLoader>(); setDelegates(delegates); } delegates.add(delegate); return true; }
Additionally I added code to the BaseClassLoaderDomain that finds the loader that the handler had just added.
// Process potential delegates that were added lazily to the policy // This could for example be done in a {@link ClassNotFoundHandler} List<? extends DelegateLoader> policyDelegates = info.getPolicy().getDelegates(); if (policyDelegates != null && policyDelegates.isEmpty() == false) { for (DelegateLoader delegate : policyDelegates) { if (delegates.contains(delegate) || delegate.getImportType() != type) continue; if (trace) log.trace(this + " trying to load " + name + " from delegate added lazily to policy " + delegate + " for " + info.getClassLoader()); if (delegate.getResource(name) != null) { info.cacheLoader(name, delegate); return delegate; } } }
Have a look at this changeset http://fisheye.jboss.org/changelog/JBossAS/projects/jboss-cl/branches/tdi/jbosgi323?cs=104780
which I havn't merged to trunk yet.
This approach fixes all DynamicImport-Package test cases that we have in the framework both for explicit and wildcard packages.
However I believe, this is somewhat of a hack and should be done differently to maintain consistency for module dependencies. The exporter for a DynamicImport-Package should probably be added to the list of dependencies like an exporter for any other ImportPackage. Only that this can happen after the Module is already resolved.
The additional code in BaseClassLoaderDomain was needed because ClassLoaderInformation that it works of is not accessible for clients. The general question is, what should a ClassNotFoundHandler do to make loadClass succeed? AFAICS, it must modify or replace the ClassLoaderInformation somehow, but I could not figure out how that would be done.