Thursday, August 6, 2009

What's cool in Java EE 6 -- Singleton EJB's and Pruning

Well, with the all the excitement about the addition of JSR 330 Dependency Injection into the EE 6 platform, I figured I'd switch gears a bit and talk about the changes in a different spec -- this time, the EJB spec... Now, this spec is enormous -- 618 pages to be exact -- but luckily, we can ignore most of it! I mean seriously, who wants to read about EJB QL, 2.1 Client Views and the like... I'd rather dig into the meatier stuff, like...

Singleton Session Beans
We all know the Singleton pattern -- it may be one of the most well known designs patterns, and it's easy to understand... basically, if something's a singleton, then there's only one of 'em... ever... that means if you lose it or break it, it's gone for good... ok, that last part isn't true, and really the parts before that aren't quite true, either -- in most implementations of the Singleton pattern, folks conveniently forget to account for the fact that Enterprise applications often live in clusters, and instead they make singletons so that there is only one per virtual machine... most of the time this is fine -- they're often used for things like Service Locators, which were used back in the olden days of EJB 2 as a convenient way to do all the nasty JNDI lookups for EJB's...

Enter Singleton Session Beans... these are pretty much what you'd expect them to be -- an EJB that you can only find one of per JVM (the spec is specific about that last part, so really, they're only Singletons part of the time)... frankly, I was somewhat underwhelmed when I first heard of them, but they do add one interesting capability that I've needed on several EJB applications in the past, and have never had in an easy, portable way to pull off -- the ability to execute on application startup...

This can be achieved with a few simple annotations, like so:

@Singleton
@Startup
public class InitBean {
@PostConstruct
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void initializeSomeStuff() {
...
}



I know, lot's of annotations on that sucker, but it's pretty straight forward:
  • @Singleton -- defines this as a Singleton Session Bean... big surprise here, eh?
  • @Startup -- this is an indication to the app server that this class must be created and initialized at application startup, and yes, the spec uses the 'must', so there's no soft language here
  • @PostConstruct and @TransactionAttribute -- these are the old tags that we all know and love...
Anyone who's read this blog before may notice a hidden gem in here that makes all of this worthwhile... I'll give you a hint -- go read this entry on @PostConstruct... yes, that's right, this is a Transactional @PostConstruct method... and it will work! They got this one right when they wrote the EJB 3.1 spec, in that it guarantees that @PostConstruct methods will obey your @TransactionAttribute attributes! Now if only they would extend this capability to all EJB's, we'd have all of our problems solved! Ok, perhaps not all of them...

So anyway, you can do pretty much whatever you want in here, including initializing 3rd party systems, creating timers, sending email, truncating all of your database tables, etc... what most intrigues me, though, is data bootstrapping, similar to what you get with the Grails Bootstrap class:

...

@PostConstruct
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void setupUserData() {

List expectedUsers = getExpectedUsers();

for (User u : expectedUsers) {
User found = em.find(User.class, u.getId());
if (found == null) {
log.info("Persisting user "+u.getUsername());
em.persist(u);
} else if (!found.equals(u)) {
log.info("Updating user "+u.getUsername());
em.merge(u);
} else {
log.info("No need to update user "+u.getUsername());
}
}
}

...


In other words, this Singleton will ensure that your standard set of users will always be available upon application startup -- if this is something you want only in a development environment, you can configure each machine with environment information via JNDI, System properties, or a configuration that is set in your build so that it only takes these actions on a development environment -- it is a great way to ensure a consistent development environment across your team...

A Word about Pruning
Ok, this is, by far, my favorite feature of Java EE 6 -- and it's not even a feature! That's right, we're finally going to be able to actually remove some of the stuff that's been added to Java EE over the years! This could include a wide variety of things, but just think -- some day in the near future, you won't have to sift through details about Entity Beans, outdated communication protocols like JAX-RPC, EJB Home interfaces... instead, it will all be gone! Unfortunately, this day hasn't come just yet -- they're just telling us that they're planning to do it some day, but at least it's a step in the right direction... now I have to wonder -- will Java EE 7 actually be a smaller spec than EE 6? Now that would be impressive :)

In the meantime, you can get a little hint of the pruning goodness by taking advantage of the new Web Profile -- this is a nice customization of the spec that removes a lot of the stuff that most folks don't use anyway, but that is still pretty useful in some cases... unfortunately, you still do have to thumb past all of the extra stuff if you decide to go diving into the spec documents...

Conclusion
It seems odd to me that it took so long for Java EE to standardize such a simple, but useful piece of functionality as startup logic -- to me, this is more important than the fact that this EJB is a (kind-of-)Singleton! Sure, I've always been able to do this through an eagerly loaded Servlet, or with WebLogic or JBoss specific code, but the Servlet solution is just plain weird if I'm not acting upon my web container, and this is obviously more portable than an app-server specific piece of functionality... the above example just seems a little less... dirty to me :)

But who am I to complain about a nice new feature :)

M

4 comments:

Reza Rahman said...

Matt,

Thanks a bunch for covering EJB 3.1 singletons. Keep in mind, EJB singletons are not intended to be services. They are intended primarily to store shared state for an application as well as binding to the container life-cycle (both are relative edge cases). To that end, EJB singletons have thread-safety (and a rich set of features built around it such as read/write locks).

If you are from a Spring background, the stateless session beans are the EJB parallel, not EJB singletons. Also, although the spec can't mandate it, vendors are more-or-less expected to make singletons clustered, much like stateful session beans.

Cheers,
Reza
-----------------------------------
Independent Consultant
Independent Expert, Java EE 6 and EJB 3.1
Author, EJB 3 in Action

Matt Corey said...

@Reza,

I see your point about EJB Singletons not expected to be used as services, but there doesn't seem to be anything preventing us from using them in that way, and sometimes it might even be a good idea -- that being said, I don't see myself using them for that purpose very often :)

I do think, however, that using them as a bootstrap to preload a database with some static data or test data, can be extremely valuable, and I'm pretty excited about that -- it's one of the many things that we should have been able to do easily and portably years ago, but just... couldn't...

As for the clustered singleton, the wording in the spec guarantees that each container will be able to support one Singleton EJB per VM, but any container could feasibly extend this to a cluster -- once again, though, to write code that expects that behavior is to write non-portable code... I'm not particularly unsatisfied with the wording myself -- it is far better than if they hadn't been specific at all, but it is a detail that many folks might overlook, and should therefor be hightlighted...

M

Reza Rahman said...

Matt,

The biggest issue in using EJB singletons as services is thread-safety vs. scalability (if you think about it a second, I am sure you will get it; hint - the JPA EntityManager, Hibernate Session, etc are *not* thread safe :-)).

The problem with mandating clustering is that it forces all app server vendors to implement it - kind of going against the idea of making Java EE as light as possible.

Cheers,
Reza

aravind said...

i ran into some issues espesially when using postConstruct method on Singleton ejb on startup. where postconstruct method tries to initialize static data from db by using entitymanager. did you ever run this code that you posted on either in glassfish or jboss. i am getting exception like unable to inject JNDI.