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
87                  // + 1);
88              }
89              StackTraceElementProxy step = stepArray[stepFirstCommon + i];
90              String stepClassname = step.ste.getClassName();
91  
92              if (callerClass != null && stepClassname.equals(callerClass.getName())) {
93                  // see also LBCLASSIC-263
94                  lastExactClassLoader = callerClass.getClassLoader();
95                  if (firsExactClassLoader == null) {
96                      firsExactClassLoader = lastExactClassLoader;
97                  }
98                  ClassPackagingData pi = calculateByExactType(callerClass);
99                  step.setClassPackagingData(pi);
100             } else {
101                 missfireCount++;
102                 ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
103                 step.setClassPackagingData(pi);
104             }
105         }
106         populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
107     }
108 
109     void populateUncommonFrames(int commonFrames, StackTraceElementProxy[] stepArray,
110             ClassLoader firstExactClassLoader) {
111         int uncommonFrames = stepArray.length - commonFrames;
112         for (int i = 0; i < uncommonFrames; i++) {
113             StackTraceElementProxy step = stepArray[i];
114             ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
115             step.setClassPackagingData(pi);
116         }
117     }
118 
119     private ClassPackagingData calculateByExactType(Class<?> type) {
120         String className = type.getName();
121         ClassPackagingData cpd = cache.get(className);
122         if (cpd != null) {
123             return cpd;
124         }
125         String version = getImplementationVersion(type);
126         String codeLocation = getCodeLocation(type);
127         cpd = new ClassPackagingData(codeLocation, version);
128         cache.put(className, cpd);
129         return cpd;
130     }
131 
132     private ClassPackagingData computeBySTEP(StackTraceElementProxy step, ClassLoader lastExactClassLoader) {
133         String className = step.ste.getClassName();
134         ClassPackagingData cpd = cache.get(className);
135         if (cpd != null) {
136             return cpd;
137         }
138         Class<?> type = bestEffortLoadClass(lastExactClassLoader, className);
139         String version = getImplementationVersion(type);
140         String codeLocation = getCodeLocation(type);
141         cpd = new ClassPackagingData(codeLocation, version, false);
142         cache.put(className, cpd);
143         return cpd;
144     }
145 
146     String getImplementationVersion(Class<?> type) {
147         if (type == null) {
148             return "na";
149         }
150         Package aPackage = type.getPackage();
151         if (aPackage != null) {
152             String v = aPackage.getImplementationVersion();
153             if (v == null) {
154                 return "na";
155             } else {
156                 return v;
157             }
158         }
159         return "na";
160 
161     }
162 
163     String getCodeLocation(Class<?> type) {
164         try {
165             if (type != null) {
166                 // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
167                 CodeSource codeSource = type.getProtectionDomain().getCodeSource();
168                 if (codeSource != null) {
169                     URL resource = codeSource.getLocation();
170                     if (resource != null) {
171                         String locationStr = resource.toString();
172                         // now lets remove all but the file name
173                         String result = getCodeLocation(locationStr, '/');
174                         if (result != null) {
175                             return result;
176                         }
177                         return getCodeLocation(locationStr, '\\');
178                     }
179                 }
180             }
181         } catch (Exception e) {
182             // ignore
183         }
184         return "na";
185     }
186 
187     private String getCodeLocation(String locationStr, char separator) {
188         int idx = locationStr.lastIndexOf(separator);
189         if (isFolder(idx, locationStr)) {
190             idx = locationStr.lastIndexOf(separator, idx - 1);
191             return locationStr.substring(idx + 1);
192         } else if (idx > 0) {
193             return locationStr.substring(idx + 1);
194         }
195         return null;
196     }
197 
198     private boolean isFolder(int idx, String text) {
199         return (idx != -1 && idx + 1 == text.length());
200     }
201 
202     private Class<?> loadClass(ClassLoader cl, String className) {
203         if (cl == null) {
204             return null;
205         }
206         try {
207             return cl.loadClass(className);
208         } catch (ClassNotFoundException e1) {
209             return null;
210         } catch (NoClassDefFoundError e1) {
211             return null;
212         } catch (Exception e) {
213             e.printStackTrace(); // this is unexpected
214             return null;
215         }
216 
217     }
218 
219     /**
220      * @param lastGuaranteedClassLoader may be null
221      * @param className
222      * @return
223      */
224     private Class<?> bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader, String className) {
225         Class<?> result = loadClass(lastGuaranteedClassLoader, className);
226         if (result != null) {
227             return result;
228         }
229         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
230         if (tccl != lastGuaranteedClassLoader) {
231             result = loadClass(tccl, className);
232         }
233         if (result != null) {
234             return result;
235         }
236 
237         try {
238             return Class.forName(className);
239         } catch (ClassNotFoundException e1) {
240             return null;
241         } catch (NoClassDefFoundError e1) {
242             return null;
243         } catch (Exception e) {
244             e.printStackTrace(); // this is unexpected
245             return null;
246         }
247     }
248 
249 }