About Lukas Eder

Lukas is a Java and SQL enthusiast developer. He created the Data Geekery GmbH. He is the creator of jOOQ, a comprehensive SQL library for Java, and he is blogging mostly about these three topics: Java, SQL and jOOQ.

Inadvertent Recursion Protection with Java ThreadLocals

Now here’s a little trick for those of you hacking around with third-party tools, trying to extend them without fully understanding them (yet!). Assume the following situation:

  • You want to extend a library that exposes a hierarchical data model (let’s assume you want to extend Apache Jackrabbit)
  • That library internally checks access rights before accessing any nodes of the content repository
  • You want to implement your own access control algorithm
  • Your access control algorithm will access other nodes of the content repository
  • … which in turn will again trigger access control
  • … which in turn will again access other nodes of the content repository

… Infinite recursion, possibly resulting in a StackOverflowError, if you’re not recursing breadth-first.

Now, you have two options:

  1. Take the time, sit down, understand the internals, and do it right. You probably shouldn’t recurse into your access control once you’ve reached your own extension. In the case of extending Jackrabbit, this would be done by using a System Session to further access nodes within your access control algorithm. A System Session usually bypasses access control.
  2. Be impatient, wanting to get results quickly, and prevent recursion with a trick

Of course, you really should opt for option 1. But who has the time to understand everything?

Here’s how to implement that trick.

/**
 * This thread local indicates whether you've
 * already started recursing with level 1
 */
static final ThreadLocal<Boolean> RECURSION_CONTROL
    = new ThreadLocal<Boolean>();
 
/**
 * This method executes a delegate in a "protected"
 * mode, preventing recursion. If a inadvertent
 * recursion occurred, return a default instead
 */
public static <T> T protect(
        T resultOnRecursion,
        Protectable<T> delegate)
throws Exception {
 
    // Not recursing yet, allow a single level of
    // recursion and execute the delegate once
    if (RECURSION_CONTROL.get() == null) {
        try {
            RECURSION_CONTROL.set(true);
            return delegate.call();
        }
        finally {
            RECURSION_CONTROL.remove();
        }
    }
 
    // Abort recursion and return early
    else {
        return resultOnRecursion;
    }
}
 
/**
 * An API to wrap your code with
 */
public interface Protectable<T> {
    T call() throws Exception;
}

This works easily as can be seen in this usage example:

public static void main(String[] args)
throws Exception {
    protect(null, new Protectable<Void>() {
        @Override
        public Void call() throws Exception {
 
            // Recurse infinitely
            System.out.println("Recursing?");
            main(null);
 
            System.out.println("No!");
            return null;
        }
    });
}

The recursive call to the main() method will be aborted by the protect method, and return early, instead of executing call(). This idea can also be further elaborated by using a Map of ThreadLocals instead, allowing for specifying various keys or contexts for which to prevent recursion. Then, you could also put an Integer into the ThreadLocal, incrementing it on recursion, allowing for at most N levels of recursion.

static final ThreadLocal<Integer> RECURSION_CONTROL
    = new ThreadLocal<Integer>();
 
public static <T> T protect(
        T resultOnRecursion,
        Protectable<T> delegate)
throws Exception {
    Integer level = RECURSION_CONTROL.get();
    level = (level == null) ? 0 : level;
 
    if (level < 5) {
        try {
            RECURSION_CONTROL.set(level + 1);
            return delegate.call();
        }
        finally {
            if (level > 0)
                RECURSION_CONTROL.set(level - 1);
            else
                RECURSION_CONTROL.remove();
        }
    }
    else {
        return resultOnRecursion;
    }
}

But again. Maybe you should just take a couple of minutes more and learn about how the internals of your host library really work, and get things right from the beginning… As always, when applying tricks and hacks!
 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


two × 8 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books