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.util;
20  
21  import java.beans.BeanInfo;
22  import java.beans.IntrospectionException;
23  import java.beans.Introspector;
24  import java.beans.PropertyDescriptor;
25  import java.io.ByteArrayOutputStream;
26  import java.io.IOException;
27  import java.io.PrintStream;
28  import java.util.HashSet;
29  import java.util.Map;
30  import java.util.logging.Level;
31  import java.util.logging.Logger;
32  
33  import javax.el.MethodExpression;
34  import javax.el.ValueExpression;
35  import javax.faces.FacesException;
36  import javax.faces.component.UICommand;
37  import javax.faces.component.UIComponent;
38  import javax.faces.component.UIInput;
39  import javax.faces.component.UIViewRoot;
40  import javax.faces.component.ValueHolder;
41  import javax.faces.context.FacesContext;
42  import javax.faces.event.FacesListener;
43  import javax.faces.validator.Validator;
44  
45  /**
46   * Utilities for logging.
47   * 
48   * @author Manfred Geiler (latest modification by $Author$)
49   * @version $Revision$ $Date$
50   */
51  public class DebugUtils
52  {
53      private static final Logger log = Logger.getLogger(DebugUtils.class.getName());
54  
55      // Attributes that should not be printed
56      private static final HashSet<String> IGNORE_ATTRIBUTES;
57      static
58      {
59          IGNORE_ATTRIBUTES = new HashSet<String>();
60          IGNORE_ATTRIBUTES.add("attributes");
61          IGNORE_ATTRIBUTES.add("children");
62          IGNORE_ATTRIBUTES.add("childCount");
63          IGNORE_ATTRIBUTES.add("class");
64          IGNORE_ATTRIBUTES.add("facets");
65          IGNORE_ATTRIBUTES.add("facetsAndChildren");
66          IGNORE_ATTRIBUTES.add("parent");
67          IGNORE_ATTRIBUTES.add("actionListeners");
68          IGNORE_ATTRIBUTES.add("valueChangeListeners");
69          IGNORE_ATTRIBUTES.add("validators");
70          IGNORE_ATTRIBUTES.add("rowData");
71          IGNORE_ATTRIBUTES.add("javax.faces.webapp.COMPONENT_IDS");
72          IGNORE_ATTRIBUTES.add("javax.faces.webapp.FACET_NAMES");
73          IGNORE_ATTRIBUTES.add("javax.faces.webapp.CURRENT_VIEW_ROOT");
74      }
75  
76      private static final String JSF_COMPONENT_PACKAGE = "javax.faces.component.";
77      private static final String MYFACES_COMPONENT_PACKAGE = "org.apache.myfaces.component.";
78  
79      private DebugUtils()
80      {
81          // hide from public access
82      }
83  
84      public static void assertError(boolean condition, Logger logger, String msg) throws FacesException
85      {
86          if (!condition)
87          {
88              logger.severe(msg);
89              throw new FacesException(msg);
90          }
91      }
92  
93      public static void assertFatal(boolean condition, Logger logger, String msg) throws FacesException
94      {
95          if (!condition)
96          {
97              logger.severe(msg);
98              throw new FacesException(msg);
99          }
100     }
101 
102     public static void traceView(String additionalMsg)
103     {
104         if (log.isLoggable(Level.FINEST))
105         {
106             FacesContext facesContext = FacesContext.getCurrentInstance();
107             if (facesContext == null)
108             {
109                 log.severe("Cannot not print view to console (no FacesContext).");
110                 return;
111             }
112             UIViewRoot viewRoot = facesContext.getViewRoot();
113             if (viewRoot == null)
114             {
115                 log.severe("Cannot not print view to console (no ViewRoot in FacesContext).");
116                 return;
117             }
118 
119             traceView(additionalMsg, viewRoot);
120         }
121     }
122 
123     /**
124      * Be careful, when using this version of traceView: Some component properties (e.g. getRenderer) assume, that there
125      * is a valid viewRoot set in the FacesContext!
126      * 
127      * @param additionalMsg
128      * @param viewRoot
129      */
130     private static void traceView(String additionalMsg, UIViewRoot viewRoot)
131     {
132         ByteArrayOutputStream baos = new ByteArrayOutputStream();
133         PrintStream ps = new PrintStream(baos);
134         if (additionalMsg != null)
135         {
136             ps.println(additionalMsg);
137         }
138         ps.println("========================================");
139         printView(viewRoot, ps);
140         ps.println("========================================");
141         ps.close();
142         log.finest(baos.toString());
143     }
144 
145     public static void printView(UIViewRoot uiViewRoot, PrintStream stream)
146     {
147         printComponent(uiViewRoot, stream, 0, true, null);
148     }
149 
150     public static void printComponent(UIComponent comp, PrintStream stream)
151     {
152         printComponent(comp, stream, 0, false, null);
153     }
154 
155     private static void printComponent(UIComponent comp, PrintStream stream, int indent, boolean withChildrenAndFacets,
156                                        String facetName)
157     {
158         printIndent(stream, indent);
159         stream.print('<');
160 
161         String compType = comp.getClass().getName();
162         if (compType.startsWith(JSF_COMPONENT_PACKAGE))
163         {
164             compType = compType.substring(JSF_COMPONENT_PACKAGE.length());
165         }
166         else if (compType.startsWith(MYFACES_COMPONENT_PACKAGE))
167         {
168             compType = compType.substring(MYFACES_COMPONENT_PACKAGE.length());
169         }
170         stream.print(compType);
171         
172         printAttribute(stream, "id", comp.getId());
173 
174         if (facetName != null)
175         {
176             printAttribute(stream, "facetName", facetName);
177         }
178         
179         for (Map.Entry<String, Object> entry : comp.getAttributes().entrySet())
180         {
181             if (!"id".equals(entry.getKey()))
182             {
183                 printAttribute(stream, entry.getKey(), entry.getValue());
184             }
185         }
186         
187         // HACK: comp.getAttributes() only returns attributes, that are NOT backed
188         // by a corresponding component property. So, we must explicitly get the
189         // available properties by Introspection:
190         BeanInfo beanInfo;
191         try
192         {
193             beanInfo = Introspector.getBeanInfo(comp.getClass());
194         }
195         catch (IntrospectionException e)
196         {
197             throw new RuntimeException(e);
198         }
199 
200         if (!compType.startsWith("org.apache.myfaces.view.facelets.compiler"))
201         {
202             PropertyDescriptor propDescriptors[] = beanInfo.getPropertyDescriptors();
203             for (int i = 0; i < propDescriptors.length; i++)
204             {
205                 if (propDescriptors[i].getReadMethod() != null)
206                 {
207                     String name = propDescriptors[i].getName();
208                     if (!"id".equals(name))
209                     {
210                         ValueExpression ve = comp.getValueExpression(name);
211                         if (ve != null)
212                         {
213                             printAttribute(stream, name, ve.getExpressionString());
214                         }
215                         else
216                         {
217                             if (name.equals("value") && comp instanceof ValueHolder)
218                             {
219                                 // -> localValue
220                             }
221                             else if (!IGNORE_ATTRIBUTES.contains(name))
222                             {
223                                 try
224                                 {
225                                     Object value = comp.getAttributes().get(name);
226                                     printAttribute(stream, name, value);
227                                 }
228                                 catch (Exception e)
229                                 {
230                                     log.log(Level.SEVERE, e.getMessage() , e);
231                                     printAttribute(stream, name, null);
232                                 }
233                             }
234                         }
235                     }
236                 }
237             }
238         }
239 
240         boolean mustClose = true;
241         boolean nestedObjects = false;
242 
243         if (comp instanceof UICommand)
244         {
245             FacesListener[] listeners = ((UICommand)comp).getActionListeners();
246             if (listeners != null && listeners.length > 0)
247             {
248                 nestedObjects = true;
249                 stream.println('>');
250                 mustClose = false;
251                 for (int i = 0; i < listeners.length; i++)
252                 {
253                     FacesListener listener = listeners[i];
254                     printIndent(stream, indent + 1);
255                     stream.print('<');
256                     stream.print(listener.getClass().getName());
257                     stream.println("/>");
258                 }
259             }
260         }
261 
262         if (comp instanceof UIInput)
263         {
264             FacesListener[] listeners = ((UIInput)comp).getValueChangeListeners();
265             if (listeners != null && listeners.length > 0)
266             {
267                 nestedObjects = true;
268                 stream.println('>');
269                 mustClose = false;
270                 for (int i = 0; i < listeners.length; i++)
271                 {
272                     FacesListener listener = listeners[i];
273                     printIndent(stream, indent + 1);
274                     stream.print('<');
275                     stream.print(listener.getClass().getName());
276                     stream.println("/>");
277                 }
278             }
279 
280             Validator[] validators = ((UIInput)comp).getValidators();
281             if (validators != null && validators.length > 0)
282             {
283                 nestedObjects = true;
284                 stream.println('>');
285                 mustClose = false;
286                 for (int i = 0; i < validators.length; i++)
287                 {
288                     Validator validator = validators[i];
289                     printIndent(stream, indent + 1);
290                     stream.print('<');
291                     stream.print(validator.getClass().getName());
292                     stream.println("/>");
293                 }
294             }
295         }
296 
297         if (withChildrenAndFacets)
298         {
299             int childCount = comp.getChildCount();
300             if (childCount > 0 || comp.getFacetCount() > 0)
301             {
302                 Map<String, UIComponent> facetsMap = comp.getFacets();
303                 nestedObjects = true;
304                 if (mustClose)
305                 {
306                     stream.println('>');
307                     mustClose = false;
308                 }
309 
310                 if (childCount > 0)
311                 {
312                     for (int i = 0; i < childCount; i++)
313                     {
314                         UIComponent child = comp.getChildren().get(i);
315                         printComponent(child, stream, indent + 1, true, null);
316                     }
317                 }
318 
319                 for (Map.Entry<String, UIComponent> entry : facetsMap.entrySet())
320                 {
321                     printComponent(entry.getValue(), stream, indent + 1, true, entry.getKey());
322                 }
323             }
324         }
325 
326         if (nestedObjects)
327         {
328             if (mustClose)
329             {
330                 stream.println("/>");
331             }
332             else
333             {
334                 printIndent(stream, indent);
335                 stream.print("</");
336                 stream.print(compType);
337                 stream.println('>');
338             }
339         }
340         else
341         {
342             stream.println("/>");
343         }
344     }
345 
346     private static void printAttribute(PrintStream stream, String name, Object value)
347     {
348         if (IGNORE_ATTRIBUTES.contains(name))
349         {
350             return;
351         }
352         if (name.startsWith("javax.faces.webapp.UIComponentTag."))
353         {
354             name = name.substring("javax.faces.webapp.UIComponentTag.".length());
355         }
356         stream.print(' ');
357         stream.print(name);
358         stream.print("=\"");
359         if (value != null)
360         {
361             if (value instanceof UIComponent)
362             {
363                 stream.print("[id:");
364                 stream.print(((UIComponent)value).getId());
365                 stream.print(']');
366             }
367             else if (value instanceof MethodExpression)
368             {
369                 stream.print(((MethodExpression)value).getExpressionString());
370             }
371             else
372             {
373                 stream.print(value.toString());
374             }
375         }
376         else
377         {
378             stream.print("NULL");
379         }
380         stream.print('"');
381     }
382 
383     private static void printIndent(PrintStream stream, int depth)
384     {
385         for (int i = 0; i < depth; i++)
386         {
387             stream.print("  ");
388         }
389     }
390 
391     public static String componentAsString(UIComponent comp)
392     {
393         try
394         {
395             ByteArrayOutputStream baos = new ByteArrayOutputStream();
396             printComponent(comp, new PrintStream(baos));
397             baos.close();
398             return baos.toString();
399         }
400         catch (IOException e)
401         {
402             throw new RuntimeException(e);
403         }
404     }
405 }