Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 217 posts at DZone. You can read more from them at their website. View Full User Profile

CDI - An Overview: Part 2

06.01.2010
| 8463 views |
  • submit to reddit

In the previous part of CDI, we saw some injection, qualifiers and scope. Now, it’s time to browse through more advanced features.

Producers

Previous examples cannot resolve all our use-cases. Some of these include:

  • injection of random values
  • injection of context-dependent value
  • in general, places where the injection process cannot be narrowed down to a simple new()

These hint at a very well-known pattern, the factory. Factories are implemented in JSR-299 as producers.

Let’s take a simple example, the injection of a connection from a data source. The code that gets the connection either creates it with a direct connection to the database or retrieves it from a data source pool. In the latest case, the following code would fit:

public @interface FromDataSource {}

public class ConnectionProducer {

@Produces @FromDataSource
public Connection getConnection() throws Exception {

Context ctx = new InitialContext();

// Read the data source name from web.xml
String name = ...

DataSource ds = (DataSource) ctx.lookup(name);

return ds.getConnection();
}
}

Interceptors

With Java EE 6, you can harness the power of AOP without AOP. Like in the previous example, using interceptors is very straightforward. There are 3 steps. Let’s implement a simple timer, for benchmarking purposes.

The first step is the declaration of the interceptor. To do so, just use the @InterceptorBinding:

@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Benchmarkable{}

The second step is the interceptor implementation. It uses the @Interceptor annotation, coupled with the previously defined one:

@Benchmarkable @Interceptor</pre>
public class BenchmarkInterceptor {

@AroundInvoke
public Object logPerformance(InvocationContext ic) throws Exception {

long start = System.currentTimeMillis();

Object value = ic.proceed();

System.out.println(System.currentTimeMillis() - start);

return value;
}
}
Notice:
  • the method annotated with @AroundInvoke returns an Object
  • it uses a parameter of type InvocationContext

The last step is to declare such interceptors in WEB-INF/beans.xml because interceptors are deactivated by default.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>ch.frankel.blog.weld.BenchmarkInterceptor</class>
</interceptors>
</beans>

The beans.xml also tells the container about how to order the interceptors in case they is more than one.

There are two other interceptor types, @PostConstruct and @AroundTimeout (for EJB).

Decorators

Decorators, guess what, implement the Decorator design pattern. They are very similar to interceptors with two interesting differences:

  • a decorator must implement the interface it is decorating (and yet can be abstract, so it does not have to implement the methods)
  • a decorator can have a reference to the object it decorates. It is done through injection

Like interceptors, they must be referenced in the beans.xml file in order to be activated. Let’s take a simple example and create an interface which contract is to return an HTML representation of an object:

public interface Htmlable {

String toHtml();
}

Now I need a date class that knows its HTML representation. I know the design is quite bad but bear with me.

public class HtmlDate extends Date implements Htmlable {

public String toHtml() {

return toString();
}
}

If I want a decorator that puts the HTML inside <strong> tags, here’s the way:

@Decorator
public class StrongDecorator implements Htmlable {

@Inject @Delegate @Any
private Htmlable html;

public String toHtml() {

return "<strong>" + html.toHtml() + "</strong>";
}
}

Observers

CDI also implements the Observer design pattern, thus at last enabling simple event-driven development paradigm on the Java EE platform. The basis for it is the event type. An event type is a simple POJO.

The Observer is also a POJO: in order for a method of the Observer to be called when an event is fired, just add a parameter of the right event type and annotate it with @Observes:

public class EventObserverService {

public void afterPostEvent(@Observes PostEvent event) {

... // Do what must be done
}
}

On the other side, the event producer should have an attribute of type javax.enterprise.Event parameterized with the same event type. In order to fire the event, call event.fireEvent() with an event instance:

public class WeldServlet extends HttpServlet {

@Inject
private Event<PostEvent> event;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

event.fire(new PostEvent());
}
}

Now, when sending a POST request to the servlet, the afterPostEvent() method of the EventObserverService will be called.

Alternatives

In the previous article, I adressed the mock service with calling the setter and passing a newly created instance “by hand”. This is all fine and well in a unit testing case, but I also want to manage integration testing. The situation is thus the following:

  • there are two implementations of the same interface on the classpath
  • you can’t change the servlet code (for example, add a qualifier to the service attribute)

Given the deterministic nature of CDI, you should basically be toast. In fact, nothing could be further from the truth. Just use the @Alternative annotation and CDI will conveniently ignore the annotated class.

@Report(MAIL) @Alternative
public class MockMailReportServiceImpl implements ReportService {
...
}

What’s the point then to create it in the first place? Remember the unused-till-then beans.xml from above. It will come to our help, since it accepts <alternative> tags. These tags activate the alternatives.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>ch.frankel.blog.weld.service.MockMailReportServiceImpl</class>
</alternatives>
</beans>

As such, you could have two beans.xml:

  • a basically empty standard context
  • and another integration testing context full of alternatives

Platform

Seam logoGlassFish v3 logoThis article was written with GlassFish v3, which uses Weld v1.0.1, as a platform. Weld is CDI reference implementation, and also a part of the Seam framework.

I had no problems using the platform overall, yet, I couldn’t make alternatives, interceptors and decorators work. Strangely enough, all three must be configured in the WEB-INF/beans.xml. I do not know if I did something wrong or if there’s a bug in the current implementation though.

Conclusion

This 2-parts article only brushes at the surface of CDI. Nevertheless, IMHO, it looks very promising and I wish it much success.

To go further:

  • CDI, an overview – part 1
  • Commons annotations (JSR-250) page: Commons annotations has annotation for DI in Java (@Resource)
  • CDI (JSR-299) page: amazingly enough, CDI is about DI in Java EE
  • Weld’s documentation: Weld is CDI JBoss implementation and also the reference implementation
  • Article on the merits of JSR-299 compared to the merits of JSR-330

 

From http://blog.frankel.ch/cdi-an-overview-part-2

Published at DZone with permission of Nicolas Frankel, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Nicklas Karlsson replied on Tue, 2010/06/01 - 3:11am

Alternatives, decorators and interceptors have to be enabled in the beans.xml according to the specification so it's not a bug.

Nicolas Frankel replied on Tue, 2010/06/01 - 10:37am in response to: Nicklas Karlsson

Thanks for your comment, event though I already wrote it: "The last step is to declare such interceptors in WEB-INF/beans.xml because interceptors are deactivated by default."

This is not the cause since I declared them. There's still something else fishy here.

Cloves Almeida replied on Tue, 2010/06/01 - 4:02pm

With JBoss 6, it works like a charm.

Coupled together with Vaadin, it's the most sane development plataform ever. It's almost too easy...

While Seam 3 promise to deliver a lot of useful extensions, creating one is pretty straightforward. I did my own Quartz extension in a couple of hours. Should be a nice subject for a part 3.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.