Coverage Report - org.apache.myfaces.renderkit.html.HtmlResponseStateManager
 
Classes in this File Line Coverage Branch Coverage Complexity
HtmlResponseStateManager
0%
0/96
0%
0/38
2.5
HtmlResponseStateManager$1
N/A
N/A
2.5
HtmlResponseStateManager$DefaultStateTokenProcessor
0%
0/10
0%
0/4
2.5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.renderkit.html;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.util.Map;
 23  
 import java.util.logging.Logger;
 24  
 
 25  
 import javax.faces.context.ExternalContext;
 26  
 import javax.faces.context.FacesContext;
 27  
 import javax.faces.context.ResponseWriter;
 28  
 import javax.faces.lifecycle.ClientWindow;
 29  
 import javax.faces.render.RenderKitFactory;
 30  
 import javax.faces.render.ResponseStateManager;
 31  
 
 32  
 import org.apache.myfaces.application.StateCache;
 33  
 import org.apache.myfaces.application.StateCacheFactory;
 34  
 import org.apache.myfaces.application.viewstate.StateCacheFactoryImpl;
 35  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 36  
 import org.apache.myfaces.renderkit.MyfacesResponseStateManager;
 37  
 import org.apache.myfaces.renderkit.StateTokenProcessor;
 38  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 39  
 import org.apache.myfaces.shared.renderkit.html.HTML;
 40  
 import org.apache.myfaces.shared.util.StateUtils;
 41  
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
 42  
 
 43  
 /**
 44  
  * @author Manfred Geiler (latest modification by $Author$)
 45  
  * @version $Revision$ $Date$
 46  
  */
 47  
 public class HtmlResponseStateManager extends MyfacesResponseStateManager
 48  
 {
 49  0
     private static final Logger log = Logger.getLogger(HtmlResponseStateManager.class.getName());
 50  
 
 51  
     public static final String STANDARD_STATE_SAVING_PARAM = "javax.faces.ViewState";
 52  
     
 53  
     private static final String VIEW_STATE_COUNTER = "oam.partial.VIEW_STATE_COUNTER";
 54  
     private static final String CLIENT_WINDOW_COUNTER = "oam.partial.CLIENT_WINDOW_COUNTER";
 55  
     
 56  
     private static final String SESSION_TOKEN = "oam.rsm.SESSION_TOKEN";
 57  
 
 58  
     /**
 59  
      * Define if the state caching code should be handled by the ResponseStateManager or by the StateManager used.
 60  
      * <p>
 61  
      * This param is used to keep compatibility with previous state managers implementations depending from old myfaces
 62  
      * way to deal with this. For example, JspStateManagerImpl requires this param set to false, but by default 
 63  
      * it is set to true, to keep aligned with the Reference Implementation (RI). Note also the default StateManagerImpl
 64  
      * requires this property set to true in order to work correctly, so if you set this param to false, please
 65  
      * remember to add an entry into your faces-config.xml setting up JspStateManagerImpl as the state manager to use.
 66  
      * </p> 
 67  
      * @deprecated 
 68  
      */
 69  
     @Deprecated
 70  
     @JSFWebConfigParam(since="2.0.6", expectedValues="true, false", defaultValue="true", group="state",
 71  
         deprecated=true)
 72  
     public static final String INIT_PARAM_HANDLE_STATE_CACHING_MECHANICS
 73  
             = "org.apache.myfaces.HANDLE_STATE_CACHING_MECHANICS";
 74  
     
 75  
     /**
 76  
      * Add autocomplete="off" to the view state hidden field. Enabled by default.
 77  
      */
 78  
     @JSFWebConfigParam(since="2.2.8, 2.1.18, 2.0.24", expectedValues="true, false", 
 79  
            defaultValue="true", group="state")
 80  
     public static final String INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE = 
 81  
             "org.apache.myfaces.AUTOCOMPLETE_OFF_VIEW_STATE";
 82  
             
 83  
     private StateCacheFactory _stateCacheFactory;
 84  
     
 85  
     private StateTokenProcessor _stateTokenProcessor;
 86  
     
 87  
     private Boolean _autoCompleteOffViewState;
 88  
     
 89  
     public HtmlResponseStateManager()
 90  0
     {
 91  0
         _stateCacheFactory = new StateCacheFactoryImpl();
 92  0
         _stateTokenProcessor = new DefaultStateTokenProcessor();
 93  0
         _autoCompleteOffViewState = null;
 94  0
     }
 95  
     
 96  
     @Override
 97  
     public void writeState(FacesContext facesContext, Object state) throws IOException
 98  
     {
 99  0
         ResponseWriter responseWriter = facesContext.getResponseWriter();
 100  
 
 101  0
         Object savedStateObject = null;
 102  
         
 103  0
         if (!facesContext.getViewRoot().isTransient())
 104  
         {
 105  
             // Only if the view is not transient needs to be saved
 106  0
             savedStateObject = getStateCache(facesContext).encodeSerializedState(facesContext, state);
 107  
         }
 108  
 
 109  
         // write the view state field
 110  0
         writeViewStateField(facesContext, responseWriter, savedStateObject);
 111  
 
 112  
         // renderKitId field
 113  0
         writeRenderKitIdField(facesContext, responseWriter);
 114  
         
 115  
         // windowId field
 116  0
         writeWindowIdField(facesContext, responseWriter);
 117  0
     }
 118  
     
 119  
     private void writeWindowIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException
 120  
     {
 121  0
         ClientWindow clientWindow = facesContext.getExternalContext().getClientWindow();
 122  0
         if (clientWindow != null)
 123  
         {
 124  0
             responseWriter.startElement(HTML.INPUT_ELEM, null);
 125  0
             responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
 126  0
             responseWriter.writeAttribute(HTML.ID_ATTR, generateUpdateClientWindowId(facesContext), null);
 127  0
             responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.CLIENT_WINDOW_PARAM, null);
 128  0
             responseWriter.writeAttribute(HTML.VALUE_ATTR, clientWindow.getId(), null);
 129  0
             responseWriter.endElement(HTML.INPUT_ELEM);
 130  
         }
 131  0
     }
 132  
     
 133  
     @Override
 134  
     public void saveState(FacesContext facesContext, Object state)
 135  
     {
 136  0
         if (!facesContext.getViewRoot().isTransient())
 137  
         {
 138  0
             getStateCache(facesContext).saveSerializedView(facesContext, state);
 139  
         }
 140  0
     }
 141  
 
 142  
     private void writeViewStateField(FacesContext facesContext, ResponseWriter responseWriter, Object savedState)
 143  
         throws IOException
 144  
     {
 145  0
         String serializedState = _stateTokenProcessor.encode(facesContext, savedState);
 146  0
         ExternalContext extContext = facesContext.getExternalContext();
 147  0
         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(extContext);
 148  
 
 149  0
         responseWriter.startElement(HTML.INPUT_ELEM, null);
 150  0
         responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
 151  0
         responseWriter.writeAttribute(HTML.NAME_ATTR, STANDARD_STATE_SAVING_PARAM, null);
 152  0
         if (myfacesConfig.isRenderViewStateId())
 153  
         {
 154  
             // responseWriter.writeAttribute(HTML.ID_ATTR, STANDARD_STATE_SAVING_PARAM, null);
 155  
             // JSF 2.2 if javax.faces.ViewState is used as the id, in portlet
 156  
             // case it will be duplicate ids and that not xml friendly.
 157  0
             responseWriter.writeAttribute(HTML.ID_ATTR,
 158  
                 HtmlResponseStateManager.generateUpdateViewStateId(
 159  
                     facesContext), null);
 160  
         }
 161  0
         responseWriter.writeAttribute(HTML.VALUE_ATTR, serializedState, null);
 162  0
         if (this.isAutocompleteOffViewState(facesContext))
 163  
         {
 164  0
             responseWriter.writeAttribute(HTML.AUTOCOMPLETE_ATTR, "off", null);
 165  
         }
 166  0
         responseWriter.endElement(HTML.INPUT_ELEM);
 167  0
     }
 168  
 
 169  
     private void writeRenderKitIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException
 170  
     {
 171  
 
 172  0
         String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
 173  0
         if (defaultRenderKitId != null && !RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(defaultRenderKitId))
 174  
         {
 175  0
             responseWriter.startElement(HTML.INPUT_ELEM, null);
 176  0
             responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
 177  0
             responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.RENDER_KIT_ID_PARAM, null);
 178  0
             responseWriter.writeAttribute(HTML.VALUE_ATTR, defaultRenderKitId, null);
 179  0
             responseWriter.endElement(HTML.INPUT_ELEM);
 180  
         }
 181  0
     }
 182  
 
 183  
     @Override
 184  
     public Object getState(FacesContext facesContext, String viewId)
 185  
     {
 186  0
         Object savedState = getSavedState(facesContext);
 187  0
         if (savedState == null)
 188  
         {
 189  0
             return null;
 190  
         }
 191  
 
 192  0
         return getStateCache(facesContext).restoreSerializedView(facesContext, viewId, savedState);
 193  
     }
 194  
 
 195  
     /**
 196  
      * Reconstructs the state from the "javax.faces.ViewState" request parameter.
 197  
      * 
 198  
      * @param facesContext
 199  
      *            the current FacesContext
 200  
      * 
 201  
      * @return the reconstructed state, or <code>null</code> if there was no saved state
 202  
      */
 203  
     private Object getSavedState(FacesContext facesContext)
 204  
     {
 205  0
         Object encodedState = 
 206  
             facesContext.getExternalContext().getRequestParameterMap().get(STANDARD_STATE_SAVING_PARAM);
 207  0
         if(encodedState==null || (((String) encodedState).length() == 0))
 208  
         {
 209  0
             return null;
 210  
         }
 211  
 
 212  0
         Object savedStateObject = _stateTokenProcessor.decode(facesContext, (String)encodedState);
 213  
         
 214  0
         return savedStateObject;
 215  
     }
 216  
 
 217  
     /**
 218  
      * Checks if the current request is a postback
 219  
      * 
 220  
      * @since 1.2
 221  
      */
 222  
     @Override
 223  
     public boolean isPostback(FacesContext context)
 224  
     {
 225  0
         return context.getExternalContext().getRequestParameterMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM);
 226  
     }
 227  
 
 228  
     @Override
 229  
     public String getViewState(FacesContext facesContext, Object baseState)
 230  
     {
 231  
         // If the view is transient, baseState is null, so it should return null.
 232  
         // In this way, PartialViewContext will skip <update ...> section related
 233  
         // to view state (stateless view does not have state, so it does not need
 234  
         // to update the view state section). 
 235  0
         if (baseState == null)
 236  
         {
 237  0
             return null;
 238  
         }
 239  0
         if (facesContext.getViewRoot().isTransient())
 240  
         {
 241  0
             return null;
 242  
         }
 243  
         
 244  0
         Object state = getStateCache(facesContext).saveSerializedView(facesContext, baseState);
 245  
 
 246  0
         return _stateTokenProcessor.encode(facesContext, state);
 247  
     }
 248  
 
 249  
     @Override
 250  
     public boolean isStateless(FacesContext context, String viewId)
 251  
     {
 252  0
         if (context.isPostback())
 253  
         {
 254  0
             String encodedState = 
 255  
                 context.getExternalContext().getRequestParameterMap().get(STANDARD_STATE_SAVING_PARAM);
 256  0
             if(encodedState==null || (((String) encodedState).length() == 0))
 257  
             {
 258  0
                 return false;
 259  
             }
 260  
 
 261  0
             return _stateTokenProcessor.isStateless(context, encodedState);
 262  
         }
 263  
         else 
 264  
         {
 265  
             // "... java.lang.IllegalStateException - if this method is invoked 
 266  
             // and the statefulness of the preceding call to writeState(
 267  
             // javax.faces.context.FacesContext, java.lang.Object) cannot be determined.
 268  0
             throw new IllegalStateException(
 269  
                 "Cannot decide if the view is stateless or not, since the request is "
 270  
                 + "not postback (no preceding writeState(...)).");
 271  
         }
 272  
     }
 273  
 
 274  
     @Override
 275  
     public String getCryptographicallyStrongTokenFromSession(FacesContext context)
 276  
     {
 277  0
         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
 278  0
         String savedToken = (String) sessionMap.get(SESSION_TOKEN);
 279  0
         if (savedToken == null)
 280  
         {
 281  0
             savedToken = getStateCache(context).createCryptographicallyStrongTokenFromSession(context);
 282  0
             sessionMap.put(SESSION_TOKEN, savedToken);
 283  
         }
 284  0
         return savedToken;
 285  
     }
 286  
     
 287  
     @Override
 288  
     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
 289  
     {
 290  0
         return getStateCache(facesContext).isWriteStateAfterRenderViewRequired(facesContext);
 291  
     }
 292  
 
 293  
     protected StateCache getStateCache(FacesContext facesContext)
 294  
     {
 295  0
         return _stateCacheFactory.getStateCache(facesContext);
 296  
     }
 297  
 
 298  
     public static String generateUpdateClientWindowId(FacesContext facesContext)
 299  
     {
 300  
         // JSF 2.2 section 2.2.6.1 Partial State Rendering
 301  
         // According to the javascript doc of jsf.ajax.response,
 302  
         //
 303  
         // The new syntax looks like this:
 304  
         // <update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ClientWindow<SEP><UNIQUE_PER_VIEW_NUMBER>">
 305  
         //    <![CDATA[...]]>
 306  
         // </update>
 307  
         //
 308  
         // UNIQUE_PER_VIEW_NUMBER aim for portlet case. In that case it is possible to have
 309  
         // multiple sections for update. In servlet case there is only one update section per
 310  
         // ajax request.
 311  
         
 312  
         String id;
 313  0
         char separator = facesContext.getNamingContainerSeparatorChar();
 314  0
         Integer count = (Integer) facesContext.getAttributes().get(CLIENT_WINDOW_COUNTER);
 315  0
         if (count == null)
 316  
         {
 317  0
             count = Integer.valueOf(0);
 318  
         }
 319  0
         count += 1;
 320  0
         id = facesContext.getViewRoot().getContainerClientId(facesContext) + 
 321  
             separator + ResponseStateManager.CLIENT_WINDOW_PARAM + separator + count;
 322  0
         facesContext.getAttributes().put(CLIENT_WINDOW_COUNTER, count);
 323  0
         return id;
 324  
     }
 325  
     
 326  
     public static String generateUpdateViewStateId(FacesContext facesContext)
 327  
     {
 328  
         // JSF 2.2 section 2.2.6.1 Partial State Rendering
 329  
         // According to the javascript doc of jsf.ajax.response,
 330  
         //
 331  
         // The new syntax looks like this:
 332  
         // <update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ViewState<SEP><UNIQUE_PER_VIEW_NUMBER>">
 333  
         //    <![CDATA[...]]>
 334  
         // </update>
 335  
         //
 336  
         // UNIQUE_PER_VIEW_NUMBER aim for portlet case. In that case it is possible to have
 337  
         // multiple sections for update. In servlet case there is only one update section per
 338  
         // ajax request.
 339  
         
 340  
         String id;
 341  0
         char separator = facesContext.getNamingContainerSeparatorChar();
 342  0
         Integer count = (Integer) facesContext.getAttributes().get(VIEW_STATE_COUNTER);
 343  0
         if (count == null)
 344  
         {
 345  0
             count = Integer.valueOf(0);
 346  
         }
 347  0
         count += 1;
 348  0
         id = facesContext.getViewRoot().getContainerClientId(facesContext) + 
 349  
             separator + ResponseStateManager.VIEW_STATE_PARAM + separator + count;
 350  0
         facesContext.getAttributes().put(VIEW_STATE_COUNTER, count);
 351  0
         return id;
 352  
     }
 353  
 
 354  0
     private static class DefaultStateTokenProcessor extends StateTokenProcessor
 355  
     {
 356  
         private static final String STATELESS_TOKEN = "stateless";
 357  
 
 358  
         @Override
 359  
         public Object decode(FacesContext facesContext, String token)
 360  
         {
 361  0
             if (STATELESS_TOKEN.equals(token))
 362  
             {
 363  
                 // Should not happen, because ResponseStateManager.isStateless(context,viewId) should
 364  
                 // catch it first
 365  0
                 return null;
 366  
             }
 367  0
             Object savedStateObject = StateUtils.reconstruct((String)token, facesContext.getExternalContext());
 368  0
             return savedStateObject;
 369  
         }
 370  
 
 371  
         @Override
 372  
         public String encode(FacesContext facesContext, Object savedStateObject)
 373  
         {
 374  0
             if (facesContext.getViewRoot().isTransient())
 375  
             {
 376  0
                 return STATELESS_TOKEN;
 377  
             }
 378  0
             String serializedState = StateUtils.construct(savedStateObject, facesContext.getExternalContext());
 379  0
             return serializedState;
 380  
         }
 381  
 
 382  
         @Override
 383  
         public boolean isStateless(FacesContext facesContext, String token)
 384  
         {
 385  0
             return STATELESS_TOKEN.equals(token);
 386  
         }
 387  
     }
 388  
     
 389  
     private boolean isAutocompleteOffViewState(FacesContext facesContext)
 390  
     {
 391  0
         if (_autoCompleteOffViewState == null)
 392  
         {
 393  0
             _autoCompleteOffViewState = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
 394  
                     INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE, true);
 395  
         }
 396  0
         return _autoCompleteOffViewState;
 397  
     }
 398  
 }