View Javadoc

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.logging.Level;
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.render.RenderKitFactory;
29  import javax.faces.render.ResponseStateManager;
30  
31  import org.apache.myfaces.application.StateCache;
32  import org.apache.myfaces.application.StateCacheFactory;
33  import org.apache.myfaces.application.viewstate.StateCacheFactoryImpl;
34  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
35  import org.apache.myfaces.renderkit.MyfacesResponseStateManager;
36  import org.apache.myfaces.shared.config.MyfacesConfig;
37  import org.apache.myfaces.shared.renderkit.html.HTML;
38  import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
39  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
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: lu4242 $)
45   * @version $Revision: 1670538 $ $Date: 2015-04-01 01:32:35 +0000 (Wed, 01 Apr 2015) $
46   */
47  public class HtmlResponseStateManager extends MyfacesResponseStateManager
48  {
49      //private static final Log log = LogFactory.getLog(HtmlResponseStateManager.class);
50      private static final Logger log = Logger.getLogger(HtmlResponseStateManager.class.getName());
51  
52      //private static final int TREE_PARAM = 2;
53      private static final int STATE_PARAM = 0;
54      private static final int VIEWID_PARAM = 1;
55  
56      public static final String STANDARD_STATE_SAVING_PARAM = "javax.faces.ViewState";
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       */
68      @JSFWebConfigParam(since="2.0.6", expectedValues="true, false", defaultValue="true", group="state")
69      public static final String INIT_PARAM_HANDLE_STATE_CACHING_MECHANICS
70              = "org.apache.myfaces.HANDLE_STATE_CACHING_MECHANICS";
71      
72      /**
73       * Add autocomplete="off" to the view state hidden field. Enabled by default.
74       */
75      @JSFWebConfigParam(since="2.2.8, 2.1.18, 2.0.24", expectedValues="true, false", 
76             defaultValue="true", group="state")
77      public static final String INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE = 
78              "org.apache.myfaces.AUTOCOMPLETE_OFF_VIEW_STATE";
79      
80      private Boolean _handleStateCachingMechanics;
81      
82      private StateCacheFactory _stateCacheFactory;
83      
84      private Boolean _autoCompleteOffViewState;
85      
86      public HtmlResponseStateManager()
87      {
88          _stateCacheFactory = new StateCacheFactoryImpl();
89          _autoCompleteOffViewState = null;
90      }
91      
92      protected boolean isHandlingStateCachingMechanics(FacesContext facesContext)
93      {
94          if (_handleStateCachingMechanics == null)
95          {
96              _handleStateCachingMechanics
97                      = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
98                          INIT_PARAM_HANDLE_STATE_CACHING_MECHANICS, true);
99          }
100         return _handleStateCachingMechanics.booleanValue();
101     }
102     
103     public void writeState(FacesContext facesContext, Object state) throws IOException
104     {
105         ResponseWriter responseWriter = facesContext.getResponseWriter();
106 
107         Object savedStateObject = null;
108         
109         if (isHandlingStateCachingMechanics(facesContext))
110         {
111             //token = getStateCache(facesContext).saveSerializedView(facesContext, state);
112             savedStateObject = getStateCache(facesContext).encodeSerializedState(facesContext, state);
113         }
114         else
115         {
116             Object token = null;
117             Object[] savedState = new Object[2];
118             token = state;
119             
120             if (log.isLoggable(Level.FINEST))
121             {
122                 log.finest("Writing state in client");
123             }
124 
125 
126             if (token != null)
127             {
128                 savedState[STATE_PARAM] = token;
129             }
130             else
131             {
132                 if (log.isLoggable(Level.FINEST))
133                 {
134                     log.finest("No component states to be saved in client response!");
135                 }
136             }
137 
138             savedState[VIEWID_PARAM] = facesContext.getViewRoot().getViewId();
139 
140             if (log.isLoggable(Level.FINEST))
141             {
142                 log.finest("Writing view state and renderKit fields");
143             }
144             
145             savedStateObject = savedState;
146         }
147 
148         // write the view state field
149         writeViewStateField(facesContext, responseWriter, savedStateObject);
150 
151         // renderKitId field
152         writeRenderKitIdField(facesContext, responseWriter);
153     }
154     
155     @Override
156     public void saveState(FacesContext facesContext, Object state)
157     {
158         if (isHandlingStateCachingMechanics(facesContext))
159         {
160             getStateCache(facesContext).saveSerializedView(facesContext, state);
161         }
162         else
163         {
164             //This is done outside
165         }
166     }
167 
168     private void writeViewStateField(FacesContext facesContext, ResponseWriter responseWriter, Object savedState)
169         throws IOException
170     {
171         String serializedState = StateUtils.construct(savedState, facesContext.getExternalContext());
172         ExternalContext extContext = facesContext.getExternalContext();
173         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(extContext);
174         // Write Javascript viewstate if enabled and if javascript is allowed,
175         // otherwise write hidden input
176         if (JavascriptUtils.isJavascriptAllowed(extContext) && myfacesConfig.isViewStateJavascript())
177         {
178             HtmlRendererUtils.renderViewStateJavascript(facesContext, STANDARD_STATE_SAVING_PARAM, serializedState);
179         }
180         else
181         {
182             responseWriter.startElement(HTML.INPUT_ELEM, null);
183             responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
184             responseWriter.writeAttribute(HTML.NAME_ATTR, STANDARD_STATE_SAVING_PARAM, null);
185             if (myfacesConfig.isRenderViewStateId())
186             {
187                 responseWriter.writeAttribute(HTML.ID_ATTR, STANDARD_STATE_SAVING_PARAM, null);
188             }
189             responseWriter.writeAttribute(HTML.VALUE_ATTR, serializedState, null);
190             if (this.isAutocompleteOffViewState(facesContext))
191             {
192                 responseWriter.writeAttribute(HTML.AUTOCOMPLETE_ATTR, "off", null);
193             }
194             responseWriter.endElement(HTML.INPUT_ELEM);
195         }
196     }
197 
198     private void writeRenderKitIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException
199     {
200 
201         String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
202         if (defaultRenderKitId != null && !RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(defaultRenderKitId))
203         {
204             responseWriter.startElement(HTML.INPUT_ELEM, null);
205             responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
206             responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.RENDER_KIT_ID_PARAM, null);
207             responseWriter.writeAttribute(HTML.VALUE_ATTR, defaultRenderKitId, null);
208             responseWriter.endElement(HTML.INPUT_ELEM);
209         }
210     }
211 
212     @Override
213     public Object getState(FacesContext facesContext, String viewId)
214     {
215         Object savedState = getSavedState(facesContext);
216         if (savedState == null)
217         {
218             return null;
219         }
220 
221         if (isHandlingStateCachingMechanics(facesContext))
222         {
223             return getStateCache(facesContext).restoreSerializedView(facesContext, viewId, savedState);
224         }
225         else
226         {
227             return ((Object[])savedState)[STATE_PARAM];
228         }
229     }
230 
231     /* There methods are no longer required
232     @Override
233     public Object getTreeStructureToRestore(FacesContext facesContext, String viewId)
234     {
235         // Although this method won't be called anymore,
236         // it has been kept for backward compatibility.
237         Object[] savedState = getSavedState(facesContext);
238         if (savedState == null)
239         {
240             return null;
241         }
242 
243         return savedState[TREE_PARAM];
244     }
245 
246     @Override
247     public Object getComponentStateToRestore(FacesContext facesContext)
248     {
249         // Although this method won't be called anymore,
250         // it has been kept for backward compatibility.
251         Object[] savedState = getSavedState(facesContext);
252         if (savedState == null)
253         {
254             return null;
255         }
256 
257         return savedState[STATE_PARAM];
258     }*/
259 
260     /**
261      * Reconstructs the state from the "javax.faces.ViewState" request parameter.
262      * 
263      * @param facesContext
264      *            the current FacesContext
265      * 
266      * @return the reconstructed state, or <code>null</code> if there was no saved state
267      */
268     private Object getSavedState(FacesContext facesContext)
269     {
270         Object encodedState = 
271             facesContext.getExternalContext().getRequestParameterMap().get(STANDARD_STATE_SAVING_PARAM);
272         if(encodedState==null || (((String) encodedState).length() == 0))
273         {
274             return null;
275         }
276 
277         Object savedStateObject = StateUtils.reconstruct((String)encodedState, facesContext.getExternalContext());
278         
279         if (isHandlingStateCachingMechanics(facesContext))
280         {
281             return savedStateObject;
282         }
283         else
284         {
285             Object[] savedState = (Object[]) savedStateObject;
286 
287             if (savedState == null)
288             {
289                 if (log.isLoggable(Level.FINEST))
290                 {
291                     log.finest("No saved state");
292                 }
293                 return null;
294             }
295 
296             String restoredViewId = (String)savedState[VIEWID_PARAM];
297 
298             if (restoredViewId == null)
299             {
300                 // no saved state or state of different viewId
301                 if (log.isLoggable(Level.FINEST))
302                 {
303                     log.finest("No saved state or state of a different viewId: " + restoredViewId);
304                 }
305 
306                 return null;
307             }
308 
309             return savedState;
310         }
311     }
312 
313     /**
314      * Checks if the current request is a postback
315      * 
316      * @since 1.2
317      */
318     @Override
319     public boolean isPostback(FacesContext context)
320     {
321         return context.getExternalContext().getRequestParameterMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM);
322     }
323 
324     @Override
325     public String getViewState(FacesContext facesContext, Object baseState)
326     {
327         if (baseState == null)
328         {
329             return null;
330         }
331         
332         Object state = null;
333         if (isHandlingStateCachingMechanics(facesContext))
334         {
335             state = getStateCache(facesContext).saveSerializedView(facesContext, baseState);
336         }
337         else
338         {
339             //state = baseState;
340             Object[] savedState = new Object[2];
341 
342             if (state != null)
343             {
344                 savedState[STATE_PARAM] = baseState;
345             }
346 
347             savedState[VIEWID_PARAM] = facesContext.getViewRoot().getViewId();
348             
349             state = savedState;
350         }
351         return StateUtils.construct(state, facesContext.getExternalContext());
352     }
353     
354     @Override
355     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
356     {
357         return getStateCache(facesContext).isWriteStateAfterRenderViewRequired(facesContext);
358     }
359 
360     protected StateCache getStateCache(FacesContext facesContext)
361     {
362         return _stateCacheFactory.getStateCache(facesContext);
363     }
364     
365     private boolean isAutocompleteOffViewState(FacesContext facesContext)
366     {
367         if (_autoCompleteOffViewState == null)
368         {
369             _autoCompleteOffViewState = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
370                     INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE, true);
371         }
372         return _autoCompleteOffViewState;
373     }
374 }