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