Wednesday, August 4, 2010

Love that injection

The EJB 3 specification greatly simplified the world of EJBs by borrowing ideas from the Spring camp, the most powerful of which is the idea of POJOs coupled with annotations if you are living in a >= Java 5 world.

I've been playing around with Spring 3 lately, and I'm starting to find the value again in having an XML configuration/deployment descriptor within my EJB work. In all the annotation hype for EJB3, I wonder if we haven't lost sight of the flexibility that text based configuration can bring us in regards to injecting resources into our beans. Sure XML can be tedious but with the amount of great XML tooling available, is that a good reason not to use the XML options? We can get autocomplete, validation and syntax highlighting out of the box. I personally use Eclipse but other IDEs have the capability. Unfortunately we can see the annotation vs XML debate spiral mostly into personal preference. Sure having the configuration for an external resource in the code is great - if it rarely changes. If you have constant change (say on a per environment basis) perhaps a text based approach is better. Text based configuration is perhaps easier for us to create scenarios for our testing.

I've recently had the problem where I've had to change the JDBC driver for a DataSource due to environment issues. The ORM work is done by JPA. The annotation on the EntityManager points to a particular persistence unit. Great, because that's hardly ever going to change. If we do change the persistence unit, it may have an impact on the rest of the codes behaviour so going into the source is worth it. However there are two different persistence unit configurations - one for production (in the container) and the other for unit testing (out of the container). These are configured in the persistence.xml, and the code is oblivious. Just as it should be.

Unfortunately the wheel is reinvented too many times. Are we really doing anything different by the fact that we need configurable code? I admire an engineers ability to solve the problem. That's what we get paid for. Maybe bundle a properties file in the JAR, find it on the classpath (and load it). Maybe there is a property that we can then use to do a JNDI lookup to get a handle/reference to a DataSource. But why would you do that when you have the <resource-ref> tag available to you in the deployment descriptor.

<session>
<ejb-name>SomeBean</ejb-name>
<resource-ref>
<res-ref-name>${propertyName}</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<injection-target>
<injection-target-class>org.foo.SomeBean
</injection-target-class>
<injection-target-name>dataSource
</injection-target-name>
</injection-target>
</resource-ref>
</session>
The beauty of course is that ${propertyName} can be substituted in by your build framework. The Ant <expandproperties> filter is great for this. You're also within the spirit of the framework, and hopefully your code is more maintainable, with no custom loading and no lookups. Of course you may have this requirement across multiple beans so you may have to replicate your custom code across those beans. Ugly!! If you're worried about people not being able to trace what's going on, place a comment over the class attribute "This is configured in the deployment descriptor". That of course is a no brainer because good developers always document their code for future readers ;).

On a technical note, there is an unfortunate pitfall to using deployment descriptor features like a <resource-ref> that can frustrate a developer and make him/her reach for their own custom solution. Say within a bean you have

@Resource(name="jdbc/SomeDS")
private javax.sql.DataSource dataSource;
What you're doing is essentially two things

  1. Requesting a DataSource object at the JNDI location jdbc/SomeDS

  2. Requesting that the reference be assigned to the class attribute dataSource

I've seen plenty of examples recently when the <injection-target> element has been forgotten. So step 1 is executed, but not step 2. Thus dataSource will not have been injected leading to a Null Pointer Exception. ^Developers love those.^

My current thinking at the moment is about how we can leverage technology like EJB3 and Spring to cleanly and efficiently solve clients needs. The first onus falls on guys like me, the developers to know our material. The second is that if we don't know the answer to a question (for example how can I change the JNDI location of my injected DataSource at build time) then we need to do our homework. I'm just as much guilty as the next guy for failing to do this. Doesn't mean I shouldn't get a rap over the knuckles for it.

No comments:

Post a Comment