View Javadoc

1   /**
2    * Logback: the generic, reliable, fast and flexible logging framework.
3    * 
4    * Copyright (C) 2000-2008, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.classic.spi;
11  
12  import java.net.URL;
13  import java.util.HashMap;
14  import java.util.LinkedList;
15  import java.util.List;
16  
17  import sun.reflect.Reflection;
18  import ch.qos.logback.classic.spi.ThrowableDataPoint.ThrowableDataPointType;
19  
20  /**
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;
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    public PackagingDataCalculator() {
53    }
54  
55    public void calculate(ThrowableDataPoint[] tdpArray) {
56      int steStart = 0;
57      StackTraceElementProxy[] stepArray = new StackTraceElementProxy[0];
58      do {
59        steStart = findSTEStartIndex(tdpArray, steStart + stepArray.length);
60        stepArray = getSTEPArray(tdpArray, steStart);
61        populateFrames(stepArray);
62      } while (steStart != -1);
63    }
64  
65    void populateFrames(StackTraceElementProxy[] stepArray) {
66      // in the initial part of this method we populate package informnation for
67      // common stack frames
68      final Throwable t = new Throwable("local stack reference");
69      final StackTraceElement[] localteSTEArray = t.getStackTrace();
70      final int commonFrames = STEUtil.findNumberOfCommonFrames(localteSTEArray,
71          stepArray);
72      final int localFirstCommon = localteSTEArray.length - commonFrames;
73      final int stepFirstCommon = stepArray.length - commonFrames;
74  
75      ClassLoader lastExactClassLoader = null;
76      ClassLoader firsExactClassLoader = null;
77  
78      int missfireCount = 0;
79      for (int i = 0; i < commonFrames; i++) {
80        Class callerClass = null;
81        if (GET_CALLER_CLASS_METHOD_AVAILABLE) {
82          callerClass = Reflection.getCallerClass(localFirstCommon + i
83              - missfireCount + 1);
84        }
85        StackTraceElementProxy step = stepArray[stepFirstCommon + i];
86        String stepClassname = step.ste.getClassName();
87  
88        if (callerClass != null && stepClassname.equals(callerClass.getName())) {
89          lastExactClassLoader = callerClass.getClassLoader();
90          if (firsExactClassLoader == null) {
91            firsExactClassLoader = callerClass.getClassLoader();
92          }
93          ClassPackagingData pi = calculateByExactType(callerClass);
94          step.setClassPackagingData(pi);
95        } else {
96          missfireCount++;
97          ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
98          step.setClassPackagingData(pi);
99        }
100     }
101     populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
102   }
103 
104   int findSTEStartIndex(final ThrowableDataPoint[] tdpArray, final int from) {
105     final int len = tdpArray.length;
106     if (from < 0 || from >= len) {
107       return -1;
108     }
109     for (int i = from; i < len; i++) {
110       if (tdpArray[i].type == ThrowableDataPointType.STEP) {
111         return i;
112       }
113     }
114     return -1;
115   }
116 
117   private StackTraceElementProxy[] getSTEPArray(
118       final ThrowableDataPoint[] tdpArray, final int from) {
119     List<StackTraceElementProxy> stepList = new LinkedList<StackTraceElementProxy>();
120     int len = tdpArray.length;
121     if (from < 0 || from >= len) {
122       return stepList.toArray(STEP_ARRAY_TEMPLATE);
123     }
124     for (int i = from; i < len; i++) {
125       final ThrowableDataPoint tdp = tdpArray[i];
126 
127       if (tdp.type == ThrowableDataPointType.STEP) {
128         stepList.add(tdp.getStackTraceElementProxy());
129       } else {
130         break;
131       }
132     }
133     return stepList.toArray(STEP_ARRAY_TEMPLATE);
134   }
135 
136   void populateUncommonFrames(int commonFrames,
137       StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) {
138     int uncommonFrames = stepArray.length - commonFrames;
139     for (int i = 0; i < uncommonFrames; i++) {
140       StackTraceElementProxy step = stepArray[i];
141       ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
142       step.setClassPackagingData(pi);
143     }
144   }
145 
146   private ClassPackagingData calculateByExactType(Class type) {
147     String className = type.getName();
148     ClassPackagingData cpd = cache.get(className);
149     if (cpd != null) {
150       return cpd;
151     }
152     String version = getImplementationVersion(type);
153     String codeLocation = getCodeLocation(type);
154     cpd = new ClassPackagingData(codeLocation, version);
155     cache.put(className, cpd);
156     return cpd;
157   }
158 
159   private ClassPackagingData computeBySTEP(StackTraceElementProxy step,
160       ClassLoader lastExactClassLoader) {
161     String className = step.ste.getClassName();
162     ClassPackagingData cpd = cache.get(className);
163     if (cpd != null) {
164       return cpd;
165     }
166     Class type = bestEffortLoadClass(lastExactClassLoader, className);
167     String version = getImplementationVersion(type);
168     String codeLocation = getCodeLocation(type);
169     cpd = new ClassPackagingData(codeLocation, version, false);
170     cache.put(className, cpd);
171     return cpd;
172   }
173 
174   String getImplementationVersion(Class type) {
175     if(type == null) {
176       return "na";
177     }
178     Package aPackage = type.getPackage();
179     if (aPackage != null) {
180       String v = aPackage.getImplementationVersion();
181       if (v == null) {
182         return "na";
183       } else {
184         return v;
185       }
186     }
187     return "na";
188 
189   }
190 
191   String getCodeLocation(Class type) {
192     try {
193       if (type != null) {
194         // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
195         URL resource = type.getProtectionDomain().getCodeSource().getLocation();
196         if (resource != null) {
197           String locationStr = resource.toString();
198           // now lets remove all but the file name
199           String result = getCodeLocation(locationStr, '/');
200           if (result != null) {
201             return result;
202           }
203           return getCodeLocation(locationStr, '\\');
204         }
205       }
206     } catch (Exception e) {
207       // ignore
208     }
209     return "na";
210   }
211 
212   private String getCodeLocation(String locationStr, char separator) {
213     int idx = locationStr.lastIndexOf(separator);
214     if (isFolder(idx, locationStr)) {
215       idx = locationStr.lastIndexOf(separator, idx - 1);
216       return locationStr.substring(idx + 1);
217     } else if (idx > 0) {
218       return locationStr.substring(idx + 1);
219     }
220     return null;
221   }
222 
223   private boolean isFolder(int idx, String text) {
224     return (idx != -1 && idx + 1 == text.length());
225   }
226 
227   private Class loadClass(ClassLoader cl, String className) {
228     if (cl == null) {
229       return null;
230     }
231     try {
232       return cl.loadClass(className);
233     } catch (ClassNotFoundException e1) {
234       return null;
235     } catch (Exception e) {
236       e.printStackTrace(); // this is unexpected
237       return null;
238     }
239 
240   }
241 
242   /**
243    * 
244    * @param lastGuaranteedClassLoader may be null
245    *                
246    * @param className
247    * @return
248    */
249   private Class bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader,
250       String className) {
251     Class result = loadClass(lastGuaranteedClassLoader, className);
252     if (result != null) {
253       return result;
254     }
255     ClassLoader tccl = Thread.currentThread().getContextClassLoader();
256     if (tccl != lastGuaranteedClassLoader) {
257       result = loadClass(tccl, className);
258     }
259     if (result != null) {
260       return result;
261     }
262 
263     try {
264       return Class.forName(className);
265     } catch (ClassNotFoundException e1) {
266       return null;
267     } catch (Exception e) {
268       e.printStackTrace(); // this is unexpected
269       return null;
270     }
271   }
272 
273 }