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/** 021 * Given a classname locate associated PackageInfo (jar name, version name). 022 * 023 * @author James Strachan 024 * @author Ceki Gülcü 025 */ 026public class PackagingDataCalculator { 027 028 final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0]; 029 030 HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>(); 031 032 private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; // private static boolean 033 // HAS_GET_CLASS_LOADER_PERMISSION = false; 034 035 static { 036 // if either the Reflection class or the getCallerClass method 037 // are unavailable, then we won't invoke Reflection.getCallerClass() 038 // This approach ensures that this class will *run* on JDK's lacking 039 // sun.reflect.Reflection class. However, this class will *not compile* 040 // on JDKs lacking sun.reflect.Reflection. 041 try { 042 // Reflection.getCallerClass(2); 043 // GET_CALLER_CLASS_METHOD_AVAILABLE = true; 044 } catch (NoClassDefFoundError e) { 045 } catch (NoSuchMethodError e) { 046 } catch (UnsupportedOperationException e) { 047 } catch (Throwable e) { 048 System.err.println("Unexpected exception"); 049 e.printStackTrace(); 050 } 051 } 052 053 public void calculate(IThrowableProxy tp) { 054 while (tp != null) { 055 populateFrames(tp.getStackTraceElementProxyArray()); 056 IThrowableProxy[] suppressed = tp.getSuppressed(); 057 if (suppressed != null) { 058 for (IThrowableProxy current : suppressed) { 059 populateFrames(current.getStackTraceElementProxyArray()); 060 } 061 } 062 tp = tp.getCause(); 063 } 064 } 065 066 @SuppressWarnings("unused") 067 void populateFrames(StackTraceElementProxy[] stepArray) { 068 // in the initial part of this method we populate package information for 069 // common stack frames 070 final Throwable t = new Throwable("local stack reference"); 071 final StackTraceElement[] localSTEArray = t.getStackTrace(); 072 final int commonFrames = STEUtil.findNumberOfCommonFrames(localSTEArray, stepArray); 073 final int localFirstCommon = localSTEArray.length - commonFrames; 074 final int stepFirstCommon = stepArray.length - commonFrames; 075 076 ClassLoader lastExactClassLoader = null; 077 ClassLoader firsExactClassLoader = null; 078 079 int missfireCount = 0; 080 for (int i = 0; i < commonFrames; i++) { 081 Class<?> callerClass = null; 082 if (GET_CALLER_CLASS_METHOD_AVAILABLE) { 083 // callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount 084 // + 1); 085 } 086 StackTraceElementProxy step = stepArray[stepFirstCommon + i]; 087 String stepClassname = step.ste.getClassName(); 088 089 if (callerClass != null && stepClassname.equals(callerClass.getName())) { 090 // see also LBCLASSIC-263 091 lastExactClassLoader = callerClass.getClassLoader(); 092 if (firsExactClassLoader == null) { 093 firsExactClassLoader = lastExactClassLoader; 094 } 095 ClassPackagingData pi = calculateByExactType(callerClass); 096 step.setClassPackagingData(pi); 097 } else { 098 missfireCount++; 099 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}