[ https://issues.apache.org/jira/browse/WICKET-3737?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13041053#comment-13041053 ]
Daniël van 't Ooster commented on WICKET-3737: ---------------------------------------------- I was not expecting a fix in the 1.4 range, it was only to demonstrate the concept. I will start working on a set of bundles and patch based on 1.5 (trunk is also 1.5?) and attach them to this ticket. > Remove DynamicImport-Package header from Wicket bundles > ------------------------------------------------------- > > Key: WICKET-3737 > URL: https://issues.apache.org/jira/browse/WICKET-3737 > Project: Wicket > Issue Type: Improvement > Components: wicket-core > Affects Versions: 1.4.17 > Reporter: Daniël van 't Ooster > Assignee: Igor Vaynberg > Labels: osgi > Attachments: classloading.patch > > > Hi, > Wicket can be used in an OSGi container, out of the box it seems to work > quite well. However, we experience some problems because of having multiple > bundles which depend on Wicket. > Problem: bundle refreshes caused by DynamicImport-Package > All Wicket bundles have a DynamicImport-Package: * entry in their manifests. > This makes class loading easy, because the class loaders of the Wicket > bundles have access to any exported packages from all bundles. This approach > has one drawback; the Wicket bundles become implicitly dependent of all > bundles which are used at least one time by a Wicket class loader. According > to the OSGi specification, when a bundle is refreshed, all bundles which > (explicitly or implicitly) depend on that bundle will also be refreshed. > Example case: there is an OSGi container with some Wicket bundles loaded, and > 2 bundles (bundleA and bundleB), both contain some Wicket components, so they > import Wicket packages, they do not import packages from each other. Now we > refresh BundleA, we expect only a refresh of only bundleA. In practice all > Wicket bundles, bundleA and bundleB are refreshed. This is caused by the > DynamicImport-Package, which makes the Wicket bundles implicitly import > exported packages from bundleA and bundleB. When bundleA is refreshed, it > will refresh also the Wicket bundles. Because of bundleB depends on Wicket, > bundleB will also be refreshed. In a small project with a few bundles, this > may not be a problem, but it can become a problem when the projects gets > larger and you refresh bundles frequently (e.g. when using an OSGi container > during development). > Solution: delegate class loading to another bundle > While deserializing components, Wicket uses its IClassResolver implementation > to load the classes. The DefaultClassResolver uses the thread's context class > loader (TCCL) and the classloader of the wicket core bundle (with the > DynamicImport-Package). My idea is to remove the DynamicImport-Package header > from all Wicket bundles and delegate class loading to another bundle. This > bundle exports its OsgiClassResolver (implements IClassResolver) via the OSGi > service registry (or a service and reference of Spring DM / Blueprint). This > bundle will refresh when bundleA or bundleB is refreshed. Because of there > are no bundles which depend on classes from the classResolver bundle, a > refresh will not refresh other bundles and will be fast. > Issues: A few class loading issues > There are a few spots where we experienced class loading issues. One of the > problems is a missing implementation of resolveProxyClass in > ObjectInputStream subclasses. Some work is done to solve class loading > issues, e.g. by overriding the resolveClass method, but this works for normal > classes, proxy classes are handled differently. This is a problem on 2 > locations in org.apache.wicket.util.lang.Objects and in > org.apache.wicket.util.io.IObjectStreamFactory. Last known issue is in > org.apache.wicket.proxy.LazyInitProxyFactory (wicket-ioc), but this one is > more complicated. To be able to create a new proxy instance, it needs a > classLoader, which can access all interfaces used for the proxy. In a bundle > with a DynamicImport, it is safe to pass the classloader of any class in that > bundle, but when the DynamicImport-Package is removed, not all classes are > visible, and it will throw an exception when one of the classes is not > visible. To solve this, I had to extend the IClassResolver interface with one > method: getClassLoader(); this classLoader is used when generating the proxy. > So summarized, the complete solution (based on Spring Dynamic Modules) looks > like: > // applicationContext.xml: > ... > <osgi:reference id="classResolver" > interface="org.apache.wicket.application.IClassResolver" /> > ... > <bean id="wicketApplication" class="com.company.WicketApplication" > p:classResolver-ref="classResolver" > ... > /> > ... > // set up application > public class WicketApplication extends Application { > private IClassResolver classResolver; > public void setClassResolver(IClassResolver classResolver) { > this.classResolver = classResolver; > } > @Override > public void init() { > super.init(); > IApplicationSettings settings = getApplicationSettings(); > settings.setClassResolver(this.classResolver); > } > } > // ClassResolver bundle: > <bean id="classResolver" class="com.company.osgi.OSGiClassResolver" /> > <osgi:service ref="classResolver"> > <osgi:interfaces> > <value>org.apache.wicket.application.IClassResolver</value> > </osgi:interfaces> > </osgi:service> > public class OSGiClassResolver extends > org.apache.wicket.application.DefaultClassResolver { > @Override > public ClassLoader getClassLoader() { > return OSGiClassResolver.class.getClassLoader(); > } > } > What do you think about this approach? Attached patch is created based on > 1.4.10, seems to be compatible with versions up to 1.4.16. > best regards, > Daniël -- This message is automatically generated by JIRA. For more information on JIRA, see: http://www.atlassian.com/software/jira