View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j;
18  
19  import org.apache.log4j.spi.ThrowableRenderer;
20  
21  import java.io.File;
22  import java.lang.reflect.Method;
23  import java.net.URL;
24  import java.security.CodeSource;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  /**
29   * Enhanced implementation of ThrowableRenderer.  Uses Throwable.getStackTrace
30   * if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render
31   * on earlier virtual machines.
32   *
33   * @since 1.2.16
34   */
35  public final class EnhancedThrowableRenderer implements ThrowableRenderer {
36      /**
37       * Throwable.getStackTrace() method.
38       */
39      private Method getStackTraceMethod;
40      /**
41       * StackTraceElement.getClassName() method.
42       */
43      private Method getClassNameMethod;
44  
45  
46      /**
47       * Construct new instance.
48       */
49      public EnhancedThrowableRenderer() {
50          try {
51              Class[] noArgs = null;
52              getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
53              Class ste = Class.forName("java.lang.StackTraceElement");
54              getClassNameMethod = ste.getMethod("getClassName", noArgs);
55          } catch(Exception ex) {
56          }
57      }
58  
59      /**
60       * {@inheritDoc}
61       */
62      public String[] doRender(final Throwable throwable) {
63          if (getStackTraceMethod != null) {
64              try {
65                  Object[] noArgs = null;
66                  Object[] elements = (Object[]) getStackTraceMethod.invoke(throwable, noArgs);
67                  String[] lines = new String[elements.length + 1];
68                  lines[0] = throwable.toString();
69                  Map classMap = new HashMap();
70                  for(int i = 0; i < elements.length; i++) {
71                      lines[i+1] = formatElement(elements[i], classMap);
72                  }
73                  return lines;
74              } catch(Exception ex) {
75              }
76          }
77          return DefaultThrowableRenderer.render(throwable);
78      }
79  
80      /**
81       * Format one element from stack trace.
82       * @param element element, may not be null.
83       * @param classMap map of class name to location.
84       * @return string representation of element.
85       */
86      private String formatElement(final Object element, final Map classMap) {
87          StringBuffer buf = new StringBuffer("\tat ");
88          buf.append(element);
89          try {
90              String className = getClassNameMethod.invoke(element, (Object[]) null).toString();
91              Object classDetails = classMap.get(className);
92              if (classDetails != null) {
93                  buf.append(classDetails);
94              } else {
95                  Class cls = findClass(className);
96                  int detailStart = buf.length();
97                  buf.append('[');
98                  try {
99                      CodeSource source = cls.getProtectionDomain().getCodeSource();
100                     if (source != null) {
101                         URL locationURL = source.getLocation();
102                         if (locationURL != null) {
103                             //
104                             //   if a file: URL
105                             //
106                             if ("file".equals(locationURL.getProtocol())) {
107                                 String path = locationURL.getPath();
108                                 if (path != null) {
109                                     //
110                                     //  find the last file separator character
111                                     //
112                                     int lastSlash = path.lastIndexOf('/');
113                                     int lastBack = path.lastIndexOf(File.separatorChar);
114                                     if (lastBack > lastSlash) {
115                                         lastSlash = lastBack;
116                                     }
117                                     //
118                                     //  if no separator or ends with separator (a directory)
119                                     //     then output the URL, otherwise just the file name.
120                                     //
121                                     if (lastSlash <= 0 || lastSlash == path.length() - 1) {
122                                         buf.append(locationURL);
123                                     } else {
124                                         buf.append(path.substring(lastSlash + 1));
125                                     }
126                                 }
127                             } else {
128                                 buf.append(locationURL);
129                             }
130                         }
131                     }
132                 } catch(SecurityException ex) {
133                 }
134                 buf.append(':');
135                 Package pkg = cls.getPackage();
136                 if (pkg != null) {
137                     String implVersion = pkg.getImplementationVersion();
138                     if (implVersion != null) {
139                         buf.append(implVersion);
140                     }
141                 }
142                 buf.append(']');
143                 classMap.put(className, buf.substring(detailStart));
144             }
145         } catch(Exception ex) {
146         }
147         return buf.toString();
148     }
149 
150     /**
151      * Find class given class name.
152      * @param className class name, may not be null.
153      * @return class, will not be null.
154      * @throws ClassNotFoundException thrown if class can not be found.
155      */
156     private Class findClass(final String className) throws ClassNotFoundException {
157      try {
158        return Thread.currentThread().getContextClassLoader().loadClass(className);
159      } catch (ClassNotFoundException e) {
160        try {
161          return Class.forName(className);
162        } catch (ClassNotFoundException e1) {
163           return getClass().getClassLoader().loadClass(className);
164       }
165     }
166   }
167 
168 }