/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the license at * https://jsftemplating.dev.java.net/cddl1.html or * jsftemplating/cddl1.txt. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at jsftemplating/cddl1.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * you own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. */ package com.sun.jsftemplating.layout; import com.sun.jsftemplating.layout.descriptors.LayoutComponent; import com.sun.jsftemplating.layout.descriptors.LayoutDefinition; import com.sun.jsftemplating.layout.descriptors.LayoutElement; import com.sun.jsftemplating.layout.descriptors.LayoutFacet; import com.sun.jsftemplating.layout.descriptors.Resource; import com.sun.jsftemplating.util.Util; import com.sun.jsftemplating.util.LogUtil; import com.sun.jsftemplating.util.fileStreamer.Context; import com.sun.jsftemplating.util.fileStreamer.FacesStreamerContext; import com.sun.jsftemplating.util.fileStreamer.FileStreamer; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Iterator; import java.util.Locale; import java.util.Map; import javax.faces.FactoryFinder; import javax.faces.application.StateManager; import javax.faces.application.StateManager.SerializedView; import javax.faces.application.ViewHandler; import javax.faces.component.UIComponent; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.RenderKit; import javax.faces.render.RenderKitFactory; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // FIXME: Things to consider: // FIXME: - What is necessary to support Portlets... // FIXME: - Should I attempt to clean up old unused UIComponents? // FIXME: - f:view supported setting locale, I should too... /** *
This class provides a custom ViewHandler
that is able to
* create and populate a UIViewRoot
from a
* {@link LayoutDefinition}. This is often defined by an XML document,
* the default implementation's DTD is defined in
* layout.dtd
.
Besides the default ViewHandler
behavior, this class is
* responsible for instantiating a {@link LayoutViewRoot} and using the
* given viewId
as the {@link LayoutDefinition} key. It
* will obtain the {@link LayoutDefinition}, initialize the declared
* {@link Resource}s, and instantiate UIComponent
tree using
* the {@link LayoutDefinition}'s declared {@link LayoutComponent}
* structure. During rendering, it delegates to the
* {@link LayoutDefinition}.
Constructor.
* * @param oldViewHandler The oldViewHandler
.
*/
public LayoutViewHandler(ViewHandler oldViewHandler) {
_oldViewHandler = oldViewHandler;
}
/**
* This method is invoked when restoreView does not yield a UIViewRoot * (initial requests and new pages).
* * This implementation should work with both
* LayoutDefinition
-based pages as well as traditional
* JSP pages.
If this is a resource request, this method will handle the * request.
*/ public UIViewRoot serveResource(FacesContext context, String path) { // Mark the response complete so no more processing occurs context.responseComplete(); // Create dummy UIViewRoot UIViewRoot root = new LayoutViewRoot(); root.setRenderKitId("dummy"); // Setup the FacesStreamerContext Context fsContext = new FacesStreamerContext(context); fsContext.setAttribute("filePath", path); // Get the HttpServletResponse Object obj = context.getExternalContext().getResponse(); HttpServletResponse resp = null; if (obj instanceof HttpServletResponse) { resp = (HttpServletResponse) obj; // We have an HttpServlet response, do some extra stuff... // Check the last modified time to see if we need to serve the resource long mod = fsContext.getContentSource().getLastModified(fsContext); if (mod != -1) { long ifModifiedSince = ((HttpServletRequest) context.getExternalContext().getRequest()). getDateHeader("If-Modified-Since"); // Round down to the nearest second for a proper compare if (ifModifiedSince < (mod / 1000 * 1000)) { // A ifModifiedSince of -1 will always be less resp.setDateHeader("Last-Modified", mod); } else { // Set not modified header and complete response resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return root; } } } // Stream the content try { FileStreamer.getFileStreamer().streamContent(fsContext); } catch (FileNotFoundException ex) { if (LogUtil.infoEnabled()) { LogUtil.info("WEBUI0004", (Object) path); } if (resp != null) { try { ((HttpServletResponse) resp).sendError( HttpServletResponse.SC_NOT_FOUND); } catch (IOException ioEx) { // Ignore } } } catch (IOException ex) { if (LogUtil.infoEnabled()) { LogUtil.info("WEBUI0004", (Object) path); if (LogUtil.fineEnabled()) { LogUtil.fine( "Resource (" + path + ") not available!", ex); } } } // Return dummy UIViewRoot to avoid NPE return root; } /** *This method checks the given viewId and returns a the path to the * requested resource if it refers to a resource. Resources are * things like JavaScript files, images, etc. Basically anything that * is not a JSF page that you'd like to serve up via the FacesServlet. * Serving resources this way allows you to bundle the resources in a * jar file, this is useful if you want to package up part of an app * (or a JSF component) in a single file.
* * A request for a resource must be prefixed by the resource prefix,
* see @{link #getResourcePrefix}. This prefix must also be mapped to
* the FacesServlet
in order for this class to handle the
* request.
This method returns the prefix that a URL must contain in order to
* retrieve a "resource" through this ViewHandler
.
The prefix itself does not manifest itself in the file system / * classpath.
* *If the prefix is not set, then an init parameter (see * {@link #RESOURCE_PREFIX}) will be checked. If that is still not * specified, then the {@link DEFAULT_RESOURCE_PREFIX} will be * used.
*/ public String getResourcePrefix() { if (_resourcePrefix == null) { // Check to see if it's specified by a context param // Get context parameter map (initParams in JSF are context params) _resourcePrefix = (String) FacesContext.getCurrentInstance(). getExternalContext().getInitParameterMap().get(RESOURCE_PREFIX); if (_resourcePrefix == null) { // Still not set, use default _resourcePrefix = DEFAULT_RESOURCE_PREFIX; } } return _resourcePrefix; } /** * This method allows a user to set the resource prefix which will be
* checked to obtain a resource via this Viewhandler
.
* Currently, only 1 prefix is supported. The prefix itself does not
* manifest itself in the file system / classpath.
This method iterates over the child {@link LayoutElement}s of the
* given elt
to create UIComponent
s for each
* {@link LayoutComponent}.
FacesContext
.
* @param parent The parent UIComponent
of the
* UIComponent
to be found or created.
* @param elt The LayoutElement
driving everything.
*/
public static void buildUIComponentTree(FacesContext context, UIComponent parent, LayoutElement elt) {
// FIXME: Consider processing *ALL* LayoutElements so that This implementation relies on the default behavior to reconstruct * the UIViewRoot.
* *...
*/ public UIViewRoot restoreView(FacesContext context, String viewId) { Map Take any appropriate action to either immediately write out the
* current state information (by calling
* StateManager.writeState
, or noting where state
* information should later be written.
FacesContext
for the current request
*
* @exception IOException if an input/output error occurs
*/
public void writeState(FacesContext context) throws IOException {
// Check to see if we should delegate back to the legacy ViewHandler
UIViewRoot root = context.getViewRoot();
if ((root == null) || !(root instanceof LayoutViewRoot)
|| (((LayoutViewRoot) root).
getLayoutDefinition(context) == null)) {
// Use old behavior...
_oldViewHandler.writeState(context);
} else {
// b/c we pre-processed the ViewTree, we can just add it...
StateManager stateManager =
context.getApplication().getStateManager();
SerializedView view = stateManager.saveSerializedView(context);
// New versions of JSF 1.2 changed the contract so that state is
// always written (client and server state saving)
stateManager.writeState(context, view);
}
}
/**
* Return a URL suitable for rendering (after optional encoding
* perfomed by the encodeResourceURL()
method of
* ExternalContext
that selects the specifed web
* application resource. If the specified path starts with a slash,
* it must be treated as context relative; otherwise, it must be
* treated as relative to the action URL of the current view.
FacesContext
for the current request
* @param path Resource path to convert to a URL
*
* @exception IllegalArgumentException If viewId
is not
* valid for this ViewHandler
.
*/
public String getResourceURL(FacesContext context, String path) {
return _oldViewHandler.getResourceURL(context, path);
}
/**
* Return a URL suitable for rendering (after optional encoding
* performed by the encodeActionURL()
method of
* ExternalContext
that selects the specified view
* identifier.
FacesContext
for this request
* @param viewId View identifier of the desired view
*
* @exception IllegalArgumentException If viewId
is not
* valid for this ViewHandler
.
*/
public String getActionURL(FacesContext context, String viewId) {
return _oldViewHandler.getActionURL(context, viewId);
}
/**
* Returns an appropriate Locale
to use for this and
* subsequent requests for the current client.
FacesContext
for the current request
*
* @exception NullPointerException if context
is
* null
*/
public Locale calculateLocale(FacesContext context) {
return _oldViewHandler.calculateLocale(context);
}
/**
* Return an appropriate renderKitId
for this
* and subsequent requests from the current client.
The default return value is
* javax.faces.render.RenderKitFactory.HTML_BASIC_RENDER_KIT
.
*
FacesContext
for the current request.
*/
public String calculateRenderKitId(FacesContext context) {
return _oldViewHandler.calculateRenderKitId(context);
}
/**
* This is the key that may be used to identify the clientId of the * UIComponent that is to be updated via an Ajax request.
*/ public static final String AJAX_REQ_KEY = "ajaxReq"; public static final String RESTORE_VIEW_ID = "_resViewID"; /** *This is the default prefix that must be included on all requests * for resources.
*/ public static final String DEFAULT_RESOURCE_PREFIX = "/resource"; /** * The name of the context-param
to set the resource
* prefix.