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

org.apache.myfaces.renderkit.html.HtmlResponseStateManager Maven / Gradle / Ivy

Go to download

The private implementation classes of the Apache MyFaces Core JSF-2.3-next Implementation

There is a newer version: 4.1.0-RC2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.renderkit.html;

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

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.lifecycle.ClientWindow;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;

import org.apache.myfaces.application.viewstate.StateCache;
import org.apache.myfaces.renderkit.MyfacesResponseStateManager;
import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.renderkit.html.util.HTML;
import org.apache.myfaces.spi.StateCacheProvider;
import org.apache.myfaces.spi.StateCacheProviderFactory;

/**
 * @author Manfred Geiler (latest modification by $Author$)
 * @version $Revision$ $Date$
 */
public class HtmlResponseStateManager extends MyfacesResponseStateManager
{
    private static final Logger log = Logger.getLogger(HtmlResponseStateManager.class.getName());
    
    private static final String VIEW_STATE_COUNTER = "oam.partial.VIEW_STATE_COUNTER";
    private static final String CLIENT_WINDOW_COUNTER = "oam.partial.CLIENT_WINDOW_COUNTER";
    
    private static final String SESSION_TOKEN = "oam.rsm.SESSION_TOKEN";

    private StateCacheProvider stateCacheFactory;
    private MyfacesConfig myfacesConfig;
    
    public HtmlResponseStateManager()
    {        
        FacesContext facesContext = FacesContext.getCurrentInstance();
        myfacesConfig = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
        
        stateCacheFactory = StateCacheProviderFactory
                .getStateCacheProviderFactory(facesContext.getExternalContext())
                .getStateCacheProvider(facesContext.getExternalContext());
    }
    
    @Override
    public void writeState(FacesContext facesContext, Object state) throws IOException
    {
        ResponseWriter responseWriter = facesContext.getResponseWriter();

        Object savedStateObject = null;
        
        if (!facesContext.getViewRoot().isTransient())
        {
            // Only if the view is not transient needs to be saved
            savedStateObject = getStateCache(facesContext).encodeSerializedState(facesContext, state);
        }

        // write the view state field
        writeViewStateField(facesContext, responseWriter, savedStateObject);

        // renderKitId field
        writeRenderKitIdField(facesContext, responseWriter);
        
        // windowId field
        writeWindowIdField(facesContext, responseWriter);
    }
    
    private void writeWindowIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException
    {
        ClientWindow clientWindow = facesContext.getExternalContext().getClientWindow();
        if (clientWindow != null)
        {
            responseWriter.startElement(HTML.INPUT_ELEM, null);
            responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
            responseWriter.writeAttribute(HTML.ID_ATTR, generateUpdateClientWindowId(facesContext), null);
            responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.CLIENT_WINDOW_PARAM, null);
            responseWriter.writeAttribute(HTML.VALUE_ATTR, clientWindow.getId(), null);
            responseWriter.endElement(HTML.INPUT_ELEM);
        }
    }
    
    @Override
    public void saveState(FacesContext facesContext, Object state)
    {
        if (!facesContext.getViewRoot().isTransient())
        {
            getStateCache(facesContext).saveSerializedView(facesContext, state);
        }
    }

    private void writeViewStateField(FacesContext facesContext, ResponseWriter responseWriter, Object savedState)
        throws IOException
    {
        String serializedState = getStateCache(facesContext).getStateTokenProcessor(facesContext)
                .encode(facesContext, savedState);

        responseWriter.startElement(HTML.INPUT_ELEM, null);
        responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
        responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.VIEW_STATE_PARAM, null);
        if (myfacesConfig.isRenderViewStateId())
        {
            // responseWriter.writeAttribute(HTML.ID_ATTR, STANDARD_STATE_SAVING_PARAM, null);
            // JSF 2.2 if javax.faces.ViewState is used as the id, in portlet
            // case it will be duplicate ids and that not xml friendly.
            responseWriter.writeAttribute(HTML.ID_ATTR,
                HtmlResponseStateManager.generateUpdateViewStateId(facesContext),
                null);
        }
        responseWriter.writeAttribute(HTML.VALUE_ATTR, serializedState, null);
        if (myfacesConfig.isAutocompleteOffViewState())
        {
            responseWriter.writeAttribute(HTML.AUTOCOMPLETE_ATTR, "off", null);
        }
        responseWriter.endElement(HTML.INPUT_ELEM);
    }

    private void writeRenderKitIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException
    {

        String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
        if (defaultRenderKitId != null && !RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(defaultRenderKitId))
        {
            responseWriter.startElement(HTML.INPUT_ELEM, null);
            responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
            responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.RENDER_KIT_ID_PARAM, null);
            responseWriter.writeAttribute(HTML.VALUE_ATTR, defaultRenderKitId, null);
            responseWriter.endElement(HTML.INPUT_ELEM);
        }
    }

    @Override
    public Object getState(FacesContext facesContext, String viewId)
    {
        Object savedState = getSavedState(facesContext);
        if (savedState == null)
        {
            return null;
        }

        return getStateCache(facesContext).restoreSerializedView(facesContext, viewId, savedState);
    }

    /**
     * Reconstructs the state from the "javax.faces.ViewState" request parameter.
     * 
     * @param facesContext
     *            the current FacesContext
     * 
     * @return the reconstructed state, or null if there was no saved state
     */
    private Object getSavedState(FacesContext facesContext)
    {
        Object encodedState = 
            facesContext.getExternalContext().getRequestParameterMap().get(ResponseStateManager.VIEW_STATE_PARAM);
        if(encodedState==null || (((String) encodedState).length() == 0))
        {
            return null;
        }

        Object savedStateObject = getStateCache(facesContext).getStateTokenProcessor(facesContext)
                .decode(facesContext, (String)encodedState);
        
        return savedStateObject;
    }

    /**
     * Checks if the current request is a postback
     * 
     * @since 1.2
     */
    @Override
    public boolean isPostback(FacesContext context)
    {
        return context.getExternalContext().getRequestParameterMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM);
    }

    @Override
    public String getViewState(FacesContext facesContext, Object baseState)
    {
        // If the view is transient, baseState is null, so it should return null.
        // In this way, PartialViewContext will skip  section related
        // to view state (stateless view does not have state, so it does not need
        // to update the view state section). 
        if (baseState == null)
        {
            return null;
        }
        if (facesContext.getViewRoot().isTransient())
        {
            return null;
        }
        
        Object state = getStateCache(facesContext).saveSerializedView(facesContext, baseState);

        return getStateCache(facesContext).getStateTokenProcessor(facesContext).encode(facesContext, state);
    }

    @Override
    public boolean isStateless(FacesContext context, String viewId)
    {
        if (context.isPostback())
        {
            String encodedState = 
                context.getExternalContext().getRequestParameterMap().get(ResponseStateManager.VIEW_STATE_PARAM);
            if (encodedState == null || ((String) encodedState).length() == 0)
            {
                return false;
            }

            return getStateCache(context).getStateTokenProcessor(context).isStateless(context, encodedState);
        }
        else 
        {
            // "... java.lang.IllegalStateException - if this method is invoked 
            // and the statefulness of the preceding call to writeState(
            // javax.faces.context.FacesContext, java.lang.Object) cannot be determined.
            throw new IllegalStateException(
                "Cannot decide if the view is stateless or not, since the request is "
                + "not postback (no preceding writeState(...)).");
        }
    }

    @Override
    public String getCryptographicallyStrongTokenFromSession(FacesContext context)
    {
        Map sessionMap = context.getExternalContext().getSessionMap();
        String savedToken = (String) sessionMap.get(SESSION_TOKEN);
        if (savedToken == null)
        {
            savedToken = getStateCache(context).createCryptographicallyStrongTokenFromSession(context);
            sessionMap.put(SESSION_TOKEN, savedToken);
        }
        return savedToken;
    }
    
    @Override
    public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
    {
        return getStateCache(facesContext).isWriteStateAfterRenderViewRequired(facesContext);
    }

    protected StateCache getStateCache(FacesContext facesContext)
    {
        return stateCacheFactory.getStateCache(facesContext);
    }

    public static String generateUpdateClientWindowId(FacesContext facesContext)
    {
        // JSF 2.2 section 2.2.6.1 Partial State Rendering
        // According to the javascript doc of jsf.ajax.response,
        //
        // The new syntax looks like this:
        // 
        //    
        // 
        //
        // UNIQUE_PER_VIEW_NUMBER aim for portlet case. In that case it is possible to have
        // multiple sections for update. In servlet case there is only one update section per
        // ajax request.
        
        String id;
        char separator = facesContext.getNamingContainerSeparatorChar();
        Integer count = (Integer) facesContext.getAttributes().get(CLIENT_WINDOW_COUNTER);
        if (count == null)
        {
            count = Integer.valueOf(0);
        }
        count += 1;
        id = facesContext.getViewRoot().getContainerClientId(facesContext) + 
            separator + ResponseStateManager.CLIENT_WINDOW_PARAM + separator + count;
        facesContext.getAttributes().put(CLIENT_WINDOW_COUNTER, count);
        return id;
    }
    
    public static String generateUpdateViewStateId(FacesContext facesContext)
    {
        // JSF 2.2 section 2.2.6.1 Partial State Rendering
        // According to the javascript doc of jsf.ajax.response,
        //
        // The new syntax looks like this:
        // 
        //    
        // 
        //
        // UNIQUE_PER_VIEW_NUMBER aim for portlet case. In that case it is possible to have
        // multiple sections for update. In servlet case there is only one update section per
        // ajax request.
        
        String id;
        char separator = facesContext.getNamingContainerSeparatorChar();
        Integer count = (Integer) facesContext.getAttributes().get(VIEW_STATE_COUNTER);
        if (count == null)
        {
            count = Integer.valueOf(0);
        }
        count += 1;
        id = facesContext.getViewRoot().getContainerClientId(facesContext) + 
            separator + ResponseStateManager.VIEW_STATE_PARAM + separator + count;
        facesContext.getAttributes().put(VIEW_STATE_COUNTER, count);
        return id;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy