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  /**
21   * Given a classname locate associated PackageInfo (jar name, version name).
22   *
23   * @author James Strachan
24   * @author Ceki Gülcü
25   */
26  public class PackagingDataCalculator {
27  
28      final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0];
29  
30      HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>();
31  
32      private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; // private static boolean
33                                                                        // 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 (UnsupportedOperationException e) {
47          } catch (Throwable e) {
48              System.err.println("Unexpected exception");
49              e.printStackTrace();
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      @SuppressWarnings("unused")
67      void populateFrames(StackTraceElementProxy[] stepArray) {
68          // in the initial part of this method we populate package information for
69          // common stack frames
70          final Throwable t = new Throwable("local stack reference");
71          final StackTraceElement[] localSTEArray = t.getStackTrace();
72          final int commonFrames = STEUtil.findNumberOfCommonFrames(localSTEArray, stepArray);
73          final int localFirstCommon = localSTEArray.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 - missfireCount
84                  // + 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, StackTraceElementProxy[] stepArray,
107             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, ClassLoader lastExactClassLoader) {
130         String className = step.ste.getClassName();
131         ClassPackagingData cpd = cache.get(className);
132         if (cpd != null) {
133             return cpd;
134         }
135         Class<?> type = bestEffortLoadClass(lastExactClassLoader, className);
136         String version = getImplementationVersion(type);
137         String codeLocation = getCodeLocation(type);
138         cpd = new ClassPackagingData(codeLocation, version, false);
139         cache.put(className, cpd);
140         return cpd;
141     }
142 
143     String getImplementationVersion(Class<?> type) {
144         if (type == null) {
145             return "na";
146         }
147         Package aPackage = type.getPackage();
148         if (aPackage != null) {
149             String v = aPackage.getImplementationVersion();
150             if (v == null) {
151                 return "na";
152             } else {
153                 return v;
154             }
155         }
156         return "na";
157 
158     }
159 
160     String getCodeLocation(Class<?> type) {
161         try {
162             if (type != null) {
163                 // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
164                 CodeSource codeSource = type.getProtectionDomain().getCodeSource();
165                 if (codeSource != null) {
166                     URL resource = codeSource.getLocation();
167                     if (resource != null) {
168                         String locationStr = resource.toString();
169                         // now lets remove all but the file name
170                         String result = getCodeLocation(locationStr, '/');
171                         if (result != null) {
172                             return result;
173                         }
174                         return getCodeLocation(locationStr, '\\');
175                     }
176                 }
177             }
178         } catch (Exception e) {
179             // ignore
180         }
181         return "na";
182     }
183 
184     private String getCodeLocation(String locationStr, char separator) {
185         int idx = locationStr.lastIndexOf(separator);
186         if (isFolder(idx, locationStr)) {
187             idx = locationStr.lastIndexOf(separator, idx - 1);
188             return locationStr.substring(idx + 1);
189         } else if (idx > 0) {
190             return locationStr.substring(idx + 1);
191         }
192         return null;
193     }
194 
195     private boolean isFolder(int idx, String text) {
196         return (idx != -1 && idx + 1 == text.length());
197     }
198 
199     private Class<?> loadClass(ClassLoader cl, String className) {
200         if (cl == null) {
201             return null;
202         }
203         try {
204             return cl.loadClass(className);
205         } catch (ClassNotFoundException e1) {
206             return null;
207         } catch (NoClassDefFoundError e1) {
208             return null;
209         } catch (Exception e) {
210             e.printStackTrace(); // this is unexpected
211             return null;
212         }
213 
214     }
215 
216     /**
217      * @param lastGuaranteedClassLoader may be null
218      * @param className
219      * @return
220      */
221     private Class<?> bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader, String className) {
222         Class<?> result = loadClass(lastGuaranteedClassLoader, className);
223         if (result != null) {
224             return result;
225         }
226         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
227         if (tccl != lastGuaranteedClassLoader) {
228             result = loadClass(tccl, className);
229         }
230         if (result != null) {
231             return result;
232         }
233 
234         try {
235             return Class.forName(className);
236         } catch (ClassNotFoundException e1) {
237             return null;
238         } catch (NoClassDefFoundError e1) {
239             return null;
240         } catch (Exception e) {
241             e.printStackTrace(); // this is unexpected
242             return null;
243         }
244     }
245 
246 }