Skip to content

Commit

Permalink
Create PackagingDataCalculator.java
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolaVeber authored Jun 20, 2022
1 parent 5259c2d commit 171e6a8
Showing 1 changed file with 310 additions and 0 deletions.
310 changes: 310 additions & 0 deletions PackagingDataCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2015, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.classic.spi;

import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

//import sun.reflect.Reflection;

// import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction;
/**
* Given a classname locate associated PackageInfo (jar name, version name).
*
* Original change was done in https://github.com/chrisdolan/logback/commits/eb1b03ac72f0534d9152279e39d85a81615a5e78
* from logback-classic 0.9.30
* Then I started over from the version from logback-classic 1.2.3 and did my best to reapply the OSGi fix
*
* @author James Strachan
* @author Ceki Gülcü
* @author Chris Dolan -- backport of http://jira.qos.ch/browse/LBCLASSIC-296 "Allow hook/override in PackagingDataCalculator"
*
* TODO - remove this class from pax-logging-logback when LBCLASSIC-296 is resolved and we pull in a Logback jar
* that has that fix
*/
@SuppressWarnings("Duplicates")
public class PackagingDataCalculator {

final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0];

HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>();

private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; // private static boolean
// HAS_GET_CLASS_LOADER_PERMISSION = false;

static {
// if either the Reflection class or the getCallerClass method
// are unavailable, then we won't invoke Reflection.getCallerClass()
// This approach ensures that this class will *run* on JDK's lacking
// sun.reflect.Reflection class. However, this class will *not compile*
// on JDKs lacking sun.reflect.Reflection.
try {
//Reflection.getCallerClass(2);
//GET_CALLER_CLASS_METHOD_AVAILABLE = true;
} catch (NoClassDefFoundError e) {
} catch (NoSuchMethodError e) {
} catch (UnsupportedOperationException e) {
} catch (Throwable e) {
System.err.println("Unexpected exception");
e.printStackTrace();
}
}

private static final PackagingDataStrategy[] dataStrategies;

static {
List<PackagingDataStrategy> strategies = new ArrayList<>();
try {
strategies.add(new OsgiPackagingDataStrategy());
} catch (Throwable t) {
// ignore, probably org.osgi.framework.* is simply not in the classpath, or it's too old (need v4.2.0)
}
strategies.add(new DefaultPackagingDataStrategy());
dataStrategies = strategies.toArray(new PackagingDataStrategy[0]);
}

public void calculate(IThrowableProxy tp) {
while (tp != null) {
populateFrames(tp.getStackTraceElementProxyArray());
IThrowableProxy[] suppressed = tp.getSuppressed();
if (suppressed != null) {
for (IThrowableProxy current : suppressed) {
populateFrames(current.getStackTraceElementProxyArray());
}
}
tp = tp.getCause();
}
}

void populateFrames(StackTraceElementProxy[] stepArray) {
// in the initial part of this method we populate package information for
// common stack frames
final Throwable t = new Throwable("local stack reference");
final StackTraceElement[] locateSTEArray = t.getStackTrace();
final int commonFrames = STEUtil.findNumberOfCommonFrames(locateSTEArray, stepArray);
final int localFirstCommon = locateSTEArray.length - commonFrames;
final int stepFirstCommon = stepArray.length - commonFrames;

ClassLoader lastExactClassLoader = null;
ClassLoader firsExactClassLoader = null;

int missfireCount = 0;
for (int i = 0; i < commonFrames; i++) {
Class<?> callerClass = null;
if (GET_CALLER_CLASS_METHOD_AVAILABLE) {
//callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount + 1);
}
StackTraceElementProxy step = stepArray[stepFirstCommon + i];
String stepClassname = step.ste.getClassName();

if (callerClass != null && stepClassname.equals(callerClass.getName())) {
// see also LBCLASSIC-263
lastExactClassLoader = callerClass.getClassLoader();
if (firsExactClassLoader == null) {
firsExactClassLoader = lastExactClassLoader;
}
ClassPackagingData pi = calculateByExactType(callerClass);
step.setClassPackagingData(pi);
} else {
missfireCount++;
ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
step.setClassPackagingData(pi);
}
}
populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
}

void populateUncommonFrames(int commonFrames, StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) {
int uncommonFrames = stepArray.length - commonFrames;
for (int i = 0; i < uncommonFrames; i++) {
StackTraceElementProxy step = stepArray[i];
ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
step.setClassPackagingData(pi);
}
}

private ClassPackagingData calculateByExactType(Class<?> type) {
String className = type.getName();
ClassPackagingData cpd = cache.get(className);
if (cpd != null) {
return cpd;
}
return makePackagingFromType(type, className);
}

private ClassPackagingData computeBySTEP(StackTraceElementProxy step, ClassLoader lastExactClassLoader) {
String className = step.ste.getClassName();
ClassPackagingData cpd = cache.get(className);
if (cpd != null) {
return cpd;
}
Class<?> type = bestEffortLoadClass(lastExactClassLoader, className);
return makePackagingFromType(type, className);
}

private ClassPackagingData makePackagingFromType(Class<?> type, String className) {
ClassPackagingData cpd = null;
if (type != null) {
for (PackagingDataStrategy strategy : dataStrategies) {
cpd = strategy.makePackagingFromType(type, className);
if (cpd != null) {
break;
}
}
}
if (cpd == null) {
// should only happen if type == null
cpd = new ClassPackagingData("na", "na");
}
cache.put(className, cpd);
return cpd;
}

private Class<?> loadClass(ClassLoader cl, String className) {
if (cl == null) {
return null;
}
try {
return cl.loadClass(className);
} catch (ClassNotFoundException e1) {
return null;
} catch (NoClassDefFoundError e1) {
return null;
} catch (Exception e) {
e.printStackTrace(); // this is unexpected
return null;
}

}

/**
* @param lastGuaranteedClassLoader may be null
* @param className
* @return
*/
private Class<?> bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader, String className) {
Class<?> result = loadClass(lastGuaranteedClassLoader, className);
if (result != null) {
return result;
}
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
if (tccl != lastGuaranteedClassLoader) {
result = loadClass(tccl, className);
}
if (result != null) {
return result;
}

try {
return Class.forName(className);
} catch (ClassNotFoundException e1) {
return null;
} catch (NoClassDefFoundError e1) {
return null;
} catch (Exception e) {
e.printStackTrace(); // this is unexpected
return null;
}
}

public interface PackagingDataStrategy {
ClassPackagingData makePackagingFromType(Class<?> type, String className);
}

private static final class DefaultPackagingDataStrategy implements PackagingDataStrategy {
public ClassPackagingData makePackagingFromType(Class<?> type, String className) {
String version = getImplementationVersion(type);
String codeLocation = getCodeLocation(type);
return new ClassPackagingData(codeLocation, version);
}

private static String getImplementationVersion(Class<?> type) {
if (type == null) {
return "na";
}
Package aPackage = type.getPackage();
if (aPackage != null) {
String v = aPackage.getImplementationVersion();
if (v == null) {
return "na";
} else {
return v;
}
}
return "na";
}

private String getCodeLocation(Class<?> type) {
try {
if (type != null) {
// file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
CodeSource codeSource = type.getProtectionDomain().getCodeSource();
if (codeSource != null) {
URL resource = codeSource.getLocation();
if (resource != null) {
String locationStr = resource.toString();
// now lets remove all but the file name
String result = getCodeLocation(locationStr, '/');
if (result != null) {
return result;
}
return getCodeLocation(locationStr, '\\');
}
}
}
} catch (Exception e) {
// ignore
}
return "na";
}

private String getCodeLocation(String locationStr, char separator) {
int idx = locationStr.lastIndexOf(separator);
if (isFolder(idx, locationStr)) {
idx = locationStr.lastIndexOf(separator, idx - 1);
return locationStr.substring(idx + 1);
} else if (idx > 0) {
return locationStr.substring(idx + 1);
}
return null;
}

private boolean isFolder(int idx, String text) {
return (idx != -1 && idx + 1 == text.length());
}
}

private static final class OsgiPackagingDataStrategy implements PackagingDataStrategy {
public ClassPackagingData makePackagingFromType(Class<?> type, String className) {
try {
org.osgi.framework.Bundle bundle = org.osgi.framework.FrameworkUtil.getBundle(type);
if (bundle != null) {
org.osgi.framework.Version bundleVersion = bundle.getVersion();
String version = bundleVersion == org.osgi.framework.Version.emptyVersion ? "na" : bundleVersion.toString();
String codeLocation = bundle.getSymbolicName();
return new ClassPackagingData(codeLocation, version);
}
} catch (NoSuchMethodError e) {
// this means that FrameworkUtil is older than v4.2.0. Give up.
} catch (RuntimeException e) { // at minimum: IllegalStateException, SecurityException
// go on to next strategy
}
return null;
}
}

}

0 comments on commit 171e6a8

Please sign in to comment.