All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.application.view.MultiViewHandler Maven / Gradle / Ivy

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

There is a newer version: 2.2.20
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.faces.application.view;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import javax.servlet.http.HttpServletResponse;

import com.sun.faces.RIConstants;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.*;

import java.util.concurrent.CopyOnWriteArraySet;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewParameter;
import javax.faces.component.UIViewRoot;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewDeclarationLanguageFactory;
import javax.faces.view.ViewMetadata;

/**
 * This {@link ViewHandler} implementation handles both JSP-based and
 * Facelets/PDL-based views.
 */
public class MultiViewHandler extends ViewHandler {

    // Log instance for this class
    private static final Logger logger = FacesLogger.APPLICATION.getLogger();

    private String[] configuredExtensions;
    private Set protectedViews;
    private boolean extensionsSet;
    
    private ViewDeclarationLanguageFactory vdlFactory;


    // ------------------------------------------------------------ Constructors


    public MultiViewHandler() {

        WebConfiguration config = WebConfiguration.getInstance();
              
        configuredExtensions = config.getConfiguredExtensions();
        extensionsSet = config.isSet(WebConfiguration.WebContextInitParameter.DefaultSuffix);
        vdlFactory = (ViewDeclarationLanguageFactory)
                FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
        protectedViews = new CopyOnWriteArraySet();

    }


    // ------------------------------------------------ Methods from ViewHandler


    /**
     * Call the default implementation of {@link javax.faces.application.ViewHandler#initView(javax.faces.context.FacesContext)}
     *
     * @see javax.faces.application.ViewHandler#initView(javax.faces.context.FacesContext)
     */
    @Override
    public void initView(FacesContext context) throws FacesException {
        super.initView(context);
    }


    /**
     * 

* Call {@link ViewDeclarationLanguage#renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot)} * if the view can be rendered. *

* * @see ViewHandler#renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot) */ public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException { Util.notNull("context", context); Util.notNull("viewToRender", viewToRender); vdlFactory.getViewDeclarationLanguage(viewToRender.getViewId()) .renderView(context, viewToRender); } /** *

* Call {@link ViewDeclarationLanguage#restoreView(javax.faces.context.FacesContext, String)}. *

* * @see ViewHandler#restoreView(javax.faces.context.FacesContext, String) */ public UIViewRoot restoreView(FacesContext context, String viewId) { Util.notNull("context", context); String actualViewId = derivePhysicalViewId(context, viewId, false); return vdlFactory.getViewDeclarationLanguage(actualViewId) .restoreView(context, actualViewId); } /** *

* Derive the actual view ID (i.e. the physical resource) and call * call {@link ViewDeclarationLanguage#createView(javax.faces.context.FacesContext, String)}. *

* * @see ViewHandler#restoreView(javax.faces.context.FacesContext, String) */ public UIViewRoot createView(FacesContext context, String viewId) { Util.notNull("context", context); String actualViewId = derivePhysicalViewId(context, viewId, false); return vdlFactory.getViewDeclarationLanguage(actualViewId).createView(context, actualViewId); } /** *

* This code is currently common to all {@link ViewHandlingStrategy} instances. *

* * @see ViewHandler#calculateLocale(javax.faces.context.FacesContext) */ public Locale calculateLocale(FacesContext context) { Util.notNull("context", context); 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 = 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; } /** *

* This code is currently common to all {@link ViewHandlingStrategy} instances. *

* * @see ViewHandler#calculateRenderKitId(javax.faces.context.FacesContext) */ public String calculateRenderKitId(FacesContext context) { Util.notNull("context", context); Map requestParamMap = context.getExternalContext() .getRequestParameterMap(); String result = requestParamMap.get( ResponseStateManager.RENDER_KIT_ID_PARAM); if (result == null) { if (null == (result = context.getApplication().getDefaultRenderKitId())) { result = RenderKitFactory.HTML_BASIC_RENDER_KIT; } } return result; } /** *

* This code is currently common to all {@link ViewHandlingStrategy} instances. *

* * @see ViewHandler#writeState(javax.faces.context.FacesContext) */ public void writeState(FacesContext context) throws IOException { Util.notNull("context", context); if (!context.getPartialViewContext().isAjaxRequest()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Begin writing marker for viewId " + context.getViewRoot().getViewId()); } WriteBehindStateWriter writer = WriteBehindStateWriter.getCurrentInstance(); if (writer != null) { writer.writingState(); } context.getResponseWriter() .write(RIConstants.SAVESTATE_FIELD_MARKER); if (logger.isLoggable(Level.FINE)) { logger.fine("End writing marker for viewId " + context.getViewRoot().getViewId()); } } } /** *

* This code is currently common to all {@link ViewHandlingStrategy} instances. *

* * @see ViewHandler#getActionURL(javax.faces.context.FacesContext, String) */ public String getActionURL(FacesContext context, String viewId) { String result = getActionURLWithoutViewProtection(context, viewId); // http://java.net/jira/browse/JAVASERVERFACES-2204 // PENDING: this code is optimized to be fast to write. // It must be optimized to be fast to run. // See git clone ssh://[email protected]/grizzly~git 1_9_36 for // how grizzly does this. ViewHandler viewHandler = context.getApplication().getViewHandler(); Set urlPatterns = viewHandler.getProtectedViewsUnmodifiable(); // Implement section 12.1 of the Servlet spec. boolean viewIdIsProtected = false; for (String cur : urlPatterns) { if (cur.equals(viewId)) { viewIdIsProtected = true; } if (viewIdIsProtected) { break; } } if (viewIdIsProtected) { StringBuilder builder = new StringBuilder(result); // If the result already has a query string... if (result.contains("?")) { // ...assume it also has one or more parameters, and // append an additional parameter. builder.append("&"); } else { // Otherwise, this is the first parameter in the result. builder.append("?"); } String rkId = viewHandler.calculateRenderKitId(context); ResponseStateManager rsm = RenderKitUtils.getResponseStateManager(context, rkId); String tokenValue = rsm.getCryptographicallyStrongTokenFromSession(context); builder.append(ResponseStateManager.NON_POSTBACK_VIEW_TOKEN_PARAM). append("=").append(tokenValue); result = builder.toString(); } return result; } private String getActionURLWithoutViewProtection(FacesContext context, String viewId) { Util.notNull("context", context); Util.notNull("viewId", viewId); if (0 == viewId.length() || viewId.charAt(0) != '/') { String message = MessageUtils.getExceptionMessageString( MessageUtils.ILLEGAL_VIEW_ID_ID, viewId); if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "jsf.illegal_view_id_error", viewId); } throw new IllegalArgumentException(message); } // Acquire the context path, which we will prefix on all results ExternalContext extContext = context.getExternalContext(); String contextPath = extContext.getRequestContextPath(); // Acquire the mapping used to execute this request (if any) String mapping = Util.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 (Util.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)) { for (String ext : configuredExtensions) { if (viewId.endsWith(ext)) { return (contextPath + viewId.substring(0, viewId.indexOf(ext)) + mapping); } } return (contextPath + viewId.substring(0, period) + mapping); } else { return (contextPath + viewId); } } /** *

* This code is currently common to all {@link ViewHandlingStrategy} instances. *

* * @see ViewHandler#getResourceURL(javax.faces.context.FacesContext, String) */ public String getResourceURL(FacesContext context, String path) { ExternalContext extContext = context.getExternalContext(); if (path.charAt(0) == '/') { return (extContext.getRequestContextPath() + path); } else { return path; } } @Override public String getBookmarkableURL(FacesContext context, String viewId, Map> parameters, boolean includeViewParams) { Map> params; if (includeViewParams) { params = getFullParameterList(context, viewId, parameters); } else { params = parameters; } ExternalContext ectx = context.getExternalContext(); return ectx.encodeActionURL(ectx.encodeBookmarkableURL(Util.getViewHandler(context).getActionURL(context, viewId), params)); } @Override public void addProtectedView(String urlPattern) { protectedViews.add(urlPattern); } @Override public Set getProtectedViewsUnmodifiable() { return Collections.unmodifiableSet(protectedViews); } @Override public boolean removeProtectedView(String urlPattern) { return protectedViews.remove(urlPattern); } /** * @see ViewHandler#getRedirectURL(javax.faces.context.FacesContext, String, java.util.Map, boolean) */ @Override public String getRedirectURL(FacesContext context, String viewId, Map> parameters, boolean includeViewParams) { String encodingFromContext = (String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY); if (null == encodingFromContext) { encodingFromContext = (String) context.getViewRoot().getAttributes(). get(RIConstants.FACELETS_ENCODING_KEY); } String responseEncoding; if (null == encodingFromContext) { try { responseEncoding = context.getExternalContext().getResponseCharacterEncoding(); } catch (Exception e) { if (logger.isLoggable(Level.FINE)) { String message = "Unable to obtain response character encoding from ExternalContext {0}. Using UTF-8."; message = MessageFormat.format(message, context.getExternalContext()); logger.log(Level.FINE, message, e); } responseEncoding = "UTF-8"; } } else { responseEncoding = encodingFromContext; } if (parameters != null) { Map> decodedParameters = new HashMap>(); for (Map.Entry> entry : parameters.entrySet()) { String string = entry.getKey(); List list = entry.getValue(); List values = new ArrayList(); for (Iterator it = list.iterator(); it.hasNext();) { String value = it.next(); try { value = URLDecoder.decode(value, responseEncoding); } catch(UnsupportedEncodingException e) { throw new RuntimeException("Unable to decode"); } values.add(value); } decodedParameters.put(string, values); } parameters = decodedParameters; } Map> params; if (includeViewParams) { params = getFullParameterList(context, viewId, parameters); } else { params = parameters; } ExternalContext ectx = context.getExternalContext(); return ectx.encodeActionURL(ectx.encodeRedirectURL(Util.getViewHandler(context).getActionURL(context, viewId), params)); } /** * @see ViewHandler#getViewDeclarationLanguage(javax.faces.context.FacesContext, String) */ @Override public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId) { return vdlFactory.getViewDeclarationLanguage(viewId); } @Override public String deriveViewId(FacesContext context, String rawViewId) { return derivePhysicalViewId(context, rawViewId, true); } @Override public String deriveLogicalViewId(FacesContext context, String rawViewId) { return derivePhysicalViewId(context, rawViewId, false); } // ------------------------------------------------------- Protected Methods /** *

if the specified mapping is a prefix mapping, and the provided * request URI (usually the value from ExternalContext.getRequestServletPath()) * starts with mapping + '/', prune the mapping from the * URI and return it, otherwise, return the original URI. * @param uri the servlet request path * @param mapping the FacesServlet mapping used for this request * @return the URI without additional FacesServlet mappings * @since 1.2 */ protected String normalizeRequestURI(String uri, String mapping) { if (mapping == null || !Util.isPrefixMapped(mapping)) { return uri; } else { int length = mapping.length() + 1; StringBuilder builder = new StringBuilder(length); builder.append(mapping).append('/'); String mappingMod = builder.toString(); boolean logged = false; while (uri.startsWith(mappingMod)) { if (!logged && logger.isLoggable(Level.WARNING)) { logged = true; logger.log(Level.WARNING, "jsf.viewhandler.requestpath.recursion", new Object[] {uri, mapping}); } uri = uri.substring(length - 1); } return uri; } } /** *

Adjust the viewID per the requirements of {@link #renderView}.

* * @param context current {@link javax.faces.context.FacesContext} * @param viewId incoming view ID * @return the view ID with an altered suffix mapping (if necessary) */ protected String convertViewId(FacesContext context, String viewId) { // if the viewId doesn't already use the above suffix, // replace or append. int extIdx = viewId.lastIndexOf('.'); int length = viewId.length(); StringBuilder buffer = new StringBuilder(length); for (String ext : configuredExtensions) { if (viewId.endsWith(ext)) { return viewId; } appendOrReplaceExtension(viewId, ext, length, extIdx, buffer); String convertedViewId = buffer.toString(); ViewDeclarationLanguage vdl = getViewDeclarationLanguage(context, convertedViewId); if (vdl.viewExists(context, convertedViewId)) { // RELEASE_PENDING (rlubke,driscoll) cache the lookup return convertedViewId; } } // unable to find any resource match that the default ViewHandler // can deal with. Fall back to legacy (JSF 1.2) id conversion. return legacyConvertViewId(viewId, length, extIdx, buffer); } protected String derivePhysicalViewId(FacesContext ctx, String rawViewId, boolean checkPhysical) { if (rawViewId != null) { String mapping = Util.getFacesMapping(ctx); String viewId; if (mapping != null) { if (!Util.isPrefixMapped(mapping)) { viewId = convertViewId(ctx, rawViewId); } else { viewId = normalizeRequestURI(rawViewId, mapping); if (viewId.equals(mapping)) { // The request was to the FacesServlet only - no // path info // on some containers this causes a recursion in the // RequestDispatcher and the request appears to hang. // If this is detected, return status 404 send404Error(ctx); } } if (checkPhysical) { ViewDeclarationLanguage vdl = getViewDeclarationLanguage(ctx, viewId); return (vdl.viewExists(ctx, viewId) ? viewId : null); } else { return viewId; } } } return rawViewId; } protected Map> getFullParameterList(FacesContext ctx, String viewId, Map> existingParameters) { Map> copy; if (existingParameters == null || existingParameters.isEmpty()) { copy = new LinkedHashMap>(4); } else { copy = new LinkedHashMap>(existingParameters); } addViewParameters(ctx, viewId, copy); return copy; } protected void addViewParameters(FacesContext ctx, String viewId, Map> existingParameters) { UIViewRoot currentRoot = ctx.getViewRoot(); String currentViewId = currentRoot.getViewId(); Collection toViewParams = Collections.emptyList(); Collection currentViewParams; boolean currentIsSameAsNew = false; currentViewParams = ViewMetadata.getViewParameters(currentRoot); if (currentViewId.equals(viewId)) { currentIsSameAsNew = true; toViewParams = currentViewParams; } else { ViewDeclarationLanguage pdl = getViewDeclarationLanguage(ctx, viewId); ViewMetadata viewMetadata = pdl.getViewMetadata(ctx, viewId); if (null != viewMetadata) { UIViewRoot root = viewMetadata.createMetadataView(ctx); toViewParams = ViewMetadata.getViewParameters(root); } } if (toViewParams.isEmpty()) { return; } for (UIViewParameter viewParam : toViewParams) { String value = null; // don't bother looking at view parameter if it's been overridden if (existingParameters.containsKey(viewParam.getName())) { continue; } if (paramHasValueExpression(viewParam)) { value = viewParam.getStringValueFromModel(ctx); } if (value == null) { if (currentIsSameAsNew) { /* * Anonymous view parameter: get string value from UIViewParameter instance stored in current view. */ value = viewParam.getStringValue(ctx); } else { /* * Or transfer string value from matching UIViewParameter instance stored in current view. */ value = getStringValueToTransfer(ctx, viewParam, currentViewParams); } } if (value != null) { List existing = existingParameters.get(viewParam.getName()); if (existing == null) { existing = new ArrayList(4); existingParameters.put(viewParam.getName(), existing); } existing.add(value); } } } /** * Attempts to find a matching locale based on pref and * list of supported locales, using the matching algorithm * as described in JSTL 8.3.2. * @param context the FacesContext for the current request * @param pref the preferred locale * @return the Locale based on pref and the matching alogritm specified * in JSTL 8.3.2 */ protected Locale findMatch(FacesContext context, Locale pref) { Locale result = null; Iterator it = context.getApplication().getSupportedLocales(); while (it.hasNext()) { Locale supportedLocale = it.next(); if (pref.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 (pref.getLanguage().equals(supportedLocale.getLanguage()) && supportedLocale.getCountry().length() == 0) { result = supportedLocale; } } } // if it's not in the supported locales, if (null == result) { Locale defaultLocale = context.getApplication().getDefaultLocale(); if (defaultLocale != null) { if ( pref.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 (pref.getLanguage().equals(defaultLocale.getLanguage()) && defaultLocale.getCountry().length() == 0) { result = defaultLocale; } } } } return result; } /** *

* Send {@link HttpServletResponse#SC_NOT_FOUND} (404) to the client. *

* * @param context the {@link FacesContext} for the current request */ protected void send404Error(FacesContext context) { try { context.responseComplete(); context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, ""); } catch (IOException ioe) { throw new FacesException(ioe); } } // --------------------------------------------------------- Private Methods private static boolean paramHasValueExpression(UIViewParameter param) { return (param.getValueExpression("value") != null); } private static String getStringValueToTransfer(FacesContext context, UIViewParameter param, Collection viewParams) { if (viewParams != null && !viewParams.isEmpty()) { for (UIViewParameter candidate : viewParams) { if ((null != candidate.getName() && null != param.getName()) && candidate.getName().equals(param.getName())) { return candidate.getStringValue(context); } } } return param.getStringValue(context); } // Utility method used by viewId conversion. Appends the extension // if no extension is present. Otherwise, replaces the extension. private void appendOrReplaceExtension(String viewId, String ext, int length, int extIdx, StringBuilder buffer) { buffer.setLength(0); buffer.append(viewId); if (extIdx != -1) { buffer.replace(extIdx, length, ext); } else { // no extension in the provided viewId, append the suffix buffer.append(ext); } } private String legacyConvertViewId(String viewId, int length, int extIdx, StringBuilder buffer) { // In 1.2, the viewId was converted by replacing the extension // with the single extension specified by javax.faces.DEFAULT_SUFFIX, // which defaulted to ".jsp". In 2.0, javax.faces.DEFAULT_SUFFIX // may specify multiple extensions. If javax.faces.DEFAULT_SUFFIX is // explicitly set, we honor it and pick off the first specified // extension. If javax.faces.DEFAULT_SUFFIX is not explicitly set, // we honor the default 1.2 behavior and use ".jsp" as the suffix. String ext = (extensionsSet && !(configuredExtensions.length == 0)) ? configuredExtensions[0] : ".jsp"; if (viewId.endsWith(ext)) { return viewId; } appendOrReplaceExtension(viewId, ext, length, extIdx, buffer); return buffer.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy