Home > Java > getCaller() hack

getCaller() hack

Disclaimer: the following is a devious hack, it should only be used if you know what you’re doing.

As developers, we should only call public APIs. However, the Java language cannot differentiate between public API and private stuff: as soon as a class and one of its method is public, we can reference the former and call the later. Therefore, we are exposed to the Dark Side of the Force, and sometimes tempted to use it.

A good example of this terrible temptation is the sun.reflect.Reflection.getCaller(int) method. As its name implies, this evil piece returns which class called your current code, letting you tailor your code behavior depending on the calling class. The Dark Side can be seductive indeed!

This way, we can check whether some caller has some property and let it invoke our stuff or not accordingly. For example, let’s design a code piece that can be invoked only from “privileged” code. Such privileges include: be a specific class, come from a specific package, be annotated with a specific annotation, etc. The following code uses the third option.

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Privileged {}

Now, checking for this annotation’s existence in the calling code is achieved like this:

public class Restricted {

    public Restricted() {

    int i = 2;

    while (true) {

        Class caller = Reflection.getCallerClass(i++);

        if (caller == null) {

            throw new SecurityException();
        }

        if (caller.getAnnotation(Privileged.class) != null) {

            break;
        }
    }
}
  1. The int parameter in the getCallerClass() method refers to the number of frames up the calling stack. Index 0 is Reflection.getCallerClass() itself, while index 1 is our restricted constructor, so that real work should begin at 2.
  2. The algorithm is to search up the stack until either the whole stack has been browsed and no privileged code has been found or stop when the matching annotation is found. In the former case, throw an exception, in the latter, do the job as expected.
  3. Of course, this algorithm shouldn’t be hard-coded but factorized in a aspect and injected at some determined pointcut (perhaps a @Restricted annotation?)

Ok, but what use-case does this solve? Well, I’ve always thought about what requirements Hibernate (or JPA) put on your design. One of such requirement is to provide a no-arg constructor, for the framework to instantiate. After that, it can use setter or inject attributes directly. Unfortunately, this conflicts with my desire to provide immutable objects that have all mandatory parameters in the constructor. A basic solution would be to provide both constructors but document them accordingly (/** Do not use: this one is for Hibernate. */). However, to really enforce this rule, you’d probably need to use getCallerClass() with a check on an org.hibernate package.

Beware that Oracle intended to remove this method in the next JDK. Again, do not use this at home, but it opens some interesting perspectives, doesn’t it? […evil laugh…]

Download the above code in IntelliJ/Maven format here.

email
Send to Kindle
Categories: Java Tags:
  1. October 13th, 2013 at 20:39 | #1

    Thanks for this hack. We in our team are stringing to implement DDD and one central concept in DDD is ValueObject who must be immutable by definition.

    Because of Hibernate, we need to have no-args constructors and private setters, and we comment this /* for hibernate use only*/ , exactly the same way you have described. Using your hack add an additional control to enforce immutability of the object, and it can be nice.

    Thought, I have one reserve concerning performance : depending on your application, you might call the @privileged check many times. For each setter of any value object returned by Hibernate. Can be huge. Do u have any idea about the performance cost of getCaller() ?

    Anyway, there is also a valid alternative to have persistent value object without setters, and default constructors : by using UserType

  2. October 15th, 2013 at 11:50 | #2

    Yes, you should not use sun.reflect.Reflection. But you don’t need to, because the same functionality is available using public API:

    public class Test {
    public static void main(String… args) throws Exception {
    System.out.println(getCallerClass());
    }
    public static Class getCallerClass() {
    return new SecurityManager() {
    Class clazz = getClassContext()[2];
    }.clazz;
    }
    }

  1. No trackbacks yet.