Injection with CDI (Part III)

If you follow this blog you should know that latelly I’ve been writing (and talking) about CDI (Contexts and Dependency Injection). CDI has many aspects to it but until now I’ve focused on how to boostrap CDI in several environments, how to add CDI to an existing Java EE 6 application,  and more recently how to use injection with CDI. Actually this post is the third on CDI Injection : Part I focused on default injection and qualifiers, and Part II on all the possible injection points (field, constructor and setters). In this post I’ll explain producers or “how you can inject anything anywhere in a type safe manner“.

Injecting just beans ?

Until now I’ve shown you how to inject beans with a simple @Inject. If we focus on the book number generator example I’ve been using, we have a Servlet and a RESTService being injected an implementation of the NumberGenerator interface. Thanks to qualifiers, the servlet can specifically ask to get the IsbnGenerator by specifying the @ThirteenDigit qualifier on the injection point and the rest service an IssnGenerator with a @EightDigits (check my first post). The following class diagram shows you some combinations of bean injection :

But as you can see, all this is are beans injecting other beans. Can we just inject beans with CDI ? The answer is no, you can inject anything anywhere.

Producers

Yes, you can inject anything anywhere, the only thing you have to do is produce the thing you want to inject. For that, CDI has producers (a nice implementation of the Factory pattern) A producer exposes any sort of :

  • Class : unrestricted set of bean types, superclass, and all interfaces it implements directly or indirectly
  • Interface : unrestricted set of bean types, interfaces it extends directly or indirectly, and java.lang.Object
  • Primitive and Java array type

So by that you can inject a java.util.Date, java.lang.String or even an int. Let’s start by producing and injecting some data types and primitives.

Producing data types and primitives

One example of injecting anything anywhere is the possibility to inject data types or primitives. So let’s inject a String and an int. For that I need to add extra classes to our model. Until now, the IsbnGenerator would generate a random number like 13-124-454644-4. I can say that this number is made of a String that acts like a prefix (13-124) and an int that acts like a suffix (4). The following class diagram shows the two new classes PrefixGenerator and PostfixGeneratorthat will be used by the number generators :

If we look at the code of the PrefixGenerator for example, you can see that the class itself is not annotated by any qualifier, but the methods are. The method getIsbnPrefix returns a String that is qualified with @ThirteenDigits. This String is produced by CDI (thanks to @Produces), meaning that you can now inject it with @Inject using its qualifier (@ThirteenDigits)

public class PrefixGenerator {

    @Produces @ThirteenDigits
    public String getIsbnPrefix() {
        return "13-84356";
    }

    @Produces @EightDigits
    public String getIssnPrefix() {
        return "8";
    }
}

And now look carefully at the class  PostfixGenerator. The code is exactly the same as previously except in this case we produce an intthat can now be injected.

public class PostfixGenerator {

    @Produces @ThirteenDigits
    public int getIsbnPostfix() {
        return 13;
    }

    @Produces @EightDigits
    public int getIssnPostfix() {
        return 8;
    }
}

So now let’s change the way an ISBN number is generated. The bean IsbnGenerator now injects both a String and an int with @Inject @ThirteenDigits. You understand now what strong typing means with CDI.  Using the same syntax (@Inject @ThirteenDigits), CDI knows that it needs to inject a String, an int or an implementation of a NumberGenerator.

@ThirteenDigits
public class IsbnGenerator implements NumberGenerator {

    @Inject @ThirteenDigits
    private String prefix;

    @Inject @ThirteenDigits
    private int postfix;

	public String generateNumber() {
        return prefix + "-" + Math.abs(new Random().nextInt()) + "-" + postfix;
    }
}

Injecting Java EE resources with producer fields

So if CDI can inject anything anywhere, if CDI can even inject Strings and integers, what about Java EE resources ? That’s another story and producers are here to help. As I’ve said before, CDI is all about type safety : CDI doesn’t like Strings, so there is no way to inject a resource by its JNDI name such as @Inject(name=”jms/OrderQueue”). A common example is the entity manager. This is how you must inject it in Java EE 6 if you don’t use CDI :

@Stateless
public class ItemEJB {

    @PersistenceContext(unitName = "cdiPU")
    private EntityManager em;
    ...
}

So why can’t you just @Inject an entity manager? If you remember my first post about ambiguous injection, this is the same problem. You can have several persistence units (named with a string), so if you just use an @Inject, CDI will not know which persistence unit to inject. Instead you must produce the entity manager first, give it a name (if you don’t want the @Default) and then an @Inject as follow :

public class DatabaseProducer {

    @Produces
    @PersistenceContext(unitName = "cdiPU")
    @BookStoreDatabase
    private EntityManager em;
}

The DatabaseProducer class uses the @PersistenceContext to inject the entity manager with the persistence unit cdiPU. It gives it a name using a qualifier (@BookStoreDatabase) and produces it so it can now be injected in an EJB as follow :

@Stateless
public class ItemEJB {

    @Inject
    @BookStoreDatabase
    private EntityManager em;
    ...
}

Another nice use case is to produce JMS factories and destinations. @Resource allows you to get a JNDI reference to a specific JMS factory or destination, the qualifier @Order gives it a name, and the @Produces allows you to inject it :

public class JMSResourceProducer {

    @Produces @Order @Resource(name = "jms/OrderConnectionFactory")
    private QueueConnectionFactory orderConnectionFactory;

    @Produces @Order @Resource(name = "jms/OrderQueue")
    private Queue orderQueue;
}

Now your EJB can use @Inject in a type-safe way :

@Stateless
public class ItemEJB {

    @Inject @Order
    private QueueConnectionFactory orderConnectionFactory;

    @Inject @Order
    private Queue orderQueue;
    ...
}

Producing Java EE resources with producer methods

The examples above are pretty simple : produce a field and you can then inject it. That’s called producer field. But sometimes you need a more complex business logic to produce a bean, that’s what we called producer method. Let’s take another use case. When you need to send a JMS message you always end up injecting a JMS Factory, a destination (queue or topic) creating a connection, then a session and so on until you actually send your message. Because this code is repetitive and error prone, why not externalize all of if into a single class and produce a session so the other components can use when sending a message. This class could look something like that :

public class JMSResourceProducer {

    @Resource(name = "jms/OrderConnectionFactory")
    private QueueConnectionFactory orderConnectionFactory;
    @Produces @Order @Resource(name = "jms/OrderQueue")
    private Queue orderQueue;

    @Produces @Order
    public QueueConnection createOrderConnection() throws JMSException {
        return orderConnectionFactory.createQueueConnection();
    }

    @Produces @Order
    public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException {
        return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
    }
}

First, the class uses the @Resource to get a reference of the QueueConnectionFactory and Queue. For the same reason I just explain above, you can have several JMS factories and destinations and you must distinguish them by their JNDI name. And because CDI does not allow you to give a string name on an injection point, you must still use @Resource instead of @Inject. The createOrderConnection method take a QueueConnectionFactory creates a QueueConnection and produces it (while giving it the name of @Order). If you look carefully at the method createOrderSession it takes the produced QueueConnection and creates a QueueSession. An external component then just needs to inject the JMS session without going through the process of creating it :

@Stateless
public class ItemEJB {

    @Inject @Order
    private QueueSession session;

    @Inject @Order
    private Queue orderQueue;

    private void sendOrder(Book book) throws Exception {
        QueueSender sender = session.createSender(orderQueue);
        TextMessage message = session.createTextMessage();
        message.setText(marshall(book));
        sender.send(message);
    }
    ...
}

Producing and… disposing

I can hear you saying “ok, that’s nice, I have an external class doing all the plumbing and creating a connection and a session…. but who is going to close it ?“. Indeed, someone needs to free these resources by closing them. And that’s when CDI brings you another nice bit of magic : @Disposes.

public class JMSResourceProducer {

    @Resource(name = "jms/OrderConnectionFactory")
    private QueueConnectionFactory orderConnectionFactory;
    @Produces @Order @Resource(name = "jms/OrderQueue")
    private Queue orderQueue;

    @Produces @Order
    public QueueConnection createOrderConnection() throws JMSException {
        return orderConnectionFactory.createQueueConnection();
    }

    @Produces @Order
    public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException {
        return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
    }

    public void closeOrderSession(@Disposes @Order QueueConnection conn) throws JMSException {
        conn.close();
    }

    public void closeOrderSession(@Disposes @Order QueueSession session) throws JMSException {
        session.close();
    }
}

To ask CDI to close a resource you just need to create a method which signature is the same as the one that created it (createOrderSession(@Order QueueConnection conn) creates a session and closeOrderSession(@Order QueueConnection conn) closes it) and add a @Disposes. CDI will dispose the resources in the right order (session first, connection second) for you. I haven’t mentioned it yet but CDI will create and dispose resources depending on the scope (resquest, session, application, conversation…). But that’s for another post.

Conclusion

As you’ve seen in my previous posts (Part I, Part II), CDI is about injection (I’ll explain other topics later). So far I’ve been showing you how to inject beans, but you know now that you can inject anything (String, integer, entity manager, JMS factories…) anywhere (in POJOs, Servlets, EJBs…). You just need to produce what you need to inject (either with field producers or method producers).
The next article will cover alternatives, so stay tuned.

Download

Download the code, give it a try, and give me some feedback.

References

About these ads
Comments
15 Responses to “Injection with CDI (Part III)”
  1. Pedro Kowalski says:

    Thanks a lot Antonio for your post.

    With @Produces it allows me to understand that the injected @EJB is conforming to the EJB container rules (pooling, transactionality, locating through beanName, beanInterface, etc.).

    However, I just wander, in one of your previous post you’ve said that basically you can just swap @EJB to @Inject (in case of injecting EJB’s) and that it will work.
    I wonder – without the @Produces method on @EJB annotated resource, does the CDI’s @Inject obey the EJB-container rules? I.e., will it inject me the EJB taken from the EJB-container pool?

    I guess that the cooperation between CDI container and EJB container without specifying the explicit @Produces methods is quite misty for me…

    Thanks in advance for any comments on this topic.

  2. Hi.
    Can i translate your series about CDI into russian and publish it on my blog? http://hpcru.wordpress.com and habrahabr.ru . Will be grateful if you answer me via email ahriman@tpu.ru .

    • agoncal says:

      Hi. Well, I have no objection with this idea. The only concern I have is that I won’t be able to understand your translation and, therefore, approve or disapprove it. But at the end of the day, if your translation points to my original post, readers can then make their mine. So if you feel like it, I’ll be honoured to have a russian translation.

  3. agoncal says:

    Good point Pedro and that gives me inspiration for another blog (the next one maybe). From Java EE 6, JNDI name are standards. That means if you deploy an EJB, the container gives it a standard unique name. CDI “knows” about this name, so it can @Inject the EJB. But that’s not true with remote interfaces, you need to use @Produces in this case.

    Keep an eye on this blog, I’ll try to answer this question with deeper information.

  4. Pedro Kowalski says:

    Oh, so the CDI uses the standardized JNDI under the hood.. didn’t know that but this makes sense.

    I will surely keep an eye on your blog, thanks!

    Another thing. Let’s assume I have a local business interface and more than one EJB implementation of it. Using ‘old-schooled’ @EJB annotation I can use @EJB(beanName=…) or @EJB(lookup=…).
    I guess that with CDI the plain @Inject on interface type doesn’t make any sense (just like the plain @EJB – injector doesn’t know what implementation to inject), so I would end with Configuration class with i.e.:

    @Produces
    @EJB(beanName=….)
    @BeanName1
    MyInterface myEjb1;

    @Produces
    @EJB(lookup=….)
    @BeanName2
    MyInterface myEjb2;

    And then in class inject it using @Inject @BeanName1?

    TIA.

  5. Pedro Kowalski says:

    Antonio, you’ve said that the CDI knows about the JNDI name of the EJB. If so, does it mean that the CDI uses the JNDI lookup to obtain the EJB?

    There is a part in Java EE 6 spec which talks that the JNDI lookup is required to return a new instance of the requested object.
    Does it mean that each @Inject uses JNDI lookup and therefore creates a new instance of the EJB?

  6. Anshul says:

    Hi Antonio, i tried to test the ItemServlet on embedded glassfish 3.1.1 and upon invocation of InputStream os = con.getInputStream(); from my test case during the test run, the servlet gave a NPE which means during runtime within the servlet
    @Inject
    @NumberOfDigits(Digits.THIRTEEN)
    private NumberGenerator numberGenerator;

    ItemServlet code that threw NPE during test on embedded glassfish 3.1.1

    String number = numberGenerator.generateNumber(); //npe here
    // Creates an instance of book
    Book book = new Book(“title”, 999.99F, “description”, 666, false, “english”, “scifi”, number);
    itemEJB.createBook(book);

    the above injection of @NumberOfDigits(Digits.THIRTEEN) numbergenerator didn’t work, but when i ran the code on jboss 6.1.0 it worked beautifully.
    Any clues as to why the NumberGenerator wasn’t injected correctly?

    Test code below:

    @Before
    public void initWebContainer() throws Exception {

    GlassFishProperties gfProperties = new GlassFishProperties();
    gfProperties.setPort(“http-listener”, 8080);
    gf = GlassFishRuntime.bootstrap().newGlassFish(gfProperties);
    gf.start();
    dy = gf.getDeployer();
    ScatteredArchive archive = new ScatteredArchive(“injection-enum”, ScatteredArchive.Type.WAR);
    archive.addClassPath(new File(“target”, “classes”));
    appName = dy.deploy(archive.toURI());
    }

    @Test
    public void TestItemServlet() throws Exception {

    // Read the response from the servlet hosted at “/itemServlet”
    URL url = new URL(“http://localhost:8080/injection-enum/itemServlet”);
    URLConnection con = url.openConnection();
    InputStream os = con.getInputStream();

    StringBuffer sb = new StringBuffer();
    int ch=0;
    while((ch = os.read()) != -1) {
    sb.append((char)ch);
    }

    System.out.println(sb.toString());
    }

  7. Sam says:

    “CDI will create and dispose resources depending on the scope (resquest, session, application, conversation…)”

    This note is worth highlighting as the JMS example provided didn’t work in JBoss without specifying a scope because the connection was being disposed/closed immediately after the session was created. Add @RequestScoped after all the @Produces annotations to resolve this problem.

  8. rafaeltuelho says:

    Hi Antonio!
    It’s a nice post.

    I tried to download the sample code but the link (http://antoniogoncalves.org/xwiki/bin/download/Main/Attach/CDIInjectionIII.zip) appears to be broke. Can you point to the correct one?

    Thanks!

  9. bala says:

    Hi,

    great blog with great articles.
    However I’m a bit confused:

    First you wrote:
    @Produces @Order @Resource(name = “jms/OrderConnectionFactory”)
    private QueueConnectionFactory orderConnectionFactory;

    Then in your next step:
    @Resource(name = “jms/OrderConnectionFactory”)
    private QueueConnectionFactory orderConnectionFactory;

    An here comes my question:
    Why doesn’t we need in the second case the two annotation(@Produces @Order )?

    Maybe i missed something.

    Thanks!

  10. Cloud_Strife says:

    Is this a little typo in @Disposes chapter?

    public void closeOrderSession(@Disposes @Order QueueConnection conn) throws JMSException {
    conn.close();
    }

    Is this the right method name or should it be “closeOrderConnection()”?

Trackbacks
Check out what others are saying...
  1. […] post on CDI here helped me solve the PersistenceContext injection […]



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 7,228 other followers

%d bloggers like this: