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