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.ArrayList;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Set;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.FactoryFinder;
30  import javax.faces.application.StateManager;
31  import javax.faces.component.NamingContainer;
32  import javax.faces.component.UIComponent;
33  import javax.faces.component.UIComponentBase;
34  import javax.faces.component.UIViewRoot;
35  import javax.faces.context.FacesContext;
36  import javax.faces.render.RenderKit;
37  import javax.faces.render.RenderKitFactory;
38  import javax.faces.render.ResponseStateManager;
39  import javax.faces.view.StateManagementStrategy;
40  import javax.faces.view.ViewDeclarationLanguage;
41  
42  import org.apache.myfaces.application.viewstate.StateCacheUtils;
43  import org.apache.myfaces.context.RequestViewContext;
44  
45  public class StateManagerImpl extends StateManager
46  {
47      private static final Logger log = Logger.getLogger(StateManagerImpl.class.getName());
48      
49      private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
50          StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
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                 
147                 uiViewRoot = tsm.restoreTreeStructure(((Object[])stateArray[0])[0]);
148 
149                 if (uiViewRoot != null)
150                 {
151                     facesContext.setViewRoot (uiViewRoot);
152                     uiViewRoot.processRestoreState(facesContext, stateArray[1]);
153                     
154                     RequestViewContext.getCurrentInstance(facesContext).refreshRequestViewContext(
155                             facesContext, uiViewRoot);
156                     
157                     // If state is saved fully, there outer f:view tag handler will not be executed,
158                     // so "contracts" attribute will not be set properly. We need to save it and
159                     // restore it from here. With PSS, the view will always be built so it is not
160                     // necessary to save it on the state.
161                     Object rlc = ((Object[])stateArray[0])[1];
162                     if (rlc != null)
163                     {
164                         facesContext.setResourceLibraryContracts((List) UIComponentBase.
165                             restoreAttachedState(facesContext, rlc));
166                     }
167                 }
168             }            
169         }
170         if (log.isLoggable(Level.FINEST))
171         {
172             log.finest("Exiting restoreView - " + viewId);
173         }
174 
175         return uiViewRoot;
176     }
177 
178     /**
179      * Wrap the original method and redirect to VDL StateManagementStrategy when
180      * necessary
181      */
182     @Override
183     public Object saveView(FacesContext facesContext)
184     {
185         UIViewRoot uiViewRoot = facesContext.getViewRoot();
186         
187         if (uiViewRoot.isTransient())
188         {
189             return null;
190         }
191         
192         Object serializedView = null;
193         ResponseStateManager responseStateManager = facesContext.getRenderKit().getResponseStateManager();
194         
195         String viewId = uiViewRoot.getViewId();
196         ViewDeclarationLanguage vdl = facesContext.getApplication().
197             getViewHandler().getViewDeclarationLanguage(facesContext,viewId);
198         
199         try
200         {
201             facesContext.getAttributes().put(StateManager.IS_SAVING_STATE, Boolean.TRUE);
202             if (vdl != null)
203             {
204                 StateManagementStrategy sms = vdl.getStateManagementStrategy(facesContext, viewId);
205                 
206                 if (sms != null)
207                 {
208                     if (log.isLoggable(Level.FINEST))
209                     {
210                         log.finest("Calling saveView of StateManagementStrategy: " + sms.getClass().getName());
211                     }
212                     
213                     serializedView = sms.saveView(facesContext);
214                     
215                     // If MyfacesResponseStateManager is used, give the option to do
216                     // additional operations for save the state if is necessary.
217                     if (StateCacheUtils.isMyFacesResponseStateManager(responseStateManager))
218                     {
219                         StateCacheUtils.getMyFacesResponseStateManager(responseStateManager).
220                                 saveState(facesContext, serializedView);
221                     }
222                     
223                     return serializedView; 
224                 }
225             }
226     
227             // In StateManagementStrategy.saveView there is a check for transient at
228             // start, but the same applies for VDL without StateManagementStrategy,
229             // so this should be checked before call parent (note that parent method
230             // does not do this check).
231             if (uiViewRoot.isTransient())
232             {
233                 return null;
234             }
235     
236             if (log.isLoggable(Level.FINEST))
237             {
238                 log.finest("Entering saveSerializedView");
239             }
240     
241             checkForDuplicateIds(facesContext, facesContext.getViewRoot(), new HashSet<String>());
242     
243             if (log.isLoggable(Level.FINEST))
244             {
245                 log.finest("Processing saveSerializedView - Checked for duplicate Ids");
246             }
247     
248             // SerializedView already created before within this request?
249             serializedView = facesContext.getAttributes().get(SERIALIZED_VIEW_REQUEST_ATTR);
250             if (serializedView == null)
251             {
252                 if (log.isLoggable(Level.FINEST))
253                 {
254                     log.finest("Processing saveSerializedView - create new serialized view");
255                 }
256     
257                 // first call to saveSerializedView --> create SerializedView
258                 Object treeStruct = getTreeStructureToSave(facesContext);
259                 Object compStates = getComponentStateToSave(facesContext);
260                 Object rlcStates = !facesContext.getResourceLibraryContracts().isEmpty() ? 
261                     UIComponentBase.saveAttachedState(facesContext, 
262                                 new ArrayList<String>(facesContext.getResourceLibraryContracts())) : null;
263                 serializedView = new Object[] {
264                         new Object[]{treeStruct, rlcStates} ,
265                         compStates};
266                 facesContext.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR,
267                                                     serializedView);
268     
269                 if (log.isLoggable(Level.FINEST))
270                 {
271                     log.finest("Processing saveSerializedView - new serialized view created");
272                 }
273             }
274             
275             // If MyfacesResponseStateManager is used, give the option to do
276             // additional operations for save the state if is necessary.
277             if (StateCacheUtils.isMyFacesResponseStateManager(responseStateManager))
278             {
279                 StateCacheUtils.getMyFacesResponseStateManager(responseStateManager).
280                         saveState(facesContext, serializedView);
281             }
282     
283             if (log.isLoggable(Level.FINEST))
284             {
285                 log.finest("Exiting saveView");
286             }
287         }
288         finally
289         {
290             facesContext.getAttributes().remove(StateManager.IS_SAVING_STATE);
291         }
292 
293         return serializedView;
294     }
295 
296     private static void checkForDuplicateIds(FacesContext context,
297                                              UIComponent component,
298                                              Set<String> ids)
299     {
300         String id = component.getId();
301         if (id != null && !ids.add(id))
302         {
303             throw new IllegalStateException("Client-id : " + id +
304                                             " is duplicated in the faces tree. Component : " + 
305                                             component.getClientId(context)+", path: " +
306                                             getPathToComponent(component));
307         }
308         
309         if (component instanceof NamingContainer)
310         {
311             ids = new HashSet<String>();
312         }
313         
314         int facetCount = component.getFacetCount();
315         if (facetCount > 0)
316         {
317             for (UIComponent facet : component.getFacets().values())
318             {
319                 checkForDuplicateIds (context, facet, ids);
320             }
321         }
322         for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
323         {
324             UIComponent child = component.getChildren().get(i);
325             checkForDuplicateIds (context, child, ids);
326         }
327     }
328 
329     private static String getPathToComponent(UIComponent component)
330     {
331         StringBuffer buf = new StringBuffer();
332 
333         if(component == null)
334         {
335             buf.append("{Component-Path : ");
336             buf.append("[null]}");
337             return buf.toString();
338         }
339 
340         getPathToComponent(component,buf);
341 
342         buf.insert(0,"{Component-Path : ");
343         buf.append("}");
344 
345         return buf.toString();
346     }
347 
348     private static void getPathToComponent(UIComponent component, StringBuffer buf)
349     {
350         if(component == null)
351         {
352             return;
353         }
354 
355         StringBuffer intBuf = new StringBuffer();
356 
357         intBuf.append("[Class: ");
358         intBuf.append(component.getClass().getName());
359         if(component instanceof UIViewRoot)
360         {
361             intBuf.append(",ViewId: ");
362             intBuf.append(((UIViewRoot) component).getViewId());
363         }
364         else
365         {
366             intBuf.append(",Id: ");
367             intBuf.append(component.getId());
368         }
369         intBuf.append("]");
370 
371         buf.insert(0,intBuf.toString());
372 
373         getPathToComponent(component.getParent(),buf);
374     }
375 
376     @Override
377     public void writeState(FacesContext facesContext,
378                            Object state) throws IOException
379     {
380         if (log.isLoggable(Level.FINEST))
381         {
382             log.finest("Entering writeState");
383         }
384 
385         //UIViewRoot uiViewRoot = facesContext.getViewRoot();
386         //save state in response (client)
387         RenderKit renderKit = facesContext.getRenderKit();
388         ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
389 
390         responseStateManager.writeState(facesContext, state);
391 
392         if (log.isLoggable(Level.FINEST))
393         {
394             log.finest("Exiting writeState");
395         }
396 
397     }
398 
399     //helpers
400 
401     protected RenderKitFactory getRenderKitFactory()
402     {
403         if (_renderKitFactory == null)
404         {
405             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
406         }
407         return _renderKitFactory;
408     }
409 
410 }