Thursday, December 11, 2008

Classloading of GridSphere

JSR-168 portlet applications developed for GridSphere 3.1 has a $CATALINA_HOME/webapps/$PORTLET_WEBAPP/WEB-INF/lib/gridsphere-portletservlet-3.1.jar, which contains a single org.gridsphere.provider.portlet.jsr.PortletServlet class. Originally I thought this class is intended to override the class with the same name in $CATALINA_HOME/shared/lib, according to the rules of Tomcat class loading, by some "clever" Java programmer. Later I found out this class is exactly the same class as in $CATALINA_HOME/shared/lib. So though it replaces the one in $CATALINA_HOME/shared/lib, but it changes nothing. In fact, it does change one thing: the defining class loader.

So if removing $CATALINA_HOME/webapps/$PORTLET_WEBAPP/WEB-INF/lib/gridsphere-portletservlet-3.1.jar, running GridSphere will raise such an exception:

16825:ERROR:(PortletServlet.java:loadJSRPortletWebapp:102)
Unable to create jsr portlet instance: org.gridsphere.gsexamples.portlets.HelloWorld

java.lang.ClassNotFoundException: org.gridsphere.gsexamples.portlets.HelloWorld
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:164)
at org.gridsphere.provider.portlet.jsr.PortletServlet.loadJSRPortletWebapp(PortletServlet.java:87)
at org.gridsphere.provider.portlet.jsr.PortletServlet.service(PortletServlet.java:182)

So basically it complains it can not find the portlet class. Let me explain why.

First org.gridsphere.provider.portlet.jsr.PortletServlet is defined in the portlet web.xml as the generic servlet. Not surprisingly, the portlet application is just a web servlet application in GridSphere.

Second, on line 87 of PortletServlet, there is
Portlet portletInstance = (Portlet) Class.forName(portletClass).newInstance();

It says to use the defining class loader of the current class, i.e., PortletServlet, to load the portlet class. So if PortletServlet is inside $CATALINA_HOME/shared/lib, then there is no way to find the portlet class. PortletServlet has to be put inside $CATALINA_HOME/webapps/$PORTLET_WEBAPP/WEB-INF/lib/.

I am not very convinced that this is a very elegant solution towards portlet classloading. Somehow I have a feeling that it might be better if the portal and portlets stay in the same web application, and the webapp classloader acts as the parent of portlet classloaders. Thus, at least, classloading of portlets is not affected by Tomcat classloading rules.

No comments: