View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.spi;
15  
16  import java.net.URL;
17  import java.util.HashMap;
18  
19  import sun.reflect.Reflection;
20  // import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction;
21  /**
22   * Given a classname locate associated PackageInfo (jar name, version name).
23   *
24   * @author James Strachan
25   * @Ceki Gülcü
26   */
27  public class PackagingDataCalculator {
28  
29    final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0];
30  
31    HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>();
32  
33    private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; //private static boolean HAS_GET_CLASS_LOADER_PERMISSION = false;
34  
35    static {
36      // if either the Reflection class or the getCallerClass method
37      // are unavailable, then we won't invoke Reflection.getCallerClass()
38      // This approach ensures that this class will *run* on JDK's lacking
39      // sun.reflect.Reflection class. However, this class will *not compile*
40      // on JDKs lacking sun.reflect.Reflection.
41      try {
42        Reflection.getCallerClass(2);
43        GET_CALLER_CLASS_METHOD_AVAILABLE = true;
44      } catch (NoClassDefFoundError e) {
45      } catch (NoSuchMethodError e) {
46      } catch (Throwable e) {
47        System.err.println("Unexpected exception");
48        e.printStackTrace();
49      }
50    }
51  
52  
53    public void calculate(IThrowableProxy tp) {
54      while (tp != null) {
55        populateFrames(tp.getStackTraceElementProxyArray());
56        IThrowableProxy[] suppressed = tp.getSuppressed();
57        if(suppressed != null) {
58          for(IThrowableProxy current:suppressed) {
59            populateFrames(current.getStackTraceElementProxyArray());
60          }
61        }
62        tp = tp.getCause();
63      }
64    }
65  
66    void populateFrames(StackTraceElementProxy[] stepArray) {
67      // in the initial part of this method we populate package information for
68      // common stack frames
69      final Throwable t = new Throwable("local stack reference");
70      final StackTraceElement[] localteSTEArray = t.getStackTrace();
71      final int commonFrames = STEUtil.findNumberOfCommonFrames(localteSTEArray,
72              stepArray);
73      final int localFirstCommon = localteSTEArray.length - commonFrames;
74      final int stepFirstCommon = stepArray.length - commonFrames;
75  
76      ClassLoader lastExactClassLoader = null;
77      ClassLoader firsExactClassLoader = null;
78  
79      int missfireCount = 0;
80      for (int i = 0; i < commonFrames; i++) {
81        Class callerClass = null;
82        if (GET_CALLER_CLASS_METHOD_AVAILABLE) {
83          callerClass = Reflection.getCallerClass(localFirstCommon + i
84                  - missfireCount + 1);
85        }
86        StackTraceElementProxy step = stepArray[stepFirstCommon + i];
87        String stepClassname = step.ste.getClassName();
88  
89        if (callerClass != null && stepClassname.equals(callerClass.getName())) {
90          // see also LBCLASSIC-263
91          lastExactClassLoader = callerClass.getClassLoader();
92          if (firsExactClassLoader == null) {
93            firsExactClassLoader = lastExactClassLoader;
94          }
95          ClassPackagingData pi = calculateByExactType(callerClass);
96          step.setClassPackagingData(pi);
97        } else {
98          missfireCount++;
99          ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
100         step.setClassPackagingData(pi);
101       }
102     }
103     populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
104   }
105 
106   void populateUncommonFrames(int commonFrames,
107                               StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) {
108     int uncommonFrames = stepArray.length - commonFrames;
109     for (int i = 0; i < uncommonFrames; i++) {
110       StackTraceElementProxy step = stepArray[i];
111       ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
112       step.setClassPackagingData(pi);
113     }
114   }
115 
116   private ClassPackagingData calculateByExactType(Class type) {
117     String className = type.getName();
118     ClassPackagingData cpd = cache.get(className);
119     if (cpd != null) {
120       return cpd;
121     }
122     String version = getImplementationVersion(type);
123     String codeLocation = getCodeLocation(type);
124     cpd = new ClassPackagingData(codeLocation, version);
125     cache.put(className, cpd);
126     return cpd;
127   }
128 
129   private ClassPackagingData computeBySTEP(StackTraceElementProxy step,
130                                            ClassLoader lastExactClassLoader) {
131     String className = step.ste.getClassName();
132     ClassPackagingData cpd = cache.get(className);
133     if (cpd != null) {
134       return cpd;
135     }
136     Class type = bestEffortLoadClass(lastExactClassLoader, className);
137     String version = getImplementationVersion(type);
138     String codeLocation = getCodeLocation(type);
139     cpd = new ClassPackagingData(codeLocation, version, false);
140     cache.put(className, cpd);
141     return cpd;
142   }
143 
144   String getImplementationVersion(Class type) {
145     if (type == null) {
146       return "na";
147     }
148     Package aPackage = type.getPackage();
149     if (aPackage != null) {
150       String v = aPackage.getImplementationVersion();
151       if (v == null) {
152         return "na";
153       } else {
154         return v;
155       }
156     }
157     return "na";
158 
159   }
160 
161   String getCodeLocation(Class type) {
162     try {
163       if (type != null) {
164         // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
165         URL resource = type.getProtectionDomain().getCodeSource().getLocation();
166         if (resource != null) {
167           String locationStr = resource.toString();
168           // now lets remove all but the file name
169           String result = getCodeLocation(locationStr, '/');
170           if (result != null) {
171             return result;
172           }
173           return getCodeLocation(locationStr, '\\');
174         }
175       }
176     } catch (Exception e) {
177       // ignore
178     }
179     return "na";
180   }
181 
182   private String getCodeLocation(String locationStr, char separator) {
183     int idx = locationStr.lastIndexOf(separator);
184     if (isFolder(idx, locationStr)) {
185       idx = locationStr.lastIndexOf(separator, idx - 1);
186       return locationStr.substring(idx + 1);
187     } else if (idx > 0) {
188       return locationStr.substring(idx + 1);
189     }
190     return null;
191   }
192 
193   private boolean isFolder(int idx, String text) {
194     return (idx != -1 && idx + 1 == text.length());
195   }
196 
197   private Class loadClass(ClassLoader cl, String className) {
198     if (cl == null) {
199       return null;
200     }
201     try {
202       return cl.loadClass(className);
203     } catch (ClassNotFoundException e1) {
204       return null;
205     } catch (NoClassDefFoundError e1) {
206       return null;
207     } catch (Exception e) {
208       e.printStackTrace(); // this is unexpected
209       return null;
210     }
211 
212   }
213 
214   /**
215    * @param lastGuaranteedClassLoader may be null
216    * @param className
217    * @return
218    */
219   private Class bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader,
220                                     String className) {
221     Class result = loadClass(lastGuaranteedClassLoader, className);
222     if (result != null) {
223       return result;
224     }
225     ClassLoader tccl = Thread.currentThread().getContextClassLoader();
226     if (tccl != lastGuaranteedClassLoader) {
227       result = loadClass(tccl, className);
228     }
229     if (result != null) {
230       return result;
231     }
232 
233     try {
234       return Class.forName(className);
235     } catch (ClassNotFoundException e1) {
236       return null;
237     } catch (NoClassDefFoundError e1) {
238       return null;
239     } catch (Exception e) {
240       e.printStackTrace(); // this is unexpected
241       return null;
242     }
243   }
244 
245 }