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 }