BlogicBlog: View from the trenches

The blog about Java and XML with focus on troubleshooting issues and tools.

Monday, November 15, 2004

Re: Code Generation: good or bad?

Permanent article link

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

6 Comments:

At September 01, 2005 5:01 AM, Anonymous Anonymous said...

How did you solve the WebLogic Oracle BLOB wrapper ClassCastException?

 
At September 01, 2005 10:54 PM, Blogger BlogicBlogger said...

You found a bad one to ask about.

After I decompiled the class, I could see what it did or to be specific what it did not (it was a very plain wrapper). That pointed out that the problem must have come from somewhere else, either Oracle classes that were being wrapped or even from some obscure JVM bug that was having its stack blown.

So, we did not exactly resolve it 100%. In the end, I think the client changed the confugiration a bit and the problem had gone away.

 
At September 02, 2005 5:14 AM, Anonymous Anonymous said...

We were trying to get away with not having a WebLogic dependency in the code (by casting to the weblogic...OracleThinBlob) in order to get the Blob OutputStream.

No dependency required for Websphere or JBoss (just have to cast to oracle.sql.BLOB). But couldnt get around this WebLogic dependency.

In fact didnt want to tie to Oracle specific code either (SQLServer just uses setBinaryInputStream(...) etc. - but we have to import java.oracle.BLOB for Oracle).

 
At September 03, 2005 10:35 AM, Blogger BlogicBlogger said...

Have you looked at: getVendorConnection() ?
See:
http://e-docs.bea.com/wls/docs81/javadocs/weblogic/jdbc/extensions/WLConnection.html#getVendorConnection()
http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1077833

The whole point of the wrapper class was that it was generated completely dynamically and inherited all the same interfaces as the real oracle class. Therefore you could call any oracle methods on it and it would pass it on to the real class, but also transparently manage WLS specific info.

 
At September 05, 2005 4:25 AM, Anonymous Anonymous said...

Thanks for the links.

 
At September 05, 2005 4:25 AM, Anonymous Anonymous said...

Thanks for the links.

 

Post a Comment

<< Home