/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2013 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.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 javax.faces.render; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.application.StateManager; import javax.faces.application.StateManager.SerializedView; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * <p><strong class="changed_modified_2_0 changed_modified_2_2"> * ResponseStateManager</strong> * is the helper class to {@link javax.faces.application.StateManager} * that knows the specific rendering technology being used to generate * the response. It is a singleton abstract class, vended by the {@link * RenderKit}. This class knows the mechanics of saving state, whether * it be in hidden fields, session, or some combination of the two.</p> */ public abstract class ResponseStateManager { private static Logger log = Logger.getLogger("javax.faces.render"); /** * <p>The name of the request parameter used by the default * implementation of {@link * javax.faces.application.ViewHandler#calculateRenderKitId} to * derive a RenderKit ID.</p> */ public static final String RENDER_KIT_ID_PARAM = "javax.faces.RenderKitId"; /** * <p><span class="changed_modified_2_0 * changed_modified_2_2">Implementations</span> must use this * constant field value as the name of the client parameter in which * to save the state between requests. <span * class="changed_added_2_2">The <code>id</code> attribute must be a * concatenation of the return from {@link * javax.faces.component.UIViewRoot#getContainerClientId}, the * return from {@link * javax.faces.component.UINamingContainer#getSeparatorChar}, this * constant field value, the separator char, and a number that is * guaranteed to be unique with respect to all the other instances of * this kind of client parameter in the view.</span> * </p> * <p class="changed_added_2_0">It is strongly recommend that * implementations guard against cross site scripting attacks by at * least making the value of this parameter difficult to * predict.</p> * * @since 1.2 */ public static final String VIEW_STATE_PARAM = "javax.faces.ViewState"; /** * <p class="changed_added_2_2">The name of the hidden field that * refers to the encoded ClientWindow. This field is only used if * {@link javax.faces.lifecycle.ClientWindow#CLIENT_WINDOW_MODE_PARAM_NAME} * is not "none". The <code>id</code> attribute must be a * concatenation of the return from {@link * javax.faces.component.UIViewRoot#getContainerClientId}, the * return from {@link * javax.faces.component.UINamingContainer#getSeparatorChar}, this * constant field value, the separator char, and a number that is * guaranteed to be unique with respect to all the other instances of * this kind of client parameter in the view. The value of this parameter * is the return from {@link javax.faces.lifecycle.ClientWindow#getId}.</p> * * @since 2.2 * */ public static final String CLIENT_WINDOW_PARAM = "javax.faces.ClientWindow"; /** * <p class="changed_added_2_2">The name of the URL query parameter for transmitting * the client window id. This parameter is only used if * {@link javax.faces.lifecycle.ClientWindow#CLIENT_WINDOW_MODE_PARAM_NAME} * is not "none". The name of the parameter is given by the constant value * of this field. The value of this parameter * is the return from {@link javax.faces.lifecycle.ClientWindow#getId}. * </p> * * @since 2.2 */ public static final String CLIENT_WINDOW_URL_PARAM = "jfwid"; /** * <p class="changed_added_2_2">The value of this constant is taken * to be the name of a request parameter whose value is inspected * to verify the safety of an incoming non-postback request with respect * to the currently configured <code>Set</code> of protected views * for this application.</p> * * @since 2.2 */ public static final String NON_POSTBACK_VIEW_TOKEN_PARAM = "javax.faces.Token"; /** * <p><span class="changed_modified_2_2">Take</span> the argument * <code>state</code> and write it into the * output using the current {@link ResponseWriter}, which must be * correctly positioned already.</p> * * <p class="changed_added_2_2">Call {@link FacesContext#getViewRoot()}. * If {@link javax.faces.component.UIComponent#isTransient()} * returns {@code true}, take implementation specific action so that the * following call to {@link #isStateless} returns {@code true} and return. * Otherwise, proceed as follows.</p> * * <p>If the state is to be written out to hidden fields, the * implementation must take care to make all necessary character * replacements to make the Strings suitable for inclusion as an * HTTP request paramater.</p> * * <p>If the state saving method for this application is {@link * javax.faces.application.StateManager#STATE_SAVING_METHOD_CLIENT}, * the implementation <span class="changed_modified_2_2">must</span> * encrypt the state to be saved to the * client <span class="changed_modified_2_2">in a tamper evident * manner</span>.</p> * * <p>If the state saving method for this application is {@link * javax.faces.application.StateManager#STATE_SAVING_METHOD_SERVER}, * and the current request is an <code>Ajax</code> request * {@link javax.faces.context.PartialViewContext#isAjaxRequest} returns * <code>true</code>), use the current view state identifier if it is * available (do not generate a new identifier).</p> * * <p>Write out the render kit identifier associated with this * <code>ResponseStateManager</code> implementation with the name * as the value of the <code>String</code> constant * <code>ResponseStateManager.RENDER_KIT_ID_PARAM</code>. The * render kit identifier must not be written if:</p> * <ul> * <li>it is the default render kit identifier as returned by * {@link javax.faces.application.Application#getDefaultRenderKitId()} or</li> * <li>the render kit identfier is the value of * <code>javax.faces.render.RenderKitFactory.HTML_BASIC_RENDER_KIT</code> and * {@link javax.faces.application.Application#getDefaultRenderKitId()} returns <code>null</code>. * </li> * </ul> * * <p>For backwards compatability with existing * <code>ResponseStateManager</code> implementations, the default * implementation of this method checks if the argument is an * instance of <code>SerializedView</code>. If so, it calls through * to {@link * #writeState(javax.faces.context.FacesContext,javax.faces.application.StateManager.SerializedView)}. * If not, it expects the state to be a two element Object array. It creates * an instance of <code>SerializedView</code> and * stores the state as the treeStructure, and passes it to {@link * #writeState(javax.faces.context.FacesContext,javax.faces.application.StateManager.SerializedView)}.</p> * * <p class="changed_added_2_2">The {@link * javax.faces.lifecycle.ClientWindow} must be written using these * steps. Call {@link * javax.faces.context.ExternalContext#getClientWindow}. If the * result is <code>null</code>, take no further action regarding the * <code>ClientWindow</code>. If the result is * non-<code>null</code>, write a hidden field whose name is {@link * #CLIENT_WINDOW_PARAM} and whose id is * <code><VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ClientWindow<SEP><UNIQUE_PER_VIEW_NUMBER></code> * where <SEP> is the currently configured * <code>UINamingContainer.getSeparatorChar()</code>. * <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from * <code>UIViewRoot.getContainerClientId()</code> on the view from * whence this state originated. <UNIQUE_PER_VIEW_NUMBER> is * a number that must be unique within this view, but must not be * included in the view state. The value of the field is implementation * dependent but must uniquely identify this window within the user's session.</p> * * * @since 1.2 * * @param context The {@link FacesContext} instance for the current request * @param state The serialized state information previously saved * @throws IOException if the state argument is not an array of length 2. * */ public void writeState(FacesContext context, Object state) throws IOException { SerializedView view; if (state instanceof SerializedView) { view = (SerializedView) state; } else { if (state instanceof Object[]) { Object[] stateArray = (Object[])state; if (2 == stateArray.length) { StateManager stateManager = context.getApplication().getStateManager(); view = stateManager.new SerializedView(stateArray[0], stateArray[1]); } else { //PENDING - I18N if (log.isLoggable(Level.SEVERE)) { log.log(Level.SEVERE, "State is not an expected array of length 2."); } throw new IOException("State is not an expected array of length 2."); } } else { //PENDING - I18N if (log.isLoggable(Level.SEVERE)) { log.log(Level.SEVERE, "State is not an expected array of length 2."); } throw new IOException("State is not an expected array of length 2."); } } writeState(context, view); } /** * <p>Take the argument <code>state</code> and write it into * the output using the current {@link ResponseWriter}, which * must be correctly positioned already.</p> * * <p>If the {@link * javax.faces.application.StateManager.SerializedView} is to be * written out to hidden fields, the implementation must take care * to make all necessary character replacements to make the Strings * suitable for inclusion as an HTTP request paramater.</p> * * <p>If the state saving method for this application is {@link * javax.faces.application.StateManager#STATE_SAVING_METHOD_CLIENT}, * the implementation may encrypt the state to be saved to the * client. We recommend that the state be unreadable by the client, * and also be tamper evident. The reference implementation follows * these recommendations. </p> * * @deprecated This method has been replaced by {@link * #writeState(javax.faces.context.FacesContext,java.lang.Object)}. * The default implementation creates a two element * <code>Object</code> array with the first element being the return * from calling {@link SerializedView#getStructure}, and the second * being the return from {@link SerializedView#getState}. It then * passes this <code>Object</code> array to {@link #writeState}. * * @param context The {@link FacesContext} instance for the current request * @param state The serialized state information previously saved * * @throws IOException if the state cannot be written for any reason * */ public void writeState(FacesContext context, SerializedView state) throws IOException { if (state != null) { writeState(context, new Object[]{state.getStructure(), state.getState()}); } } /** * <p class="changed_added_2_2">If the preceding call to {@link #writeState(javax.faces.context.FacesContext, java.lang.Object)} * was stateless, return {@code true}. If the preceding call to {@code writeState()} was * stateful, return {@code false}. Otherwise throw {@code IllegalStateException}.</p> * * <div class="changed_added_2_2"> * * <p>To preserve backward compatibility * with custom implementations that may have extended from an earlier * version of this class, an implementation is provided that returns * <code>false</code>. A compliant implementation must override this * method to take the specified action.</p> * * </div> * * @param context The {@link FacesContext} instance for the current request * @param viewId View identifier of the view to be restored * @throws NullPointerException if the argument {@code context} is {@code null}. * @throws IllegalStateException if this method is invoked and the statefulness * of the preceding call to {@link #writeState(javax.faces.context.FacesContext, java.lang.Object)} * cannot be determined. * * @since 2.2 * * * @return the value of the statelessness of this run through the * lifecycle. * */ public boolean isStateless(FacesContext context, String viewId) { return false; } /** * <p><span class="changed_modified_2_2">The</span> implementation must * inspect the current request and return * an Object representing the tree structure and component state * passed in to a previous invocation of {@link * #writeState(javax.faces.context.FacesContext,java.lang.Object)}.</p> * * <p class="changed_added_2_2">If the state saving method for this * application is {@link * javax.faces.application.StateManager#STATE_SAVING_METHOD_CLIENT}, * <code>writeState()</code> will have encrypted the state in a tamper * evident manner. If the state fails to decrypt, or decrypts but * indicates evidence of tampering, a * {@link javax.faces.application.ProtectedViewException} must be thrown.</p> * * <p>For backwards compatability with existing * <code>ResponseStateManager</code> implementations, the default * implementation of this method calls {@link * #getTreeStructureToRestore} and {@link * #getComponentStateToRestore} and creates and returns a two * element <code>Object</code> array with element zero containing * the <code>structure</code> property and element one containing * the <code>state</code> property of the * <code>SerializedView</code>.</p> * * @since 1.2 * * @param context The {@link FacesContext} instance for the current request * @param viewId View identifier of the view to be restored * * @return the tree structure and component state Object passed in * to <code>writeState</code>. If this is an initial request, this * method returns <code>null</code>. */ public Object getState(FacesContext context, String viewId) { Object stateArray[] = { getTreeStructureToRestore(context, viewId), getComponentStateToRestore(context) }; return stateArray; } /** * <p>The implementation must inspect the current request and return * the tree structure Object passed to it on a previous invocation of * <code>writeState()</code>.</p> * * @deprecated This method has been replaced by {@link #getState}. * The default implementation returns <code>null</code>. * * @param context The {@link FacesContext} instance for the current request * @param viewId View identifier of the view to be restored * * @return the tree structure portion of the state */ public Object getTreeStructureToRestore(FacesContext context, String viewId) { return null; } /** * <p>The implementation must inspect the current request and return * the component state Object passed to it on a previous invocation * of <code>writeState()</code>.</p> * * @deprecated This method has been replaced by {@link #getState}. * The default implementation returns <code>null</code>. * * @param context The {@link FacesContext} instance for the current request * * @return the component state portion of the state * */ public Object getComponentStateToRestore(FacesContext context) { return null; } /** * <p>Return true if the current request is a postback. This method * is leveraged from the <i>Restore View Phase</i> to determine if * {@link javax.faces.application.ViewHandler#restoreView} or {@link * javax.faces.application.ViewHandler#createView} should be called. * The default implementation must return <code>true</code> if this * <code>ResponseStateManager</code> instance wrote out state on a * previous request to which this request is a postback, * <code>false</code> otherwise.</p> * * <p>The implementation of this method for the Standard HTML * RenderKit must consult the {@link * javax.faces.context.ExternalContext}'s * <code>requestParameterMap</code> and return <code>true</code> if * and only if there is a key equal to the value of the symbolic * constant {@link #VIEW_STATE_PARAM}.</p> * * <p>For backwards compatability with implementations of * <code>ResponseStateManager</code> prior to JSF 1.2, a default * implementation is provided that consults the {@link * javax.faces.context.ExternalContext}'s <code>requestParameterMap</code> and return * <code>true</code> if its size is greater than 0.</p> * * @param context the {@code FacesContext} for the current request. * * @return the value as specified above * * @since 1.2 */ public boolean isPostback(FacesContext context) { return (!context.getExternalContext().getRequestParameterMap().isEmpty()); } /** * <p> * Return the specified state as a <code>String</code> without any markup * related to the rendering technology supported by this ResponseStateManager. * </p> * * @param context the {@link FacesContext} for the current request * @param state the state from which the String version will be generated * from * @return the view state for this request without any markup specifics * * @since 2.0 */ public String getViewState(FacesContext context, Object state) { return null; } /** * <p class="changed_added_2_2">Compliant implementations must return a * cryptographically strong token for use to protect views in this * application. For backwards compatability with earlier revisions, a * default implementation is provided that simply returns <code>null</code>. * </p> * * @param context the {@link FacesContext} for the current request * * @return a cryptographically strong value * * @since 2.2 */ public String getCryptographicallyStrongTokenFromSession(FacesContext context) { return null; } }