Websphere 6.0.2.x has a mighty annoying classloader bug which prevents the JetspeedServlet to initialize.
When Websphere bootstraps a web application, it uses not the (properly) created web application classloader (configured with PARENT_LAST)
but its own com.ibm.ws.bootstrap.ExtClassLoader (which defaults to PARENT_FIRST) for class initializing (<clinit>) the servlet.
Note: the correct web application classloader is used to initialize the servlet itself (method init(ServletConfig)), but not the static
class initialization (<clinit>).
And, starting (again) the portal through the WAS admin console, exposes a similar classloader bug. This time, not the bootstrap.ExtClassLoader
is set as context ClassLoader but the console app classloader during class initialization!
We currently have two static initialized Commons Logging Log members defined in the JetspeedServlet.
Furthermore, through the jetspeed-webapp-logging component, we setup a custom Log implementation (IsolatedLog4JLogger) which Commons Logging
picks up from the commons-logging.properties resource file (contained within the jetpeed-webapp-logging jar).
But, WebSphere 6.0.2.x (and maybe even 6.0 but I haven't checked that one) also provides such a commons-logging.properties resource contained within
the ws-commons-logging.jar from the globally shared lib directory.
In it, it defines:
Note: this wasn't the case yet in WAS 5!
Now, because of the above described classloader bug (e.g. a PARENT_FIRST bootstrap classloader) during static initialization of the JetspeedServlet,
Commons Logging will not see the commons-logging.properties resource as provided by our jetspeed-webapp-logging jar but load it from the global
org.apache.commons.logging.LogConfigurationException: Class org.apache.commons.logging.impl.Jdk14Logger does not implement Log
... 25 more
A quick workaround would be removing the Log definition from the commons-logging.properties in ws-commons-logging.jar.
Better yet: IBM should fix this classloader bug!
But luckily (in this situation) the JetspeedServlet can be modified such that this problem won't occurr:
I'm going to move the initialization of the static Log instances in JetspeedServlet to the init(ServletConfig).
When that method is invoked by WAS, it is done with the proper context ClassLoader and Log initialization uses our IsolatedLog4JLogger again.
And then, after I fixed this, another classloader problem popped up!
JetspeedServlet also implements HttpSessionListener which it uses for closure of PortalStatistics logging when a user logs out.
Now, it turns out WAS 6.0.2.x calls the Servlet.destroy() method before calling sessionDestroyed(HttpSessionEvent) for existing sessions!
It seemingly does so (again) with an incorrect context classloader.
In our JetspeedServlet.sessionDestroyed() implementation we try to access the PortalStatistics component from the SpringComponentManager.
But, as the JetspeedEngine (and thus the ComponentManager) have already shutdown because JetspeedServlet.destroy() has already been called,
Spring is going to reload its configuration. And, as it wants to logs some info messages doing that, its going to (re)initialize Commons Logging
again too (which also already was shutdown through the Log4JConfigurator ContextListener).
And there it is again, same exception as shown above: Jdk14Logger does not implement Log
So, I've also modified the JetspeedServlet.sessionDestroyed() method which will skip PortalStatistics closure logging when the destroy() method
has been called already.
A last remark for anyone wanting to deploy a Jetspeed based portal on WAS and has dependencies on xerces, xalan and xml-apis for its
portlet applications (just as Jetspeed itself has).
You need to move these three jars (as well as the other already required shared libs) inside the ear besides the war files, define a Class-Path
entry in each war file META-INF/MANIFEST.MF referencing these "shared" libs, and remove these three jars from your war WEB-INF/lib folder.
This is standard shared library ear file packaging, check the J2EE docs for further information.
And finally: make sure to change the default WAS Class Loader Mode to PARENT_LAST for your portal ear and each of its contained (portlet) web applications.