/*
* $Id: CbpViewHandler.java,v 1.10 2009/04/17 14:11:13 civilis Exp $
* Created on 21.6.2004 by tryggvil
*
* Copyright (C) 2004 Idega Software hf. All Rights Reserved.
*
* This software is the proprietary information of Idega hf.
* Use is subject to license terms.
*/
package com.idega.faces.componentbased;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Locale;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKitFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.application.MyfacesStateManager;
import org.apache.myfaces.application.jsp.JspViewHandlerImpl;
import com.idega.core.view.ViewManager;
import com.idega.core.view.ViewNode;
import com.idega.presentation.IWContext;
import com.idega.repository.data.RefactorClassRegistry;
import com.idega.util.CoreConstants;
/**
* <p>
* This is a special implementation of a ViewHandler for "Component-based" Pages.<br/>
* This ViewHandler is used for example when rendering out Builder pages of type IBXML and HTML.
* <br>
* </p>
* Copyright (C) idega software 2004-2005<br>
*
* Last modified: $Date: 2009/04/17 14:11:13 $ by $Author: civilis $
*
* @author <a href="mailto:tryggvil@idega.com">tryggvil</a>
* @version $Revision: 1.10 $
*/
public class CbpViewHandler extends ViewHandler {
private static Log log = LogFactory.getLog(CbpViewHandler.class);
private ViewHandler parentViewHandler;
// private StateManager stateManager;
/**
* Default constructor
*/
public CbpViewHandler() {
// stateManager = new CbpStateManagerImpl();
}
public CbpViewHandler(ViewHandler parentViewHandler) {
//super(parentViewHandler);
setParentViewHandler(parentViewHandler);
}
/**
* @see javax.faces.application.ViewHandler#renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot)
*/
public void renderView(FacesContext ctx, UIViewRoot viewRoot) throws IOException, FacesException {
// Apparently not all versions of tomcat have the same
// default content-type.
// So we'll set it explicitly.
HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
response.setContentType("text/html");
// make sure to set the responsewriter
initializeResponseWriter(ctx);
if(viewRoot == null) {
throw new RuntimeException("CbpViewHandler: No component tree is available !");
}
/* if (ctx instanceof BridgeFacesContext) {
D2DViewHandler view = new D2DViewHandler();
view.renderView(ctx, viewRoot);
return;
}
*/
String renderkitId = viewRoot.getRenderKitId();
if (renderkitId == null) {
renderkitId = calculateRenderKitId(ctx);
}
viewRoot.setRenderKitId(renderkitId);
ResponseWriter out = ctx.getResponseWriter();
//try {
out.startDocument();
renderComponent(ctx.getViewRoot(),ctx);
out.endDocument();
try {
writeOutResponseAndClientState(ctx);
}
catch (JspException e) {
//e.printStackTrace();
throw new RuntimeException(e);
}
//} catch (RuntimeException e) {
//throw new SmileRuntimeException(e.getMessage(),e);
//}
}
public void writeOutResponseAndClientState(FacesContext facesContext) throws JspException
{
if (log.isTraceEnabled()) {
log.trace("entering ViewTag.doAfterBody");
}
try
{
//BodyContent bodyContent = getBodyContent();
//if (bodyContent != null)
//{
//FacesContext facesContext = FacesContext.getCurrentInstance();
StateManager stateManager = facesContext.getApplication().getStateManager();
StateManager.SerializedView serializedView = null;
try{
serializedView = stateManager.saveSerializedView(facesContext);
}
catch(ArrayIndexOutOfBoundsException ar){
System.err.println("StateManager.saveSerializedView caused ArrayIndexOutOfBoundsException:");
ar.printStackTrace();
throw new JspException(ar);
}
//catch(IllegalStateException se){
// System.err.println("CbpViewHandler.writeOutResponseAndClientState : IllegalStateException : "+se.getMessage());
//}
if (serializedView != null)
{
//until now we have written to a buffer
ResponseWriter bufferWriter = facesContext.getResponseWriter();
bufferWriter.flush();
//now we switch to real output
ResponseWriter realWriter = bufferWriter.cloneWithWriter(getRealResponseWriter(facesContext));
facesContext.setResponseWriter(realWriter);
//String bodyStr = bodyContent.getString();
String bodyStr = getOutputAsString(bufferWriter);
int form_marker = bodyStr.indexOf(JspViewHandlerImpl.FORM_STATE_MARKER);
// TODO: myfaces commented this out with following comment, so behavior here should be changed (civilis)
/* this one is never used
public static final String URL_STATE_MARKER = "JSF_URL_STATE_MARKER=DUMMY";
public static final int URL_STATE_MARKER_LEN = URL_STATE_MARKER.length();
*/
String URL_STATE_MARKER = "JSF_URL_STATE_MARKER=DUMMY";
int URL_STATE_MARKER_LEN = URL_STATE_MARKER.length();
// int url_marker = bodyStr.indexOf(HtmlLinkRendererBase.URL_STATE_MARKER);
int url_marker = bodyStr.indexOf(URL_STATE_MARKER);
int lastMarkerEnd = 0;
while (form_marker != -1 || url_marker != -1)
{
if (url_marker == -1 || (form_marker != -1 && form_marker < url_marker))
{
//replace form_marker
realWriter.write(bodyStr, lastMarkerEnd, form_marker - lastMarkerEnd);
stateManager.writeState(facesContext, serializedView);
lastMarkerEnd = form_marker + JspViewHandlerImpl.FORM_STATE_MARKER_LEN;
form_marker = bodyStr.indexOf(JspViewHandlerImpl.FORM_STATE_MARKER, lastMarkerEnd);
}
else
{
//replace url_marker
realWriter.write(bodyStr, lastMarkerEnd, url_marker - lastMarkerEnd);
if (stateManager instanceof MyfacesStateManager)
{
((MyfacesStateManager)stateManager).writeStateAsUrlParams(facesContext,
serializedView);
}
else
{
log.error("Current StateManager is no MyfacesStateManager and does not support saving state in url parameters.");
}
// lastMarkerEnd = url_marker + HtmlLinkRendererBase.URL_STATE_MARKER_LEN;
// url_marker = bodyStr.indexOf(HtmlLinkRendererBase.URL_STATE_MARKER, lastMarkerEnd);
lastMarkerEnd = url_marker + URL_STATE_MARKER_LEN;
url_marker = bodyStr.indexOf(URL_STATE_MARKER, lastMarkerEnd);
}
}
realWriter.write(bodyStr, lastMarkerEnd, bodyStr.length() - lastMarkerEnd);
//}
// else
//{
// bodyContent.writeOut(getPreviousOut());
//}
//Now we do endDocument on the real responseWriter.
realWriter.endDocument();
realWriter.flush();
}
}
catch (Exception e)
{
log.debug("Error writing serialized page", e);
//System.err.println("CbpViewHandler.writeOutResponseAndClientState(): "+e.getClass().getName()+" : "+e.getMessage());
log.error(""+e.getClass().getName()+" : "+e.getMessage());
System.out.println("______________xxxxxxxxxxxxxxx1");
e.printStackTrace();
System.out.println("______________xxxxxxxxxxxxxxx2");
//throw new JspException(e);
try {
ResponseWriter bufferWriter = facesContext.getResponseWriter();
bufferWriter.flush();
//now we switch to real output
ResponseWriter realWriter = bufferWriter.cloneWithWriter(getRealResponseWriter(facesContext));
facesContext.setResponseWriter(realWriter);
String bodyStr = getOutputAsString(bufferWriter);
realWriter.write(bodyStr);
}
catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
if (log.isTraceEnabled()) {
log.trace("leaving ViewTag.doAfterBody");
//return super.doAfterBody();
}
}
/**
* <p>
* Get the content that was buffered to the "fake" writer.
* </p>
* @param bufferWriter
* @return
*/
private String getOutputAsString(ResponseWriter bufferWriter) {
if(bufferWriter instanceof HtmlStringBufferedResponseWriter) {
HtmlStringBufferedResponseWriter responseWriter = (HtmlStringBufferedResponseWriter)bufferWriter;
StringWriter writer = responseWriter.getStringWriter();
//BlockCacheResponseWriter blockWriter = (BlockCacheResponseWriter)bufferWriter;
return writer.getBuffer().toString();
} else
return CoreConstants.EMPTY;
}
/**
* <p>
* TODO tryggvil describe method getRealResponseWriter
* </p>
* @param facesContext
* @return
*/
private Writer getRealResponseWriter(FacesContext facesContext) {
HttpServletResponse response = (HttpServletResponse)facesContext.getExternalContext().getResponse();
try {
return response.getWriter();
}
catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
//return null;
throw new RuntimeException(e);
}
}
/**
* @see javax.faces.application.ViewHandler#restoreView(javax.faces.context.FacesContext, java.lang.String)
*/
public UIViewRoot restoreView(FacesContext ctx, String viewId) {
// if (getStateManager().isSavingStateInClient(ctx)) {
// //get the state from the client
return getStateManager().restoreView(ctx,viewId, calculateRenderKitId(ctx));
// }
// UIViewRoot currentViewRoot = (UIViewRoot) ctx.getExternalContext().getSessionMap().get(SESSION_KEY_CURRENT_VIEW);
// if (currentViewRoot == null) {
// return null;
// } else if (currentViewRoot.getViewId().equals(viewId)) {
// return currentViewRoot;
// }
// return null;
}
protected ViewManager getViewManager(FacesContext context){
return ViewManager.getInstance(context);
}
/* (non-Javadoc)
* @see net.sourceforge.smile.application.CbpViewHandlerImpl#createView(javax.faces.context.FacesContext, java.lang.String)
*/
public UIViewRoot createView(FacesContext ctx, String viewId) {
//return getParentViewHandler().createView(arg0, arg1);
ViewNode node = getViewManager(ctx).getViewNodeForContext(ctx);
if(node!=null){
if(node.isComponentBased()){
UIComponent comp = node.createComponent(ctx);
UIViewRoot root = null;
if(comp instanceof UIViewRoot){
root = (UIViewRoot)comp;
}
else {
root = new UIViewRoot();
root.setViewId(viewId);
if (comp != null) {
root.getChildren().add(comp);
}
}
//set the locale
root.setLocale(calculateLocale(ctx));
return root;
}
}
if(getParentViewHandler()!=null){
return getParentViewHandler().createView(ctx,viewId);
}
else{
//return createView(ctx,vewId);
throw new RuntimeException ("No parent ViewHandler");
}
}
/**
* @see javax.faces.application.ViewHandler#createView(javax.faces.context.FacesContext, java.lang.String)
*/
public UIViewRoot createViewOld(FacesContext ctx, String viewId) {
UIViewRoot ret = new UIViewRoot();
ret.setViewId(viewId);
// TODO : Hack to allow unit tests to select empty views
if(viewId.startsWith("unittesttree.")) {
return ret;
}
try {
Class descriptorClazz = getDescriptorClassNameForViewId(viewId);
if(descriptorClazz == null) {
// JSP page....
} else {
if(Page.class.isAssignableFrom(descriptorClazz)) {
Page page = (Page) descriptorClazz.newInstance();
page.init(ctx,ret);
} else {
throw new RuntimeException("CbpViewHandler: Descriptor Class for '" + viewId + "' must implement net.sourceforge.smile.context.Page !");
}
}
} catch(IllegalAccessException e) {
throw new RuntimeException("CbpViewHandler: Please make sure that the default constructor for descriptor class of <" + viewId + "> is public.",e);
} catch(InstantiationException e) {
throw new RuntimeException("CbpViewHandler: An exception was generated by the default constructor of the descriptor class of <" + viewId + ">.",e);
} catch(Throwable t) {
throw new RuntimeException("CbpViewHandler: Descriptor Class for '" + viewId + "' threw an exception during initialize() !",t);
}
//set the locale
ret.setLocale(calculateLocale(ctx));
//set the view on the session
//ctx.getExternalContext().getSessionMap().put(net.sourceforge.smile.application.CbpStateManagerImpl.SESSION_KEY_CURRENT_VIEW,ret);
return ret;
}
/**
* @see javax.faces.application.ViewHandler#getStateManager()
*/
public StateManager getStateManager() {
FacesContext facesContext = FacesContext.getCurrentInstance();
Application application = facesContext.getApplication();
return application.getStateManager();
}
/**
* @see javax.faces.application.ViewHandler#writeState(javax.faces.context.FacesContext)
*/
public void writeState(FacesContext ctx) throws IOException {
StateManager.SerializedView serializedView = null;
if (null != (serializedView = getStateManager().saveSerializedView(ctx))) {
getStateManager().writeState(ctx,serializedView);
}
}
/**
* @see javax.faces.application.ViewHandler#getViewIdPath(javax.faces.context.FacesContext, java.lang.String)
*/
public String getViewIdPath(FacesContext ctx, String viewId) {
// TODO implement conversion
if (viewId != null && viewId.startsWith("/")) {
return viewId.substring(1);
} else {
return viewId;
}
}
/**
* @see javax.faces.application.ViewHandler#calculateLocale(javax.faces.context.FacesContext)
*/
public Locale calculateLocale(FacesContext ctx) {
IWContext iwc = IWContext.getIWContext(ctx);
return iwc.getCurrentLocale();
}
/**
* make sure a ResponseWriter instance is set on the component
*/
private void initializeResponseWriter(FacesContext ctx) throws FacesException {
//check if running in httpservlet environment
//boolean httpServletEnv = true;
StringWriter bufferWriter = new StringWriter();
if (!(ctx.getExternalContext().getRequest() instanceof HttpServletRequest)) {
throw new RuntimeException("CbpViewHandler: idegaWeb currently does not support environments other than Http Servlet Environment.");
}
HttpServletRequest request = (HttpServletRequest) ctx.getExternalContext().getRequest();
//HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
String contextType = "text/html";
String characterEncoding = request.getCharacterEncoding();
/* if (ctx instanceof BridgeFacesContext) { // ICEfaces
BridgeFacesContext iceFacesContext = (BridgeFacesContext) ctx;
try {
iceFacesContext.createAndSetResponseWriter();
} catch (IOException e) {
throw new FacesException(e.getMessage(),e);
}
return;
}
*/
//try {
//This responsewriter is first constructed with a buffer that is later written out.
ResponseWriter responseWriter = new HtmlStringBufferedResponseWriter(bufferWriter,contextType,characterEncoding);
// ResponseWriter responseWriter = new ResponseWriterImplDecorated(response.getWriter(),contextType,characterEncoding);
ctx.setResponseWriter(responseWriter);
//} catch (IOException e) {
// throw new FacesException(e.getMessage(),e);
//}
}
/**
* Recursive operation to render a specific component in the view tree.
*
* @param component
* @param context
*/
private void renderComponent(UIComponent component, FacesContext ctx) {
try {
component.encodeBegin(ctx);
if(component.getRendersChildren()) {
component.encodeChildren(ctx);
} else {
Iterator it;
UIComponent currentChild;
it = component.getChildren().iterator();
while(it.hasNext()) {
currentChild = (UIComponent) it.next();
renderComponent(currentChild,ctx);
}
}
//if (component instanceof Screen) {
// writeState(ctx);
//}
component.encodeEnd(ctx);
} catch(IOException e) {
log.error("Component <" + component.getId() + "> could not render ! Continuing rendering of view <" + ctx.getViewRoot().getViewId() + ">...");
}
}
/**
* This function is responsible for finding the descripto class for a given
* viewId. The policy may change over time, like supporting multiple packages
* or mory flexible mapping of viewIds to descriptor classes,etc..
*
* @param viewId
* @return the descriptor class for a given viewId, or null if no descriptor found.
*/
private Class getDescriptorClassNameForViewId(String viewId) {
Class ret = null;
String className;
FacesContext ctx = FacesContext.getCurrentInstance();
// TODO : We should implement a configurable scheme with more than one package.
if(viewId.endsWith(".jsf") || viewId.endsWith(".jsp")) {
String shortClassName = viewId.substring(0,viewId.length()-4);
if(shortClassName.startsWith("/")) {
shortClassName = shortClassName.substring(1,shortClassName.length());
}
className = getDescriptorPackage(ctx) + shortClassName + getDescriptorPostfix(ctx);
try {
ret = RefactorClassRegistry.forName(className);
} catch(ClassNotFoundException e) {
ret = null;
}
}
return ret;
}
/**
* Determines the package where the descriptor classes are located.
* @param context
* @return
*/
private String getDescriptorPackage(FacesContext context) {
String ret = ""; // Default package.
String temp;
// Try to determine descriptor package...
temp = context.getExternalContext().getInitParameter("net.sourceforge.smile.descriptor.package");
if(temp != null) {
ret = temp;
if(!ret.endsWith(".")) {
ret = ret + ".";
}
}
return ret;
}
/**
* Determines the postfix for the descriptor class.
* @param context
* @return
*/
private String getDescriptorPostfix(FacesContext context) {
String ret = ""; // Default.
String temp;
// Try to determine descriptor package...
temp = context.getExternalContext().getInitParameter("net.sourceforge.smile.descriptor.postfix");
if(temp != null) {
ret = temp;
}
return ret;
}
/**
* @see javax.faces.application.ViewHandler#calculateRenderKitId(javax.faces.context.FacesContext)
*/
public String calculateRenderKitId(FacesContext ctx) {
return RenderKitFactory.HTML_BASIC_RENDER_KIT;
}
/**
* @see javax.faces.application.ViewHandler#getActionURL(javax.faces.context.FacesContext, java.lang.String)
*/
public String getActionURL(FacesContext ctx, String viewId) {
// TODO Look into this:
//return ctx.getExternalContext().encodeActionURL(viewId);
// TODO implement conversion
if (viewId != null && viewId.startsWith("/")) {
return viewId.substring(1);
} else {
return viewId;
}
}
/**
* @see javax.faces.application.ViewHandler#getResourceURL(javax.faces.context.FacesContext, java.lang.String)
*/
public String getResourceURL(FacesContext ctx, String path) {
// TODO Auto-generated method stub
String resourceUrl = ctx.getExternalContext().encodeResourceURL(path);
return resourceUrl;
}
protected ViewHandler getParentViewHandler(){
return this.parentViewHandler;
}
protected void setParentViewHandler(ViewHandler viewHandler){
this.parentViewHandler=viewHandler;
}
}