/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.ajax4jsf.application;
import java.io.IOException;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletResponse;
import org.ajax4jsf.Messages;
import org.ajax4jsf.application.ViewHandlerWrapper;
import org.ajax4jsf.component.AjaxViewRoot;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.ViewIdHolder;
import org.ajax4jsf.renderkit.AjaxContainerRenderer;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.ajax4jsf.resource.InternetResource;
import org.ajax4jsf.resource.InternetResourceBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.5 $ $Date: 2007/02/08 15:02:04 $
*
*/
public class AjaxViewHandler extends ViewHandlerWrapper {
public static final String STATE_MARKER_KEY = "org.ajax4jsf.view.state";
public static final String SERIALIZED_STATE_KEY = "org.ajax4jsf.view.serializedstate";
private static final Log _log = LogFactory.getLog(AjaxViewHandler.class);
public static final String VIEW_EXPIRED = "org.ajax4jsf.view.EXPIRED";
public static final String VIEW_ID_KEY = "org.ajax4jsf.VIEW_ID";
/**
* @param parent
*/
public AjaxViewHandler(ViewHandler parent) {
super(parent);
if (_log.isDebugEnabled()) {
_log.debug("Create instance of Ajax ViewHandler");
}
}
/*
* (non-Javadoc) For creating ViewRoot by Application Instead of new . Not
* nessesary for MyFaces ( simple copy from it ) or RI 1.2
*
* @see javax.faces.application.ViewHandler#createView(javax.faces.context.FacesContext,
* java.lang.String)
*/
public UIViewRoot createView(FacesContext facesContext, String viewId) {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext);
// Check for simple keep new ViewId in navigation cases.
ViewIdHolder viewIdHolder = ajaxContext.getViewIdHolder();
UIViewRoot riRoot;
if (null != viewIdHolder && viewIdHolder.skipNavigation(viewId)) {
viewIdHolder.setViewId(viewId);
riRoot = facesContext.getViewRoot();
} else {
UIViewRoot root = super.createView(facesContext, viewId);
// TODO -setup render-kit where instead of Listener ?
if (null == root || root instanceof AjaxViewRoot) {
riRoot = root;
} else {
// HACK - since only RI 1.1 use new for create ViewRoot instead
// of Application.createComponent,
// we can use it as flag for custom creation.
riRoot = (UIViewRoot) facesContext.getApplication()
.createComponent(UIViewRoot.COMPONENT_TYPE);
// fill properties from default.
riRoot.setViewId(root.getViewId());
riRoot.setLocale(root.getLocale());
String renderKitId = root.getRenderKitId();
// Fix facelets bug - for debug requests renderKitId is null !
if (null == renderKitId) {
renderKitId = calculateRenderKitId(facesContext);
}
riRoot.setRenderKitId(renderKitId);
}
// if (null ==
// facesContext.getExternalContext().getRequestMap().get(VIEW_EXPIRED))
// {
// Clear ajax request flag ( except new view creation )- for navigation states, full page will be
// send.
if(null != facesContext.getViewRoot()){
ajaxContext.setAjaxRequest(false);
}
// }
}
return riRoot;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ViewHandlerWrapper#writeState(javax.faces.context.FacesContext)
*/
public void writeState(FacesContext context) throws IOException {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
if (ajaxContext.isAjaxRequest(context)) {
// TODO - detect case of JSF 1.1 + JSP. for all other we need own
// state marker for
// self-rendered regions only.
// write marker html element - input field. Will be parsed in filter
// and
// replaced with real state.
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.SPAN_ELEM, null);
writer.writeAttribute(HTML.id_ATTRIBUTE, STATE_MARKER_KEY, null);
writer.writeAttribute(HTML.NAME_ATTRIBUTE, STATE_MARKER_KEY, null);
// writer.writeAttribute("style", "display:none;", null);
if (!ajaxContext.isSelfRender()) {
super.writeState(context);
}
writer.endElement(HTML.SPAN_ELEM);
} else {
super.writeState(context);
}
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ViewHandlerWrapper#restoreView(javax.faces.context.FacesContext,
* java.lang.String)
*/
public UIViewRoot restoreView(FacesContext context, String viewId) {
UIViewRoot viewRoot = super.restoreView(context, viewId);
ExternalContext externalContext = context.getExternalContext();
Map requestMap = externalContext.getRequestMap();
if (null == viewRoot) {
requestMap.put(VIEW_EXPIRED, Messages
.getMessage(Messages.AJAX_VIEW_EXPIRED));
if (_log.isDebugEnabled()) {
_log
.debug("Detect session expiration in AJAX request - view don't restored for a viewId "
+ viewId);
}
}
return viewRoot;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ViewHandlerWrapper#getResourceURL(javax.faces.context.FacesContext,
* java.lang.String)
*/
public String getResourceURL(FacesContext context, String url) {
String resourceURL;
if (url.startsWith(InternetResource.RESOURCE_URI_PREFIX)) {
InternetResource resource = InternetResourceBuilder
.getInstance()
.createResource(
null,
url
.substring(InternetResource.RESOURCE_URI_PREFIX_LENGTH));
resourceURL = resource.getUri(context, null);
} else {
resourceURL = super.getResourceURL(context, url);
}
return resourceURL;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ViewHandlerWrapper#renderView(javax.faces.context.FacesContext,
* javax.faces.component.UIViewRoot)
*/
public void renderView(FacesContext context, UIViewRoot root)
throws IOException, FacesException {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
// Broadcast Ajax events before rendering, to setup areas to update.
if (root instanceof AjaxViewRoot) {
AjaxViewRoot ajaxRoot = (AjaxViewRoot) root;
Map requestMap = context.getExternalContext().getRequestMap();
// broadcast ajax events before render response.
if (ajaxContext.isAjaxRequest(context)
&& null == requestMap
.get(AjaxRendererUtils.AJAX_AREAS_RENDERED)) {
processAjaxEvents(context, ajaxRoot);
}
if (!context.getResponseComplete()) {
super.renderView(context, root);
if (ajaxContext.isAjaxRequest(context)
&& null == requestMap
.get(AjaxRendererUtils.AJAX_AREAS_RENDERED)) {
// HACK for MyFaces ( <f:view> tag not call renderers )
if (_log.isDebugEnabled()) {
_log
.debug(Messages
.getMessage(Messages.AJAX_RESPONSE_NOT_RENDERED_INFO));
}
ajaxContext.setSelfRender(true);
// ServletResponse response = (ServletResponse) context
// .getExternalContext().getResponse();
Object response = (Object) context.getExternalContext()
.getResponse();
try {
response.getClass().getDeclaredMethod("reset",
new Class[0]).invoke(response, new Object[0]);
// response.reset();
} catch (Exception e) {
// Do nothing - we will use directly and reset
// wrapper
}
ajaxContext.renderSubmittedAjaxRegion(context, true);
}
}
} else {
super.renderView(context, root);
}
ajaxContext.processHeadResources(context);
}
/**
* @param context
* @param ajaxRoot
*/
private void processAjaxEvents(FacesContext context, AjaxViewRoot ajaxRoot) {
// First - process reRender from active components.
if (_log.isDebugEnabled()) {
_log.debug(Messages.getMessage(Messages.PROCESS_AJAX_EVENTS_INFO));
}
ajaxRoot.broadcastAjaxEvents(context);
}
}