Get started with CDI standalone API
CDI brings a new standalone API, the intent is to make more portable the writing of standalone applications.
Why would you use CDI in a standalone application? Because it is common for a lot of use cases actually:
- batch: if you use JBatch it will enable you to write standard code and handle the bootstraping smoothly
- cli: for common line interface programs it is common to use CDI as IoC/backbone and the SE API allows to bridge it without any specific internal API
- others: the limitation here is only the scope of your program, if you do something very specific or small, CDI SE API will be a good fit but if you build a JAX-RS server for instance you would need to wire a lot of options in a specific way which maybe means you need to look for a more "standard" (de facto) solution like tomee embedded or the excellent meecrowave to avoid to have to manage the cost of options maintenance having direct implications on the deployments
Dependencies
<dependencies>
<!- spec dependencies -->
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jcdi_2.0_spec</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-el_2.2_spec</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-annotation_1.3_spec</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-atinject_1.0_spec</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-interceptor_1.2_spec</artifactId>
<version>1.0</version>
</dependency>
<!-- implementation (openwebbeans here, meecrowave-core would work too) -->
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-se</artifactId>
<version>${openwebbeans2.version}</version>
</dependency>
</dependencies>
Nothing crazy here, just the JavaEE dependencies required by CDI and the implementation we want (openwebbeans or weld in general).
For the impatients
Once your project is setup you just need to use the brand new SE API. As for many EE specification you go through a loader layer which will give you transparently an implementation instance. In CDI SE case it will load a SeContainerInitializer which will then enable you to configure the container you want (like a builder) and then allow you to instantiate the container through its intiialize method:
try (final SeContainer container = SeContainerInitializer.newInstance().initialize()) {
// be creative here
}
Indeed SeContainer is AutoCloseable but it is also an Instance which allows you to use it to lookup any application instance. It is rarely what you use to code the full business of your application but often a good entry point to link the main to the CDI managed instances:
try (final SeContainer container = SeContainerInitializer.newInstance().initialize()) {
container.select(MyService.class).run(args);
}
SeContainer has two other methods, one to get the BeanManager and one to know if the container is still running. The first one can be useful to integrate with 3rd party libraries and the last one is intended to be used if your container is passed through some deeper layers of your application. I would recommand to not use this one outside the main itself since it would couple the way you launch your application to the implementation making it hard to migrate to a more standard deployment.
Going further in CDI SE API investigation
If you investigate SeContainerInitializer API, you will realize the container behavior is quite configurable which is quite nice compared to a "EE CDI" where the configuration is limited to beans.xml one and is mainly related to scanning exclusions.
What the initializer will enable you to configure is:
- the scanning: which classes/packages are considered as scanned
- the extensions: which extensions are active
- the beans.xml configuration for the standalone beans (active interceptors, decorators, alternatives ...)
- which classloader is used for the application (to create proxies etc)
- you can also completely disable the default implicit scanning to just use the previous methods to fully control the CDI context
Here is an example:
try (final SeContainer container = SeContainerInitializer.newInstance()
.disableDiscovery()
.addPackages(true, MyApplication.class.getPackage())
.setClassLoader(new ThrowableAwayClassLoader())
.addExtensions(new MyRepositoryExtension())
.initialize()) {
container.select(MyService.class).run(args);
}
As you can see it gives you a lot of control of what the container does. It is also a nice - even if a bit surprising - way to have a feature toggle implementatino since you can add/remove extensions dependeing the program arguments.
Going further in CDI SE API usage
One direct implementation of the extension configuration is the ability to pass extension instance. It sounds nothing but it means you can get your extension configured from your main directly and make them dynamic from the command line. Here is an example using args4j library:
public class Main {
@Option(name = "-web", usage = "active web endpoints")
private boolean web;
@Option(name = "-socket", usage = "active socket endpoints")
private boolean socket;
public static void main(String[] args) {
final Main config = new Main();
final CmdLineParser parser = new CmdLineParser(config, ParserProperties.defaults().withUsageWidth(80));
try {
parser.parseArgument(args);
try (final SeContainer container = SeContainerInitializer.newInstance()
.addExtensions(new Extension() {
void deployWebEndpoints(@Observes final AfterDeploymentValidation afterDeploymentValidation) {
if (config.web) {
// do the work related to this
}
if (config.socket) {
// do the work related to this
}
}
})
.initialize()) {
}
} catch (final CmdLineException e) {
parser.printUsage(System.err);
}
}
}
This example can be rewritten without doing the switch logic in the extension but just in the container bootstraping but if you want a more dynamic deployment based on application beans (ProcessAnnotatedType or ProcessBean) then this code style is really efficient.
Another smooth use case is to activate or not the monitoring and wrap some beans to add them a @Monitoring annotation:
public class Main {
@Option(name = "-monitoring", usage = "active monitoring")
private boolean monitoring;
@Option(name = "-monitoring-package", usage = "filter monitored packages when monitoring=true")
private String monitoringPackage;
public static void main(String[] args) {
final Main config = new Main();
final CmdLineParser parser = new CmdLineParser(config, ParserProperties.defaults().withUsageWidth(80));
try {
parser.parseArgument(args);
try (final SeContainer container = SeContainerInitializer.newInstance()
.addExtensions(new Extension() {
<T> void activateMonitoring(@Observes final ProcessBeanAttributes<T> pba) {
if (config.monitoring && AnnotatedType.class.isInstance(pba.getAnnotated()) &&
AnnotatedType.class.cast(pba.getAnnotated()).getJavaClass().getName().startsWith(config.monitoringPackage)) {
pba.setBeanAttributes(
pba.configureBeanAttributes()
.addQualifier(new MonitoringLiteral()));
}
}
})
.initialize()) {
}
} catch (final CmdLineException e) {
parser.printUsage(System.err);
}
}
}
Here you see how the new Configurator (builder) API helps to mutate a CDI model to convert it to a new one (configureBeanAttributes()). Mixed with the anonymous Extension it is a real enabler to makes the application dynamic, allowing to activate very easily debug mode.
I'll stop here with the use case examples but another one which is as trivial to implement with such a setup is the addition of some beans. For instance, if you use CXF JAX-RS CDI module you would be able to add any Feature pretty easily, like the logging one to see the request/response pairs just with a CLI flag.
Pre-Conclusion
CDI SE API is not something changing the way you will code applications, you should ensure to keep your application code the same as today and "portable" on CDI 1.2 applications for maintenance reasons but it will enable to make the deliverables more production ready and portable. Advantages are important for all the small utilities we have to write while working around a big application but less impacting for others kind of deliverable which would benefit more from a already built-in solution. As always it is this plain old question of the trade-off you have to define before picking a solution ;).
Real Conclusion
Pre-Conclusion was assuming "full" containers can't implement CDI SE API. Meecrowave - which is not a full container but already have enough configuration to make it part of that category in the context of that post - implemented the CDI SE API. This means you can use the SE API with meecrowave and it got the nice idea to forward all CDI SE properties (addProperty) to its own configuration. This just means you can use SE API and still have the full strength of Meecrowave. Extending this logic, TomEE embedded flavor could also do it - as it did for EJBContainer and therefore you can just see CDI SE API as a way to control what you put in your full container.
If you step back one second you will quickly realize it is one more step to a convergence of the java ecosystem. Originally - and very caricaturally - EE was scanning and spring was declaring but now both are able to work in both modes which just enables any user of any stack to cover pretty much the same scope in term of application category making the end user having both a smooth experience but also able to get the full control.
From the same author:
In the same category: