tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Adam Jenkins <AdamJenk...@nti.com.au>
Subject NullPointerException 5.5.17 Request.setAttribute(Request.java:1376)
Date Tue, 19 Aug 2008 06:05:00 GMT
Hi All,
I'be been tearing my hair out for a couple of weeks over this one and would appreciate any
assistance anyone could give.  It's a bit of a unique scenario, so it'll take me a little
while to explain:
We have a legacy struts app, forwarding to a jsf app (details of how below) running on tomcat
5.5.17.  99.9% of the time everythign works great, but every now and then (which in users
speak translates to 'I keep on getting', hence we can't ignore it) a NullPointerException
gets thrown from org.apache.catalina.connector.Request.setAttribute.
The flow is:
Third party application ---<<http redirect>>---> JSFBridgeFilter (will explain
later) -----> Struts application ----> JSFBridge--->JSF Application--->Response
back to third party app.
The struts application contains proprietary logic to interface with the third party app, so
we can't get rid of it. Business wants all development internally to be done in JSF, so we
can't get rid of that.
The JSFBridgeFilter instantiates the JSF Context for later use, and wraps the request that
is used to instantiate it in a request wrapper that translates ".do" context paths to ".jsf"
context paths (otherwise all the forms submit back to ".do" after coming from the struts framework.
 It looks like this:
public void doFilter(ServletRequest strutsRequest, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

HttpServletRequest jsfRequest = new StrutsToJsfRequestWrapper((HttpServletRequest)strutsRequest);

JSFBridge.getFacesContext(viewId, (HttpServletRequest)jsfRequest, (HttpServletResponse)response);

chain.doFilter(strutsRequest, response); 

}

...and the StrutsToJsfRequestWrapper contains only one override method (it extends HttpServletRequestWrapper):
@Override

public String getServletPath() {

	String oldPath = super.getServletPath();

	String newPath = null;

	int index = oldPath.lastIndexOf(".do");

	if (index >= 0) {

		newPath = oldPath.substring(0, index + 1) + ".jsf";

	} else {

		newPath = oldPath;

	}

	return newPath;

}

 

Now, you'll notice in the filter that the request wrapper is used to instantiate the faces
context, and the original request passed onto the struts application.  The 'getFacesContext'
method implements some code from the apache myfaces wiki that shows a way to call into the
JSF framework from non jsf artifacts:

 

It looks like this:

 

public class JSFBridge {

 

	private abstract static class InnerFacesContext extends FacesContext {

		protected static void setFacesContextAsCurrentInstance(final FacesContext facesContext)
{

			FacesContext.setCurrentInstance(facesContext);

		}

	}

 

	public static FacesContext getFacesContext(final String viewId, final ServletRequest request,
final ServletResponse response) { 

		FacesContext facesContext = FacesContext.getCurrentInstance();

		if (facesContext != null) { 

			return facesContext;

		} 

		return getNewFacesContext(viewId, request, response);

	}

 

	public static FacesContext getNewFacesContext(final String viewId, final ServletRequest request,
final ServletResponse response) {

		FacesContext facesContext;

		FacesContextFactory contextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);

		LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);

		Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

		facesContext = contextFactory.getFacesContext(((HttpServletRequest)request).getSession().getServletContext(),request,
response, lifecycle);

		InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);

		UIViewRoot view = facesContext.getApplication().getViewHandler().createView(facesContext,
viewId);

		facesContext.setViewRoot(view); 

		return facesContext;

	} 

}

 

And the final piece of the puzzle is a struts action that actually loads the context and forwards
to the appropriate JSF method...which looks like this:

 

public class ForwardToJSFAction extends Action {

	

	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception { 

		JSFMapping viewMapping = (JSFMapping) mapping;

		FacesContext ctx = JSFBridge.getFacesContext(viewMapping.getViewId(), request, response);


		MethodBinding mb = lookupMethodBinding(viewMapping, ctx); 

		navigateToMethodBinding(viewMapping, ctx, mb);

		return null;

	}

 

	private MethodBinding lookupMethodBinding(JSFMapping viewMapping, FacesContext ctx) {

		return ctx.getApplication().createMethodBinding("#{" + viewMapping.getJsfAction() + "}",
new Class[] {});

	}

 

	private void navigateToMethodBinding(JSFMapping viewMapping, FacesContext ctx, MethodBinding
mb) {

		Object obj = mb.invoke(ctx, new Object[] {});

		NavigationHandler navHandler = ctx.getApplication().getNavigationHandler();

		navHandler.handleNavigation(ctx, "#{" + viewMapping.getJsfAction() + "}", (String) obj);

	}

}

 

Now, as odd as all this looks, it actually works quite well in 99.9% of the cases.  We get
a request in struts, which forwards quite nicely onto a JSF method and completes the business
process in JSF.  Every now and then though, we get the following error when we try to invoke
the method binding:

Caused by: java.lang.NullPointerException
	at org.apache.catalina.connector.Request.setAttribute(Request.java:1376)
	at org.apache.catalina.connector.RequestFacade.setAttribute(RequestFacade.java:500)
	at javax.servlet.ServletRequestWrapper.setAttribute(ServletRequestWrapper.java:283)
	at org.apache.myfaces.context.servlet.RequestMap.setAttribute(RequestMap.java:46)
	at org.apache.myfaces.context.servlet.AbstractAttributeMap.put(AbstractAttributeMap.java:104)
	at org.apache.myfaces.el.VariableResolverImpl.resolveVariable(VariableResolverImpl.java:301)
	at org.apache.myfaces.config.LastVariableResolverInChain.resolveVariable(LastVariableResolverInChain.java:42)
	at org.apache.myfaces.el.ValueBindingImpl$ELVariableResolver.resolveVariable(ValueBindingImpl.java:574)
	at org.apache.commons.el.NamedValue.evaluate(NamedValue.java:124)
	at org.apache.myfaces.el.ValueBindingImpl.resolveToBaseAndProperty(ValueBindingImpl.java:455)
	at org.apache.myfaces.el.MethodBindingImpl.resolveToBaseAndProperty(MethodBindingImpl.java:180)
	at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:114)
Diving into Request.java(Line 1376), shows that it's happening on this line:
Object listeners[] = context.getApplicationEventListeners();
So I'm guessing that 'context' is null for some reason.  Since it is so rare, it's almost
impossible to reproduce in a manner consistent enough to actually diagnose and debug it.
Can anyone see, from the above, anything that could shed some light on this?
Cheers
Adam

Number 1 in Truck Insurance

______________________________________________________________________
CAUTION - This message is intended for the addressee named above.

It may contain privileged or confidential information. If you are 
not the intended recipient of this message you must not use, 
copy, distribute or disclose it to anyone other than the addressee.
If you have received this email in error please return the message to the sender by replying
to it and then delete the message from your computer.

Internet e-mails are not necessarily secure. 
National Transport Insurance does not accept responsibility for changes made to this message
after it was sent.
______________________________________________________________________
Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message