A strange NullPointerException inside Beehive

This entry is a small analysis of a stange NullPointerException that we had on our production servers when the server started and the first client tried to connect. I have no idea what provoked this exception but maybe someone will have a hint.

Environment

Code biopsy

This is the stack trace:

java.lang.NullPointerException
at org.apache.beehive.netui.pageflow.AutoRegisterActionServlet.process(AutoRegisterActionServlet.java:622)
at org.apache.beehive.netui.pageflow.PageFlowActionServlet.process(PageFlowActionServlet.java:158)
at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
...

The code of the AutoRegisterActionServlet.java around the line 622 is:
//
// Try to select the appropriate Struts module and delegate to its RequestProcessor.
//
ModuleConfig moduleConfig = InternalUtils.selectModule( modulePath, request, servletContext );
// If this module came from an abstract page flow controller class, send an error.
ControllerConfig cc = moduleConfig.getControllerConfig();//622 line
if (cc instanceof PageFlowControllerConfig && ((PageFlowControllerConfig) cc).isAbstract()) {
InternalUtils.sendDevTimeError( "PageFlow_AbstractPageFlow", null, HttpServletResponse.SC_NOT_FOUND,
request, response, servletContext,
new Object[]{ modulePath } );
return;
}

The most logical conclusion is that the moduleConfig variable is null and moduleConfig.getControllerConfig will throw the NullPointerException.
Now, let’s take a look at how the moduleConfig variable is computed; the code of the InternalUtils#selectModule is:

/**
* Set the given Struts module in the request, and expose its set of MessageResources as request attributes.
*
* @param prefix the prefix of the desired module.
* @param request the current HttpServletRequest.
* @param servletContext the current ServletContext.
* @return the selected ModuleConfig, or
null if there is none for the given module prefix.
*/
public static ModuleConfig selectModule( String prefix, HttpServletRequest request, ServletContext servletContext )
{
ModuleConfig moduleConfig = getModuleConfig( prefix, servletContext );
if ( moduleConfig == null )
{
request.removeAttribute( Globals.MODULE_KEY );
return null;
}
// If this module came from an abstract page flow controller class, don’t select it.
ControllerConfig cc = moduleConfig.getControllerConfig();
if (cc instanceof PageFlowControllerConfig && ((PageFlowControllerConfig) cc).isAbstract()) {
return moduleConfig;
}
// Just return it if it’s already registered.
if ( request.getAttribute( Globals.MODULE_KEY ) == moduleConfig ) return moduleConfig;
request.setAttribute( Globals.MODULE_KEY, moduleConfig );
MessageResourcesConfig[] mrConfig = moduleConfig.findMessageResourcesConfigs();
Object formBean = unwrapFormBean( getCurrentActionForm( request ) );
for ( int i = 0; i < mrConfig.length; i++ )
{
String key = mrConfig[i].getKey();
MessageResources resources = ( MessageResources ) servletContext.getAttribute( key + prefix );
if ( resources != null )
{
if ( ! ( resources instanceof ExpressionAwareMessageResources ) )
{
resources = new ExpressionAwareMessageResources( resources, formBean, request, servletContext );
}
request.setAttribute( key, resources );
}
else
{
request.removeAttribute( key );
}
}
return moduleConfig;
}

The first problem that we see is that the InternalUtils#selectModule method can return a null but the AutoRegisterActionServlet#process never verifies this case.
As we can see the moduleConfig variable is computed by the getModuleConfig variable and is never nullified afterward inside the method, so we go deeper into the code to see how the getModuleConfig method computes the moduleConfig variable.
The code of the InternalUtils#getModuleConfig is :

/**
* Get the Struts ModuleConfig for the given module path.
*/
public static ModuleConfig getModuleConfig( String modulePath, ServletContext context )
{
return ( ModuleConfig ) context.getAttribute( Globals.MODULE_KEY + modulePath );
}

The moduleConfig object is retrieved from the ServletContext and if no attribute exists under the name Globals.MODULE_KEY + modulePath then a null object is returned (see ServletContext#getAttribute javadoc). The full name of attribute will be org.apache.struts.action.MODULEmodulePath (to see what can be the value of the modulePath variable, go to the next section).

Struts modules and Beehive

Now, let’s take a look at the meaning of the org.apache.struts.config.ModuleConfig interface. The ModuleConfig is the API representation of a Struts-module(Beehive uses under the hood the Struts framework).
A Struts module is a Struts configuration file and a set of corresponding actions, form beans, and Web pages. A Struts application consists of one module by default but may contain more than one module.
The Beehive framework will create dynamically a Struts module for every executed page flow controller.
For example when the https://&#8230;./login url is called, the login.LoginController class will be instantiated and used to treat the request and Beehive will automatically create an instance of org.apache.struts.config.ModuleConfig and store it into the ServletContext under the name org.apache.struts.action.MODULE/login. Normally, the Struts module is created just before the first use of the page flow controller.
My supposition is that the org.apache.struts.config.ModuleConfig objects are singletons (one instance by controller) and are created and added by Beehive into the ServletContext only one time (first time when a controller is used).

Some kind of conclusion

So, the source of the NullPointerException is due to a missing Struts module (which, of course should never happen). Maybe the Beehive had a problem creating the Struts modules or maybe some “mysterious” process just removed all the objects from the ServletContext.
It is possible to monitor the ServletContext “traffic” (what objects are added/removed/updated) by implementing a ServletContextAttributeListener; the problem of this solution is his intrusiveness (a new class should be added to the web application and the application must be redeployed).

Comments are closed.

%d bloggers like this: