Skip to content

Commit

Permalink
Merge pull request #411 from scijava/fix-logservice-calling-class
Browse files Browse the repository at this point in the history
LogService: Make getLevel() more robust against ClassNotFoundException
  • Loading branch information
ctrueden authored Dec 9, 2020
2 parents 13c1763 + b80f6fe commit afe795a
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 10 deletions.
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
34 changes: 34 additions & 0 deletions src/main/java/org/scijava/log/CallingClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

package org.scijava.log;

import org.scijava.Context;

/**
* Utility class for getting the calling class of a method.
*
Expand All @@ -43,12 +45,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 = Context.getClassLoader().loadClass(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();
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();
}
}
}

0 comments on commit afe795a

Please sign in to comment.