Friday, 19 December 2014

CDI for Java SE already standardised with DeltaSpike

Introduction

One of the things which are scheduled for the upcoming CDI 2.0 release, is the standardisation of the usage in a Java SE environment.

The Context part of CDI, the different scopes like RequestScoped or ApplicationScoped, isn’t the most useful thing in the Java SE environment.  But having a dependency injection and event mechanism on the other hand is very handy.

Weld and OpenWebBeans

But you don’t have to wait until the release of a CDI 2.0 compatible implementation before you can use it in a Java SE environment.

Weld and OpenWebBeans are at the moment the 2 most important implementations.  They have already the possibility to use CDI in a Java SE environment.

But both frameworks have different ways to start up the CDI environment because in CDI 1.x it isn’t standardised yet.

DeltaSpike is a collection of CDI extensions, and one of the things it provides, is a uniform way of starting CDI in a Java SE environment. And you can use OWB or Weld as your implementation.

DeltaSpike Container Control module

Here the uniform startup is defined;  You have one api module which defines the CDI implementation neutral (so not related to Weld or OWB) classes. And then there exists 2 Implementation modules, one for each CDI implementation.

Other things you need are
- Deltaspike core api and implementation modules
- OWB or Weld implementation with there transitive dependencies if any.

A sample maven project file can be derived from one of the DeltaSpike examples or you can use the one I have assembled, see further on.

When the maven config is in place, you can start for example the CDI container from your main method as follows:

public static void main(String[] args) {
    CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
    cdiContainer.boot();
    ContextControl contextControl = cdiContainer.getContextControl();
    contextControl.startContext(ApplicationScoped.class);
    //containerControl.startContexts();
}

Uber JAR

When you create a Java SE application, most of the times you wil create an Uber jar with a proper manifest file so that you can start your application easily with the command (the executable jar)

java -jar myProgram.jar

This can be achieved by using the shade plugin of maven.  You can find various resources on the internet how you can integrate and configure it in your project.

But using this procedure for distributing your CDI based application with DeltaSpike has a few pitfalls but workarounds are available. However, they arent related to DeltaSpike, nor OWB or Weld. It is a consequence of the deployment format.

The first issue that you should be aware of is that some files can be in multiple dependency jar files. Files like beans.xml and javax.enterprise.inject.spi.Extension are present multiple times in dependencies of your maven project.

If you don’t specify a certain configuration of the shade plugin, these files will overwrite each other and thus your program will not function.

You should use :
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>

Another issue that I found is that the asm transitive dependency, used in OWB, isn't properly packed into the Uber jar file.
So you need to add the asm:asm:3.3.1 dependency to your own pom as dependency otherwise the application isn’t starting due to some missing classes.

And the last pitfall is that a lot of frameworks aren’t CDI compatible.  In a Java EE application, this isn’t a problem since there is no beans.xml file in the jar files of those frameworks.  This means that the classes in these jar files aren’t considered as CDI bean and thus no problem occurs during the startup of the application.
But in an Uber jar, all classes are in the same jar file which has a beans.xml file.  Those classes, or better some packages, can be excluded the easiest way when you use Weld, as it has support for a custom configuration in the beans.xml file which allows you to exclude some packages.

<weld:scan>    <weld:exclude name="org.jboss.weld.**" /></weld:scan>

Starter project

To get you started easily with a Java SE application which uses CDI 1.x, I created a basic maven application which has everything configured correctly.
You can download it here.

It has 2 profiles, one for OWB and the other is for Weld.  There exists also a third profile, called shade, which is needed in the case you are using the shade plugin on a project which uses OWB.  It makes sure that the asm transitive dependency is included in your final jar file.

Conclusion

So you don’t have to wait for CDI 2.0 to use CI in Java SE, you can use it already today. And with the use of DeltaSpike SE support module, you can even hide the details of starting the OWB or WELD container which makes it even easier.


Have fun with it.

1 comment:

  1. Very good remark from @struberg. The ASM dependency is only needed for version 1.1.8 that I used. So issue 2 is not relevant anymore with later versions of OWB.

    ReplyDelete