com.sun.faces.application.ViewHandlerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.faces.application.ViewHandlerImpl.java

Source

/* 
 * $Id: ViewHandlerImpl.java,v 1.45.12.2.2.1 2006/04/12 19:32:04 ofung Exp $ 
 */

/*
 * 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://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
 */

// ViewHandlerImpl.java 

package com.sun.faces.application;

import com.sun.faces.RIConstants;
import com.sun.faces.util.Util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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.render.RenderKitFactory;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.jstl.core.Config;

import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import javax.faces.application.StateManager;
import javax.faces.application.StateManager.SerializedView;

/**
 * <B>ViewHandlerImpl</B> is the default implementation class for ViewHandler.
 *
 * @version $Id: ViewHandlerImpl.java,v 1.45.12.2.2.1 2006/04/12 19:32:04 ofung Exp $
 * @see javax.faces.application.ViewHandler
 */
public class ViewHandlerImpl extends ViewHandler {

    //
    // Private/Protected Constants
    //
    private static final Log log = LogFactory.getLog(ViewHandlerImpl.class);

    /**
     * <p>The <code>request</code> scoped attribute to store the
     * {@link javax.faces.webapp.FacesServlet} path of the original
     * request.</p>
     */
    private static final String INVOCATION_PATH = RIConstants.FACES_PREFIX + "INVOCATION_PATH";

    //
    // Relationship Instance Variables
    //

    /**
     * <p>Store the value of <code>DEFAULT_SUFFIX_PARAM_NAME</code>
     * or, if that isn't defined, the value of <code>DEFAULT_SUFFIX</code>
     */
    private String contextDefaultSuffix;

    public ViewHandlerImpl() {
        if (log.isDebugEnabled()) {
            log.debug("Created ViewHandler instance ");
        }
    }

    public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {

        if (null == context || null == viewToRender) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " context " + context + " viewToRender " + viewToRender;
            throw new NullPointerException(message);
        }

        ApplicationAssociate associate = ApplicationAssociate.getInstance(context.getExternalContext());

        if (null != associate) {
            associate.responseRendered();
        }
        String requestURI = viewToRender.getViewId();
        if (log.isDebugEnabled()) {
            log.debug("About to render view " + requestURI);
        }

        String mapping = getFacesMapping(context);
        String newViewId = requestURI;
        // If we have a valid mapping (meaning we were invoked via the
        // FacesServlet) and we're extension mapped, do the replacement.
        if (mapping != null && !isPrefixMapped(mapping)) {
            if (log.isDebugEnabled()) {
                log.debug("Found URL pattern mapping to FacesServlet " + mapping);
            }
            newViewId = convertViewId(context, requestURI);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Found no URL patterns mapping to FacesServlet ");
            }
        }

        viewToRender.setViewId(newViewId);

        // update the JSTL locale attribute in request scope so that JSTL
        // picks up the locale from viewRoot. This attribute must be updated
        // before the JSTL setBundle tag is called because that is when the
        // new LocalizationContext object is created based on the locale.
        // PENDING: this only works for servlet based requests
        if (context.getExternalContext().getRequest() instanceof ServletRequest) {
            Config.set((ServletRequest) context.getExternalContext().getRequest(), Config.FMT_LOCALE,
                    context.getViewRoot().getLocale());
        }
        if (log.isTraceEnabled()) {
            log.trace("Before dispacthMessage to newViewId " + newViewId);
        }
        context.getExternalContext().dispatch(newViewId);
        if (log.isTraceEnabled()) {
            log.trace("After dispacthMessage to newViewId " + newViewId);
        }

    }

    public UIViewRoot restoreView(FacesContext context, String viewId) {
        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " context " + context;
            throw new NullPointerException(message);
        }

        ExternalContext extContext = context.getExternalContext();

        // set the request character encoding. NOTE! This MUST be done
        // before any request praameter is accessed.

        /*
        HttpServletRequest request =
        (HttpServletRequest) extContext.getRequest();
        */
        Map headerMap = extContext.getRequestHeaderMap();
        String contentType = null, charEnc = null;

        // look for a charset in the Content-Type header first.
        if (null != (contentType = (String) headerMap.get("Content-Type"))) {
            // see if this header had a charset
            String charsetStr = "charset=";
            int len = charsetStr.length(), i = 0;

            // if we have a charset in this Content-Type header AND it
            // has a non-zero length.
            if (-1 != (i = contentType.indexOf(charsetStr)) && (i + len < contentType.length())) {
                charEnc = contentType.substring(i + len);
            }
        }
        // failing that, look in the session for a previously saved one
        if (null == charEnc) {
            if (null != extContext.getSession(false)) {
                charEnc = (String) extContext.getSessionMap().get(CHARACTER_ENCODING_KEY);
            }
        }
        if (null != charEnc) {
            try {
                if (log.isTraceEnabled()) {
                    log.trace("set character encoding on request to " + charEnc);
                }
                Object request = extContext.getRequest();
                if (request instanceof ServletRequest) {
                    ((ServletRequest) request).setCharacterEncoding(charEnc);
                }
            } catch (java.io.UnsupportedEncodingException uee) {
                if (log.isErrorEnabled()) {
                    log.error(uee.getMessage(), uee);
                }
                throw new FacesException(uee);
            }
        }

        String mapping = getFacesMapping(context);
        UIViewRoot viewRoot = null;

        if (mapping != null && !isPrefixMapped(mapping)) {
            viewId = convertViewId(context, viewId);
        }

        // maping could be null if a non-faces request triggered
        // this response.
        if (extContext.getRequestPathInfo() == null && mapping != null && isPrefixMapped(mapping)) {
            // this was probably an initial request
            // send them off to the root of the web application
            try {
                context.responseComplete();
                if (log.isDebugEnabled()) {
                    log.debug("Response Complete for" + viewId);
                }
                extContext.redirect(extContext.getRequestContextPath());
            } catch (IOException ioe) {
                throw new FacesException(ioe);
            }
        } else {
            // this is necessary to allow decorated impls.
            ViewHandler outerViewHandler = context.getApplication().getViewHandler();
            String renderKitId = outerViewHandler.calculateRenderKitId(context);
            viewRoot = Util.getStateManager(context).restoreView(context, viewId, renderKitId);
        }

        return viewRoot;
    }

    public UIViewRoot createView(FacesContext context, String viewId) {
        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + "context " + context;
            throw new NullPointerException(message);
        }
        Locale locale = null;
        String renderKitId = null;

        // use the locale from the previous view if is was one which will be
        // the case if this is called from NavigationHandler. There wouldn't be 
        // one for the initial case.
        if (context.getViewRoot() != null) {
            locale = context.getViewRoot().getLocale();
            renderKitId = context.getViewRoot().getRenderKitId();
        }
        UIViewRoot result = new UIViewRoot();
        result.setViewId(viewId);
        if (log.isDebugEnabled()) {
            log.debug("Created new view for " + viewId);
        }
        // PENDING(): not sure if we should set the RenderKitId here.
        // The UIViewRoot ctor sets the renderKitId to the default
        // one.
        // if there was no locale from the previous view, calculate the locale 
        // for this view.
        if (locale == null) {
            locale = context.getApplication().getViewHandler().calculateLocale(context);
            if (log.isDebugEnabled()) {
                log.debug("Locale for this view as determined by calculateLocale " + locale.toString());
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Using locale from previous view " + locale.toString());
            }
        }

        if (renderKitId == null) {
            renderKitId = context.getApplication().getViewHandler().calculateRenderKitId(context);
            if (log.isDebugEnabled()) {
                log.debug("RenderKitId for this view as determined by calculateRenderKitId " + renderKitId);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Using renderKitId from previous view " + renderKitId);
            }
        }

        result.setLocale(locale);
        result.setRenderKitId(renderKitId);

        return result;
    }

    public Locale calculateLocale(FacesContext context) {

        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + "context " + context;
            throw new NullPointerException(message);
        }

        Locale result = null;
        // determine the locales that are acceptable to the client based on the 
        // Accept-Language header and the find the best match among the 
        // supported locales specified by the client.
        Iterator locales = context.getExternalContext().getRequestLocales();
        while (locales.hasNext()) {
            Locale perf = (Locale) locales.next();
            result = findMatch(context, perf);
            if (result != null) {
                break;
            }
        }
        // no match is found.
        if (result == null) {
            if (context.getApplication().getDefaultLocale() == null) {
                result = Locale.getDefault();
            } else {
                result = context.getApplication().getDefaultLocale();
            }
        }
        return result;
    }

    public String calculateRenderKitId(FacesContext context) {

        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + "context " + context;
            throw new NullPointerException(message);
        }
        String result = null;

        if (null == (result = context.getApplication().getDefaultRenderKitId())) {
            result = RenderKitFactory.HTML_BASIC_RENDER_KIT;
        }
        return result;
    }

    /**
     * Attempts to find a matching locale based on <code>perf</code> and
     * list of supported locales, using the matching algorithm
     * as described in JSTL 8.3.2.
     */
    protected Locale findMatch(FacesContext context, Locale perf) {
        Locale result = null;
        Iterator it = context.getApplication().getSupportedLocales();
        while (it.hasNext()) {
            Locale supportedLocale = (Locale) it.next();

            if (perf.equals(supportedLocale)) {
                // exact match
                result = supportedLocale;
                break;
            } else {
                // Make sure the preferred locale doesn't have country
                // set, when doing a language match, For ex., if the
                // preferred locale is "en-US", if one of supported
                // locales is "en-UK", even though its language matches
                // that of the preferred locale, we must ignore it.
                if (perf.getLanguage().equals(supportedLocale.getLanguage())
                        && supportedLocale.getCountry().equals("")) {
                    result = supportedLocale;
                }
            }
        }
        // if it's not in the supported locales,
        if (null == result) {
            Locale defaultLocale = context.getApplication().getDefaultLocale();
            if (defaultLocale != null) {
                if (perf.equals(defaultLocale)) {
                    // exact match
                    result = defaultLocale;
                } else {
                    // Make sure the preferred locale doesn't have country
                    // set, when doing a language match, For ex., if the
                    // preferred locale is "en-US", if one of supported
                    // locales is "en-UK", even though its language matches
                    // that of the preferred locale, we must ignore it.
                    if (perf.getLanguage().equals(defaultLocale.getLanguage())
                            && defaultLocale.getCountry().equals("")) {
                        result = defaultLocale;
                    }
                }
            }
        }

        return result;
    }

    public void writeState(FacesContext context) throws IOException {
        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + "context " + context;
            throw new NullPointerException(message);
        }

        if (log.isTraceEnabled()) {
            log.trace("Begin writing state to response for viewId" + context.getViewRoot().getViewId());
        }
        context.getResponseWriter().writeText(RIConstants.SAVESTATE_FIELD_MARKER, null);
        if (log.isTraceEnabled()) {
            log.trace("End writing state to response for viewId" + context.getViewRoot().getViewId());
        }

    }

    public String getActionURL(FacesContext context, String viewId) {

        if (context == null || viewId == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + "context " + context + " viewId " + viewId;
            throw new NullPointerException(message);
        }

        if (viewId.charAt(0) != '/') {
            String message = Util.getExceptionMessageString(Util.ILLEGAL_VIEW_ID_ID, new Object[] { viewId });
            if (log.isErrorEnabled()) {
                log.error(message + " " + viewId);
            }
            throw new IllegalArgumentException(message);
        }

        // Acquire the context path, which we will prefix on all results
        String contextPath = context.getExternalContext().getRequestContextPath();

        // Acquire the mapping used to execute this request (if any)
        String mapping = getFacesMapping(context);

        // If no mapping can be identified, just return a server-relative path
        if (mapping == null) {
            return contextPath + viewId;
        }

        // Deal with prefix mapping
        if (isPrefixMapped(mapping)) {
            if (mapping.equals("/*")) {
                return contextPath + viewId;
            } else {
                return contextPath + mapping + viewId;
            }
        }

        // Deal with extension mapping
        int period = viewId.lastIndexOf(".");
        if (period < 0) {
            return contextPath + viewId + mapping;
        } else if (!viewId.endsWith(mapping)) {
            return contextPath + viewId.substring(0, period) + mapping;
        } else {
            return contextPath + viewId;
        }

    }

    public String getResourceURL(FacesContext context, String path) {

        if (path.startsWith("/")) {
            return context.getExternalContext().getRequestContextPath() + path;
        } else {
            return (path);
        }

    }

    /**
     * <p>Returns the URL pattern of the
     * {@link javax.faces.webapp.FacesServlet} that
     * is executing the current request.  If there are multiple
     * URL patterns, the value returned by
     * <code>HttpServletRequest.getServletPath()</code> and
     * <code>HttpServletRequest.getPathInfo()</code> is
     * used to determine which mapping to return.</p>
     * If no mapping can be determined, it most likely means
     * that this particular request wasn't dispatched through
     * the {@link javax.faces.webapp.FacesServlet}.
     *
     * @param context the {@link FacesContext} of the current request
     * @return the URL pattern of the {@link javax.faces.webapp.FacesServlet}
     *         or <code>null</code> if no mapping can be determined
     * @throws NullPointerException if <code>context</code> is null
     */
    private String getFacesMapping(FacesContext context) {
        // PENDING (rlubke) Need to handle the Portlet case

        if (context == null) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " context " + context;
            throw new NullPointerException(message);
        }

        // Check for a previously stored mapping   
        ExternalContext extContext = context.getExternalContext();
        String mapping = (String) extContext.getRequestMap().get(INVOCATION_PATH);

        if (mapping == null) {

            Object request = extContext.getRequest();
            String servletPath = null;
            String pathInfo = null;

            // first check for javax.servlet.forward.servlet_path
            // and javax.servlet.forward.path_info for non-null
            // values.  if either is non-null, use this
            // information to generate determine the mapping.

            if (request instanceof HttpServletRequest) {
                servletPath = extContext.getRequestServletPath();
                pathInfo = extContext.getRequestPathInfo();
            }

            mapping = getMappingForRequest(servletPath, pathInfo);
            if (mapping == null) {
                String message = Util.getExceptionMessageString(Util.FACES_SERVLET_MAPPING_CANNOT_BE_DETERMINED_ID,
                        new Object[] { servletPath });
                if (log.isWarnEnabled()) {
                    log.warn(message);
                }
                //throw new FacesException(message);
            }
        }

        if (mapping != null) {
            extContext.getRequestMap().put(INVOCATION_PATH, mapping);
        }
        if (log.isDebugEnabled()) {
            log.debug("URL pattern of the FacesServlet executing the current request " + mapping);
        }
        return mapping;
    }

    /**
     * <p>Return the appropriate {@link javax.faces.webapp.FacesServlet} mapping
     * based on the servlet path of the current request.</p>
     *
     * @param servletPath the servlet path of the request
     * @param pathInfo    the path info of the request
     * @see HttpServletRequest#getServletPath()
     */
    private String getMappingForRequest(String servletPath, String pathInfo) {

        if (servletPath == null) {
            return null;
        }
        if (log.isTraceEnabled()) {
            log.trace("servletPath " + servletPath);
            log.trace("pathInfo " + pathInfo);
        }

        // If the path returned by HttpServletRequest.getServletPath()
        // returns a zero-length String, then the FacesServlet has
        // been mapped to '/*'.
        if (servletPath.length() == 0) {
            return "/*";
        }

        // presence of path info means we were invoked
        // using a prefix path mapping
        if (pathInfo != null) {
            return servletPath;
        } else if (pathInfo == null && servletPath.indexOf('.') < 0) {
            // if pathInfo is null and no '.' is present, assume the
            // FacesServlet was invoked using prefix path but without
            // any pathInfo - i.e. GET /contextroot/faces or
            // GET /contextroot/faces/
            return servletPath;
        } else {
            // Servlet invoked using extension mapping
            return servletPath.substring(servletPath.lastIndexOf('.'));
        }
    }

    /**
     * <p>Returns true if the provided <code>url-mapping</code> is
     * a prefix path mapping (starts with <code>/</code>).</p>
     *
     * @param mapping a <code>url-pattern</code>
     * @return true if the mapping starts with <code>/</code>
     */
    private static boolean isPrefixMapped(String mapping) {
        return (mapping.charAt(0) == '/');
    }

    /**
     * <p>Adjust the viewID per the requirements of {@link #renderView}.</p>
     *
     * @param context current {@link FacesContext}
     * @param viewId  incoming view ID
     * @return the view ID with an altered suffix mapping (if necessary)
     */
    private String convertViewId(FacesContext context, String viewId) {
        synchronized (this) {
            if (contextDefaultSuffix == null) {
                contextDefaultSuffix = context.getExternalContext()
                        .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
                if (contextDefaultSuffix == null) {
                    contextDefaultSuffix = ViewHandler.DEFAULT_SUFFIX;
                }
                if (log.isDebugEnabled()) {
                    log.debug("contextDefaultSuffix " + contextDefaultSuffix);
                }
            }
        }
        String convertedViewId = viewId;
        // if the viewId doesn't already use the above suffix,
        // replace or append.
        if (!convertedViewId.endsWith(contextDefaultSuffix)) {
            StringBuffer buffer = new StringBuffer(convertedViewId);
            int extIdx = convertedViewId.lastIndexOf('.');
            if (extIdx != -1) {
                buffer.replace(extIdx, convertedViewId.length(), contextDefaultSuffix);
            } else {
                // no extension in the provided viewId, append the suffix
                buffer.append(contextDefaultSuffix);
            }
            convertedViewId = buffer.toString();
            if (log.isDebugEnabled()) {
                log.debug("viewId after appending the context suffix " + convertedViewId);
            }

        }
        return convertedViewId;
    }

}