| Summary: | RWTServletContextListener should use context class loader to load configurator class | ||||||
|---|---|---|---|---|---|---|---|
| Product: | [RT] RAP | Reporter: | Henning Blohm <henning.blohm> | ||||
| Component: | RWT | Assignee: | Project Inbox <rap-inbox> | ||||
| Status: | RESOLVED FIXED | QA Contact: | |||||
| Severity: | normal | ||||||
| Priority: | P3 | CC: | fwaibel | ||||
| Version: | unspecified | ||||||
| Target Milestone: | 2.0 M2 | ||||||
| Hardware: | PC | ||||||
| OS: | Linux | ||||||
| Whiteboard: | sr152? | ||||||
| Attachments: |
|
||||||
I agree that the "org.eclipse.rwt.Configurator" init-param can only be used when RWT is not shared / uses the same class loader as the web app.
However, I don't see how consulting the contextClassLoader would help here. The contextClassLoader could only be set in yet another context-listener, or do I miss anything? To my knowledge, it is undefined whether the execution order of context-listeners aligns with the order in which they are delcared, hence the contextClassLoader might not always be set.
In addition to the org.eclipse.rwt.Configurator startup hook, RWT can also be started from an application-defined context-listener. This should solve the class loader issue.
web.xml:
<listener>
<listener-class>org.example.MyServletContextListener</listener-class>
</listener>
MyServletContextListener.java:
private Application application;
public void contextInitialized( ServletContextEvent evt ) {
ServletContext servletContext = evt.getServletContext();
ApplicationConfigurator configurator = new MyApplicationConfigurator();
application = new Application( configurator, servletContext );
application.start();
}
public void contextDestroyed( ServletContextEvent evt ) {
application.stop();
application = null;
}
Please re-open if there are still issues.
Sorry that it took so long to get back to this issue. Running into the same problem with the latest RAP version again but ApplicationConfigurator is no longer it seems. Anyway, the point about the Thread's context class loader is exactly that it is set by the container, i.e. the Web Container in this case, so that the class loading scope of the Web app is available to whatever is called by the Web app. Just as it is was meant to be used when it was introduced sometime in the last century. I would go as far as claiming that any well-behaving framework should fall back to using the thread's context class loader for loading configuration or other extensions. And almost all do. So, for me, the issue still stands, Thanks, Henning The ApplicationConfigurator has been renamed. See http://eclipsesource.com/blogs/2012/05/09/the-new-application-api-in-rap/ Thanks for that pointer! In order to use a shared RAP library, I am using a modified version of RWTServletContextListener now. I applied the same modifications as described in the origina1 post. Works ok it seems. Thanks, Henning Just finished some testing with Virgo Jetty Server 3.5.0.RELEASE and the proposed "common established practice" to use the TCCL to load the ApplicationConfiguration. When deploying a RAP application as webbundle beside the RAP bundles using a Virgo plan we have an almost similar situation as when putting the RAP libraries for example into $CATALINA_HOME/lib. Without delegating to the TCCL the RAP application fails to start: org.eclipse.rap.rwt.internal.util.ClassInstantiationException: Failed to load type: com.eclipsesource.rap.examples.helloworld.HelloWorldConfiguration at org.eclipse.rap.rwt.internal.util.ClassUtil.newInstance(ClassUtil.java:28) Created attachment 220919 [details]
With this patch RWTServletContextListener now tries to load the IEntryPoint and ApplicationConfiguration with the TCCL first.
I just packaged the proposed solution so credits should go to the original author Henning Blohm.
Short summary: In an application container, the RWT library may reside in a shared folder. In this case, the classloader for RWT cannot load classes from a web application. Hence, using the RWT classloader to load an ApplicationConfiguration fails in RWTServletContextListener. Solution: Use the thread's context classloader if available, otherwise fall back on the RWT classloader. The container should ensure that this context classloader is be able to load the application's classes. Applied the attached patch to master, together with a more effective test, in commit 07a625d2fe1790eae53bc15281511e815b95234e. I think we should consider backporting this change to 1.5.2. For 1.5.1 it's too late. I think that the fix is safe to be backported to 1.5.2 too. +1. I've tried to backport this fix to 1.5, but the test from commit 07a625d2fe1790eae53bc15281511e815b95234e and the same test from master constantly failed in 1.5. I don't know if this is a test only problem or additional changes are needed for 1.5. |
Build Identifier: Observed in RAP 1.5.0.20111213: The current code loads the Configurator class as well as the EntryPointRunnerConfigurator using the class loader of the RWTServletContextListener. This requires to have the RAP libraries at the same class loading level as the web application. It is not untypical however to share libraries like RAP across web applications. It is common established practice to use the current's thread context class loader to load customizing application classes from web application frameworks. The web container is required to set the context class loader appropriately. I.e. instead of ClassLoader loader = getClass().getClassLoader(); as today, it should use ClassLoader loader = getClassLoader(); where private ClassLoader getClassLoader() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader==null) { loader = this.getClass().getClassLoader(); } return loader; } Reproducible: Always