Sunday, March 13, 2011

JSF 2 Internals - Efficient way of Redirecting to a Page

There are certain cases where you are needed to redirect  to a different page than requested by user e.g. if user has requested a secure page for which he/she is not authorized, your application will have to redirect to login page or some other page. I have created a utility method to redirect to a page. You can call anywhere but for example given above, you can call it a beforePhase method of RestoreViewPhaseListener. Here is the method code:

public static void redirect(String viewId) {

execute(FacesContext.getCurrentInstance(), viewId);
}


private static void execute(FacesContext facesContext, String viewId)
throws FacesException {

UIViewRoot viewRoot = null;
ViewDeclarationLanguage vdl = facesContext.getApplication()
.getViewHandler()
.getViewDeclarationLanguage(facesContext, viewId);

if (vdl != null) {
// If we have one, get the ViewMetadata...
ViewMetadata metadata = vdl.getViewMetadata(facesContext,
viewId);

if (metadata != null) { // perhaps it's not supported
// and use it to create the ViewRoot. This will have, at
// most
// the UIViewRoot and its metadata facet.
viewRoot = metadata.createMetadataView(facesContext);

}
}

facesContext.setViewRoot(viewRoot);
facesContext.renderResponse();
assert (null != viewRoot);

}

viewId is the name of page to which it should redirect e.g. "/redirect.xml" (assuming page is at root level). Inside execute method, you get the ViewDeclarationLanguage which is page type specific e.g. if page is in facelet markup, you get DefaultFaceletViewDeclarationLanguage and if page is in JSP markup, then you will get corresponding ViewDeclarationLanguage. After this, page root tree is constructed by calling createMetadataView if it is not already constructed. Finally set the new ViewRoot and call renderResponse method, which will bypass all phases and will jump directly to render phase.

Tuesday, March 8, 2011

JSF 2 Internals - Configurations

In JSF 2, there is almost no need to mention any configuration in a small application and defaults values are sufficient enough to fulfill the purpose. All the default configurations are in com.sun.faces.WebConfiguration. As i mentioned in my previous blog post, JSF has implemented ServletContainerInitializer which is new interface in Servlet 3.0 in order to register servlets and other stuff programmatically at the time of loading application. In 'onStartUp' method, it adds listener com.sun.faces.ConfigureListener. It implements various interface listeners like ServletRequestListener, HttpSessionListener, ServletContextListener, ServletRequestAttributeListener, HttpSessionAttributeListener, ServletContextAttributeListener. Most important for loading configuration is ServletCotextListeenr which has two methods contextInitialized and contextDestroyed. contextInitialized  is called when application loads and ServletContext is created, so this is the best time to load configurations in JEE web applications. JSF 2 also gets benefit of this and creates WebConfiguration Object and loads default values for configurations. If application has its own values mentioned in web.xml, they are overriden.

Wednesday, March 2, 2011

JSF 2 Internals - Entry of FacesServlet is optional

In JEE 6, major milestone achieved was reduction of configuration files e.g. in servlet based web application, no need to mention web.xml and application assumes default configuration settings. Same thing was also achieved in JSF 2. Now there is no need to mention FaceServlet or servlet mapping in web.xml. But question arises from where application picks these defaults? Answer is the "ServletContainerInitializer" interface which allows a library/runtime to be notified of a web application's startup phase and perform any required programmatic registration of servlets,filters, and listeners in response to it. Inside of "onStartup" implementation of JSF 2, all these defaults are set, Sample code is given below:
ServletRegistration reg =
servletContext.addServlet("FacesServlet",
"javax.faces.webapp.FacesServlet");
reg.addMapping("/faces/*", "*.jsf", "*.faces");
servletContext.setAttribute(RIConstants.FACES_INITIALIZER_MAPPINGS_ADDED, Boolean.TRUE);
JSF 2 class which implements this interface is "FacesInitializer" class and have been mentioned under jsf-ri.jar's META-INF/services folder.