Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Simplify enterprise Java development with EJB 3.0, Part 1

Use annotations to develop POJO services

  • Print
  • Feedback

Page 5 of 6

An MDB class must implement the MessageListener interface. When the container detects a message for this bean, it invokes the onMessage() method and passes the incoming message as the call parameter. The MDB decides what to do with the message in the onMessage() method. You can use annotations to configure which message queues this MDB monitors. When the MDB is deployed, the container uses the configuration information specified in the annotations. In the following example, the CalculatorBean MDB is invoked when the container detects an incoming message in the queue/mdb JMS queue. The MDB parses the message and performs the investment calculation based on the message content.

 @MessageDriven(activateConfig =
{
  @ActivationConfigProperty(propertyName="destinationType",
    propertyValue="javax.jms.Queue"),
  @ActivationConfigProperty(propertyName="destination",
    propertyValue="queue/mdb")
})
public class CalculatorBean implements MessageListener {

public void onMessage (Message msg) { try { TextMessage tmsg = (TextMessage) msg; Timestamp sent = new Timestamp(tmsg.getLongProperty("sent")); StringTokenizer st = new StringTokenizer(tmsg.getText(), ",");

int start = Integer.parseInt(st.nextToken()); int end = Integer.parseInt(st.nextToken()); double growthrate = Double.parseDouble(st.nextToken()); double saving = Double.parseDouble(st.nextToken());

double result = calculate (start, end, growthrate, saving); RecordManager.addRecord (sent, result);

} catch (Exception e) { e.printStackTrace (); } }

// ... ... }


Message-driven beans and POJOs
It is important to note that EJB 3.0 MDBs are not pure POJOs. They are lightweight components as they still need to implement the MessageListener interface. JBoss has a message-driven POJO solution (see References) that uses EJB 3.0-style annotations to build completely POJO-based messaging applications.


Dependency injection

In the previous section, you learned how to develop loosely coupled service components. However, to access those service objects, you need to look up stub objects (for session beans) or message queues (for MDBs) from the server's JNDI. JNDI lookup is a key step that decouples the client from the actual implementation of the service object. However, plain JNDI lookup based on string names is not elegant. Here are a few reasons:

  • The client and service must agree on the string-based name. It is a contract not enforced by the compiler or any deployment-time checks.
  • The type of the retrieved service object is not checked at compile-time and could result in a casting error at runtime.
  • The verbose lookup code with its own try-catch block is repeated and littered across the application.

EJB 3.0 features a simple and elegant way to make decoupled service objects and resources available to any POJO. Using the @EJB annotation, you can inject an EJB stub object into any POJO managed by the EJB 3.0 container. If the annotation is tagged on a field variable, the container will assign the correct value to the variable before the first time it is accessed. The following example shows how to inject a CalculatorBean stateless session bean stub into a CalculatorMDB MDB class:

 public class CalculatorMDB implements MessageListener {

@EJB Calculator cal; // Use the cal variable // ... ... }


If the annotation is tagged on a JavaBean-style setter method for a property, the container automatically calls the setter method with the correct parameters before the property is first used. The following snippet illustrates how that works:

 public class CalculatorMDB implements MessageListener {

Calculator cal; @EJB public void setCal (Calculator cal) { this.cal = cal; } // Use the cal variable // ... ... }


In addition to the @EJB annotation, EJB 3.0 supports the @Resource annotation to inject any resource from the JNDI. In the following example, I illustrate how to inject the server's default TimerService and SessionContext objects, as well as how to inject named database and JMS resources from the JNDI:

 @Resource
TimerService tms;

@Resource SessionContext ctx;

@Resource (name="DefaultDS") DataSource myDb;

@Resource (name="ConnectionFactory") QueueConnectionFactory factory;

@Resource (name="queue/A") Queue queue;


Furthermore, you can also inject a container-managed persistence manager (i.e., the EntityManager—it resembles the Hibernate Session object) into EJB 3.0 POJOs. I will cover EntityManager in this article's second installment.

Deliver container services to POJOs

In addition to managing the lifecycle and access of the loosely coupled service objects, the EJB 3.0 container also provides runtime services to its managed POJOs via simple annotations.

Transaction

The most useful container service is probably the transaction service, which ensures database integrity in case of application failure or exception. You can simply annotate a POJO method to declare its transaction attribute. The container runs the method in the appropriate transactional context. For instance, the following code declares that the container should create a new transaction to run the updateExchangeRate() method. The transaction commits when the method exits. In fact, all the methods called from within updateExchangeRate() also execute in the same transaction context, unless explicitly declared otherwise. The database operations performed in the updateExchangeRate() method either all succeed or all fail.

 @Stateless
public class CalculatorBean implements Calculator {

// ... ...

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void updateExchangeRate (double newrate) throws Exception { // Update the database in a loop. // ... ... // The operations in the loop must all be successful or // the database is not updated at all. } }


Security

The container can also provide security services to authenticate users and limit access to its managed POJOs based on user roles. For each POJO class, you can specify a security domain using the @SecurityDomain annotation, which tells the container where to find the password and user role lists. The other domain in JBoss indicates that the files are users.properties and roles.properties files in the classpath. Then, for each method, you can tag a security constraint annotation to specify who is allowed to run this method. For instance, in the following example, the container authenticates all users who attempt to execute the addFund() method and only allows users with the AdminUser role to actually run it. If you are not logged in or are logged in as a nonadministrative user, a security exception will be thrown.

  • Print
  • Feedback

Resources