View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, 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.security.CodeSource;
18  import java.util.HashMap;
19  
20  //import sun.reflect.Reflection;
21  
22  // import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction;
23  /**
24   * Given a classname locate associated PackageInfo (jar name, version name).
25   *
26   * @author James Strachan
27   * @Ceki Gülcü
28   */
29  public class PackagingDataCalculator {
30  
31      final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0];
32  
33      HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>();
34  
35      private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; // private static boolean
36                                                                        // HAS_GET_CLASS_LOADER_PERMISSION = false;
37  
38      static {
39          // if either the Reflection class or the getCallerClass method
40          // are unavailable, then we won't invoke Reflection.getCallerClass()
41          // This approach ensures that this class will *run* on JDK's lacking
42          // sun.reflect.Reflection class. However, this class will *not compile*
43          // on JDKs lacking sun.reflect.Reflection.
44          try {
45              //Reflection.getCallerClass(2);
46              //GET_CALLER_CLASS_METHOD_AVAILABLE = true;
47          } catch (NoClassDefFoundError e) {
48          } catch (NoSuchMethodError e) {
49          } catch (UnsupportedOperationException e) {
50          } catch (Throwable e) {
51              System.err.println("Unexpected exception");
52              e.printStackTrace();
53          }
54      }
55  
56      public void calculate(IThrowableProxy tp) {
57          while (tp != null) {
58              populateFrames(tp.getStackTraceElementProxyArray());
59              IThrowableProxy[] suppressed = tp.getSuppressed();
60              if (suppressed != null) {
61                  for (IThrowableProxy current : suppressed) {
62                      populateFrames(current.getStackTraceElementProxyArray());
63                  }
64              }
65              tp = tp.getCause();
66          }
67      }
68  
69      @SuppressWarnings("unused")
70      void populateFrames(StackTraceElementProxy[] stepArray) {
71          // in the initial part of this method we populate package information for
72          // common stack frames
73          final Throwable t = new Throwable("local stack reference");
74          final StackTraceElement[] localSTEArray = t.getStackTrace();
75          final int commonFrames = STEUtil.findNumberOfCommonFrames(localSTEArray, stepArray);
76          final int localFirstCommon = localSTEArray.length - commonFrames;
77          final int stepFirstCommon = stepArray.length - commonFrames;
78  
79          ClassLoader lastExactClassLoader = null;
80          ClassLoader firsExactClassLoader = null;
81  
82          int missfireCount = 0;
83          for (int i = 0; i < commonFrames; i++) {
84              Class<?> callerClass = null;
85              if (GET_CALLER_CLASS_METHOD_AVAILABLE) {
86                  //callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount + 1);
87              }
88              StackTraceElementProxy step = stepArray[stepFirstCommon + i];
89              String stepClassname = step.ste.getClassName();
90  
91              if (callerClass != null && stepClassname.equals(callerClass.getName())) {
92                  // see also LBCLASSIC-263
93                  lastExactClassLoader = callerClass.getClassLoader();
94                  if (firsExactClassLoader == null) {
95                      firsExactClassLoader = lastExactClassLoader;
96                  }
97                  ClassPackagingData pi = calculateByExactType(callerClass);
98                  step.setClassPackagingData(pi);
99              } else {
100                 missfireCount++;
101                 ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
102                 step.setClassPackagingData(pi);
103             }
104         }
105         populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
106     }
107 
108     void populateUncommonFrames(int commonFrames, StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) {
109         int uncommonFrames = stepArray.length - commonFrames;
110         for (int i = 0; i < uncommonFrames; i++) {
111             StackTraceElementProxy step = stepArray[i];
112             ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
113             step.setClassPackagingData(pi);
114         }
115     }
116 
117     private ClassPackagingData calculateByExactType(Class<?> type) {
118         String className = type.getName();
119         ClassPackagingData cpd = cache.get(className);
120         if (cpd != null) {
121             return cpd;
122         }
123         String version = getImplementationVersion(type);
124         String codeLocation = getCodeLocation(type);
125         cpd = new ClassPackagingData(codeLocation, version);
126         cache.put(className, cpd);
127         return cpd;
128     }
129 
130     private ClassPackagingData computeBySTEP(StackTraceElementProxy step, 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                 CodeSource codeSource = type.getProtectionDomain().getCodeSource();
166                 if (codeSource != null) {
167                     URL resource = codeSource.getLocation();
168                     if (resource != null) {
169                         String locationStr = resource.toString();
170                         // now lets remove all but the file name
171                         String result = getCodeLocation(locationStr, '/');
172                         if (result != null) {
173                             return result;
174                         }
175                         return getCodeLocation(locationStr, '\\');
176                     }
177                 }
178             }
179         } catch (Exception e) {
180             // ignore
181         }
182         return "na";
183     }
184 
185     private String getCodeLocation(String locationStr, char separator) {
186         int idx = locationStr.lastIndexOf(separator);
187         if (isFolder(idx, locationStr)) {
188             idx = locationStr.lastIndexOf(separator, idx - 1);
189             return locationStr.substring(idx + 1);
190         } else if (idx > 0) {
191             return locationStr.substring(idx + 1);
192         }
193         return null;
194     }
195 
196     private boolean isFolder(int idx, String text) {
197         return (idx != -1 && idx + 1 == text.length());
198     }
199 
200     private Class<?> loadClass(ClassLoader cl, String className) {
201         if (cl == null) {
202             return null;
203         }
204         try {
205             return cl.loadClass(className);
206         } catch (ClassNotFoundException e1) {
207             return null;
208         } catch (NoClassDefFoundError e1) {
209             return null;
210         } catch (Exception e) {
211             e.printStackTrace(); // this is unexpected
212             return null;
213         }
214 
215     }
216 
217     /**
218      * @param lastGuaranteedClassLoader may be null
219      * @param className
220      * @return
221      */
222     private Class<?> bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader, String className) {
223         Class<?> result = loadClass(lastGuaranteedClassLoader, className);
224         if (result != null) {
225             return result;
226         }
227         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
228         if (tccl != lastGuaranteedClassLoader) {
229             result = loadClass(tccl, className);
230         }
231         if (result != null) {
232             return result;
233         }
234 
235         try {
236             return Class.forName(className);
237         } catch (ClassNotFoundException e1) {
238             return null;
239         } catch (NoClassDefFoundError e1) {
240             return null;
241         } catch (Exception e) {
242             e.printStackTrace(); // this is unexpected
243             return null;
244         }
245     }
246 
247 }