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