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.application;
20  
21  import java.io.IOException;
22  import java.util.HashSet;
23  import java.util.Set;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.FactoryFinder;
28  import javax.faces.application.StateManager;
29  import javax.faces.component.NamingContainer;
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIViewRoot;
32  import javax.faces.context.FacesContext;
33  import javax.faces.render.RenderKit;
34  import javax.faces.render.RenderKitFactory;
35  import javax.faces.render.ResponseStateManager;
36  import javax.faces.view.StateManagementStrategy;
37  import javax.faces.view.ViewDeclarationLanguage;
38  
39  import org.apache.myfaces.application.viewstate.StateCacheUtils;
40  import org.apache.myfaces.context.RequestViewContext;
41  
42  public class StateManagerImpl extends StateManager
43  {
44      private static final Logger log = Logger.getLogger(StateManagerImpl.class.getName());
45      
46      private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
47          StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
48      
49      private RenderKitFactory _renderKitFactory = null;
50      
51      public StateManagerImpl()
52      {
53      }
54  
55      @Override
56      protected Object getComponentStateToSave(FacesContext facesContext)
57      {
58          if (log.isLoggable(Level.FINEST))
59          {
60              log.finest("Entering getComponentStateToSave");
61          }
62  
63          UIViewRoot viewRoot = facesContext.getViewRoot();
64          if (viewRoot.isTransient())
65          {
66              return null;
67          }
68  
69          Object serializedComponentStates = viewRoot.processSaveState(facesContext);
70          //Locale is a state attribute of UIViewRoot and need not be saved explicitly
71          if (log.isLoggable(Level.FINEST))
72          {
73              log.finest("Exiting getComponentStateToSave");
74          }
75          return serializedComponentStates;
76      }
77  
78      /**
79       * Return an object which contains info about the UIComponent type
80       * of each node in the view tree. This allows an identical UIComponent
81       * tree to be recreated later, though all the components will have
82       * just default values for their members.
83       */
84      @Override
85      protected Object getTreeStructureToSave(FacesContext facesContext)
86      {
87          if (log.isLoggable(Level.FINEST))
88          {
89              log.finest("Entering getTreeStructureToSave");
90          }
91          UIViewRoot viewRoot = facesContext.getViewRoot();
92          if (viewRoot.isTransient())
93          {
94              return null;
95          }
96          TreeStructureManager tsm = new TreeStructureManager();
97          Object retVal = tsm.buildTreeStructureToSave(viewRoot);
98          if (log.isLoggable(Level.FINEST))
99          {
100             log.finest("Exiting getTreeStructureToSave");
101         }
102         return retVal;
103     }
104 
105     @Override
106     public UIViewRoot restoreView(FacesContext facesContext, String viewId, String renderKitId)
107     {
108         if (log.isLoggable(Level.FINEST))
109         {
110             log.finest("Entering restoreView - viewId: " + viewId + " ; renderKitId: " + renderKitId);
111         }
112 
113         UIViewRoot uiViewRoot = null;
114         
115         ViewDeclarationLanguage vdl = facesContext.getApplication().
116             getViewHandler().getViewDeclarationLanguage(facesContext,viewId);
117         StateManagementStrategy sms = null; 
118         if (vdl != null)
119         {
120             sms = vdl.getStateManagementStrategy(facesContext, viewId);
121         }
122         
123         if (sms != null)
124         {
125             if (log.isLoggable(Level.FINEST))
126             {
127                 log.finest("Redirect to StateManagementStrategy: " + sms.getClass().getName());
128             }
129             
130             uiViewRoot = sms.restoreView(facesContext, viewId, renderKitId);
131         }
132         else
133         {
134             RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
135             ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
136 
137             Object state = responseStateManager.getState(facesContext, viewId);
138 
139             if (state != null)
140             {
141                 Object[] stateArray = (Object[])state;
142                 TreeStructureManager tsm = new TreeStructureManager();
143                 uiViewRoot = tsm.restoreTreeStructure(stateArray[0]);
144 
145                 if (uiViewRoot != null)
146                 {
147                     facesContext.setViewRoot (uiViewRoot);
148                     uiViewRoot.processRestoreState(facesContext, stateArray[1]);
149                     
150                     RequestViewContext.getCurrentInstance(facesContext).refreshRequestViewContext(
151                             facesContext, uiViewRoot);
152                 }
153             }            
154         }
155         if (log.isLoggable(Level.FINEST))
156         {
157             log.finest("Exiting restoreView - " + viewId);
158         }
159 
160         return uiViewRoot;
161     }
162 
163     /**
164      * Wrap the original method and redirect to VDL StateManagementStrategy when
165      * necessary
166      */
167     @Override
168     public Object saveView(FacesContext facesContext)
169     {
170         Object serializedView = null;
171         UIViewRoot uiViewRoot = facesContext.getViewRoot();
172         ResponseStateManager responseStateManager = facesContext.getRenderKit().getResponseStateManager();
173         
174         String viewId = uiViewRoot.getViewId();
175         ViewDeclarationLanguage vdl = facesContext.getApplication().
176             getViewHandler().getViewDeclarationLanguage(facesContext,viewId);
177         
178         try
179         {
180             facesContext.getAttributes().put(StateManager.IS_SAVING_STATE, Boolean.TRUE);
181             if (vdl != null)
182             {
183                 StateManagementStrategy sms = vdl.getStateManagementStrategy(facesContext, viewId);
184                 
185                 if (sms != null)
186                 {
187                     if (log.isLoggable(Level.FINEST))
188                     {
189                         log.finest("Calling saveView of StateManagementStrategy: " + sms.getClass().getName());
190                     }
191                     
192                     serializedView = sms.saveView(facesContext);
193                     
194                     // If MyfacesResponseStateManager is used, give the option to do
195                     // additional operations for save the state if is necessary.
196                     if (StateCacheUtils.isMyFacesResponseStateManager(responseStateManager))
197                     {
198                         StateCacheUtils.getMyFacesResponseStateManager(responseStateManager).
199                                 saveState(facesContext, serializedView);
200                     }
201                     
202                     return serializedView; 
203                 }
204             }
205     
206             // In StateManagementStrategy.saveView there is a check for transient at
207             // start, but the same applies for VDL without StateManagementStrategy,
208             // so this should be checked before call parent (note that parent method
209             // does not do this check).
210             if (uiViewRoot.isTransient())
211             {
212                 return null;
213             }
214     
215             if (log.isLoggable(Level.FINEST))
216             {
217                 log.finest("Entering saveSerializedView");
218             }
219     
220             checkForDuplicateIds(facesContext, facesContext.getViewRoot(), new HashSet<String>());
221     
222             if (log.isLoggable(Level.FINEST))
223             {
224                 log.finest("Processing saveSerializedView - Checked for duplicate Ids");
225             }
226     
227             // SerializedView already created before within this request?
228             serializedView = facesContext.getAttributes().get(SERIALIZED_VIEW_REQUEST_ATTR);
229             if (serializedView == null)
230             {
231                 if (log.isLoggable(Level.FINEST))
232                 {
233                     log.finest("Processing saveSerializedView - create new serialized view");
234                 }
235     
236                 // first call to saveSerializedView --> create SerializedView
237                 Object treeStruct = getTreeStructureToSave(facesContext);
238                 Object compStates = getComponentStateToSave(facesContext);
239                 serializedView = new Object[] {treeStruct, compStates};
240                 facesContext.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR,
241                                                     serializedView);
242     
243                 if (log.isLoggable(Level.FINEST))
244                 {
245                     log.finest("Processing saveSerializedView - new serialized view created");
246                 }
247             }
248             
249             // If MyfacesResponseStateManager is used, give the option to do
250             // additional operations for save the state if is necessary.
251             if (StateCacheUtils.isMyFacesResponseStateManager(responseStateManager))
252             {
253                 StateCacheUtils.getMyFacesResponseStateManager(responseStateManager).
254                         saveState(facesContext, serializedView);
255             }
256     
257             if (log.isLoggable(Level.FINEST))
258             {
259                 log.finest("Exiting saveView");
260             }
261         }
262         finally
263         {
264             facesContext.getAttributes().remove(StateManager.IS_SAVING_STATE);
265         }
266 
267         return serializedView;
268     }
269 
270     private static void checkForDuplicateIds(FacesContext context,
271                                              UIComponent component,
272                                              Set<String> ids)
273     {
274         String id = component.getId();
275         if (id != null && !ids.add(id))
276         {
277             throw new IllegalStateException("Client-id : " + id +
278                                             " is duplicated in the faces tree. Component : " + 
279                                             component.getClientId(context)+", path: " +
280                                             getPathToComponent(component));
281         }
282         
283         if (component instanceof NamingContainer)
284         {
285             ids = new HashSet<String>();
286         }
287         
288         int facetCount = component.getFacetCount();
289         if (facetCount > 0)
290         {
291             for (UIComponent facet : component.getFacets().values())
292             {
293                 checkForDuplicateIds (context, facet, ids);
294             }
295         }
296         for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
297         {
298             UIComponent child = component.getChildren().get(i);
299             checkForDuplicateIds (context, child, ids);
300         }
301     }
302 
303     private static String getPathToComponent(UIComponent component)
304     {
305         StringBuffer buf = new StringBuffer();
306 
307         if(component == null)
308         {
309             buf.append("{Component-Path : ");
310             buf.append("[null]}");
311             return buf.toString();
312         }
313 
314         getPathToComponent(component,buf);
315 
316         buf.insert(0,"{Component-Path : ");
317         buf.append("}");
318 
319         return buf.toString();
320     }
321 
322     private static void getPathToComponent(UIComponent component, StringBuffer buf)
323     {
324         if(component == null)
325         {
326             return;
327         }
328 
329         StringBuffer intBuf = new StringBuffer();
330 
331         intBuf.append("[Class: ");
332         intBuf.append(component.getClass().getName());
333         if(component instanceof UIViewRoot)
334         {
335             intBuf.append(",ViewId: ");
336             intBuf.append(((UIViewRoot) component).getViewId());
337         }
338         else
339         {
340             intBuf.append(",Id: ");
341             intBuf.append(component.getId());
342         }
343         intBuf.append("]");
344 
345         buf.insert(0,intBuf.toString());
346 
347         getPathToComponent(component.getParent(),buf);
348     }
349 
350     @Override
351     public void writeState(FacesContext facesContext,
352                            Object state) throws IOException
353     {
354         if (log.isLoggable(Level.FINEST))
355         {
356             log.finest("Entering writeState");
357         }
358 
359         //UIViewRoot uiViewRoot = facesContext.getViewRoot();
360         //save state in response (client)
361         RenderKit renderKit = facesContext.getRenderKit();
362         ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
363 
364         responseStateManager.writeState(facesContext, state);
365 
366         if (log.isLoggable(Level.FINEST))
367         {
368             log.finest("Exiting writeState");
369         }
370 
371     }
372 
373     //helpers
374 
375     protected RenderKitFactory getRenderKitFactory()
376     {
377         if (_renderKitFactory == null)
378         {
379             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
380         }
381         return _renderKitFactory;
382     }
383 
384 }