Thursday, April 24, 2008

How to: Always eagerly load objects from database with Hibernate 3 (without changing configuration xmls)

So it was getting annoying to debug my app. with Hibernate 3 for it's 'lazy load everything by default' feature. Every time I wanted to see what's the value of some collection I will see these weird CGLIB proxy members lurking in debug views.

CGLIB$CALLBACK_0    org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer@9be3aa
class$net$sf$cglib$proxy$InvocationHandler interface net.sf.cglib.proxy.InvocationHandler
class$net$sf$cglib$proxy$NoOp interface net.sf.cglib.proxy.NoOp
class$org$hibernate$proxy$pojo$BasicLazyInitializer null
componentIdType null
constructed true

For every association I would either evaluate the code or create expressions to see what's coming from DB. Lazy loading makes debugging more slower than it already is. What's the big deal?, you will say, Just go ahead and change configuration to lazy-load = "false". Well, I can do it but changing more than five hundred .hbm.xml files will help me more with retiring early than fixing the problem at hand.

So before it becomes nightmare for me I thought of hacking hibernate to make it eagerly load entire object from database. I'm saying hacking because there's no API in Session or SessionFactory interfaces that would allow you to unwrap these un-dead proxies automatically.

Here's how you can hack hibernate to prevent lazy loading:

The basic idea of making hibernate do what you want is to invert it's assumption, i.e. make everything load eager by default, we'll plug ourself to Mappings class and will force eager load implicitely. Follow these steps:

1. First create a class in org.hibernate.cfg package which extends Mappings:

package org.hibernate.cfg;

import java.util.List;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.mapping.PersistentClass;

public class MyMapping extends Mappings {


public MyMapping(Map classes, Map collections, Map tables, Map queries, Map sqlqueries, Map sqlResultSetMappings,
Map imports, List secondPasses, List propertyReferences, NamingStrategy namingStrategy,
Map typeDefs, Map filterDefinitions, Map extendsQueue, List auxiliaryDatabaseObjects,
Map tableNamebinding, Map columnNameBindingPerTable) {

super(classes, collections, tables, queries, sqlqueries, sqlResultSetMappings, imports, secondPasses,
propertyReferences, namingStrategy, typeDefs, filterDefinitions, extendsQueue, auxiliaryDatabaseObjects,
tableNamebinding, columnNameBindingPerTable);

}

@Override
public void addClass(PersistentClass persistentClass) throws MappingException {

persistentClass.setLazy(false); // No Proxies, load everything in one shot
super.addClass(persistentClass);

}

}

The 'addClass' method achieves all we need, just make persistenceClass non-lazy, it would be lazy by default.

2. Now hook this class with Configuration class.
    public class MyConfiguration extends Configuration {
@Override
public Mappings createMappings() {
return new MyMapping(classes, collections, tables, namedQueries, namedSqlQueries, sqlResultSetMappings,
imports, secondPasses, propertyReferences, namingStrategy, typeDefs,
filterDefinitions, extendsQueue, auxiliaryDatabaseObjects, tableNameBinding,
columnNameBindingPerTable);
}
}

There you go, You can now create session factory out of it and you will get fully debug-compatible Java objects loaded from db.

This was way easier to achieve than I could think hooking hairy SessionImpl beast and making org.hibernate.event.LoadEventListener.LoadType INTERNAL_LOAD_EAGER from INTERNAL_LOAD_LAZY without changing SessionImpl itself.

Other than that, I found some talking about lazy loading being problematic with XStream serialization. Threre's a JIRA defect for XStream relating to this as well. I faced the same few weeks before when I thought of removing hibernate from test cases, XStream basically throws up when it finds a confusing CGLIB proxy like this:

com.thoughtworks.xstream.converters.ConversionException: Cannot handle CGLIB enhanced proxies with multiple callbacks
at com.thoughtworks.xstream.converters.reflection.CGLIBEnhancedConverter.marshal(CGLIBEnhancedConverter.java:85)
at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:55)
at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:50)
at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:73)
at com.thoughtworks.xstream.core.ReferenceByXPathMarshallingStrategy.marshal(ReferenceByXPathMarshallingStrategy.java:34)
at com.thoughtworks.xstream.XStream.marshal(XStream.java:765)
at com.thoughtworks.xstream.XStream.marshal(XStream.java:754)
at com.thoughtworks.xstream.XStream.toXML(XStream.java:735)
at com.thoughtworks.xstream.XStream.toXML(XStream.java:725)

I faced this problem before as well and now I don't have to worry about it anymore. You guys can probably just go ahead and load your objects as I described above and your serialization would work fine.

Thursday, March 13, 2008

Unusual tip to find refactoring hot-spot

Disclaimer: This post contains generalization based on my experience, it may or may not be applicable in every state of code-base resembling this situation.

Most of you have probably been through this list of smells to refactoring. In Recent refactoring sessions I discovered a new (at least unusually new) way to identify "hot-spot" needing refactoring.

This might be in turn related to some other smells (like most of them), but here it is:

- A (set of) source file with significantly more revisions compared to it's dependencies demands refactoring.

The obvious question here is why?
- Number of revisions for a source file indicate that it is being modified more frequently than it should. There can be many reasons why a source file is being modified among:
  • Imperative requirements change or
  • A source file that tries to do so much than it should
Now, requirements change is something very common in everyday life. But generally speaking ( opps! ), Not all requirements ask for a significant change in control flow but data. Depending on the domain, you can externalize the portions which keep changing to helper classes or configurations.

A source file trying to do more than it could is always a problem and is generally perceived as 'God module', it's time to slice it down.

So next time you feel like hunting for bad code, look at the revisions and you will have hint where to start. Good hunting.

PS: Note that I've deliberately generalized this to 'source files' and not the configurations.

Tuesday, March 04, 2008

How to find which jar file contains your class at 'runtime'

If you ever wanted to know which jar your class belongs, or wanted to access the meta-information from that jar file (such as certs from Manifest.mf etc.), here's how you can do it.

    public URL getJarURL() {
URL clsUrl = getClass().getResource(getClass().getSimpleName() + ".class");
if (clsUrl != null) {
try {
URLConnection conn = clsUrl.openConnection();
if (conn instanceof JarURLConnection) {
JarURLConnection connection = (JarURLConnection) conn;
return connection.getJarFileURL();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
This would work in most of the enterprisely Java environment (unless your classloader is hacked by some stupid framework in between), not sure if this would be valid under OSGi container.

Why in the name of programming I need to write such trivial hacks? O Java Module system (JSR277), I await you.

Note: This is the simplest way I could write, if you know smarter way, feel free to comment here.

Monday, February 25, 2008

Six reasons why you should know internals of your IDE

May be this is overwhelmingly obvious to geeks, but I am going to describe it nonetheless. This might not significantly impress non-developers or the people and programmers (yes, they are different) who can't describe the word 'tooling'. A developer's productivity is highly dependent on the tools they use, whether it's a fancy big fat IDE platform or an editor as abstemious as VI.

Apparently, the business we work for doesn't really care for the tools we use most of the time - unless of course they are suppose to pay for it. But as simple as it is, If you know how the IDE you use works and how you can make it work the way you want, can make all the difference in your productivity. Here are some unimpressive hows:

1. Many mundane and stupefyingly boring tasks can be automated if your IDE supports macros or extensions. Learn it, it will relieve your back/wrist pain.

2. You may have some fun exploring a totally different software architecture than you may be writing day-to-day, this may turn out to be way too interesting than your day job. Learn it you will know how to design.

3. Sometimes your IDE is just used for a single purpose - coding, but If you know how to extend it you might be motivated to add some cool stuff to it like chat view next to problems view in Eclipse or may be rolling reddit RSS entries in status bar. Learn it, possibilities are never ending.

4. Integrate your IDE to reproduce bugs in your application. This is similar to 1 but different in the way it solves your problem, this may not always possible but worth a thought. I managed to write several plug ins for quickly reproducing bugs related to my application components. Learn it and you can quickly fix them.

5. You can devise custom metrics from your code like how many lines you delete everyday compared to how many you write. Learn it and you will get to know your weird programming habits.

6. You never know when you get in to alternative job market if you know your tools well. Tool developers are far less than application developers. So learn how your IDE works and you might end up earning more money than your own job pays.


All of these, among others I can't really word without beer, are from my own experience. Since Level 1 Human Programmer in 'M$-Land' I've been curious about the tools I use, right from Visual Studio Add-in to Eclipse plug-ins, it has been wonderfully interesting journey so far. Given a chance to speak and I would totally be interested in how Eclipse is architected and how Gel used to support extensions and how painful it used to be to debug Add-ins in Visual Studio 6.0. Boy, it is interesting!