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