-
1. Re: NullPointer at FacesContextBridge
ssilvert Mar 17, 2008 4:25 PM (in response to lmeng)Are you running in a cluster? JSFUnit/Cactus doesn't work with round-robin load balancing. If you are running in a cluster then every client request must go to the same server. See http://jakarta.apache.org/cactus/faq.html#faq_load_balancing
To help debug, you can always get the session id like this:String jsessionid = webConversation.getCookieValue("JSESSIONID");
I've never tried mixing JSF and Struts requests in a JSFUnit test, but it looks like what you have should work. So if you can figure out exactly where it is losing the session, maybe we can pin it down.
Stan -
2. Re: NullPointer at FacesContextBridge
lmeng Mar 17, 2008 6:00 PM (in response to lmeng)Thanks for the quick reply.
I am not using cluster, but I am running on Weblogic 9.
I modified my code by injecting those checks on session id. The observation is that the session id at 'point B' is different from 'point A'.
My deployment is that we have two instance of Weblogic 9 (different ports). One is a Portal server for SSO, the other one is my target instance. The unit test code is on the target instance. To access JSF pages on my target instance, I need to be authenticated by the Portal server.
Is there a way to handle situation like this?
Regards,
mlxpublic void testInitialPage() throws IOException, SAXException { WebConversation webConversation = WebConversationFactory .makeWebConversation(); // Initial JSF request WebRequest req = new GetMethodWebRequest(WebConversationFactory.getWARURL() + "/main.jsf"); String jsessionid = webConversation.getCookieValue("JSESSIONID"); System.out.println("--- point A --- " + jsessionid); this.mWebResponse = webConversation.getResponse(req); if (this.mWebResponse.getURL().toString().endsWith("startPortal.do")) { WebForm[] forms = this.mWebResponse.getForms(); WebForm loginForm = forms[0]; assertEquals("loginForm", loginForm.getName()); loginForm.setParameter("username", "PA"); loginForm.setParameter("password", "PA"); this.mWebResponse = loginForm.submit(); // get the session established this.mWebResponse = webConversation.getResponse(req); } jsessionid = webConversation.getCookieValue("JSESSIONID"); System.out.println("--- point B --- " + jsessionid); mClient = new JSFClientSession(webConversation, "/main.jsf"); jsessionid = webConversation.getCookieValue("JSESSIONID"); System.out.println("--- point C --- " + jsessionid);
-
3. Re: NullPointer at FacesContextBridge
ssilvert Mar 17, 2008 8:12 PM (in response to lmeng)You might want to take a look at this to see in more detail how JSFUnit works: http://wiki.jboss.org/wiki/JSFUnitSessionAndThreads
When FacesContextBridge.getCurrentInstance() gets the session, it will always use the one referenced in your ThreadLocal. This is the reference set up by the JSFUnitFilter.
So when you log in to your SSO server, the JSESSIONID is reset and you do your initial JSF request. But it does it against a different session. So when the FacesContextBridge tries to find the FacesContext, it's sitting in a different session.
I'm pretty sure the solution is to just reset the cookie on the WebConversation so that you get back to the original JSESSIONID. Or it should work if you just use the single-arg constructor of JSFClientSession. That will create a new WebConversation with the correct JSESSIONID.
Stan -
4. Re: NullPointer at FacesContextBridge
lmeng Mar 18, 2008 12:21 PM (in response to lmeng)Based on the link you posted. 'a multi-threaded test where two JSFClientSession instances are making JSF requests at the same time is not supported.'. If that's the case, why not change the use of ThreadLocal to a global static variable for holding the session.
Is there a better solution?
mlx
While a multi-threaded test is not supported, multi-client testing IS supported. In other words, you might have more than one developer hitting a single server instance. Each developer should be able to run a JSFUnit test against that server at the same time. A ThreadLocal works for that but a global static variable does not.
I think I have a cleaner solution for you. It sounds like the way your SSO server works is that you log in and it resets the JSESSIONID to what it considers to be an "authenticated" session. That is the one you need to use for all future requests.
So instead of kicking of JSFUnit by a call to the ServletTestRunner, call your own FooServlet that logs into your SSO server using plain HttpUnit. Then take that same WebConversation with the newly authenticated JSESSIONID and call the ServletTestRunner. Get the result from that call and write it to the response of the FooServlet.
If you are not running JSFUnit from the browser then you will need to change your mappings in web.xml so that Cactus hits your FooServlet instead of the ServletRedirector. If that is the case, I can walk you through it. But try my suggestion running from the browser first. That should work.
Stan -
5. Re: NullPointer at FacesContextBridge
lmeng Mar 18, 2008 1:34 PM (in response to lmeng)Thanks for the message. I'll try it later and keep you updated.
One more thought. If you provide another 'makeWebConversation()', which allows user to decide whether to call 'clearSession()' or not, people in similar situation won't have to build the extra 'FooServlet'.
We can just use browser to login our application first, then start the testing with the same browser. It is the same concept as your solution, I guess.
Is there a reason that the session must be cleared?
mlx -
6. Re: NullPointer at FacesContextBridge
ssilvert Mar 18, 2008 1:59 PM (in response to lmeng)"lmeng" wrote:
Is there a reason that the session must be cleared?
mlx
If you didn't clear the session you wouldn't be able to run more than one test. When you run your second test you would have junk left over from the last one. Each time you call "new JSFClientSession()" you should be assured that, indeed, that's what you are getting.
I don't think clearing the session is the issue in your case. The problem is that you need to run Cactus under an authenticated session. Instead of authenticating the session that hit the SSO server, your SSO server is resetting that session id. So there is no getting around the fact that you need some sort of pre-handler to log into your SSO server before Cactus will work.
Of course, this all assumes that I understand how your SSO server works.
Stan -
7. Re: NullPointer at FacesContextBridge
lmeng Mar 19, 2008 8:05 PM (in response to lmeng)I tried the solution of providing another FooServlet. It still failed.
I examed the session ID of the wc after the login() (highlighted below). It is different from the one I got in JSFUnitFilter later.
Did I get your idea wrong?
The FooServlet looks like:... public void doGet(HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException { ... WebResponse response; try { String warUrl = makeWARURL(theRequest); response = run(warUrl, suiteClassName, xslParam, encoding, transformParam); theResponse.setContentType(response.getContentType()); PrintWriter pw = theResponse.getWriter(); pw.println(response.getText()); } catch (SAXException e) { throw new ServletException(e); } } protected WebResponse run(String warUrl, String suiteClassName, String xslParam, String encoding, String transformParam ) throws ServletException, MalformedURLException, IOException, SAXException { WebConversation wc = login(warUrl); // exam the session id here StringBuilder realCactusURL = new StringBuilder(); ... // Initial JSF request WebRequest req = new GetMethodWebRequest(warUrl + realCactusURL.toString()); WebResponse response = wc.getResponse(req); return (response); } protected WebConversation login(String warUrl) throws MalformedURLException, IOException, SAXException, ServletException { WebConversation webConversation = new WebConversation(); // Initial JSF request WebRequest req = new GetMethodWebRequest(warUrl+ "/main.jsf"); WebResponse webResponse = webConversation.getResponse(req); if (webResponse.getURL().toString().endsWith("startPortal.do")) { WebForm[] forms = webResponse.getForms(); WebForm loginForm = forms[0]; ... webResponse = loginForm.submit(); } return (webConversation); } ...
-
8. Re: NullPointer at FacesContextBridge
ssilvert Mar 19, 2008 10:04 PM (in response to lmeng)That looks right except I'm not sure about this:
// Initial JSF request WebRequest req = new GetMethodWebRequest(warUrl + realCactusURL.toString());
This is not a JSF request. It should be a request to the ServletTestRunner. But the fact that you say your request hit the JSFUnitFilter tells me that realCactusURL is a URL to hit ServletTestRunner?
What I don't understand is how the WebConversation can have the jsessionid set correctly by the SSO server and then when it hits the JSFUnitFilter via above line of code, jsessionid is changed?
Stan -
9. Re: NullPointer at FacesContextBridge
lmeng Mar 20, 2008 11:44 AM (in response to lmeng)It is a request to the ServletTestRunner, which in turn calls 'testInitialPage()'. From there, a JSF request is sent out. My comment in the code is wrong. It was copy-and-paste.
I have to look into how the session got changed later.
Thanks for all the help.