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]