It seems that it is possible to reuse UIViewRoot object during the restore view phase in JSF as suggested in this link here
I am dealing with a very large form here with hundreds of data elements and on profiling the application, I found that that the RestoreView phase is taking the longest amount of time.
So following this suggestion, I tried to cache the UIViewRoot object in the session object and it seems to work and increase the performance well.
But what I need to know is that will it cause any problem (like stale data or any other known issues) with the framework.
Is there any other way of caching the View Root.
Every time the FullVisitContext is called, it takes a long time and PartialVisitContext is not working for me.
This is what I have written in the view handler
package com.app.customviewhandler;
import java.io.IOException;
import java.util.Enumeration;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import configuration.ComponentConfigurationContext;
import configuration.ComponentConfigurationFactory;
import logging.LoggingService;
import com.app.common.utilities.StringUtils;
public class CustomViewHandler extends ViewHandlerWrapper {
private ViewHandler wrapped;
private static LoggingService loggingService;
private static final String _USER_CURRENT_VIEW_ID = "USER_CURRENT_VIEW_ID";
private static final String _CURRENT_REQUEST_URI = "CURRENT_REQUEST_URI";
private static final String _PREVIOUS_REQUEST_URI = "PREVIOUS_REQUEST_URI";
static {
ComponentConfigurationContext ctx = ComponentConfigurationFactory.getInstance().getComponentConfigurationContext();
loggingService = (LoggingService) ctx.getObject("loggingService");
}
/**
*
*/
public CustomViewHandler(ViewHandler wrapped) {
this.wrapped = wrapped;
loggingService.info(this, "JSF: Registered a new instance of CustomViewHandler");
}
public UIViewRoot restoreView(FacesContext context, String viewId) {
try {
loggingService.info(this, "JSF: restoreView: start");
HttpSession session = (HttpSession) context.getCurrentInstance().getExternalContext().getSession(true);
HttpServletRequest request = (HttpServletRequest) context.getCurrentInstance().getExternalContext().getRequest();
/**
* Whenever an URL redirection occurs, we have to reset the cache
*/
/**
* Set the current view name
*/
request.setAttribute(_USER_CURRENT_VIEW_ID, viewId);
loggingService.info(this, "JSF: restoreView: setting _USER_CURRENT_VIEW_ID[" + viewId + "]");
UIViewRoot cachedView = null;
String previousRequestURI = (String)session.getAttribute(_PREVIOUS_REQUEST_URI);
String currentRequestURI = request.getRequestURI();
if(StringUtils.checkNullOrEmpty(previousRequestURI)){
loggingService.info(this, "JSF: restoreView: _PREVIOUS_REQUEST_URI is null");
resetCachedView(context);
}else{
loggingService.info(this, "JSF: restoreView: _PREVIOUS_REQUEST_URI["+previousRequestURI+"] for viewId[" + viewId + "]");
if(StringUtils.checkNullOrEmpty(currentRequestURI)){
loggingService.info(this, "JSF: restoreView: current request does not have any requestURI for viewId[" + viewId + "]");
resetCachedView(context);
}else{
if(!previousRequestURI.equalsIgnoreCase(currentRequestURI)){
loggingService.info(this, "JSF: restoreView: URL redurection has occured. resetCachedViews()");
resetCachedView(context);
}else{
loggingService.info(this, "JSF: restoreView: URL same URI requested["+currentRequestURI+"] for viewId[" + viewId + "]");
cachedView = (UIViewRoot) (session.getAttribute(viewId));
}
}
}
if (cachedView == null) {
loggingService.info(this, "JSF: restoreView: cachedView is null. So the view will be restored now");
cachedView = getWrapped().restoreView(context, viewId);
} else {
loggingService.info(this, "JSF: restoreView: the cachedView instance is not null. Returning the cached view.");
}
session.setAttribute(_PREVIOUS_REQUEST_URI, currentRequestURI);
loggingService.info(this, "JSF: restoreView: end");
return cachedView;
} catch (Exception e) {
loggingService.error(this, "JSF restoreView exception", e);
throw new RuntimeException(e);
}
}
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {
try {
loggingService.info(this, "JSF: renderView: start");
HttpSession session = (HttpSession) context.getCurrentInstance().getExternalContext().getSession(true);
HttpServletRequest request = (HttpServletRequest) context.getCurrentInstance().getExternalContext().getRequest();
String viewName = (String) request.getAttribute(_USER_CURRENT_VIEW_ID);
if (viewName != null) {
session.setAttribute(viewName, viewToRender);
}
loggingService.info(this, "JSF: renderView: caching the UIViewRoot");
getWrapped().renderView(context, viewToRender);
loggingService.info(this, "JSF: renderView: end");
} catch (IOException ioException) {
loggingService.error(this, "JSF renderView exception", ioException);
throw ioException;
} catch (FacesException facesException) {
loggingService.error(this, "JSF renderView exception", facesException);
throw facesException;
} catch (Exception e) {
loggingService.error(this, "JSF renderView exception", e);
throw new RuntimeException(e);
}
}
@Override
public ViewHandler getWrapped() {
return this.wrapped;
}
/**
* Every time a menu navigation is performed, this method is called. From TemplateManagedBean#gotoPage().
* This is to reset the cached UIViewRoot Instance
* @param context
*/
public static void resetCachedView(FacesContext context){
loggingService.info(CustomViewHandler.class, "JSF: resetCachedView: start");
HttpSession session = (HttpSession) context.getCurrentInstance().getExternalContext().getSession(true);
Enumeration<String> sessionAttributeNames = session.getAttributeNames();
if(sessionAttributeNames!=null){
while(sessionAttributeNames.hasMoreElements()){
String sessionAttributeName = sessionAttributeNames.nextElement();
if(session.getAttribute(sessionAttributeName) instanceof UIViewRoot){
session.removeAttribute(sessionAttributeName);
loggingService.info(CustomViewHandler.class, "JSF: resetCachedView: removing cached view root :"+sessionAttributeName);
}
}
}
loggingService.info(CustomViewHandler.class, "JSF: resetCachedView: end");
}
}