Ok, previously we get into conclusion that JDK 5.0 annotation feature can be used for implementing obfuscation safe reflection API usage. What other tricks it could perform.
One interesting issue when developing large scale software is to ensure that API specifications are honored properly. Within java language context this means that API spec is strictly specified with javadoc, and implementation must honor this specification.
Would it be possible to add additional verifications? Surely answer is yes (as you may have guessed…).
Let’s combine two usefull techniques, namely annotations and dynamic proxy. Cook for a while over hot oven, and enjoy the meal.
1) Let’s define annotation for assertion
[code lang=”java”]
@Retention(RetentionPolicy.RUNTIME)
public @interface KAssert {
AssertType value();
}
[/code]
And assertion type looks like this:
[code lang=”java”]
public enum AssertType {
/**
* value must be non null
*/
NULL,
/**
* Value must be non empty
*/
EMPTY,
}
[/code]
2) Dynamic Proxy for applying assertion
[code lang=”java”]
public final class AssertHandler implements InvocationHandler {
private final Object mObject;
public AssertHandler(final Object pObject) {
mObject = pObject;
}
public Object invoke(
final Object proxy,
final Method pMethod,
final Object[] pArgs) throws Throwable
{
Object result = pMethod.invoke(mObject, pArgs);
KAssert ass = pMethod.getAnnotation(KAssert.class);
if (ass!=null) {
if (ass.value()==AssertType.NULL && result==null) {
throw new NullPointerException(ass + ” failed”);
}
}
return result;
}
}
[/code]
And utility for creating assertion,
[code lang=”java”]
…
/**
* Create assert handler. All interfaces of Object are
* asserted.
*/
public static
Class[] types = ClassUtil.collectInterfaces(pObject.getClass());
return (T)Proxy.newProxyInstance(
AssertHandler.class.getClassLoader(),
types,
new AssertHandler(pObject));
}
}
[/code]
3) Some fancy operation, which is required to be asserted.
[code lang=”java”]
public interface SaveOperation {
@KAssert(AssertType.NULL)
Object doSave(Object pContent);
}
[/code]
4) Let’s have some buggy implementation of operation
[code lang=”java”]
public class SaveImpl implements SaveOperation {
public Object doSave(Object pContent) {
return null;
}
}
[/code]
OK, that should surely fail assertion, since it returns always null.
4) Apply logic into use
[code lang=”java”]
public class CallFrame extends KApplicationFrame {
…
public void saveFile(ActionContext pCtx) {
SaveOperation save = AssertHandler.create(createSave());
save.doSave(”dummy content”);
}
protected SaveOperation createSave() {
return new SaveImpl();
}
…
}
[/code]
And voila, assertion is auto-magically done for the calls done for all operation calls.
This specific example, is a bit trivial, but concept can be extended a bit more
when more boilerplate code is implemented automatically by the frameworks. Technique can be used for both parameter and return value validation, or perform specific tracing, etc. based into annotation tags.
Yes, you should be now thinking that this sounds a bit like Aspect Oriented Programming, but while you are there, please notice that this is different concept.
Naturally there is few caveat emptors included (as always). One important limitations is that his logic applies only into interfaces due to dynamic proxy logic.