Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Wednesday, June 17, 2015

Short Circuiting AOP in Google Guice

Aspects -- called Interceptors in Google Guice's AOP inclusion -- provide you with the ability to apply a cross-cutting concern across a broad base of functionality.  Consider a lazy initialization function where you want an init() called en route to business methods.  Put an Interceptor on the business method, but be sure that the init() method itself isn't subject to the Interceptor.

This code example uses ThreadLocal storage in an Interceptor to disable and enable the Interceptor allowing its init() call to be skipped thereby avoiding an infinite recursion.


Business Service


MyClass is a business service with a method formatMessage() called externally.  MyClass uses itself in the formatMessage() call by calling its quoteMe() method.  There is an initialized variable that needs to be set as a condition of calling either of these methods.  The initialized mode is expected to verify some object resource gathering that's omitted from the example.

public class MyClass {

    boolean initialized = false;

    public void init() {
        initialized = true;
    }

    public boolean isInitialized() { return initialized; }

    public String formatMessage(String input, boolean useQuotes) {

        if( !initialized ) {
            throw new IllegalStateException("MyClass not initialized; call init()");
        }

        System.out.println(Thread.currentThread().getName() + 
              " : formatMessage(), useQuotes=" + useQuotes);
        if( useQuotes ) {
            return quoteMe(input);
        }
        return input;
    }

    public String quoteMe(String input) {

        if( !initialized ) {
            throw new IllegalStateException("MyClass not initialized; call init()");
        }

        System.out.println(Thread.currentThread().getName() + " :quoteMe()");
        return"'"+input+"'";
    }
}

Since there are public isInitialized() and init() methods, you can initialize the object after construction by simply calling the init() method.  However, even this trivial step can be forgotten.  And, if there are other lifecycle states to be added (refresh state, destroy state), it's more likely that these calls will be forgotten.

Google Guice and AOP


Google Guice is a dependency injection framework.  Usually, it works with object constructors to provide instances that have all of their fields set by the framework.  You can add object instances directly to Guice, but this can be a bottleneck as you'd like the wiring of the app to be as simple as possible for better performance (lazy init) and for error handling.

However, you may need to do some post-wiring initialization.  This example uses an AOP Interceptor registered against the Guice-generated proxy classes via the primary configuration class, an AbstractModule subclass.

Interceptor Code



MyInterceptor makes sure that the object under Interception is initialized by checking the property through isInitialized() and calling init() if needed.  By doing the isInitialized() / init() here, I can apply this broadly to all of my classes which scales.  Requiring the init() call to be made manually could lead to an omission, especially if the call needs to be repeated across many classes in different places in the application.

public class MyInterceptor implements MethodInterceptor {

    private static final ThreadLocal<Boolean> disabled = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return false;  // default for ThreadLocal is null otherwise
        }
    };

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        if( disabled.get() ) {
            System.out.printf("%s : invoke is disabled for %s; returning\n", 
               Thread.currentThread().getName(), methodInvocation.getMethod().getName());
            return methodInvocation.proceed();
        }

        System.out.printf("\t%s : invoking on way to %s\n", 
               Thread.currentThread().getName(), methodInvocation.getMethod().getName() );

        if( methodInvocation.getThis() instanceof MyClass ) {

            MyClass mc = (MyClass)methodInvocation.getThis();

            disabled.set(true);
            if( !mc.isInitialized() ) {
                mc.init();
            }
            disabled.set(false);
        }
        return methodInvocation.proceed();
    }
}

Building for a Framework


This example hardcoded the init() calls in the Interceptor to the MyClass class, init() method, and isInitialized() method.  To use this in a framework, I recommend using a set of annotations and defaults.  Annotated MyClass with @LazyInitializable, init() with @LazyInit, and isInitialized() with @IsLazyInitialized.  Default the methods to init() and isInitialized() if a @LazyInitializable isn't using the method annotations.

Abstract Module


This is the AbstractModule for the application.  I'm binding the interceptor for a specific class, but would prefer to bind to annotations so that I don't have to repeat bindInterceptor().

public class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(Matchers.subclassesOf(MyClass.class), Matchers.any(), new MyInterceptor());
    }
}

Short Circuit



The reason that I'm calling one trivial method, quoteMe(), from another method, formatMessage(), is to demonstrate a potential problem.   I've put the Interceptor on all of MyInterceptor's methods.  This includes isInitialized() and init(), which I don't plan on calling from my app outside of the Interceptor.  Moreover, if MyClass calls its own functions, those are Intercepted as well.  This can lead to an infinite recursion as isIntialized() calls itself 1,000 times before the program crashes.

I apply the short circuit using a boolean flag "disabled".  If it's set, I disable the Interceptor's logic by skipping the additional calls to isInitialized().  I re-enable the Interceptor after the init() call by clearing the flag.

I'm also using ThreadLocal storage rather than a simple field in case many threads are calling MyClass and don't want to re-enable another thread's flag.

Test Class


Finally, here is the main() with my test cases: single threaded call and multi-threaded call.


public class AJMain {

    public static void main(String[] args) {

        Injector injector = Guice.createInjector(new MyModule());

        MyClass mc = injector.getInstance(MyClass.class);

        System.out.printf("[SINGLE THREAD TEST] message=%s\n", 
                   mc.formatMessage("carl", true));

        for( int i=0; i<10; i++ ) {
                    System.out.printf("[MULTITHREAD TEST] message=%s\n", 
                       mc.formatMessage("carl", true))
            ).start();
        }
    }
}

It's no big deal to follow up a single Guice getInstance() call with an init(), but across a large project, where many resources need to be initialized, it ends up being significant.   Resist the urge to pack more initialization into your constructors if you're using Google Guice.  Guice is best implemented when the wiring is kept at a basic level and system-level resource init problems can be dealt with in a structured fashion that allows for a recovery.

No comments:

Post a Comment