Sunday, May 22, 2011

Glimpse at the JDK7 MethodHandle

Like last Sunday, as a result of a tough week fighting with dumb legacy code, I found myself short of time. But as I committed into learning things and talk about them I decided to do small kata to understand better the new method handles into the JDK7.

The big surprise with incoming versions will be the introduction of closures allowing for example more data oriented programming (very useful in massive parallel algorithms), while leading to less verbose code style. The use of closures could also help us avoiding declaration of interfaces for single method classes, or (at last) avoiding using the use of the (infamous) inner classes, that were half way to be closures. Well designed on the language level, closures can be very self explanatory, have a look to Ruby ,. Groovy or Scala samples. Bluffing and so clean.

But it was decided to split the set of incoming features in two, and guess what, closures are planned for version 8.0 .But... new stuff has been introduced so to prepare their arrival. Deep stuff indeed with the extension of the bytcode with the definition of new opcode invokedynamic. This new opcode is provided as a support for the dynamically typed languages (JSR 292) by allowing late binding in method calls. As a matter of fact, dynamically typed languages don't provide type information until runtime. To make it fast - quoting Ed Ort - invokedynamic, as a new linkage mechanism, solves the problem catching invocations to unexisting  methods or not already bound methods and redirect them to bootstrap methods. All that happens on the bytcode level insuring efficiency and performance.

The detailed explanation of Benjamin J. Evans and Martijn Verburg in their incoming book is quite a jewel and details better  better than I can do this aspect. I must confess that thanks to their introduction I can go deeper into my understanding and the JVM. I am so ashamed I did not that before =D.

One important effect of the introduction of invokedynamic is the apparition of  a set of classes involved in the feature management and located into the java.lang.invoke package. One class attracted me specifically because it can be a real helping class while laying a test harness around unbearable legacy code. Yep that does happen to me regularly and I hate that.
The class is MethodHandle. A method handle, from my point of view can be seen as a pointer to a method not  bound yet to a receiver (very Smalltalk-like indeed, but where does Java come from ?).
When one writes:

System.out.println()

out is the receiver of the printlln() method.

Using a method handle in order to query dynamically an object seems very close to operating on object methods through the reflection API but gaining the performance of the underlying bytcode enhancement.
Having practiced Scala and Ruby, it seems quite natural to define a method abstraction and to bind it later, specifically when it does come to the point of exploratory testing.
Why? because with low level code you can gain access to private methods and when your are testing big spaghetti code functions, so it rocks.

Let us take an example inspired by a previous experience in a bank environment.

I reproduced here, the two object definitions involved in my problem. We had this transaction object that strangely succeeded in transferring money with negative amount from one account to another.
For the sake of simplicity I reproduced a basic Account implementation. First tested:

public final class AccountTest {

    private BigDecimal amountOf(final double money) {
        return BigDecimal.valueOf(money);
    }

    @Test
    public void openAccount_forAmountOfMoney_ShouldNotBeNull() {
        assertThat(openWithDeposit(amountOf(100.0d)), is(not(nullValue())));
    }

    @Test
    public void openAccount_forAmountOfMoney_ShouldHaveMatchingBalance() {
        assertThat(
                openWithDeposit(amountOf(100.0d)).balance(), 
                is(equalTo(amountOf(100.0d)))
        );
    }

    @Test
    public void deposit_forAmountOfMoney_ShouldHaveIncreasedBalance() {
        assertThat(
                openWithDeposit(amountOf(100.0d))
                                .withDeposited(amountOf(100.0)).balance(), 
                is(equalTo(amountOf(200.0)))
        );
    }

    @Test
    public void withdraw_forAmountOfMoney_ShouldHaveIncreasedBalance() {
        assertThat(
                openWithDeposit(amountOf(100.0d))
                            .withdrawed(amountOf(100.0)).balance(), 
                is(equalTo(amountOf(0.0)))
        );
    }
}


Then implemented:

public final class Account {
    private BigDecimal balance_;

    private Account(final BigDecimal money) {
        super();
        balance_ = money;
    }

    static Account openWithDeposit(final BigDecimal money) {
        assert money != null;
        return new Account(money);

    }

    BigDecimal balance() {
        return balance_;
    }

    Account withdrawed(final BigDecimal amount) {
        balance_ = balance_.subtract(amount);
        return this;
    }

    Account withDeposited(final BigDecimal amount) {
        balance_ = balance_.add(amount);
        return this;
    }
}

Remember...basic (should have final balance, with application of the value object pattern). Please remember to use BigDecimal in banking systems (I am a client too =D)

Than comes the transaction object. We need a test fixture like the following:

public final class TransactionTest {
    private BigDecimal money;
    private Account sourceAccount;
    private Account targetAccount;

    @Before
    public void setup() {
        money = amountOf(100);
        sourceAccount = openWithDeposit(amountOf(200));
        targetAccount = openWithDeposit(amountOf(100));
    }
    
    private Account toTarget() {
        return targetAccount;
    }

    private Account fromSource() {
        return sourceAccount;
    }

    private BigDecimal money() {
        return money;
    }

    private BigDecimal amountOf(final double money) {
        return BigDecimal.valueOf(money);
    }    
//....Up to come
}

The transfer must be tested, so I wrote:

    @Test
    public void transaction_forAmountOfTestMoney_ShouldNotBeNull() {
        assertThat(Transaction.forAmountOf(money()), is(not(nullValue())));
    }

    @Test
    public void transfer_ShouldOperateOnAccounts() {
        final Transaction transaction = Transaction.forAmountOf(money());
        transaction.transfer(fromSource(), toTarget());
        assertThat(fromSource().balance(), is(equalTo(amountOf(100))));
        assertThat(toTarget().balance(), is(equalTo(amountOf(200))));
    }

The matching implementation of the Transaction object came to be:

public final class Transaction {

    private BigDecimal amount;

    private Transaction(final BigDecimal amount) {
        super();
        this.amount = amount;
    }

    public boolean transfer(
            final Account fromSource, final Account toTarget
    ) {
        boolean status = false;
        if (canWithdraw(amount(), fromSource)) {
            doTransfer(fromSource, toTarget);
            status = true;
        }
        return status;
    }

    private boolean canWithdraw(
            final BigDecimal amount, final Account fromSource
    ) {
        return true;
    }

    private void doTransfer(
            final Account fromSource, final Account toTarget
    ) {
        fromSource.withdrawed(amount());
        toTarget.withDeposited(amount());
    }

    private BigDecimal amount() {
        assert amount != null;
        return amount;
    }

    public static Transaction forAmountOf(final BigDecimal money) {
        return new Transaction(money);
    }
}

The astute reader will notice that we will have to face a problem while checking if the withdrawal is possible, specifically when the amount of money is negative. The problem is that the canWithdraw method is private. I would like to challenge it without lowering its access level to package scope or protected.
This really happened a few years ago, but the method was long, big, applying remote access, all the kind of bad stuff that happens in really messy code.

The Reflection API offers a solution that works fine:

    @Test
    public void canWithdraw_ThroughReflection_WithAccessRight_ShouldReturnFalse()
    throws Exception {
        final Transaction transaction = Transaction.forAmountOf(money());
        Method method = transaction.getClass().getDeclaredMethod(
                "canWithdraw", BigDecimal.class, Account.class
        );
        method.setAccessible(true);
        final Object result = 
                  method.invoke(transaction, amountOf(-100), fromSource());
        assertThat((Boolean) result,is(equalTo(FALSE)));
    }


I create a Method abstraction, lower dynamically the access right and invoke it on my object. Done !

Using the invoke package utilities I can do the same relying on my JVM performance. All I have to do is to set a hook into my tested class, a hook that will provide me with all the access I want to all the methods in the class, whatever is their access level.

This is the hook I introduced:

    public MethodHandles.Lookup hook() {
        return MethodHandles.lookup();
    }

Of course I will use it temporary as testing method. Quoting the Javadoc a lookup "has the capability to access any method handle that the caller has access to,including direct method handles to private fields and methods". So hello private methods !

I need it, that's clear

Back to my test I then follow the standard procedure:

@Test
public void canWithdraw_ThroughLookupHandle_WithAccessRight_ShouldReturnFalse()
throws Throwable {
   final Transaction transaction = Transaction.forAmountOf(money());
   final MethodType methodType = withDrawMethodType();
   final MethodHandle handle =
           transaction.hook()
               .findVirtual(Transaction.class, "canWithdraw", methodType);
   assertThat(
       (boolean)handle.invokeExact(transaction, amountOf(-100), fromSource()),
        is(equalTo(FALSE))
    );
}

private MethodType withDrawMethodType() {
   return methodType(boolean.class, BigDecimal.class, Account.class);
}



I first define a MethodType, a kind of meta data for an unnamed, unbound methods. What uniquely defines a method if not the return type and the arguments? That's what a method type is.

I can then ask the transaction hook to find the method handle (the pointer if you prefer) to the abstraction of the method I am looking for. The helping hunting method is findVirtual, that brings me for the Transaction class, the handle for the private canWithdraw method.
Then I simply invoke the method...

Done !!The test is red but I can modify or explore my tested code on more finer level.

Very useful, and forcing us a little bit to understand the JVM. But that's pure pleasure =D (We are craftsmen after all). It will help understanding the internal mechanics because the dynamics involved for, provide inference behavior that demands to understand some of the bytecode syntax. I first achieved the test with:

assertThat(
    (Boolean)handle.invokeExact(transaction, amountOf(-100), fromSource()),
    is(equalTo(FALSE))
);

and got:

java.lang.invoke.WrongMethodTypeException: (Ljava/math/BigDecimal;Lcom/promindis/jdk7/handles/Account;)Z cannot be called as (Lcom/promindis/jdk7/handles/Transaction;Ljava/math/BigDecimal;Lcom/promindis/jdk7/handles/Account;)Ljava/lang/Boolean;

Of course the method returns a boolean primitive (Z symbol) !!!

Be seeing you !! =D

2 comments:

Martijn Verburg said...

Great stuff! I especially appreciate the please use BigDecimal reminder - people interfacing with ION beware - they send prices using doubles :(

Globulon said...

Thank you again :)

Post a Comment