Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LogService: Make getLevel() more robust against ClassNotFoundException #411

Merged
merged 2 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/org/scijava/log/AbstractLogService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/scijava/log/CallingClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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);
maarzt marked this conversation as resolved.
Show resolved Hide resolved
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();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/scijava/log/IgnoreAsCallingClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

/**
* Classes annotated with {@link IgnoreAsCallingClass} are ignored by
* {@link CallingClassUtils#getCallingClass()}.
* {@link CallingClassUtils#getCallingClassName()}.
*
* @author Matthias Arzt
*/
Expand Down
18 changes: 10 additions & 8 deletions src/test/java/org/scijava/log/CallingClassUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import org.junit.Test;

import java.util.function.Supplier;

import static org.junit.Assert.assertEquals;

/**
Expand All @@ -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();
}
}
}