Hi,

I'm using Wicket with Spring inside OSGi environment for quite a long
time and during this I had some problems related to class loading. So
below there is a small "proposal" what should be change in Wicket core
to solve these problems.

1. DefaultClassResolver - loading classes
Some time ago (april 2008) I wrote this message in
"java.io.StreamCorruptedException: invalid type code: 29" thread:

"Inside OSGI environment each bundle has its own class loader, so this
could leeds to problem when you try to use in your app a class defined
in another bundle. I've solved this problem by implementing my own
IClassResolver. The default Wicket DefaultClassResolver is a final
class, so we must make a copy of it and make a little change at the
end of resolveClass method.

The default implementation (DefaultClassResolver):

    synchronized (classes)
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null)
        {
            loader = DefaultClassResolver.class.getClassLoader();
        }
        clazz = loader.loadClass(classname);
    }

When there is a ClassLoader attached to the current thread as context
class loader, then Wicket uses it. The problem is under OSGi, that the
related class (with classname) can be located inside another bundle
and can be loaded by another class loader, not this from current
thread. So DefaultClassResolver fails to find this class. The solution
is to try in such situation use the class loader which loads
DefaultClassResolver class (= which loads all Wicket classes). In our
CustomClassResolver this block was changed to something like this:

    synchronized (classes) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            loader = DefaultClassResolver.class.getClassLoader();
            clazz = loader.loadClass(classname);
        } else {
            try {
                clazz = loader.loadClass(classname);
            } catch (ClassNotFoundException e) {
                loader = DefaultClassResolver.class.getClassLoader();
                clazz = loader.loadClass(classname);
            }
        }
    }

Then in your Application class init() method add the following line:
getApplicationSettings().setClassResolver(new CustomClassResolver());

But there is a one assumption, that bundle with Wicket classes (you
probably have a Wicket bundled somehow in your app, don't you? :)),
should have a dynamic import for all classes which can be located in
many different bundles:
DynamicImport-Package: *"

Proposal 1 (change actual method behavior):
Modify resolveClass() method in DefaultClassResolver as I wrote above

Proposal 2 (do not change actual method behavior):
Make DefaultClassResolver extensible (remove final modifier in class
declaration) and define a new method (invent a better name for it ;)):

  protected Class loadClassByClassResolver(final String classname)
throws ClassNotFoundException {
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    if (loader == null)
    {
        loader = DefaultClassResolver.class.getClassLoader();
    }
    return loader.loadClass(classname);
  }

and call it from resolveClass(..) method in synchronized(..) block.
It allows easy extend DefaultClassResolver and implement custom class
resolving by overriding loadClassByClassResolver() method.


2. LazyInitProxyFactory - Spring integration
The first minor thing is a call "Class.forName(type);" in
ProxyReplacement. Why not use IClassResolver here (and maybe in other
places in Wicket)?

The second (major) thing is class loader issue when creating proxy
using Proxy.newProxyInstance(...) (for interfaces). The current code:

                        try
                        {
                                return 
Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                                        new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
                                                        IWriteReplace.class }, 
handler);
                        }
                        catch (IllegalArgumentException e)
                        {
                                /*
                                 * STW: In some clustering environments it 
appears the context
classloader fails to
                                 * load the proxied interface (currently seen 
in BEA WLS 9.x
clusters). If this
                                 * happens, we can try and fall back to the 
classloader (current)
that actually
                                 * loaded this class.
                                 */
                                return 
Proxy.newProxyInstance(LazyInitProxyFactory.class.getClassLoader(),
                                        new Class[] { type, Serializable.class, 
ILazyInitProxy.class,
                                                        IWriteReplace.class }, 
handler);
                        }

Here again is our "friend":
Thread.currentThread().getContextClassLoader() call, which cause all
these problems in OSGi ;).

We want to inject InterfaceA declared as a spring bean in bundle1.
InterfaceA class is visible by our bundle1 (we must specify its
package in MANIFEST using Import-Package), so it is also visible by
current thread when creating this proxy. But the implementation of
InterfaceA (ClassA implements InterfaceA) which will be injected by
this proxy can be declared in other package than InterfaceA and in
another bundle. So ClassA may be not visible by class loader of
bundle1 (eg. this implementation can be provided by OSGI service).
Then invoking some method from InterfaceA on proxy to ClassA can cause
NoClassDefFoundError because some classes which are used by ClassA are
not visible by this proxy. In bundle1 we do not know which classes
they are so we cannot import their packages.

Proposal 1 (change actual method behavior):
The one solution is to always use
"LazyInitProxyFactory.class.getClassLoader()" with the assumption that
bundle with Wicket has in MANIFEST:
DynamicImport-Package: *
so all classes are visible by its class loader (this
"DynamicImport-Package: *" already exists in Wicket manifest files).
So it is this second call above (from patch for BEA), but I don't know
is it a good solution outside OSGi (?).

Proposal 2 (do not change actual method behavior):
Make LazyInitProxyFactory customizable to allows using a different
method to create proxy inside OSGi.


What do you think about such changes or maybe you have a better
solution for these problems?

--
Best regards,
Daniel

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to