1 2 3 Previous Next 44 Replies Latest reply on Feb 12, 2009 1:48 PM by ssilvert

    [IceFaces] NullPointerException when accessing JSFServerSess

    elgwappo

      Hi,

      I'm currently evaluating JSFUnit for unit testing our JSF / IceFaces applications (testing on Tomcat 5.5). I've tried to implement a simple unit test (as the one described in JSFUnit's 'getting started' guide, but every time the JSFServerSession is accessed to retrieve the current view, a NullPointerException is thrown (see code below).


       public void testInitialPage() throws IOException
       {
       JSFSession jsfSession = new JSFSession("/index.jsp");
       JSFClientSession client = jsfSession.getJSFClientSession();
       JSFServerSession server = jsfSession.getJSFServerSession();
      
       assertEquals("/index.jsp", server.getCurrentViewID());
      


      Stacktrace:
      null
      
      java.lang.NullPointerException
      at org.jboss.jsfunit.jsfsession.JSFServerSession.getCurrentViewID(JSFServerSession.java:69)
      at com.testapp.ui.backingbean.EntryPageBean2Test.testInitialPage(EntryPageBean2Test.java:40)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
      at org.apache.cactus.internal.AbstractCactusTestCase.runBareServer(AbstractCactusTestCase.java:153)
      at org.apache.cactus.internal.server.AbstractWebTestCaller.doTest(AbstractWebTestCaller.java:119)
      at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody0(AbstractWebTestController.java:93)
      at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody1$advice(AbstractWebTestController.java:224)
      at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest(AbstractWebTestController.java)
      at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody2(ServletTestRedirector.java:101)
      at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody3$advice(ServletTestRedirector.java:224)
      at org.apache.cactus.server.ServletTestRedirector.doPost(ServletTestRedirector.java)
      at org.jboss.jsfunit.framework.JSFUnitServletRedirector.doPost(JSFUnitServletRedirector.java:42)
      at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody0(ServletTestRedirector.java:72)
      at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody1$advice(ServletTestRedirector.java:224)
      at org.apache.cactus.server.ServletTestRedirector.doGet(ServletTestRedirector.java)
      at org.jboss.jsfunit.framework.JSFUnitServletRedirector.doGet(JSFUnitServletRedirector.java:48)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
      at org.jboss.jsfunit.framework.JSFUnitFilter.doFilter(JSFUnitFilter.java:122)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
      at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:874)
      at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
      at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
      at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
      at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
      at java.lang.Thread.run(Unknown Source)
      

      Is this caused by the fact that IceFaces replaces JSF's FacesServlet with it's own custom servlet and thus making IceFaces not suitable for testing with JSFUnit ? Or is there a simple solution to solve this problem ?


      Regards

        • 1. Re: [IceFaces] NullPointerException when accessing JSFServer
          ssilvert

          I think it's something simple. Unless you are using path-mapping for the FacesServlet (or IceFacesServlet), you will need to start like this:

          JSFSession jsfSession = new JSFSession("/index.jsf");


          The URL that you pass to JSFSession must map to the FacesServlet. So it should be /index.jsf or /index.faces or whatever you specified in your web.xml.

          Stan

          • 2. Re: [IceFaces] NullPointerException when accessing JSFServer
            elgwappo

            Thanks for the quick reply, Stan.

            I've tried the proposed solution, but it doesn't work unfortunately...

            As far as I understand IceFaces, I believe that requests are not passed through the default FacesServlet, but directly to IceFaces' specific PersistentFacesServlet. In fact, I can remove the entire mapping to the default FacesServlet in the web.xml and the webapp still works.

            The strange thing is, that when I inspect the JSFClientSession, I can see that the correct page is retrieved (with the correct contents). But when I inspect the JSFServerSession, the object itself is created, but the internal FacesContext field is null...

            • 3. Re: [IceFaces] NullPointerException when accessing JSFServer
              ssilvert

              Can you post your web.xml? If IceFaces is replacing the FacesServlet then you need to pass in whatever URL maps to the IceFacesServlet.

              Stan

              • 4. Re: [IceFaces] NullPointerException when accessing JSFServer
                ssilvert

                Also, if you are fronting your JSF application with a plain JSP file that redirects to JSF then you should not try to go to that JSP. You should go to whereever your redirect points to.

                • 5. Re: [IceFaces] NullPointerException when accessing JSFServer
                  elgwappo

                  Here it is....

                  <?xml version="1.0" encoding="UTF-8"?>
                  <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
                  <web-app id="WebApp_ID">
                   <display-name>CONV</display-name>
                  
                   <context-param>
                   <param-name>javax.faces.CONFIG_FILES</param-name>
                   <param-value>/WEB-INF/faces-config-entrypage.xml</param-value>
                   <description>
                   Comma separated list of URIs of (additional) faces config files.
                   (e.g. /WEB-INF/my-config.xml) See JSF 1.0 PRD2, 10.3.2 Attention:
                   You do not need to put /WEB-INF/faces-config.xml in here.
                   </description>
                   </context-param>
                  
                   <context-param>
                   <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
                   <param-value>.xhtml</param-value>
                   </context-param>
                   <context-param>
                   <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
                   <param-value>server</param-value>
                   </context-param>
                   <context-param>
                   <param-name>facelets.LIBRARIES</param-name>
                   <param-value>
                   /WEB-INF/includes/components/ajt.taglib.xml;/WEB-INF/includes/components/tomahawk.taglib.xml
                   </param-value>
                   </context-param>
                   <context-param>
                   <param-name>facelets.DEVELOPMENT</param-name>
                   <param-value>true</param-value>
                   </context-param>
                   <context-param>
                   <param-name>com.sun.faces.validateXml</param-name>
                   <param-value>true</param-value>
                   <description>
                   Set this flag to true, if you want the JavaServer Faces Reference
                   Implementation to validate the XML in your faces-config.xml
                   resources against the DTD. Default value is false.
                   </description>
                   </context-param>
                   <context-param>
                   <param-name>com.sun.faces.verifyObjects</param-name>
                   <param-value>true</param-value>
                   <description>
                   Set this flag to true, if you want the JavaServer Faces Reference
                   Implementation to verify that all of the application objects you
                   have configured (components, converters, renderers, and
                   validators) can be successfully created. Default value is false.
                   </description>
                   </context-param>
                   <context-param>
                   <param-name>com.icesoft.faces.concurrentDOMViews</param-name>
                   <param-value>false</param-value>
                   <description>
                   To allow multiple windows for a single application. See Concurrent
                   DOM Views in Chapter 3. of Developer Guide.
                   </description>
                   </context-param>
                   <context-param>
                   <param-name>com.icesoft.faces.synchronousUpdate</param-name>
                   <param-value>false</param-value>
                   <description>
                   Turn on/off application-wide synchronous or asynchronous updates.
                   See Synchronous and Asynchronous Updates in Chapter 3. of
                   Developer Guide.
                   </description>
                   </context-param>
                   <context-param>
                   <param-name>com.icesoft.faces.uploadDirectory</param-name>
                   <param-value>upload</param-value>
                   </context-param>
                   <context-param>
                   <param-name>com.icesoft.faces.uploadMaxFileSize</param-name>
                   <param-value>4048576</param-value>
                   </context-param>
                   <listener>
                   <listener-class>
                   com.icesoft.faces.util.event.servlet.ContextEventRepeater
                   </listener-class>
                   </listener>
                  
                   <servlet>
                   <servlet-name>Faces Servlet</servlet-name>
                   <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
                   <load-on-startup>1</load-on-startup>
                   </servlet>
                   <servlet>
                   <servlet-name>Persistent Faces Servlet</servlet-name>
                   <servlet-class>
                   com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet
                   </servlet-class>
                   <load-on-startup>1</load-on-startup>
                   </servlet>
                   <servlet>
                   <servlet-name>Blocking Servlet</servlet-name>
                   <servlet-class>
                   com.icesoft.faces.webapp.xmlhttp.BlockingServlet
                   </servlet-class>
                   <load-on-startup>1</load-on-startup>
                   </servlet>
                   <servlet>
                   <servlet-name>uploadServlet</servlet-name>
                   <servlet-class>
                   com.icesoft.faces.component.inputfile.FileUploadServlet
                   </servlet-class>
                   <load-on-startup>1</load-on-startup>
                   </servlet>
                  
                   <servlet-mapping>
                   <servlet-name>Persistent Faces Servlet</servlet-name>
                   <url-pattern>*.jspx</url-pattern>
                   </servlet-mapping>
                   <servlet-mapping>
                   <servlet-name>Persistent Faces Servlet</servlet-name>
                   <url-pattern>*.iface</url-pattern>
                   </servlet-mapping>
                   <servlet-mapping>
                   <servlet-name>Persistent Faces Servlet</servlet-name>
                   <url-pattern>/xmlhttp/*</url-pattern>
                   </servlet-mapping>
                   <servlet-mapping>
                   <servlet-name>Persistent Faces Servlet</servlet-name>
                   <url-pattern>/faces/*</url-pattern>
                   </servlet-mapping>
                   <servlet-mapping>
                   <servlet-name>Blocking Servlet</servlet-name>
                   <url-pattern>/block/*</url-pattern>
                   </servlet-mapping>
                   <servlet-mapping>
                   <servlet-name>uploadServlet</servlet-name>
                   <url-pattern>/uploadHtml</url-pattern>
                   </servlet-mapping>
                   <welcome-file-list>
                   <welcome-file>index.html</welcome-file>
                   <welcome-file>index.htm</welcome-file>
                   <welcome-file>index.jsp</welcome-file>
                   <welcome-file>default.html</welcome-file>
                   <welcome-file>default.htm</welcome-file>
                   <welcome-file>default.jsp</welcome-file>
                   </welcome-file-list>
                  
                  
                   <filter>
                   <filter-name>JSFUnitFilter</filter-name>
                   <filter-class>org.jboss.jsfunit.framework.JSFUnitFilter</filter-class>
                   </filter>
                  
                   <filter-mapping>
                   <filter-name>JSFUnitFilter</filter-name>
                   <servlet-name>ServletTestRunner</servlet-name>
                   </filter-mapping>
                  
                   <filter-mapping>
                   <filter-name>JSFUnitFilter</filter-name>
                   <servlet-name>ServletRedirector</servlet-name>
                   </filter-mapping>
                  
                   <servlet>
                   <servlet-name>ServletRedirector</servlet-name>
                   <servlet-class>org.jboss.jsfunit.framework.JSFUnitServletRedirector</servlet-class>
                   </servlet>
                  
                   <servlet>
                   <servlet-name>ServletTestRunner</servlet-name>
                   <servlet-class>org.apache.cactus.server.runner.ServletTestRunner</servlet-class>
                   </servlet>
                  
                   <servlet-mapping>
                   <servlet-name>ServletRedirector</servlet-name>
                   <url-pattern>/ServletRedirector</url-pattern>
                   </servlet-mapping>
                  
                   <servlet-mapping>
                   <servlet-name>ServletTestRunner</servlet-name>
                   <url-pattern>/ServletTestRunner</url-pattern>
                   </servlet-mapping>
                  </web-app>
                  


                  • 6. Re: [IceFaces] NullPointerException when accessing JSFServer
                    ssilvert

                    I don't know anything about IceFaces, but from your web.xml, I'm guessing that the right extension is iface, so you need to do:

                    JSFSession jsfSession = new JSFSession("/index.iface");


                    I'll be out for the rest of the day.

                    Stan

                    • 7. Re: [IceFaces] NullPointerException when accessing JSFServer
                      ansel1

                      I'm also trying to integrate jsfunit with icefaces. I think one aspect of the problem may be that both icefaces and jsfunit follow the same pattern of wrapping the default context factory in their own factories. I looks to me like it might work if jsfunit was wrapping icefaces' context factory, but not the other way around. And it's not yet clear to me how to control that order.

                      • 8. Re: [IceFaces] NullPointerException when accessing JSFServer
                        ssilvert

                        Thanks for that information. I haven't tried IceFaces with JSFUnit, but if that's all it takes to make it work then maybe I can figure something out.

                        Stan

                        • 9. Re: [IceFaces] NullPointerException when accessing JSFServer
                          ansel1

                          I've had some success integrating jsfunit with icefaces. Here's what I've had to do:

                          1. First off, we use junit4, and we don't use cactus, so I had to write my own junit4-capable cactus-like servlet. Luckily, JSFUnit really only has one very minor integration point with Cactus. It's a wrapper servlet around the main Cactus servlet that does some cleanup when the test run is over. I just added that same cleanup code to my own servlet that runs test tests. This method is called in the doPost() method of my servlet, after everything else is complete:

                          private void cleanUp(HttpServletRequest httpServletRequest) {
                           if (SeamUtil.isSeamInitialized()) {
                           SeamUtil.invalidateSeamSession(httpServletRequest);
                           }
                          
                           HttpSession session = httpServletRequest.getSession(false);
                           if (session != null) {
                           session.invalidate();
                           }
                           }
                          
                          


                          2. JSFUnit tries to install its own faces context factory, which is used to preserve the FacesContext from the initial faces session created by new JsfSession(). But this is incompatible with IceFaces. IceFaces uses the same pattern to install its own factory, which does not delegate to other factories. So this is fundamentally incompatible with JSFUnit. I was able to work around this by accomplishing the same task JSFUnit was trying to do with a PhaseListener:


                          public class InstallJSFUnitPhaseListener implements PhaseListener {
                          
                           public void afterPhase(PhaseEvent event) {
                           if (isJSFUnitRequest(event.getFacesContext())) {
                           // This doesn't actually release the underlying context, it just sets up
                           // some other stuff for jsfunit.
                           new JSFUnitFacesContext(event.getFacesContext()).release();
                           }
                           }
                          
                           public void beforePhase(PhaseEvent event) {
                           }
                          
                           public PhaseId getPhaseId() {
                           return PhaseId.RENDER_RESPONSE;
                           }
                          
                           private boolean isJSFUnitRequest(FacesContext facesContext) {
                           return facesContext.getExternalContext().getRequestCookieMap().containsKey(WebConversationFactory.JSF_UNIT_CONVERSATION_FLAG);
                           }
                          }
                          
                          


                          It's a little bit of a hack, but it works fine. Stan, I'm thinking you might be able to incorporate a phase listener like this into your distribution, as an alternative installation technique for situations like IceFaces.

                          That's it. My tests now work pretty well, and I'm able to do everything with the JsfServerSession. Unfortunately, JsfClientSession still isn't working right. After I create my JsfSession, if I examine the web windows, I see two windows, [0] is a top level window that contains the real content of my web app, and [1] is a FrameWindow with the name "history-frame:TUEGmJPDIZ_r36TohwlOHQ:1". It contains basically a blank web page with no content. Unfortunately, JsfClientSession's contentPage always points to the blank history frame, instead of the top level window. So none of JsfClientSession's APIs really work. I haven't been able to figure out a way to get JsfClientSession to point to the correct contentPage. Any ideas?

                          • 10. Re: [IceFaces] NullPointerException when accessing JSFServer
                            ansel1

                            Little more info on my JsfClientSession problem. Looks like I have the right content page right after I create the JsfSession. But if I do a page refresh (((HtmlPage) jsfClientSession.getContentPage()).refresh()), then JsfClientSession points to the other web window with no content. I'm guessing it is due to JsfClientSession.webWindowContentChanged(). This method gets called several times, each time with different windows. One of those windows is the one I'm interested in, but its a crap shoot in which order the windows are passed to this method. That blank page is the last page that is passed to this method, so that is the contentPage JsfClientSession gets stuck with.

                            • 11. Re: [IceFaces] NullPointerException when accessing JSFServer
                              ssilvert

                              Thanks for working on this. IceFaces support is a common request and since you've taken a serious look at it, I'm now more inclined to get it done sooner.

                              I don't think that the PhaseListener is the proper integration point. There is never any guarantee that a PhaseListener will be called. Application code can always call FacesContext.responseComplete() and short-circuit the lifecycle. You might be able to get away with a PhaseListener that executes before the Restore View phase, but even then you still have the possibility for another PhaseListener that executes before the JSFUnit PhaseListener.

                              You should be able to use JUnit 4 with Cactus. Why don't you want to use Cactus?

                              I'm not sure what your problem is with JSFClientSession. I don't see it in any of my tests.

                              You mentioned earlier that JSFUnit would probably work with IceFaces as long as the JSFUnitFacesContextFactory was allowed to wrap the IceFaces FacesContext. Did you find some reason we can't take that approach?

                              Stan

                              • 12. Re: [IceFaces] NullPointerException when accessing JSFServer
                                ansel1

                                Sorry for the delay.

                                Re Cactus: I wasn't sure how to do this without adding suite() methods to all my test classes. Also, our IDE's junit integration (Intellij), let's you run individual test methods, and I'm not sure that would work ( I think it would just call the method, not the suite()). And I wasn't sure how the @BeforeClass and @AfterClass annotations would work.

                                Our own framework is a @RemoteRunner implementation, which has the advantage of being completely transparent to tool you use to run the tests.

                                Re context factory: I thought at first the issue was not being able to control which the order in which the factories are wrapped. But then I realized that IceFaces bypasses the faces context factory completely. Even though they do have their own implementation of FacesContextFactory, they never use it. Their PersistentFacesServlet (which replaces the standard JSF FacesServlet) directly instantiates their faces context.

                                We might be able to talk to them about this design decision. They seem very willing to help integrate with other frameworks.

                                Re: the JSFClientSession: there seems to be a fundamental problem with how this class works. It basically assumes that only one web window will be created. If your webapp creates multiple web windows, JSFClientSession is going to register as a listener to all of them. And whenever any of them fires an update event, JSFClientSession is going to re-point its "contentPage" attribute to that most recently updated page.

                                I think you could even recreate this in a test case, by creating a second web window inside the test case and updating its content. Now inspect the JSFClientSession and note that it is pointing to this new page.

                                It seems that Icefaces creates a secondary "FrameWindow", and because of the way JSFClientSession works, if that is the window most recently updated, that is the page that JSFClientSession will be looking at.

                                I'm think it might be corrected with something like this:

                                public void webWindowContentChanged(WebWindowEvent webWindowEvent)
                                 {
                                 if(webWindowEvent.getWebWindow() != webWindowEvent.getWebWindow().getWebClient().getCurrentWindow())
                                 {
                                 return;
                                 }
                                 Page page = webWindowEvent.getNewPage();
                                 if(!(page instanceof HtmlPage))
                                 {
                                 this.contentPage = page;
                                 return;
                                 }
                                
                                 HtmlPage ht = (HtmlPage)page;
                                 if( ht.getDocumentElement().hasChildNodes() )
                                 {
                                 this.contentPage = page;
                                 }
                                 }
                                


                                • 13. Re: [IceFaces] NullPointerException when accessing JSFServer
                                  lightguard

                                  Hi Stan (we've been communicating via email recently) and russegan. I'm interested in helping out with the ICEfaces integration.

                                  @russegan: The extra window that ICEfaces creates (if you didn't know already) is a blank iframe that is used to buffer some of the requests to the server and to help with the back button issue, at least IIRC. I also noticed you're using Seam. We'll be doing the same thing but using TestNG, I don't foresee any issues with the stuff that you've posted about by passing cactus (makes sense to me). We may take the same approach here.

                                  Let me know how I can help!

                                  • 14. Re: [IceFaces] NullPointerException when accessing JSFServer
                                    ansel1

                                    Actually, we're not using Seam (yet, maybe late this year). I just copied those lines that mention Seam from one of JsfUnit's classes.

                                    I haven't tried it out yet, but I plan on either cloning or extending JsfUnit's JsfClientSession class, and overriding just the event handling, so it will ignore events coming from any window's besides the current window. I'm pretty sure that will fix that problem.

                                    1 2 3 Previous Next