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