Fine, I will import those two packages. FooBean is an impl so I am using cglib to proxy it. cglib adds net.cglib.proxy.Factory to the proxied interface set so I need to add that import as well. I can just about live with those, although explaining them to users will be a pain. Next, I want to hide all of the proxy implementation classes inside another bundle, so I write a service that provides approximately the same set of functionality as org.springframework.aop.framework.ProxyFactory. Easy as pie.
You would think.
The use of Factory is the first problem. When I run my little service I start getting NoClassDefFoundErrors for net.sf.cglib.core.Signature. This package is imported by my proxy service provider bundle, and the provider bundle's classloader is the one I am passing to cglib's Enhancer for proxy generation. So why can't the bundle classloader for Enhancer see Signature?
Well the bundle classloader for Enhancer turns out to be the client bundle because Enhancer and Factory are in the same package. So the fact that the client needs to see Factory also means that it imports Enhancer (rather than my provider bundle) and the whole thing unravels from there.
Spring has the same problem. The cglib entry point is defined by org.springframework.aop.framework.Cglib2AopProxy. Does that package look familiar somehow? Right, its the same package as Advised - needed by the proxy. Doh!
So now my options are:
- Change Spring and cglib - bit of a backwards compatibility problem there.
- Start massaging classloaders for individual classes - not exactly the OSGi way
- Chain the required packages into the client classloader - again subverting the OSGi class space protection model
- Create some entirely new classloader for the proxy - not clear to me that this will actually work
- Try using Enhancer.setUseFactory()
- Import the packages into my client bundle, *sigh*
Update
I have actually now used a modified version of (3) above, and it works quite nicely. The theory goes like this - the client bundle knows about classes that need augmenting, and contains the appropriate imports for these. The provider bundle knows about proxying and contains the appropriate imports for the mechanics of this. So what we actually need is a classloader that synthesizes these two. Constructing a simple delegating classloader which first tries the client classloader and then the provider ends up working really nicely. Even better I would argue that this does not subvert the OSGi model, the reasoning is that this delegate classloader is only used for proxying - no client classes are magically loaded by it - and the class space represented by the union of the two classloaders involved is still bounded by their respective imports. No need to use dynamic imports or other ugly hacks. Furthermore the class space boundary is neatly delineated by the service definition. Only classes on the client-side need to be imported and no details of the service implementation are exposed by the required imports.

No comments:
Post a Comment