Archive for February, 2008

Using Dynamic Method Invocation to “Script” Java

Although Java isn’t thought of as a dynamic language now a days, what with Ruby and Groovy being all the rage, Java does have support for dynamic features.

A Use Case for Dynamic Java

Currently I’m wrapping about a hundred EJB 2.1 LocalHome classes in DAO’s, and having them transform local EJB entities into POJO’s. Much of the code is largely boiler plate. Actually it’s mind numbingly boiler plate. Here’s a sample of wrapping a finder:

public Endowment findById ( String id ) {
	try {
		// get the local home
		EndowmentLocalHome endowmentLH = EndowmentUtil.getLocalHome ();
		// lookup the entity
		EndowmentLocal endowmentL = endowmentLH.findByPrimaryKey ( id );
		// return the entity
		return createEndowment ( endowmentL );
	} catch ( Exception e ) { throw new VRRemoteException ( e ); }
}

So, five lines of code just to call a finder, then there’s the createEndowment() method that actually does the transformation into a POJO. Additionally every method is cluttered with try {} catch {} constructs, further blurring the intent of the code.

By using reflection and dynamic method calls I’ve been able to reduce the boilerplate to:

public Member findById ( String id ) {
    return (Member) mixin.find ("findById", new Object[] { id } );
}

If by chance my finder returns a Collection, all I need to do is cast the result to Collection.

Each DAO is specific to a “type.” Not type as in a Class, but type as in a “business type,” ie the snipped deals with the “Endowment” type. For each type there are a number of EJB 2.1 and POJO classes collaborating to make the whole thing work. For the Endowment type the critical classes are:

  • EndowmentUtil: A utility class for looking up local and remote home interfaces using JNDI. This utility class shields us complete from JNDI. :) This class is generated by XDoclet.
  • EndowmentLocalHome: The local home for the Endowment EJB.
  • EndomentLocal: A local EJB entity of an endowment.
  • EndowmentValue: A value bean representing an endowment. This is generated by XDoclet.
  • EndowmentDAO: An interface for a persistence agnostic data access object.
  • EndowmementDAOEjbImpl: An implementation of EndowmentDAO that wraps the EJB 2.1 LocalHome.
  • Endowment: A POJO in a new clean package that has no connection to our old EJB 2.1/XDoclet code.

The other thing we’re doing is making our POJO’s transparently navigable, just like JPA entities are when they are managed by an EntityManager. (ie: We can do contactMechanism.getParty().getMember().)

The major issue is that I had repeating patterns like the findById() example above that are the same except that the “type” differed. The convention is the same, over and over. Reflection and my dynamic method calling to the rescue. (NB: This app needs to run on a Java 1.4 server, otherwise I’d have taken a look at generics to solve some of these issues.)

The Solution

Using the findById sample the pattern is easily identifyable:

public Endowment findById ( String id ) {
	try {
		// get the local home
		EndowmentLocalHome endowmentLH = EndowmentUtil.getLocalHome ();
		// lookup the entity
		EndowmentLocal endowmentL = endowmentLH.findByPrimaryKey ( id );
		// return the entity
		return createEndowment ( endowmentL );
	} catch ( Exception e ) { throw new VRRemoteException ( e ); }
}

Simplifying this to pseudo code can help in seeing the pattern:

public #TYPE# findById ( String id ) {
	try {
		// get the local home
		#TYPE#LocalHome localHome = #TYPE#Util.getLocalHome ();
		// lookup the entity
		#TYPE#Local localEjb = localhome.findByPrimaryKey ( id );
		// return the entity
		return create#TYPE# ( localEjb );
	} catch ( Exception e ) { throw new VRRemoteException ( e ); }
}

We can even simplify this further, because the pattern is really the same with any finder. Look up a #TYPE#Util, call getLocalHome() on it, getting a #TYPE#LocalHome, call the finder method and transform the result into a POJO.

public #TYPE# find (Object[] args ) {
	try {
		// get the local home
		#TYPE#LocalHome localHome = #TYPE#Util.getLocalHome ();
		// lookup the entity
		#TYPE#Local localEjb = localhome.find ( args );
		// return the entity
		return create#TYPE# ( localEjb );
	} catch ( Exception e ) { throw new VRRemoteException ( e ); }
}

We call some find method with some collection of arguments, represented by the Object[], which could also be absent for an no-argument finder.

With the pattern identified let’s “script” it using the dynamic method calling and the fact that the related class names all follow a convention.

Here’s the code that will call any finder using the pattern above:

public Object find (String finder, Object[] args) {
	Object result = sendMessage (finder, getLocalHome(), args);
	if (result instanceof Collection) {
		try {
			Collection originalResultList = (Collection) result;

			// If the result is empty there is nothing to convert. Return the empty collection.
			if (originalResultList.size() == 0) {
				return result;
			}

			Collection resultList = new ArrayList ( originalResultList.size () );
			for (Iterator itr = originalResultList.iterator (); itr.hasNext (); ) {
				Object ejb = itr.next ();
				resultList.add (createPojo (ejb) );
			}
			result = resultList;
		} catch (Exception e) {
			throw new VRUtilityException ("ERROR: Post-processing a collection into POJO's for type = '" + getType() + "'.", e);
		}
	} else {
		// Build a POJO from the EJB entity returned by the finder.
		return createPojo (result);
	}

	return result;
}

Most of the code in the method is involved with transforming Collection results into POJO’s.

To access the findById method I’d do:

return (TYPE) find ("findById", new Object[] { id } )

Getting the LocalHome interface is done by convention:

/** Get the local home appropriate to this mixin. */
protected Object getLocalHome () {
	// Determine utility class name.
	Class utilClass = findClass (getType() + "Util");
	if (utilClass == null) {
		throw new VRUtilityException ("ERROR: Could not look up utility class for type = '" + getType () + "' by convention.");
	}
	try {
	       return utilClass.getMethod ("getLocalHome", null).invoke (null, null);
	} catch (Exception e) {
		throw new VRUtilityException ("ERROR: Could isntantiate local home for class of type = '" + getType () + "' by convention using Method object.");
	}
}

Because I know my Util class is #TYPE#Util I can rely on that by convention to get my utility class and instantiate a LocalHome to be used. (In my case the findClass() method looks for the #TYPE#Util class in one of three packages by convention. I could have search the classpath, but that seemed like overkill under the circumstances, when I know it’s in one of those three packages.)

Using the same recognition of patterns and convention allowed the replacement of boilerplate code in our persist method to.

Before:

public void persist ( Endowment endowment ) {
	try {
		// get the local home
		EndowmentLocalHome endowmentLH = EndowmentUtil.getLocalHome ();
		// create the entity
		EndowmentLocal endowmentL = endowmentLH.create ();
		// ensure that the foreign keys are set
		setForeignKeys ( endowment );
		// populate the entity
		endowmentL.setEndowmentValue ( endowment );
		// update the ID
		endowment.setId( endowmentL.getId() );
	} catch ( Exception e ) { throw new VRRemoteException ( e ); }
}

After:

public void persist ( Endowment endowment ) {
	mixin.persist (endowment);
}

The reflection code that does it:

public void persist (Object bean) {

	// Create an entity EJB bean using the localHome.
	Object ejb = sendMessage ("create", getLocalHome (), null);

	// Populate foreign ID's from associated POJO's.
	setForeignKeys (bean);

	// Populate the entity.
	setValueOnEjb (bean, ejb);

	// Get the id from the ejb.
	String id = (String) sendMessage ("getId", ejb, null);

	// Set the id on the bean (POJO).
	sendMessage ("setId", bean, new Object[] { id });
}

The nice thing is I wrote the reflection version of the persist once and I can reuse it with one line of code. Bye bye boiler plate.

Conclusion

If you find yourself writing repetitive code, such as in my case when wrapping some API you want to abstract out, find the patterns and factor out that repetitive boilerplate code using reflection. You don’t need to waste your life writing repetitive code.

2 comments February 28, 2008

New features for Hardy Heron

The roadmap for hardy heron has a pretty big list of features, most of them, although very important, are technical and a bit uninteresting. Some of them should have been in Gutsy but couldn’t meet the schedule. Hardy Heron is going to be a Long Term Support release, so there’s also going to be a lot of fixes for existing features. So without further ado, the features which I’m anticipating the most are.Install on an existing filesystem without overwriting /home
When I moved from feisty to gutsy, I decided to do a fresh install. One of the things I had to do was back up my home folder, and when I finished installing gutsy I just copied it back onto my computer. This new feature will allow people to install the new version of Ubuntu without it overwriting their home folder.I’m sure this will come in very handy for people who like trying out different distro’s.

Hardy Hardware Detection
This is more of a bug fix than a new feature. Gutsy already has excellent hardware support and the plan for hardy is even better and more robust detection of hardware. Sounds good to me.

GDM Face Browser
One of the changes I made to my gutsy is the GDM. I replaced the old one with something that allows me to just click on a picture of my username and login. This will hopefully be the default for hardy.

Auto Detection of Monitor Frequency
While I was testing gutsy beta I had to manually configure xorg.conf to get it to the right resolution. It wasn’t fun. This should be a thing of the past with hardy as it will automatically detect everything for you. Huzzah!

Apt Authentication Reliability
Have you ever had an update fail for no reason? Well it actually fails because of ‘transient network failures’. The aim is to make hardy more robust against these errors.

Redesign Restricted-Manager Code
They want to expand the role of the restricted manager and change it so that other distro’s can share the joy.

Handling Full Disks

I’ve never had this problem with Ubuntu, but if your disk gets full, things can get quite ugly. They plan to add a notification and disk clean-up tool when your running low on space.

Desktop Effects
Make compiz fusion more robust and easier to use.

New Theme
Hardy Heron will be getting a shiny new theme, I hope they move away from the brown theme and choose something lighter and more fresh.

Easy File Sharing
To allow people to easily share files over a network. Not more I can say about this.

Dual/Multi Monitor Support
Currently you have to manually tweak Ubuntu if you want to use more than one monitor. They want to fix this for hardy.

Integrate Prefetch into Ubuntu
I noticed a slightly increased start up time in gutsy compared to feisty. Hardy will use file prefetch and other optimisations to speed up boot time.

Automatix-Ubuntu Team Collaboration
Automatix was extremely helpful for me in feisty. Although I don’t use it in gutsy, its good that they are collaborating with the automatix team.

Single Click Install
Installing software is already pretty straightforward in Ubuntu. They want to make it even easier to install third party applications. I’m not complaining.

Apparmor Integration
This is already a part of gutsy, the plan is to increase integration to make Ubuntu even safer.

Firewall
Make it easier for users to configure their firewall.

Third Party Apt
Now when you install third party apps, you have to manually add the software repository to the sources.list. This spec makes it easy for users to install third party software and have it update automatically.

Revamped Logout Screen
They want to streamline the options you have when you click that big red button, to make things less confusing.

Better Integrated Wine
Better Wine will make it easier for Windows users to convert, thus helping to solve bug #1.

Xorg 7.3
This is one of the features that missed the gutsy deadline. This should make manual configuration of xorg.conf obsolete. Another much anticipated feature is Bullet Proof X, which will go into a graphical safe mode if anything goes wrong with X.

Slick Boot
To improve the boot and shutdown process and also make the things look nicer.

1 comment February 11, 2008


Categories

AD

February 2008
M T W T F S S
« Jan   Apr »
 123
45678910
11121314151617
18192021222324
2526272829  

Archives

1

Blog Stats

Talk To Me

Text