|

Bytecode Manipulation via Reflection API and Javassist

I’ve recently come across an interesting puzzle, it went like “Guess the next value of Math.random()”. How could you possibly do it? Well, since the output is unpredictable by definition, the only option left for many of us is cheating. Once I successfully “solved” the puzzle by crippling the poor Math class to a worrying extent, I was thinking what else could I do. And most importantly, how far would I get before I had to reach for advanced frameworks like Mockito, PowerMock or jMock.

Turns out you won’t go too far with trying to work around hard-and-fast rules like trying to extend final classes or manipulate static methods. On the other hand, if you keep your expectations low, you can achieve a lot with very little code. I ended up writing a simple prototype of a mock library. A one-day job really, and that included a bit of a research and googling around.

In essence, I only made use of a subtle set of features out of the whole lot the Java Reflection API has to offer. To solve the puzzle with predictability of random numbers, I used the API to locate the relevant field, made it accessible and modified its value. In this particular case I replaced the default random generator with my own.

Here is how to make Math.random() predictable: 

[java]
try {
Class clazz = Class.forName(“java.lang.Math$RandomNumberGeneratorHolder”);
Field field = clazz.getDeclaredField(“randomNumberGenerator”);
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField(“modifiers”);
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, new Random() {
@Override
public double nextDouble() {
return 1;
}
});
} catch (Exception e) {
System.out.println(“Can’t fake it buddy!: ” + e.getMessage());
}
[/java]

Notice the change in fields visibility and how it is turned into a variable (lines 6 and 7). Also, since the field is static, the call of field.set() on line 8 isn’t associated with any particular object.

That’s fair enough, but as you can imagine such an approach is far from being ideal. Suppose I wanted to tweak the output of LocalDateTime.now(). It would take me a considerable amount of time to figure out all of the moving parts and their effect on the final value.

Proxy pattern to the rescue! Let’s not worry about internal workings of a given method. Instead, just intercept the calls and take a decision whether to return a fake value or proceed with the original method. Java reflection provides dynamic proxies. There is a great tutorial out there if you are interested. One of the limitations though is that the business class has to implement an interface. If that’s not a problem, look no further and give it a shot. 

Myself, I wasn’t really up to adding interfaces to my tiny model just for the sake of it. Javassist goes about the same task with ease and less restrictions. 

Here is a quick draft of how to intercept (and control) method invocation:

[java]
public static<T> T mock(Class<T> clazz) {
try {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(clazz);

Class aClass = factory.createClass();
final T instance = (T) aClass.newInstance();
MethodHandler handler = new MethodHandler() {
@Override
public Object invoke(Object self, Method overridden,
Method proceed, Object[] args) throws Throwable {
// This just proceeds with the method invocation,
// but you could as well simply return whatever fake value suits best
return proceed.invoke(mock, args);
}
};

((ProxyObject) instance).setHandler(handler);
// Returning a controlled object. All method calls are intercepted.
return instance;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(“Class ” + clazz.getName() + ” couldn’t be mocked.”);
}
}
[/java]

Exciting as it sounds, bear in mind there are always limitations. The one I was trying to work around, but didn’t really succeed, was dealing with classes which are supposed to remain unchanged (final and / or having a private constructor). No matter how much of reflection I used in order to expose the constructor or even create a default one, and despite the clever trick with removing the “final” modifier, all of my attempts failed miserably. Lessons learnt. Certain restrictions are close the heart of the JVM and aren’t meant to be fiddled with. 

However, if you ease up on your requirements and if you can live with the fact that writing a decent mocking tool might not be the best way of how to invest your time and effort, then working with reflection and instrumentation APIs will be a rewarding experience.

Thanks for reading and feel free to check out my humble mockito-esque library I proudly call a swindler

Similar Posts