Hello,
Here's some background on the issue.
If the action on the button is not specified correctly i.e. in our case the wrong action is "/submit.xhtml". This does not get resolved to anything during the Invoke App phase's handling of the navigation. In this case, we need to see a good error message rather than the NPE which is currently being thrown.
During the Invoke Application phase, NavigationHandlerImpl.getViewId() correctly adds a message as follows in the server.log :
#|2010-09-07T10:08:17.947-0700|FINE|glassfish3.1|javax.enterprise.resource.webcontainer.jsf.context|_ThreadID=19;_ThreadName=Thread-1;ClassName=com.sun.faces.context.FacesContextImpl;MethodName=addMessage;|Adding Message[sourceId=<<NONE>>,summary=Unable to find matching navigation case with from-view-id '/composite/simpleCompositeComponentUsingPage.xhtml' for action '#{hello.getNextAction}' with outcome '/submit.xhtml')|#]
But since this phase cannot come up with the right navigation rule, the render response phase continues to again build the same old view. During this buildView() call, the ComponentTagHandlerDelegateImpl creates a new CompositeComponentTagHandler instance but the "cc" object for this instance is null (the ComponentTagHandlerDelegateImpl created during Restore View phase is discarded. That one had the "cc" set). Since "cc" is null, we see the NPE. Now, the ComponentTagHandlerDelegateImpl is able to find the UINamingContainer instance for cc via findChild(). But it does not have any way of setting it in the CompositeComponentTagHandler. Hence the fix as attached. With the fix, after we hit the button, we come back to the same old view but with the error message in it.
Ed : The fix works in both cases of partial state saving set to true or false.
Thanks
Sheetal
https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1696
SECTION: Modified Files
----------------------------
M jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/ComponentTagHandlerDelegateImpl.java
M jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java
M jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java
SECTION: Diffs
----------------------------
Index: jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/ComponentTagHandlerDelegateImpl.java
===================================================================
--- jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/ComponentTagHandlerDelegateImpl.java (revision 8594)
+++ jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/ComponentTagHandlerDelegateImpl.java (working copy)
@@ -174,6 +174,8 @@
if (c instanceof NamingContainer) {
oldUnique = ComponentSupport.setNeedUniqueIds(ctx, false);
setUniqueIds = true;
+ if (createCompositeComponentDelegate != null)
+ createCompositeComponentDelegate.setCompositeComponent(c);
}
try {
// first allow c to get populated
@@ -568,6 +570,7 @@
interface CreateComponentDelegate {
public UIComponent createComponent(FaceletContext ctx);
+ public void setCompositeComponent(UIComponent cc);
}
Index: jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java
===================================================================
--- jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java (revision 8594)
+++ jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java (working copy)
@@ -210,6 +210,10 @@
}
+ public void setCompositeComponent(UIComponent cc) {
+ if (this.cc == null)
+ this.cc = cc;
+ }
/**
* Specialized implementation to prevent caching of the MetaRuleset when
Index: jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java
===================================================================
--- jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java (revision 8594)
+++ jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java (working copy)
@@ -970,6 +970,19 @@
assertTrue(text.matches("(?s).*collapsable\\s=\\strue.*"));
}
+ //issue 1696
+ public void testForNoNPE() throws Exception {
+ HtmlPage page = getPage("/faces/composite/simpleCompositeComponentUsingPage.xhtml");
+ List list = getAllElementsOfGivenClass(page, null,
+ HtmlSubmitInput.class);
+ HtmlSubmitInput button = (HtmlSubmitInput) list.get(0);
+ page = (HtmlPage) button.click();
+ String pageAsText = page.asText();
+ assertTrue(pageAsText.contains("Unable to find matching navigation case with from-view-id " +
+ "'/composite/simpleCompositeComponentUsingPage.xhtml' for action '#{hello.getNextAction}' " +
+ "with outcome '/submit.xhtml'"));
+ }
+
// --------------------------------------------------------- Private Methods