My thoughts on JSF 2.0

I started working with JSF, version 2.0. I’m actually quite suprised with the framework – it’s pretty well designed and learning it was fairly easy.

JSF isn’t really that different from other MVC frameworks (ie. Struts) but I wanted to quickly talk about 2 differences I noticed immediately.

First, both frameworks are added to your web app through web.xml entries, however Struts is added as a Filter and JSF is added a Servlet.

Struts 2:

<filter>
	<filter-name>struts2</filter-name>
	<filter-class>
        org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
 
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

JSF:

 <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>

This is worth noting because since Struts is setup as a Filter – you get native access to the Request/Response within the API and all Struts Actions. In JSF, you need to retreive it externally, through the ExternalContext object:

ExternalContext context = 
FacesContext.getCurrentInstance().getExternalContext();  

The second item worth noting is that JSF doesn’t really have a direct replacement for Struts 2 Interceptors. Interceptors allow you to implement logic once and apply it across as many Struts actions as needed. A common use case is Login functionality via Session protection.

In JSF there is no direct replacement. I looked at PhaseListeners, but those are for performing actions on the 6 possible events of a JSF request lifecycle. Instead, JSF lets you “scope” your ManagedBeans. The possible scopes are: RequestScoped, SessionScoped, ViewScoped, or ApplicationScoped.

So, to implement what Struts could do with Interceptors, I ended up creating a LoginBean and annotated it to be SessionScoped:

@ManagedBean
@SessionScoped
public class LoginBean {
	public String login(){ ... }
	public String logout(){ ...}
}

The idea is that this bean will be created and life in the session until we expire it (or it times out).

I have 2 methods: A Login and a Logout. The Login is self explanatory – it just performs Username/Pass authentication. ULB is a helper class that authenticates a user.

	public String login(){
		if(ulb.isValidUser(user)){
			System.out.println("Authenticated!");
			return "/secure/dashboard.jsf?faces-redirect=true";
		}
		else{
			System.out.println("Failed login!");
			return "/login.jsf";
		}
	}

The log out will clear the session. You could link this to a logout button or timer:

	public String logout(){
		System.out.println("logging out ...");
		
		FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
		
		System.out.println("session killed ...");
        return "/login.jsf?faces-redirect=true";
	}

There are two key points here: The call to invalidateSession() – this clears all session data for the current user. And the final return must specify ‘faces-redirect=true’ to instruct the browser to perform a new request for login.jsf. Once the new request is made there will be an empty session.

Finally, I added a Filter to protect all secure pages within my app (anything under the /secure/* url pattern). This filter is called BEFORE the JSF servlet is called. It looks for the SessionScoped bean in the session. If the bean is in the session, then proceed thorugh the filter chain as normal and serve the requested page. If the bean is not present in the session, redirect to the Login page to protect all secure assets.

web.xml:

	<filter>
	    <filter-name>userLoginFilter</filter-name>
	    <filter-class>somePackage.GlobalFilter</filter-class>	    
	</filter>
	
	<filter-mapping>
	    <filter-name>userLoginFilter</filter-name>
	    <url-pattern>/secure/*</url-pattern>
	</filter-mapping>

GlobalFilter:

public class GlobalFilter implements Filter{

	@Override
	public void destroy() {	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;

		LoginBean userManager = (LoginBean) req.getSession().getAttribute("loginBean");

		if (userManager != null && userManager.isLoggedIn()) {
			System.out.println("logged in ...");
			chain.doFilter(request, response);
		} else {
			System.out.println("Not logged in ...");
		    res.sendRedirect(req.getContextPath() + "/login.jsf");
		}		
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {	}
}

By default all SessionScoped beans are added to the session by their class name. You can change this using the @ManagedBean (name=”abcd”) attribute.

There are other ways to implement this logic but I wanted to make use of built-in JSF functionality 🙂

Advertisements