David Rupp writes – eloquently – that he does not understand why people are reluctant to use software that auto-generates code (e.g. Hibernate and AOP/AspectJ).
He is of course right. Code generation on the fly is in exactly the same league as JSPs, EJBs and dynamic proxies in terms of how the code one writes does not corresponds to the code that actually runs.
But he is also wrong. What we have here is not a Good/Bad breakdown, but rather a spectrum. And the measure of that spectrum is how easy it is to trace the problem with the generated/compiled class back to something one can change.
In the case of JSPs, it is fairly easy to trace back as most of the application servers allow to keep the generated java file and – at least Weblogic – will put JSPs original line numbers as comments in the generated Java code.
The same applies to any offline precompiler. However complex they are, there is always a class file produced in the end that can be used as a reference.
Finally, a third party library can – in desperate situation – be decompiled and the decompiled source will include the line numbers (unless the class had been stripped or obfuscated).
On the other hand, AFAIK code generators do not usually bother with putting in source line numbers, nor do they save anywhere the code blocks that become classes.
I have a case to the point. A week ago, I had a support case which had a ClassCast exception in the dynamically generated wrapper class that Weblogic puts around Oracle driver’s proprietary methods. The top of the exception stack was:
at weblogic.jdbc.wrapper.Blob_oracle_sql_BLOB.getBinaryStream(Unknown Source)
Of course, I did not know at first that it was a generated class. I could have searched through the whole WLS source code base forever without finding it. The only hint was the class name. Seeing
wrapper has triggered the memories of other parts of Weblogic where we use similar tecniques (RMI, EJB, etc).
Eventually, I traced this to the part of code which generated the class on the fly. And when I did, I found – as expected – that the class is just generated in memory and is loaded from bytearray without any export or debug functionality.
Basically, we were generating a weblogic class at the runtime that will implement all Oracle’s proprietary methods, but will add pre and post processing to them. A not-so-dynamic proxy, in other words.
I had to build a custom patch to dump the file out and then replicate the basic logic to trigger the class generation. Once that was done, I finally had class file to decompile and analyse.
However, this was comparatively simple problem in that it was easy to reproduce and the code generated was conditional on classpath (Oracle drivers), but not on any runtime conditions. Were it not the case, there would have been no way for me to reliably confirm the code behaviour.
The same issue will apply to woven-in Aspects once they become very popular. What is a boon for a developer is often a woe for the support engineer.
I think the lesson here is that any code generation framework must ensure that there is an easy way to get class bytecodes, whether through debug flags, explorable ClassLoaders or any other option.
BlogicBlogger Over and Out