Tuesday, December 29, 2009

@DataSourceDefinition -- A Hidden Gem from Java EE 6

In the old days, DataSources were configured -- well, they were configured in lots of different ways... That's because there was no 'one way' to do it -- in JBoss, you created an XML file that ended in '-ds.xml' and dumped it in the deploy folder... in Glassfish, you either use the admin console or muck with the domain.xml file... in WebLogic you used the web console... and this was all well and good -- until I worked with an IT guy who told me just how much of a pain in the ass it was...

Up until then, it wasn't such a big deal to me -- I set it up once, and that was that... then I ran into this guy a few jobs ago who liked to bitch and complain about how much harder it was to deploy our application than the .NET or Ruby apps he was used to... he had to deploy our data source, then he had to deploy our JMS configurations -- only then would our application work... in the other platforms, that was all built into the app (I'll have to take his word for it, since I haven't actually deployed anything in either platform)... I was a but surprised at first, and then I realized that maybe he had a point... nah, it couldn't be, he must just be having a bad day (lots of us were having bad days back then :) )...

Then I ran into Grails, which is dead simple -- you have a Groovy configuration file that has your db info in it... you even have the ability to specify different 'environments', which can change depending on how you create your archives or run your app... pretty slick...

The Gem

Well, lo and behold, we now have something that's nearly equivalent in Java EE 6 -- the @DataSourceDefinition attribute... it's a new attribute that you can put on a class that provides a standard mechanism to configure a JDBC DataSource into JNDI, and as expected, it can work with local JNDI scopes or the new global scope, meaning you can have an Environment Configuration that uses this attribute making it shareable across your server... it works like this:


import javax.annotation.sql.DataSourceDefinition;
import org.jboss.seam.envconfig.Bind;
import org.jboss.seam.envconfig.EnvironmentBinding;

@DataSourceDefinition (
className="org.apache.derby.jdbc.ClientDataSource",
name="java:global/jdbc/AppDB",
serverName="localhost",
portNumber=1527,
user="user",
password="password",
databaseName="dev-db"
)
public class Config {
...
}


As you would expect, that annotation will create a DataSource that will point to a local Derby db, and stick it into JNDI at the global address 'java:global/jdbc/AppDB', which your application, or other applications can refer to as needed... no separate deployment and no custom server-based implementation -- this code should be portable across any Java EE 6 server (including the Web Profile!)...

It's almost perfect!

In typical Java EE style, there's one thing that just doesn't appear to be working the way I'd like it -- it doesn't appear to honor JCDI Alternatives (at least not in Glassfish)... Here's what I'm thinking -- we should be able to have a different Config class for each of our different environments... in other words, we'd have a QAConfig that pointed to a different Derby db, a StagingConfig that pointed to a MySQL db somewhere on another server, and a ProductionConfig that pointed to kick ass, clustered MySQL db... we could then use Alternatives to turn on the ones that we want in certain environments with a simple XML change, and not have to muck with code... unfortunately, it doesn't appear to work -- it appears in Glassfish that it is processing them in an undeterministic order, with (presumably) the class that is processed last overwriting the others that came before it...

There is a solution, though, and it is on the lookup side of the equation -- using JCDI Alternatives, we can selectively lookup the DataSource that we're interested in, and then enable that Managed Bean in the beans.xml file... it's definitely not ideal, since we need to actually inject all of our DataSources into JNDI in all scenarios, but it works, it's something I can live with, and is probably easily fixed in a later Java EE release... Update: Looks like it's in the plan, according to this link -- thanks, Gavin :)

Here's how it works -- first the 'common' case, probably for a Development environment:


@RequestScoped
public class DSProvider {
@Resource (lookup="java:global/jdbc/AppDB")
private DataSource normal;

public DataSource getDataSource() {
return normal;
}
}


Simple enough -- has a field that looks up 'jdbc/AppDB' from JNDI, and provides a getter... now for QA:


@RequestScoped @Alternative
public class QADSProvider extends DSProvider{
@Resource (lookup="java:global/jdbc/AppQADB")
private DataSource normal;

public DataSource getDataSource() {
return normal;
}
}


Pretty much the same, except this does the lookup from 'jdbc/AppQADB', and it is annotated with @Alternative... so how do these things work together? Take a look:


@Named
public class Test {
@Inject
private DSProvider dsProvider;

...
}


Again, simple -- we're injecting a DSProvider instance here, and presumably running a few fancy queries... Nothing Dev-ish or QA-ish here at all, which is the beauty of Alternatives... finally, when building the .war file for QA, we turn on our Alternative in the beans.xml, like so:


<beans>
<alternatives>
<class>com.mcorey.alternativedatasource.QADSProvider</class>
</alternatives>
</beans>


You'll notice that this solution requires us to rebuild our .war file for QA, which I obviously don't like -- not to worry, there will be support for this in the Seam 3 Environment Configuration Module, which will effectively create a binding by mapping from one JNDI key to another... I have no idea what the syntax will look like at this point, but it should be pretty straight forward, and will allow us to -- you guessed it -- build our .war one, and copy it from place to place without modification...

M



Saturday, December 26, 2009

Say hello to the Seam 3 Environment Configuration module

A funny thing happened after my last post -- I got an email from Dan Allen, from RedHat, with some interest in making my last JCDI Portable Extension -- EnvironmentBindingExtension -- into a Seam 3 Module... pretty cool for a fairly modest effort at finding a new way to solve a problem I've faced in the past... it will be my first official foray into open source (not counting that one line NetBeans patch I submitted in, like, 2000), so it will be interesting to see how this will actually work from the authoring side, as opposed to the user side, especially in a relatively well organized project like Seam...

What it's about

The idea behind the Environment Configuration module is to inject fairly static configuration information into any JEE 6 environment... it's typically done outside of your application, in a deployment that isn't regularly deployed or updated, so you can configure each of your environments separately, including Development, Testing, QA, Staging and Production -- once this is done, you can build your application once (or better yet -- have a Continuous Integration server build it!), and copy the same binary from server to server without having to reconfigure it, ensuring that the archive that you deploy to production is the same exact archive that you tested in QA... this allows you to streamline your deployment processes, removing any possible human error involved in building your code over, and over, and over again (and in some cases, it'll save a lot of time if you have a particularly slow build!)

How's it work? It takes advantage of JNDI -- one of the resources that all JEE servers provide... say, for example, that you have a system that needs to access a database, a filesystem, and has a batch process that runs at a specific frequency -- in development, you'll want to point to a personal Derby database, use a local folder on your Windows box for your filesystem, and run the batch process very frequently for testing... QA is similar, although it has different database, but say Staging and Production run on a cluster of Linux boxes that access a MySQL database, use a mounted shared drive for its' filesystem, and have its' batch processes run once an hour...

With the Seam 3 Environment Configuration module, you can create a simple .ear file for each of these environments that contains all of this data -- create them once, deploy them once, and you're good to go... take a look at the following example of a configuration that you could use in development:


/**
* An Environment Configuration for Development
* @author Matt
*/
@EnvironmentBinding
@DataSourceDefinition (
className="org.apache.derby.jdbc.ClientDriver",
name="java:global/jdbc/AppDB",
serverName="localhost",
portNumber=1527,
user="user",
password="password",
properties={"create=true"},
databaseName="dev-db"
)
public class Config {
@Bind ("myApp/fs-root")
String rootFolder = "C:\fs-root";

@Bind ("myApp/batch-frequency")
long batchFrequencyInMs = 60 * 1000;
}


Pretty simple -- toss this class into its' own .war file, and it will define three global JNDI entries, one for each of the items mentioned above... your other applications are now free to read these resources in whatever way they need to, even using the standard @Resource(lookup="java:global/myApp/fs-root") notation... a similar configuration file would be created for QA, but perhaps the @DataSourceDefinition annotation will use a MySQL datasource, and likewise for Staging and Production...

What next?

Well, there are a few things on my list of features here, including, but not limited to:
  • Test, Test, Test!
  • Using the @Bind attribute on methods, including @Produces methods
  • Support 'unbinding', if needed
  • Create a Maven Archetype that could be used to quickly and easily setup an Environment Configuration deployment
  • Create an interface of some kind to be able to review the available findings -- either web app or simply JAX-RS based
I am, of course, interested in any ideas or feedback anyone would have, but one goal I would have here is to keep it simple and portable -- what this module is intended to do isn't exactly brain surgery, so I don't think it's necessary to throw in too many 'extras'...

M


Tuesday, December 15, 2009

External CDI Configuration with Portable Extensions

A common requirement for web and enterprise applications is that they have the capability to configure themselves for each environment without modifying the archive itself -- most commonly this is used only for environment specific attributes such as a test vs. production data store, or for Strings describing a file or directory on the file system which will be different on a developers box vs. a clustered production server, or perhaps it is a URL that points to your test payment gateway vs. your production gateway... This is the sort of thing that might easily be done with 'Alternatives' in CDI, but many shops put a premium on the ability to package the application once (on a Continuous Integration server, for example) and copy that file from development to integration to QA to staging to production, all of which are on very (very!) different platforms, using different databases, different file systems, and must integrate with different third party environments -- configuring this stuff externally means you don't have to deal with the error prone and possibly time consuming process of building for each environment... unfortunately, this doesn't appear to be a scenario that Alternatives can help us with...

One resource that works really well for this sort of configuration is JNDI... configure these items on your servers' JNDI registry independently from your application, and then have your application read the environment configuration settings from here -- and CDI makes it very easy to manage both sides of this scenario!

Reading from JNDI

The easier side of this is reading the data from JNDI, so let's start there... actually, you don't need CDI at all to start doing this -- the easiest way is to use the '@Resources' annotation provided in Java EE 5, like so:


@ApplicationScoped
public class FolderConfig {
@Resource(lookup="java:global/folderToPoll")
private String folderToPoll;

public String folderToPoll() {
return folderToPoll;
}
}


Not much to this -- we have an ApplicationScoped Managed Bean which does a lookup from JNDI, and provides a getter for the result... in this case we're pulling from the new "java:global" context that is provided with Java EE 6 -- there's no reason we couldn't map this to local context, but frankly, I wanted to fiddle with the global context :)

Ok, now on to something more interesting...

Writing to JNDI

Writing to JNDI is pretty easy -- get an InitialContext and call 'bind'... it's basically an overblown HashMap... for some reason, though, configuring JNDI outside of an application always seems to be more difficult than it should be -- several years ago, I actually had to write a JBoss plugin to do it, even though they had quite an advanced configuration mechanism for the time... all I wanted to do was put String 'A' at Key 'B', but no -- not supported out of the box!

That solution was configured by an XML file, which left me dealing with Strings... this solution is better on two accounts: 1) It can bind any Object into JNDI, and 2) it's a Portable Extension, and should therefore work on any platform... whew!

So here's how it works -- this extension would likely be packaged into a .jar library, and deployed with a simple webapp or ear archive that is packaged separately from the main application... the piece that provides the configuration is actually a class or a set of classes that are annotated to bind certain fields and/or methods into JNDI, like this:


@EnvironmentBinding
public class Env {
@Inject @Bind(jndiAddress="adminUser")
private User admin;

@Bind(jndiAddress="test") private String test = "This is a test";
}


Pretty straight forward -- what's going on here? Well, first you'll notice that the class is annotated with @EnvironmentBinding -- this is a Stereotype annotation that extends @ApplicationScoped, and acts as a marker for the class to be processed later on... further down, we have two fields that are annotated with @Bind and provided with a jndiAddress... this pretty much works as you would expect -- the value of that object is injected into JNDI, with the 'java:global/' prefix added to the front...

You'll also notice that one of the elements has its' value injected into the field -- this means that the Objects that are bound into JNDI can be derived from a more complex application if need be, so the support that we have here goes well above and beyond the simple XML file configuration that I dealt with way back when...

So how does this thing work? Well, one implementation that I put together has a two part infrastructure to do the job... remember, the end user should never be exposed to the following two items -- the extent of their exposure into this library will be the two annotations shown above...

First, our Portable Extension class:


public class EnvironmentBindingExtension implements Extension {
private Set envBeans = new HashSet();
private BeanManager beanManager;

public void discoverEnvironmentBindingClasses(@Observes ProcessBean pb, BeanManager bm) throws Exception {
this.beanManager = bm;

Bean bean = pb.getBean();
Class beanClass = bean.getBeanClass();

Set sts = bean.getStereotypes();

for (Class st : sts) {
if (st.equals(EnvironmentBinding.class)) {
log.info("Found class annotated with EnvironmentBinding: " + beanClass.getName());

envBeans.add(bean);
}
}
}

public Set getEnvBeans() {
return Collections.unmodifiableSet(envBeans);
}

public BeanManager getBeanManager() {
return beanManager;
}
}


This Extension class is pretty straight forward -- as with all Portable Extensions, it starts by implementing the 'Extension' interface... in this case, we're also creating an Observer method for the 'ProcessBean' event... this event is fired during the application startup lifecycle for every 'Bean' that is discovered in a Bean archive... this will fire for Managed Beans, EJB's, Interceptors, etc, but here, we're specifically looking for beans that have the EnvironmentBinding Stereotype on them -- that is the trigger to further process this class... in this case, our process simply consists of adding the Bean to our 'envBeans' Set for later use... in addition, we provide accessor methods for the BeanManager (which is injected into our Observer method), and the envBeans Set... Now let's have a look at what we do with these Beans...

The next class is the one that does most of the heavy lifting -- it is a Singleton EJB which is marked as a Startup bean, meaning it will be instantiated upon application startup, after the CDI discovery phases are complete... in this case, we have created a PostConstruct method to do our work for us:


@Singleton
@Startup
@ApplicationScoped
public class BindingsProcessor {
@Inject
private EnvironmentBindingExtension bindingExtension;

@PostConstruct
public void processBindings() throws Exception {
Set envBeans = bindingExtension.getEnvBeans();

log.info("Processing EnvironmentBinding Classes: "+envBeans);

Context appContext = bindingExtension.getBeanManager().getContext(ApplicationScoped.class);
for(Bean bean:envBeans) {
Class beanClass = bean.getBeanClass();

Object beanInstance = appContext.get(bean, bindingExtension.getBeanManager().createCreationalContext(bean));

Field[] fields = beanClass.getDeclaredFields();
for(Field field:fields) {
if(field.isAnnotationPresent(Bind.class)) {
field.setAccessible(true);

String jndi = field.getAnnotation(Bind.class).jndiAddress();
Object val = field.get(beanInstance);

bindValue(jndi, val);
}
}
}
}


Hey, wait a minute -- this is pretty simple, too! Iterate over the set of Beans that we've collected, use reflection to find all of the fields that are annotated with @Bind, and bind the value into the appropriate JNDI location... I've even removed the JNDI api work here, because it's not interesting at all...

This could be expanded in a couple of way, most obviously to allow methods to act as Binders as well... I do want to discuss my choice here of using the Singleton EJB as well, since I've had a few posts recently which talk about doing away with EJB's altogether -- well, initially I was attempting to use the 'AfterBeanDiscovery' or 'AfterDeploymentValidation' events to trigger this loading, but I was having trouble getting an instance of 'Env' that was capable of having its' injection points... er... injected...

The Singleton EJB is somewhat of a last-ditch sanity effort, but after considering it for a few days, I'm actually alright with it... the Startup Singleton EJB's are something that has interested me for a while, and it proves its' usefulness here, but what's more, I'm still able to take the EJB interface out of the end-user's experience here... they simply need to make use of the EnvironmentBinding annotation, and be on their merry way, as long as they are deployed in a container which supports Singletons (which all Java EE 6 containers do)... that being said, I'm hoping that Gavin will show me what the heck I was doing wrong :)

One other thing -- using an @Inject method on an ApplicationScoped bean doesn't appear to do the trick... reading the spec, it appears to be caused by the fact that ApplicationScoped beans are 'active' during Servlet calls, EJB calls, etc -- meaning it doesn't have it's own 'startup' lifecycle, but depends on the lifecycle of other Java EE component models... interesting, to be sure -- adding a more generic Startup capability would be a cinch if done similar to how I've done this...

Wow, that was a lot of words

So what does this all mean? Basically, it just shows another way of skinning that old, damn cat that is environment configuration -- but it also shows that it's pretty darn easy to put together some CDI extensions, and when working with the surrounding Java EE specs and resources, that it can be done in a minimal amount of code... in this case, I was looking at a requirement that I often have to support external configuration -- one that CDI doesn't accommodate out of the box... with a few lines of code, it turned out to be possible to break that box open and stuff some more toys inside :)

Finally, the more complete code samples can be found here -- the EnvironmentBinding project has the core code, the TestEnvironmentConfig project shows a test web application that could be used to create the binding configuration, and the EnvTest project is an application which makes use of the JNDI entries... have fun!

M



Saturday, December 12, 2009

JSR-299 Tx Interceptor code + JAX RS Sample

Quick update -- I've thrown the source code to the Transactional Interceptor from my previous post into a source repository, along with a sample webapp that shows how it can be used... they're both maven projects, and should be easy enough to download and fiddle around with -- I've tested it in Glassfish v3, but it should work in JBoss just as well...

I'm going to use this repository for a number of random tests, experiments, and whatever it is I'm fiddling with at the time -- hopefully there'll be a few more CDI projects up there, or whatever else it is that strikes my fancy at the time :)

Update: I've just thrown together a sample that shows how CDI can be combined with a JAX RS interface as well... cool, and simple!

M


Wednesday, December 9, 2009

Thoughts on JSR 330

I'm not sure what to think about JSR-330 -- what do you think?

A quick caveat before I begin -- I'm playing devils advocate here, so I have no idea if I believe any of the gibberish that I'm about to write :)

On one hand, I think it's Mostly Harmless... CDI/JSR-299 is obviously the default implementation of the spec, and perhaps having a very small portion of the DI capability abstracted so that we have the choice to use another framework if we want isn't necessarily a bad thing (even if standardizing injection targets isn't exactly ground breaking value) -- after all, if it weren't for those 'outside' framework writers, we might not have gotten EJB 3, and all the improvements that came along with Java EE 5... perhaps JSR 330 is a way to bring those folks inside of the Java EE fold, so it's not another three years before the standards catch up with the private innovation

On the other hand, is it at all likely that an application would be able to just replace the DI capability of CDI with, say, Spring or Guice, but still use the contexts, interceptors, extensions, etc? After all, Dependency Injection is actually a pretty small part of what the CDI spec provides... Perhaps, if one were to write a CDI Portable Extension that basically vetos all bean discovery, and instead uses one of the other libraries to do the injection, but that seems like a stretch to me...

What seems more likely is that another implementation would simply not use CDI, which can easily be done by not providing a beans.xml file, and using some other mechanism, like the Spring WebApplicationContext -- in that case, is there value in what the JSR-330 spec provides? Has the addition of this spec, which provides little value in-and-of itself incorporated too much confusion?

My original instinct was that it's a good addition to the Java EE fold, but I've heard a few voices of opposition (including from those who don't have a big stake in the outcome)... now I think, perhaps, that I could take it or leave it -- I haven't been convinced that it's destructive, but I'm also not convinced that it's worth it :)

What do y'all think?

M


Monday, December 7, 2009

CDI and Declarative Transactions...

Ok, so since I'm already on the books for declaring that CDI/JSR-299 will become the dominant framework of the Java EE standards, while pushing EJB 3 off to the side, let's start to figure out how this could work... I don't think too many people would argue that the number one reason for using EJB 3.x Session beans are the Declarative Transaction support -- that is, being able to create and commit a new Transaction around a method call by annotating it with @TransactionAttribute(TransactionAttributeType.REQUIRED) -- and compared to the boilerplate code that needs to be put in place to do it programatically, it is indeed a pretty nice feature...

So how can this be done in CDI? Simple -- with Interceptors... CDI actually reuses the interceptor support from EJB 3, but it takes it a step further -- it allows you to use a custom annotation to bind your code to the interceptor definition in a pretty slick way... but first, consider the following interceptor, which might be the most basic implementation of Declarative Transactions:

//import removed...

@RequiredTx @Interceptor
public class RequiredTransactionInterceptor implements Serializable {
@Resource
private UserTransaction utx;

@AroundInvoke
public Object openIfNoTransaction(InvocationContext ic) throws Throwable {
boolean startedTransaction = false;
if(utx.getStatus() != Status.STATUS_ACTIVE) {
utx.begin();
startedTransaction = true;
}

Object ret = null;
try {
ret = ic.proceed();

if(startedTransaction)
utx.commit();
} catch(Throwable t) {
if(startedTransaction)
utx.rollback();

throw t;
}

return ret;
}
}


You'll notice that most of this looks familiar -- we're not implementing any particular Interface, we can use any old name for our wrap-around method, we use the @AroundInvoke annotation to indicate which method is to be used for intercepting, and how it's used, etc. The only thing new here comes on the first line -- the dual annotations of @RequiredTx and @Interceptor... so far this is wicked easy!

So what the heck are those annotations? Well, the @Interceptor annotation is part of the javax.interceptor package, and was added as part of the CDI spec -- clever how they snuck a new annotation in but didn't put it under any CDI-specific package, eh?

The other annotation is one of my own, and when used in concert with @Interceptor, it acts as the 'binding definition' of the interceptor -- you can then put the @RequiredTx annotation on any method or class of any CDI bean, and it will be as if you've annotated an EJB with '@Interceptors(RequiredTransactionInterceptor.class)', only not as wordy (and why would you write a transactional interceptor that mimics the EJB usage, and then use it on an EJB?)

Without further ado, the definition of our annotation:

//more imports removed...

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiredTx {
}


Not much here -- the only thing you may not have seen before is that InterceptorBinding annotation, which simply declares that this annotation is used to bind an interceptor to a method or class... simple -- but how is it used? This is the cool part -- just annotate any old CDI bean with this, and you're good to go -- like this:

//yep -- no imports here either...

@Named
public class UserGetterer {
@RequiredTx
public User loadUser() {
\\Do some fancy JPA loadin' here
}
}



Load that up in your webapp, point your fancy JSF 2.0 preRenderView event to "#{userGetterer.loadUser()}", and voila -- you are loading your data in a transaction... sweet!

Doh -- almost forgot
What's that? It didn't work? Crap... oh wait, one last thing -- we need to enable our interceptor... This part is easy to forget, but easy to do -- you need to add an <interceptors> element to your beans.xml... (you did add the beans.xml file, right?)... try this:


<beans>
<interceptors>
<class>com.your.package.RequiredTransactionInterceptor</class>
</interceptors>
</beans>



Yeah, I know -- it feels kind of like we're exposing your implementation detail by asking the user to enable the interceptor instead of the annotation... kind of annoying, but I can deal with it... another thing to note is that if you pack your interceptors into a shareable library file, all other library files in your application will still need to do this, even if you enable it in your .jar file -- that's because there's no concept of a global interceptor in CDI, but to be safe, each library is forced to enable all interceptors in use, so that they can enforce the ordering of interceptors in a very intuitive manner... I can accept this reasoning simply because they have defined it, rather than letting it be an undefined mess, so again, it's more of annoyance for me -- hopefully there will be other options in the future... (Note - due to a bug in the current version of Weld, Glassfish and JBoss will both have a problem with this -- the work around at the moment is to include all of your <interceptors> definitions in one library file, or your .war file directly)

Sum It Up
So there you have it -- a bare minimum implementation of Declarative Transaction support for CDI that can be applied to both classes or methods in less than 40 lines of code... easy, and very reusable! It would be similarly easy to implement for the other EJB TransactionAttributeTypes (SUPPORTS, REQUIRES_NEW, etc), although there is some coordination to be done to support putting one annotation on a class and another on a method without having them tripping over each other...

All in all, it provides a very straight forward programming model that is at least as clean as the EJB 3.0 model -- cleaner in many ways, actually, as you'll have @RequiredTx instead of @Interceptors(RequiredTransactionInterceptor.class), better context management with @RequestScoped, @SessionScoped and @ConversationScoped instead of just @Stateful, and much better integration with JSF... Plus, you'll have the benefit of being able to customize it if you need to, adding support for things like timeouts, isolation levels, or custom logging... very nice, indeed!

M



Wednesday, December 2, 2009

CDI/JSR-299 preparing to marginalize EJB... again...

Whew, it's been a while since I've started looking at what's new in Java EE 6, partially because I've been fiddling with the newest addition to the Java EE portfolio -- JSR-299, aka CDI, f/k/a Web Beans, and this one could be big... Let's start with a bang -- CDI will eventually push EJB to obscurity... yeah, big statement, I know, but here's why I believe this will happen:
  • CDI has the potential, I believe, to provide all of the most commonly used services that the EJB spec provides, and let's face it, this mean transactions, timers, asynchronous process with MDB's, and now Singleton Startup beans. It does not mean remoting, instance pooling, and most certainly not any of the old CORBA crap :) -- also note that I'm not referring to JPA here at all, now that it has been removed from the EJB spec (as it should be!)
  • The CDI spec comes in at 98 pages.
  • The EJB 3.1 spec (PFD version), comes in at 618 pages -- yikes!
  • The CDI spec is far more flexible than the EJB spec, allows for more interesting and transparent customizations, and is built specifically to support extensions.
  • CDI's mechanisms for injection (which it inherits from JSR-330, thanks to Rod and Bob!), is more consistent, inclusive and unified than EJB's/JEE 5's.
  • CDI's inclusion of Stereotypes provides an awesome mechanism to create a lightweight framework, without directly exposing any CDI api to the end user.
  • CDI's context support is very extensive, and is far more complete and useful than EJB's Stateless and Stateful beans. Plus, it finally gives us a standard between Request and Session scoped.
  • CDI's integration with JSF is far better than EJB's (ever had a manually remove a Managed Bean from scope because its' Stateful bean has completed its' lifecycle? Come on!).
  • CDI's event mechanism provides for extensibility and easy, lightweight asynchronous processing.
  • There's no such thing as CDI Lite :)
That's not a short list, and admittedly, it's very early to make claims like this... after all, the spec has just finally been approved, we have yet to fully see the quality of the implementations (three that I know about, Weld, CanDI and OpenWebBeans -- hey, how come they got to keep the name!?), we have yet to see how the closed-source vendors will handle it, etc., but when it comes down to it, the CDI spec is simply better suited for POJO-style development, using Object Oriented core design principals, and it was designed specifically with this in mind... Contrast this with EJB, which has over a decade of cruft to carry along with it, and actually prevents the use of OO techniques altogether (historically more-so than today, but it's still an issue), and I think the future becomes fairly clear... All-in-all, I see it as great news for the Java EE platform...

One final caveat, though -- EJB has risen from the ashes before, specifically with the 3.0 version... if the vendors still feel the need to continue to push EJB, then who knows what we'll see :)

M