Accessing resources using DI and JNDI (EJB 3)

We’ve seen EJB 3 DI in its primary incarnations already—the Ojavax.ejb.EJB and Ojavax.annotation.Resource annotations. EJB 3 DI comes in two more forms—the Ojavax.persistence.PersistenceContext and Ojavax.persistence.Persistence-Unit annotations. We’ll see these two annotations in action in part 3 of this topic.

We’ve also witnessed only a small part of the power of the @Resource annotation. So far, we’ve used the @Resource annotation to inject JDBC data sources, JMS connection factories, and JMS destinations. Unlike some lightweight containers such as Spring, EJB 3 does not permit injection of POJOs that aren’t beans. However, the @Resource annotation allows for a variety of other uses, some of which we cover in the coming section. In this section we’ll show you how to use the @Resource annotation and its parameters. You’ll learn the difference between setter and field injection, and you’ll see the @Resource annotation in action when we inject a variety of resources such as e-mail, environment entries, and the timer service. Finally, you’ll learn how to look up resources using JNDI and the lookup method in EJBContext.

Resource injection using @Resource

The @Resource annotation is by far the most versatile mechanism for DI in EJB 3. As we noted, in most cases the annotation is used to inject JDBC data sources, JMS resources, and EJB contexts. However, the annotation can also be used for e-mail server resources, environment entries, ORB reference, or even EJB references. Let’s take a brief look at each of these cases. For convenience, we’ll use the familiar JDBC data source example to explain the basic features of the @Resource annotation before moving on to the more involved cases. The following code injects a data source into the PlaceBid bean from topic 2:


tmp24160_thumb

In this case, the container would not have to work very hard to figure out what resource to inject because the name parameter is explicitly specified. As we know, this parameter specifies the JNDI name of the resource to be injected, which in our case is specified as jdbc/actionBazaarDB. Although we didn’t mention this little detail before, the value specified by the name parameter is actually interpreted further by the container similar to a value specified in the res-ref-name in the <resource-ref> tag in the deployment descriptor, as in the following example:

tmp24161_thumb

The value of the name parameter in @Resource (or res-ref-name) is translated to a fully qualified JNDI mapping in the form java:comp/env/[value of the name parameter] (see the accompanying sidebar). In our example, the complete JNDI path for the resource will be java:comp/env/jdbc/actionBazaarDB. If you don’t specify the name element in the @Resource annotation, the JNDI name for the resource will be of the form java:comp/env/ [bean class name including package]/ [name of the annotated field/property]. If we didn’t specify the name element in the @Resource annotation, the container would use java:comp/env/action-bazaar.buslogic.PlaceBidBean/dataSource as the JNDI name.

The environment naming context and resolving global JNDI names

If you know how JNDI references worked in EJB 2, you’re familiar with the environment naming context (ENC). ENC allows portability of the application without having to depend on global JNDI names. Global JNDI names for resources differ between application server implementations, and ENC allows you to use a JNDI location that starts with java:comp/env/ instead of hard-coding the actual global JNDI name. EJB 3 essentially assumes that all JNDI names used in code are local references and automatically prepends names with the java: comp/env/ prefix.

This automatic interpretation of EJB 3 JNDI names into local references is a nice alternative to mentioning the local ENC (java:comp/env) prefix over and over again. However, this convenience does come at a price. Since you cannot use global names with the name parameter, you have to make sure that you perform the mapping between the ENC and global JNDI names in all cases. Fortunately, many application servers will automatically resolve the ENC name to the global JNDI name if a resource with the same global JNDI name exists. For example, if you are using the Sun GlassFish or Oracle Application Server and you define a data source as shown here, the application server will automatically map the data source to the global JNDI resource bound to jdbc/ActionBazaarDS, even if you didn’t explicitly map the resource:

tmp24162_thumb

Moreover, application servers allow you to explicitly specify a global JNDI name using the mappedName parameter of the ©Resource annotation. For example, if you’re using the JBoss Application Server and you have a data source with a global JNDI name of java:/DefaultDS, you can specify the resource mapping as follows:

tmp24163_thumb

In this case, the container will look up the data source with the global JNDI name of java:/DefaultDS when the ENC java:comp/env/jdbc/ActionBazaarDS is resolved.

However, remember that using the mappedName parameter makes code less portable. Therefore, we recommend you use deployment descriptors for mapping global JNDI names instead.

Note that, similar to the ©Resource annotation, the @ejb annotation has a mappedName parameter as well.

Behind the scenes, the container resolves the JNDI references to the resources and binds the resource to the ENC during deployment. If the resource is not found during injection, the container throws a runtime exception and the bean becomes unusable.

Beyond JNDI name mapping, the @Resource annotation is meant to be a lot more flexible when it needs to be than what is apparent in our deliberately straightforward data source injection example. To illustrate some of these robust features, let’s take a look at the definition for the annotation:

tmp24164_thumb

The first point you should note from the definition of the @Resource annotation is that it is not limited to being applied to instance variables. As the @Target value indicates, it can be applied to setter methods, and even classes.

Setter vs. field injection

Other than field injection, setter injection is the most commonly used option for injection. To see how it works, let’s transform our data source example to use setter injection:

tmp24165_thumb

As you can see, setter injection relies on JavaBeans property-naming conventions. In case you are unfamiliar with them, the conventions dictate that the instance variables of an object should always be private so that they cannot be externally accessible. Instead, an instance variable named XX should have corresponding nonprivate methods named getXX and setXX that allow it to be accessed and set externally. We’ve seen how the setter for the PlaceBidBean dataSource variable looks. The getter could look like this:

tmp24166_thumb

Just as in instance variable injection, the container inspects the @Resource annotation on the setDataSource method before a bean instance becomes usable, looks up the data source from JNDI using the name parameter value, and calls the setDataSource method using the retrieved data source as parameter.

NOTE Whether or not to use setter injection is largely a matter of taste.

Although setter injection might seem like a little more work, it provides a couple of distinct advantages. First, it is easier to unit-test by invoking the public setter method from a testing framework like JUnit. Second, it is easier to put initialization code in the setter if you need it.

In our case, we can open a database connection in the setDataSource method as soon as injection happens:

tmp24167_thumb

The optional type parameter of the @Resource annotation can be used to explicitly set the type of the injected resource. For example, we could have chosen to tell the container that the injected resource is of type javax.sql.DataSource:

tmp24168_thumb

If omitted, the type of the injected resource is assumed to be the same as the type of the instance variable or property.

The type element is mandatory when the @Resource annotation is used at the class level and uses JNDI to obtain a reference to the resource. Let’s take a closer look.

Using @Resource at the class level

You may recall from our earlier discussion that DI is supported only in the managed classes and that you cannot use injection in helper or utility classes. In most applications, you can use helper classes, and you have to use JNDI to look up a resource.To look up a resource from the helper class, you have to reference the resource in the EJB class as follows:

Before we conclude this section, let’s look at some remaining parameters of the @Resource annotation (table 5.2). The other parameters—authenticationType, shareable, description, and mappedName—are not used often and we won’t cover them in great detail.

tmp24169_thumb

You can look up the resource either from the EJB or the helper class as follows:

tmp24170_thumb

Table 5.2 The @Resource annotation can be used to inject resources. The parameters in the table are not used regularly and are included for your reference in case you need them.

Parameter

Type

Description

Default

tmp24-171 tmp24-172

The type of authentication required for accessing the resource. The container value means that the container’s security context is used for the resource. The application value means that authentication for the resource must be provided by the application. We discuss EJB security at greater length in chapter 6.

tmp24-173
tmp24-174 tmp24-175

Specifies whether the resource can be shared.

tmp24-176
tmp24-177 tmp24-178

The description of the resource.

tmp24-179
tmp24-180 tmp24-181

A vendor-specific name that the resource may be mapped to, as opposed to the JNDI name. See the sidebar "The environment naming context and resolving global JNDI names" for details on this parameter.

tmp24-182

Using injection for JDBC data sources is just the tip of the iceberg. We’ll look at the other uses of EJB DI next. In general, we avoid talking about how the resources are defined in the deployment descriptor for now; we’ll discuss that in much greater detail when we examine application packaging and deployment descriptor tags in topic 11.

The @Resource annotation in action

In the previous sections we discussed the various parameters of the @Resource annotation, and you learned how to use field or setter injection with @Resource to injectJDBC data sources. Next you’ll see how to use the @Resource annotation to inject resources such as JMS objects, mail resources, EJBContext, environment entries, and the timer service.

Injecting JMS resources

Recall the discussion on messaging and MDBs in topic 4. If your application has anything to do with messaging, it is going to need to use JMS resources such as

tmp24183_thumb

tmp24184_thumbJust like JDBC data sources, these resources are stored in the application server’s JNDI context and can be injected through the @Resource annotation. As an example, the following code injects a Queue bound to the name jms/actionBazaarQueue to the queue field:

tmp24185_thumb

EJBContext

Earlier (section 5.2) we discussed the EJBContext, SessionContext, and MessageDrivenContext interfaces. One of the most common uses of injection is to gain access to EJB contexts. The following code, used in the PlaceBid session bean, injects the EJB type specific context into the context instance variable:

tmp24186_thumb

Note that the injected session context is not stored in JNDI. In fact, it would be incorrect to try to specify the name parameters in this case at all and servers will probably ignore the element if specified. Instead, when the container detects the @Resource annotation on the context variable, it figures out that the EJB context specific to the current bean instance must be injected by looking at the variable data type, javax.ejb.SessionContext. Since PlaceBid is a session bean, the result of the injection would be the same if the variable were specified to be the parent class, EJBContext. In the following code, an underlying instance of javax.ejb. SessionContext is still injected into the context variable, even if the variable data type is javax.ejb.EJBContext:

tmp24187_thumb

Using this code in a session bean would make a lot of sense if you did not plan to use any of the bean-type specific methods available through the SessionContext interface anyway.

Accessing environment entries

If you have been working with enterprise applications for any length of time, it is likely you have encountered situations where some parameters of your application change from one deployment to another (customer site information, product version, and so on). It is overkill to save this kind of "semi-static" information in the database. This is exactly the situation environment entry values are designed to solve.

For example, in the ActionBazaar application, suppose we want to set the censorship flag for certain countries. If this flag is on, the ActionBazaar application checks items posted against a censorship list specific to the country the application deployment instance is geared toward. We can inject an instance of an environment entry as follows:

tmp24188_thumb

Environment entries are specified in the deployment descriptor and are accessible via JNDI. The ActionBazaar censorship flag could be specified like this:

tmp24189_thumb

Environment entries are essentially meant to be robust application constants and support a relatively small range of data types. Specifically, the values of the <env-entry-type> tag are limited to these Java types: String, Character, Byte, Short, Integer, Long, Boolean, Double, and Float. Because environment entries are accessible via JNDI they can be injected by name. We could inject the censorship flag environment entry into any EJB by explicitly specifying the JNDI name as follows:

tmp24190_thumb

As you might gather, the data types of the environment entry and the injected variable must be compatible. Otherwise, the container throws a runtime exception while attempting DI.

Accessing e-mail resources

In addition to JDBC data sources and JMS resources, the other heavy-duty resource that enterprise applications often use is the JavaMail API, javax.mail. Session. JavaMail Sessions that abstract e-mail server configuration can be stored in the application server JNDI registry. The Session can then be injected into an EJB (with the @Resource annotation) and used to send e-mail. In the ActionBazaar application, this is useful for sending the winning bidder a notification after bidding on an item is over. The DI code to inject the mail Session looks like this:

tmp24191_thumb

We’ll leave configuring a mail session using the deployment descriptor as an exercise for you, the reader.

Accessing the timer service

The container-managed timer service gives EJBs the ability to schedule tasks in a simple way. (You’ll learn more about timers in section 5.4.) We inject the container timer service into an EJB using the @Resource annotation:

tmp24192_thumb

Just as with the EJB context, the timer service is not saved in JNDI, but the container resolves the resource by looking at the data type of the injection target.

The @Resource annotation may be used for injecting EJB references accessible via JNDI into other EJBs. However, the @EJB annotation is intended specifically for this purpose and should be used in these circumstances instead. Refer to the discussion in topic 3 for details about this annotation.

EJB 3 and POJO injection

As you might have noted, the one DI feature glaringly missing is the ability to inject resources into POJOs and to inject POJOs that are not EJBs. You can still indirectly accomplish this by storing POJOs in the JNDI context (not a particularly easy thing to do) or using proprietary extension of your container vendor. We hope that a future version of EJB 3 will provide expanded support for POJO injection similar to other lightweight DI-capable frameworks like Spring.

You can also use POJO injection with Spring-enabled EJB 3 beans if you really need POJO injection in your EJB applications. We’ll save the topic of EJB 3 and Spring for topic 16. We have provided a workaround for POJO injection in topic 12.

@Resource and annotation inheritance

In topic 3, you learned that an EJB bean class may inherit from another EJB class or a POJO. If the superclass defines any dependencies on resources using the @Resource annotation, they are inherited by the subclass. For example, BidManagerBean extends another stateless EJB, PlaceBidBean, where PlaceBidBean defines a resource, as in this example:

tmp24193_thumb

The environment entry defined in the PlaceBidBean will be inherited by the BidManagerBean and dependency injection will occur when an instance of Bid-ManagerBean is created.

As useful as DI is, it cannot solve every problem. There are some cases where you must programmatically look up resources from a JNDI registry yourself. We’ll talk about some of these cases next, as well as show you how to perform programmatic lookups.

Looking up resources and EJBs

Although you can use the @EJB or @Resource annotation to inject resource instances, you may still need to look up items from JNDI in several advanced cases.You can use the @EJB or @Resource annotation at the EJB class level to define dependency on an EJB or a resource. There are two ways of using programmatic lookups—using either the EJB context or a JNDI initial context. We’ll look at both methods.

Recall from our earlier discussion that you can look up any object stored in JNDI using the EJBContext.lookup method (including session bean references). This technique can be used to accomplish one extremely powerful feature that DI cannot accomplish: using lookups instead of DI allows you to determine which resource to use dynamically at runtime instead of being constrained to using static configuration that cannot be changed programmatically. All you have to do is specify a different name in the lookup method to retrieve a different resource. As a result, program logic driven by data and/or user input can determine dependencies instead of deploy-time configuration.

The following code shows the EJB context lookup method in action:

tmp24194_thumb

Note that while using the class-level reference annotation you must explicitly specify the reference name as the complete JNDI pathname. Also note that once an EJB context is injected (as in the sample lookup code), it could be passed into any non-bean POJO to perform the actual lookup.

While both DI and lookup using the EJB context are relatively convenient, the problem is that they are only available inside the Java EE container (or an application client container). For POJOs outside a container, you are limited to the most basic method of looking up JNDI references—using a JNDI initial context. The code to do this is a little mechanical, but it isn’t too complex:

tmp24195_thumb

The initialContext object can be created by any code that has access to the JNDI API. Also, the object can be used to connect to a remote JNDI server, not just a local one.

Although this code probably looks harmless enough, you should avoid it if at all possible. Mechanical JNDI lookup code was one of the major pieces of avoidable complexity in EJB 2, particularly when these same bits of code are repeated hundreds of times across an application.

In the next section, we cover one of the most exciting new features in EJB 3: interceptors.

Next post:

Previous post: