001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.spi; 015 016import java.net.URL; 017import java.security.CodeSource; 018import java.util.HashMap; 019 020//import sun.reflect.Reflection; 021 022// import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction; 023/** 024 * Given a classname locate associated PackageInfo (jar name, version name). 025 * 026 * @author James Strachan 027 * @Ceki Gülcü 028 */ 029public class PackagingDataCalculator { 030 031 final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0]; 032 033 HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>(); 034 035 private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; // private static boolean 036 // HAS_GET_CLASS_LOADER_PERMISSION = false; 037 038 static { 039 // if either the Reflection class or the getCallerClass method 040 // are unavailable, then we won't invoke Reflection.getCallerClass() 041 // This approach ensures that this class will *run* on JDK's lacking 042 // sun.reflect.Reflection class. However, this class will *not compile* 043 // on JDKs lacking sun.reflect.Reflection. 044 try { 045 // Reflection.getCallerClass(2); 046 // GET_CALLER_CLASS_METHOD_AVAILABLE = true; 047 } catch (NoClassDefFoundError e) { 048 } catch (NoSuchMethodError e) { 049 } catch (UnsupportedOperationException e) { 050 } catch (Throwable e) { 051 System.err.println("Unexpected exception"); 052 e.printStackTrace(); 053 } 054 } 055 056 public void calculate(IThrowableProxy tp) { 057 while (tp != null) { 058 populateFrames(tp.getStackTraceElementProxyArray()); 059 IThrowableProxy[] suppressed = tp.getSuppressed(); 060 if (suppressed != null) { 061 for (IThrowableProxy current : suppressed) { 062 populateFrames(current.getStackTraceElementProxyArray()); 063 } 064 } 065 tp = tp.getCause(); 066 } 067 } 068 069 @SuppressWarnings("unused") 070 void populateFrames(StackTraceElementProxy[] stepArray) { 071 // in the initial part of this method we populate package information for 072 // common stack frames 073 final Throwable t = new Throwable("local stack reference"); 074 final StackTraceElement[] localSTEArray = t.getStackTrace(); 075 final int commonFrames = STEUtil.findNumberOfCommonFrames(localSTEArray, stepArray); 076 final int localFirstCommon = localSTEArray.length - commonFrames; 077 final int stepFirstCommon = stepArray.length - commonFrames; 078 079 ClassLoader lastExactClassLoader = null; 080 ClassLoader firsExactClassLoader = null; 081 082 int missfireCount = 0; 083 for (int i = 0; i < commonFrames; i++) { 084 Class<?> callerClass = null; 085 if (GET_CALLER_CLASS_METHOD_AVAILABLE) { 086 // callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount 087 // + 1); 088 } 089 StackTraceElementProxy step = stepArray[stepFirstCommon + i]; 090 String stepClassname = step.ste.getClassName(); 091 092 if (callerClass != null && stepClassname.equals(callerClass.getName())) { 093 // see also LBCLASSIC-263 094 lastExactClassLoader = callerClass.getClassLoader(); 095 if (firsExactClassLoader == null) { 096 firsExactClassLoader = lastExactClassLoader; 097 } 098 ClassPackagingData pi = calculateByExactType(callerClass); 099 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}