How to use FacesContext.getCurrentInstance(), it returns null

Andrei Dvoynos picture Andrei Dvoynos · Apr 3, 2012 · Viewed 30k times · Source

I've been struggling for the last couple of days with the login part of my web app. I've gotten to the point where I can succesfully authenticate a user using the JDBCRealm on tomcat(by reading users from a sql server database). Now I want to send some kind of feedback when the user's account has been blocked, or when the credentials are incorrect, this is where I'm stuck now.

I wanted to use this:

    try {
        request.login(request.getParameter("user"), request.getParameter("pass"));
    } catch (ServletException se) {
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Wrong Username/Password combination"));
        log(se.getMessage());
    }

But FacesContext.getCurrentInstance() always returns null..

After doing some research I found out that the request must come from a page located in /faces so that the FacesServlet gets called and the FacesContext gets initialized(at least that's what I understood).

So I moved the login page to a new folder named faces, inside the Web Pages folder. But now everytime I try to call the login.xhtml page, I get this error:

/login.xhtml Not Found in ExternalContext as a Resource  

And this is the stacktrace:

com.sun.faces.context.FacesFileNotFoundException: /login.xhtml Not Found in ExternalContext as a Resource
at com.sun.faces.facelets.impl.DefaultFaceletFactory.resolveURL(DefaultFaceletFactory.java:232)
at com.sun.faces.facelets.impl.DefaultFaceletFactory.resolveURL(DefaultFaceletFactory.java:273)
at com.sun.faces.facelets.impl.DefaultFaceletFactory.getFacelet(DefaultFaceletFactory.java:201)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:764)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:100)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:410)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:304)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)  

I get this error even when I enter the URL directly into the browser.

Im using Netbeans 7.1 with Apache Tomcat 7(which by the way is a pain because I get a lot of PermGen out of memory exceptions -.-)

Any help on how to fix this would be greatly appreciated.

Answer

BalusC picture BalusC · Apr 3, 2012

It will return null when you're not in a managed bean or any other JSF artifact. For example, in a plain vanilla servlet or a servlet filter which runs before FacesServlet runs. The FacesServlet is namely the one who creates the FacesContext and puts it as a ThreadLocal in the current HTTP request. You need to do the login job in a JSF managed bean instead the usual way. That piece of code login which you've there belongs in a JSF managed bean.

As to the login page, it's not different from any other JSF page which you've ever developed. Or have you actually never developed with JSF and is this your first JSF attempt ever? Well, you should have been more explicit about this in your question as well. Get rid of that /faces folder, just put the login.xhtml straight in the webcontent and make sure that the URL pattern of the FacesServlet in web.xml is set to *.xhtml. Then you can just open it by http://localhost:8080/yourapp/login.xhtml.

To learn JSF better, go through a bit decent book/tutorial first and don't try to cobble loose pieces which you found on the Internet together without really understanding what those lines of code are doing. You're supposed to be able to write/explain it yourself once you really understand it. Start here: Our JSF wiki page.