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