From 1e70db29dc461d25ce2c887e83c7851b12506d93 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Thu, 3 Dec 2020 15:47:10 +0100 Subject: [PATCH 1/2] LogService: Make getLevel() more robust against ClassNotFoundException --- .../org/scijava/log/AbstractLogService.java | 2 +- .../org/scijava/log/CallingClassUtils.java | 32 +++++++++++++++++++ .../org/scijava/log/IgnoreAsCallingClass.java | 2 +- .../scijava/log/CallingClassUtilsTest.java | 18 ++++++----- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/scijava/log/AbstractLogService.java b/src/main/java/org/scijava/log/AbstractLogService.java index e813e10e3..e749bf59a 100644 --- a/src/main/java/org/scijava/log/AbstractLogService.java +++ b/src/main/java/org/scijava/log/AbstractLogService.java @@ -104,7 +104,7 @@ public LogSource getSource() { @Override public int getLevel() { if (classAndPackageLevels.isEmpty()) return currentLevel; - return getLevelForClass(CallingClassUtils.getCallingClass().getName(), + return getLevelForClass(CallingClassUtils.getCallingClassName(), currentLevel); } diff --git a/src/main/java/org/scijava/log/CallingClassUtils.java b/src/main/java/org/scijava/log/CallingClassUtils.java index 5bcf98460..bbe5357ed 100644 --- a/src/main/java/org/scijava/log/CallingClassUtils.java +++ b/src/main/java/org/scijava/log/CallingClassUtils.java @@ -43,12 +43,44 @@ private CallingClassUtils() { } /** + * Inspects the stack trace to return the name of the class that calls + * this method, but ignores every class annotated with @IgnoreAsCallingClass. + *

+ * If every class on the stack trace is annotated, then the class at the + * root of the stack trace is returned. + */ + public static String getCallingClassName() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (int i = 1; i < stackTrace.length - 2; i++) { + String className = stackTrace[i].getClassName(); + if (!hasIgnoreAsCallingClassAnnotation(className)) return className; + } + return stackTrace[stackTrace.length - 1].getClassName(); + } + + private static boolean hasIgnoreAsCallingClassAnnotation(String className) { + try { + Class< ? > clazz = Class.forName(className); + return clazz.isAnnotationPresent(IgnoreAsCallingClass.class); + } + catch (ClassNotFoundException ignore) { + return false; + } + } + + /** + * @deprecated Use {@link #getCallingClassName()} instead. + * + * Warning: This method throws a IllegalStateException as soon as it comes + * across a class that can't be loaded with the default class loader. + * * Inspects the stack trace to return the class that calls this method, but * ignores every class annotated with @IgnoreAsCallingClass. * * @throws IllegalStateException if every method on the stack, is in a class * annotated with @IgnoreAsCallingClass. */ + @Deprecated public static Class getCallingClass() { try { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); diff --git a/src/main/java/org/scijava/log/IgnoreAsCallingClass.java b/src/main/java/org/scijava/log/IgnoreAsCallingClass.java index d6ff175f1..62aefe2db 100644 --- a/src/main/java/org/scijava/log/IgnoreAsCallingClass.java +++ b/src/main/java/org/scijava/log/IgnoreAsCallingClass.java @@ -34,7 +34,7 @@ /** * Classes annotated with {@link IgnoreAsCallingClass} are ignored by - * {@link CallingClassUtils#getCallingClass()}. + * {@link CallingClassUtils#getCallingClassName()}. * * @author Matthias Arzt */ diff --git a/src/test/java/org/scijava/log/CallingClassUtilsTest.java b/src/test/java/org/scijava/log/CallingClassUtilsTest.java index a23f8fee0..ff6d3df24 100644 --- a/src/test/java/org/scijava/log/CallingClassUtilsTest.java +++ b/src/test/java/org/scijava/log/CallingClassUtilsTest.java @@ -31,6 +31,8 @@ import org.junit.Test; +import java.util.function.Supplier; + import static org.junit.Assert.assertEquals; /** @@ -41,26 +43,26 @@ public class CallingClassUtilsTest { @Test public void testGetCallingClass() { - Class callingClass = CallingClassUtils.getCallingClass(); - assertEquals(this.getClass(), callingClass); + String callingClass = CallingClassUtils.getCallingClassName(); + assertEquals(this.getClass().getName(), callingClass); } @Test public void testIgnoreAsCallingClass() { - assertEquals(ClassA.class, ClassA.returnGetCallingClass()); - assertEquals(this.getClass(), ClassB.returnGetCallingClass()); + assertEquals(ClassA.class.getName(), ClassA.returnGetCallingClass()); + assertEquals(this.getClass().getName(), ClassB.returnGetCallingClass()); } public static class ClassA { - static Class returnGetCallingClass() { - return CallingClassUtils.getCallingClass(); + static String returnGetCallingClass() { + return CallingClassUtils.getCallingClassName(); } } @IgnoreAsCallingClass private static class ClassB { - static Class returnGetCallingClass() { - return CallingClassUtils.getCallingClass(); + static String returnGetCallingClass() { + return CallingClassUtils.getCallingClassName(); } } } From b80f6fe18be696716c2545a43440c14534e92115 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Wed, 9 Dec 2020 14:24:03 +0100 Subject: [PATCH 2/2] CallingClassUtils: replace Class.forName() with Context.getClassLoader() Class.forName() will fail if the threads class loader is not set. --- src/main/java/org/scijava/log/CallingClassUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/log/CallingClassUtils.java b/src/main/java/org/scijava/log/CallingClassUtils.java index bbe5357ed..66bef3263 100644 --- a/src/main/java/org/scijava/log/CallingClassUtils.java +++ b/src/main/java/org/scijava/log/CallingClassUtils.java @@ -29,6 +29,8 @@ package org.scijava.log; +import org.scijava.Context; + /** * Utility class for getting the calling class of a method. * @@ -60,7 +62,7 @@ public static String getCallingClassName() { private static boolean hasIgnoreAsCallingClassAnnotation(String className) { try { - Class< ? > clazz = Class.forName(className); + Class< ? > clazz = Context.getClassLoader().loadClass(className); return clazz.isAnnotationPresent(IgnoreAsCallingClass.class); } catch (ClassNotFoundException ignore) {