jakarta.faces.render.ResponseStateManager Maven / Gradle / Ivy
/*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.faces.render;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.faces.application.StateManager;
import jakarta.faces.application.StateManager.SerializedView;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
/**
*
* ResponseStateManager is the helper class to
* {@link jakarta.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.
*
*/
public abstract class ResponseStateManager {
private static Logger log = Logger.getLogger("jakarta.faces.render");
/**
*
* The name of the request parameter used by the default implementation of
* {@link jakarta.faces.application.ViewHandler#calculateRenderKitId} to derive a RenderKit ID.
*
*/
public static final String RENDER_KIT_ID_PARAM = "jakarta.faces.RenderKitId";
/**
*
* Implementations must use this constant field value as
* the name of the client parameter in which to save the state between requests. The
* id
attribute must be a concatenation of the return from
* {@link jakarta.faces.component.UIViewRoot#getContainerClientId}, the return from
* {@link jakarta.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.
*
*
*
* It is strongly recommend that implementations guard against cross site scripting attacks by at least making the value
* of this parameter difficult to predict.
*
*
* @since 1.2
*/
public static final String VIEW_STATE_PARAM = "jakarta.faces.ViewState";
/**
*
* The name of the hidden field that refers to the encoded ClientWindow. This field is only used if
* {@link jakarta.faces.lifecycle.ClientWindow#CLIENT_WINDOW_MODE_PARAM_NAME} is not "none". The id
* attribute must be a concatenation of the return from {@link jakarta.faces.component.UIViewRoot#getContainerClientId},
* the return from {@link jakarta.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 jakarta.faces.lifecycle.ClientWindow#getId}.
*
*
* @since 2.2
*
*/
public static final String CLIENT_WINDOW_PARAM = "jakarta.faces.ClientWindow";
/**
*
* The name of the URL query parameter for transmitting the client window id. This parameter is only used if
* {@link jakarta.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 jakarta.faces.lifecycle.ClientWindow#getId}.
*
*
* @since 2.2
*/
public static final String CLIENT_WINDOW_URL_PARAM = "jfwid";
/**
*
* 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 Set
of protected
* views for this application.
*
*
* @since 2.2
*/
public static final String NON_POSTBACK_VIEW_TOKEN_PARAM = "jakarta.faces.Token";
/**
*
* Take the argument state
and write it into the output using the
* current {@link ResponseWriter}, which must be correctly positioned already.
*
*
*
* Call {@link FacesContext#getViewRoot()}. If {@link jakarta.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.
*
*
*
* 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.
*
*
*
* If the state saving method for this application is
* {@link jakarta.faces.application.StateManager#STATE_SAVING_METHOD_CLIENT}, the implementation
* must encrypt the state to be saved to the client
* in a tamper evident manner.
*
*
*
* If the state saving method for this application is
* {@link jakarta.faces.application.StateManager#STATE_SAVING_METHOD_SERVER}, and the current request is an
* Ajax
request {@link jakarta.faces.context.PartialViewContext#isAjaxRequest} returns true
),
* use the current view state identifier if it is available (do not generate a new identifier).
*
*
*
* Write out the render kit identifier associated with this ResponseStateManager
implementation with the
* name as the value of the String
constant ResponseStateManager.RENDER_KIT_ID_PARAM
. The
* render kit identifier must not be written if:
*
*
* - it is the default render kit identifier as returned by
* {@link jakarta.faces.application.Application#getDefaultRenderKitId()} or
* - the render kit identfier is the value of
jakarta.faces.render.RenderKitFactory.HTML_BASIC_RENDER_KIT
* and {@link jakarta.faces.application.Application#getDefaultRenderKitId()} returns null
.
*
*
*
* For backwards compatability with existing ResponseStateManager
implementations, the default
* implementation of this method checks if the argument is an instance of SerializedView
. If so, it calls
* through to
* {@link #writeState(jakarta.faces.context.FacesContext,jakarta.faces.application.StateManager.SerializedView)}. If
* not, it expects the state to be a two element Object array. It creates an instance of SerializedView
and
* stores the state as the treeStructure, and passes it to
* {@link #writeState(jakarta.faces.context.FacesContext,jakarta.faces.application.StateManager.SerializedView)}.
*
*
*
* The {@link jakarta.faces.lifecycle.ClientWindow} must be written using these steps. Call
* {@link jakarta.faces.context.ExternalContext#getClientWindow}. If the result is null
, take no further
* action regarding the ClientWindow
. If the result is non-null
, write a hidden field whose
* name is {@link #CLIENT_WINDOW_PARAM} and whose id is
* <VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>jakarta.faces.ClientWindow<SEP><UNIQUE_PER_VIEW_NUMBER>
* where <SEP> is the currently configured UINamingContainer.getSeparatorChar()
.
* <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from UIViewRoot.getContainerClientId()
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.
*
*
*
* @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);
}
/**
*
* Take the argument state
and write it into the output using the current {@link ResponseWriter}, which
* must be correctly positioned already.
*
*
*
* If the {@link jakarta.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.
*
*
*
* If the state saving method for this application is
* {@link jakarta.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.
*
*
* @deprecated This method has been replaced by
* {@link #writeState(jakarta.faces.context.FacesContext,java.lang.Object)}. The default implementation creates a two
* element Object
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 Object
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
*
*/
@Deprecated
public void writeState(FacesContext context, SerializedView state) throws IOException {
if (state != null) {
writeState(context, new Object[] { state.getStructure(), state.getState() });
}
}
/**
*
* If the preceding call to {@link #writeState(jakarta.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}.
*
*
*
*
*
* To preserve backward compatibility with custom implementations that may have extended from an earlier version of this
* class, an implementation is provided that returns false
. A compliant implementation must override this
* method to take the specified action.
*
*
*
*
* @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(jakarta.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;
}
/**
*
* The 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(jakarta.faces.context.FacesContext,java.lang.Object)}.
*
*
*
* If the state saving method for this application is
* {@link jakarta.faces.application.StateManager#STATE_SAVING_METHOD_CLIENT}, writeState()
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 jakarta.faces.application.ProtectedViewException} must be thrown.
*
*
*
* For backwards compatability with existing ResponseStateManager
implementations, the default
* implementation of this method calls {@link #getTreeStructureToRestore} and {@link #getComponentStateToRestore} and
* creates and returns a two element Object
array with element zero containing the structure
* property and element one containing the state
property of the SerializedView
.
*
*
* @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 writeState
. If this is an initial
* request, this method returns null
.
*/
public Object getState(FacesContext context, String viewId) {
Object stateArray[] = { getTreeStructureToRestore(context, viewId), getComponentStateToRestore(context) };
return stateArray;
}
/**
*
* The implementation must inspect the current request and return the tree structure Object passed to it on a previous
* invocation of writeState()
.
*
*
* @deprecated This method has been replaced by {@link #getState}. The default implementation returns null
.
*
* @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
*/
@Deprecated
public Object getTreeStructureToRestore(FacesContext context, String viewId) {
return null;
}
/**
*
* The implementation must inspect the current request and return the component state Object passed to it on a previous
* invocation of writeState()
.
*
*
* @deprecated This method has been replaced by {@link #getState}. The default implementation returns null
.
*
* @param context The {@link FacesContext} instance for the current request
*
* @return the component state portion of the state
*
*/
@Deprecated
public Object getComponentStateToRestore(FacesContext context) {
return null;
}
/**
*
* Return true if the current request is a postback. This method is leveraged from the Restore View Phase to
* determine if {@link jakarta.faces.application.ViewHandler#restoreView} or
* {@link jakarta.faces.application.ViewHandler#createView} should be called. The default implementation must return
* true
if this ResponseStateManager
instance wrote out state on a previous request to which
* this request is a postback, false
otherwise.
*
*
*
* The implementation of this method for the Standard HTML RenderKit must consult the
* {@link jakarta.faces.context.ExternalContext}'s requestParameterMap
and return true
if and
* only if there is a key equal to the value of the symbolic constant {@link #VIEW_STATE_PARAM}.
*
*
*
* For backwards compatibility with implementations of ResponseStateManager
prior to Jakarta Server Faces
* 1.2, a default implementation is provided that consults the {@link jakarta.faces.context.ExternalContext}'s
* requestParameterMap
and return true
if its size is greater than 0.
*
*
* @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();
}
/**
*
* Return the specified state as a String
without any markup related to the rendering technology supported
* by this ResponseStateManager.
*
*
* @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;
}
/**
*
* 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
* null
.
*
*
* @param context the {@link FacesContext} for the current request
*
* @return a cryptographically strong value
*
* @since 2.2
*/
public String getCryptographicallyStrongTokenFromSession(FacesContext context) {
return null;
}
}