Code injection with Java Proxy example
I was using JDBC PreparedStatement’s batch updates to modify a lot of data in a database and the processing was failing for one of the batch updates because of integrity constraint violation. The exception didn’t contain enough information to find out which data caused the failure and so I’ve created a dynamic proxy for the PreparedStatement that remembered values passed into each of the batch updates and in the case of a failure it automatically printed the batch number and the data.
The crucial part of the code:
LoggingStatementDecorator.java – snippet 1
class LoggingStatementDecorator implements InvocationHandler { private PreparedStatement target; ... private LoggingStatementDecorator(PreparedStatement target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Object result = method.invoke(target, args); updateLog(method, args); // remember data, reset upon successful execution return result; } catch (InvocationTargetException e) { Throwable cause = e.getTargetException(); tryLogFailure(cause); throw cause; } } private void tryLogFailure(Throwable cause) { if (cause instanceof BatchUpdateException) { int failedBatchNr = successfulBatchCounter + 1; Logger.getLogger("JavaProxy").warning( "THE INJECTED CODE SAYS: " + "Batch update failed for batch# " + failedBatchNr + " (counting from 1) with values: [" + getValuesAsCsv() + "]. Cause: " + cause.getMessage()); } } ...
Notes:
To create a proxy, you first need to implement an InvocationHandler and its invoke method, which is called whenever any of the interface’s methods is invoked on the proxy
You can access the information about the call via the java.lang.reflect.* objects and for example delegate the call to the proxied object via method.invoke
We’ve also an utility method for creating a proxy instance for a Prepared statement:
LoggingStatementDecorator.java – snippet 2
public static PreparedStatement createProxy(PreparedStatement target) { return (PreparedStatement) Proxy.newProxyInstance( PreparedStatement.class.getClassLoader(), new Class[] { PreparedStatement.class }, new LoggingStatementDecorator(target)); };
Notes:
- You can see that the newProxyInstance call takes a classloader, an array of interfaces that the proxy should implement, and the invocation handler that calls should be delegated to (the handler itself has to manage a reference to the proxied object, if it needs it)
It is then used like this:
Main.java
... PreparedStatement rawPrepStmt = connection.prepareStatement("..."); PreparedStatement loggingPrepStmt = LoggingStatementDecorator.createProxy(rawPrepStmt); ... loggingPrepStmt.executeBatch(); ...
Notes:
- You see that we have to manually wrap a raw object with the proxy and use the proxy further on
Related Article:
Reference: Practical Introduction into Code Injection with AspectJ, Javassist, and Java Proxy from our JCG partner Jakub Holý at The Holy Java blog