Chapter 32

Introduction to Enterprise JavaBeans

This lesson introduces you to one of the Java EE technologies, Enterprise JavaBeans, which can be used for implementing the business tier in a distributed application (refer to Figure 26-1). Lessons 27 and 28 were about various ways of programming the presentation tier on the Web; in Lessons 30 and 31 you learned how to organize communication between the different tiers of the application using messaging. You programmed the application business logic in POJOs.

Now you’ll see how to program the business tier in EJBs, which are also POJOs, but managed by an EJB container. With the release of Java EE 6, EJB 3.1 and the Java Persistence API (JPA) 2.0 do offer you a clean and simple way to implement solutions for business logic and data persistence. The features covered in this lesson will work in application servers that support EJB 3.1. Such servers include GlassFish v3, JBoss 6, and WebSphere 8, and more vendors are working on making their application servers Java EE 6–certified. There is also the Apache TomTom bundle, which includes the open-source Apache OpenEJB project; at the time of this writing this bundle partially supports EJB 3.1.

In this lesson I’ll introduce you to various types of EJBs, and the next lesson is about persisting data with JPA.

Who Needs EJB Containers?

What’s wrong with POJOs? Why not just implement business logic there? You certainly can, but most likely you’d need to spend additional time manually programming a multi-threaded environment for them. The chances are that your application needs transactional support — if the business logic is located in POJO 1 and POJO 2, and the second one fails, you want to roll back whatever has been completed by the first one.

It’s great that you know how to program message receivers using JMS, but how can you make this solution scalable? What if you need a couple of dozen message listeners that concurrently de-queue the messages? Don’t forget about the tasks of integrating your POJOs with other tiers to perform authentication and JDBC operations.

EJB containers take care of all these infrastructure-related concerns without requiring manual programming on your side. You’ll simply configure pools of message-driven beans (MDBs) if you need multiple message listeners. You’ll turn on transactional support if need be. You don’t need to worry about multi-threading problems if your beans operate in an EJB container. The configuration process is as simple as adding Java annotations in the EJB classes. Intercommunication among beans is done via dependency injection or singleton beans.

If you need to scale a distributed application, EJB containers offer clustering and failover support. Security authorization is also the container’s responsibility. Without the need to manually code all these infrastructural functionalities, the code of your EJBs becomes really light.

The other interesting service offered by EJB containers is asynchronous method invocation, which enables asynchronous processing without the need for JMS or MOM.

EJB 3.1 supports embeddable containers: You can run EJB applications in a Java SE environment. This is a great idea for tasks such as testing because there’s no need to depend on the readiness of a Java EE server — just test your EJBs locally. Creating an embeddable container is as simple as executing one line of code:

EJBContainer myContainer = EJBContainer.createEJBContainer();

If the originally selected application server doesn’t deliver the performance or reliability you expected, deploy your EJBs in a different Java EE 6–compliant server without changing a line of code.

Types of EJBs

There are two major types of EJB: session beans and message-driven beans. MDBs specialize in retrieving messages from JMS queues or topics — this is all they can do. But session beans are where your application business logic will reside. There are three types of session beans:

  • A stateless session bean is one that contains business logic but doesn’t support state. In other words, it doesn’t “remember” any data specific to the client. If the same client invokes two methods in a row on the stateless bean FindBooks, the container may use two separate instances of the FindBooks bean, as it doesn’t store any intermediate data specific to the client.
  • A stateful session bean is one that contains business logic and state. The EJB container allocates a specific instance of the session bean to the client and can store results between subsequent method invocations.
  • A singleton session bean is one guaranteed to be the only instance of its kind in the container. Think of a singleton bean as a global repository, in which one bean can put some data to be used by another bean. Singleton session beans not only provide easy access to common data but also ensure that there are no race conditions in cases of concurrent access.

Older EJB specifications defined entity beans for data persistence. Formally they still exist in the EJB 3.1 specification, but they are being pruned, and most likely will disappear from future versions of Java EE releases.

An EJB container creates and maintains pools of session beans. The instances of the stateless beans are allocated to clients for much less time than stateful ones. Therefore, if you are working on a multiuser application with hundreds or thousands of concurrent requests to the EJB container, stateless beans offer a much more scalable solution, because a smaller pool can serve more users’ requests.

But even with stateful session beans the EJB container is playing smart, and those instances sitting in memory without active interaction with the client are being passivated — removed from memory and stored on the disk. When the client issues another request to a stateful bean that has been passivated, the container will activate it again.

Stateless Session Beans

I’ll introduce you to stateless session beans by creating a simple example featuring an EJB that contains the business logic to return the message “Hello World.”

The Bean

Having a class that just returns “Hello World” is an obvious case for using a stateless session bean: It’ll have only one method and there is no state to remember. Listing 32-1 shows you how to program such a bean.

download.eps

Listing 32-1: HelloWorld session bean, take 1

@Stateless
public class HelloWorldBean {
 
    public String sayHello(){
        return "Hello World!";
    }
}

Basically, you create a POJO and annotate it with one or more Java annotations. There are no special interfaces to be implemented to turn a POJO into an EJB. Accordingly there are @Stateful, @MessageDriven, and @Singleton annotations to mark other types of session beans. You may use the optional configuration file ejb-jar.xml in the application server’s directory META-INF. This file also enables you to specify metadata for the EJB, and if you need to change the metadata on a production system without recompiling the code, make the changes in ejb-jar.xml — they will override the values specified via annotations.

The Client’s View

The bean shown in Listing 32-1 will run on the server, but the client that will invoke the sayHello() method can run either in the same JVM (e.g., a servlet or another bean) or in another one (e.g., a standalone Java SE application or Java EE class deployed in another container). Beans may or may not implement interfaces. If HelloWorldBean will be used only by clients running in the same JVM, you can mark it with a @LocalBean annotation to show that it’s a no-interface bean.

If you’d like to expose certain business methods to local clients, you can create an interface and mark it with a @Local annotation. Your bean class has to implement this interface. If you’d like to expose your bean to remote clients, create an interface and mark it as @Remote. The bean has to implement this interface.

Local No-Interface Beans

I am planning to use HelloWorldBean from the servlet running in the same JVM, so the final version of the code will look like Listing 32-2. Any EJB can use regular Java classes to implement business logic: For example, the sayHello() method can create instances of other Java classes with the new operator and invoke their business methods if need be.

download.eps

Listing 32-2: HelloWorld session bean, take 2

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
 
@LocalBean
@Stateless
public class HelloWorldBean {
 
    public String sayHello(){
        return "Hello World!";
    }
}

Now let’s do it hands-on. Create a new Dynamic Web Project in Eclipse as you did in previous lessons. Name it Lesson32. Then create a new servlet class, HelloWorldServlet, in the package com.practicaljava.lesson32.client (select the menu File New Servlet). This servlet will become our client, communicating with the EJB.

Next create a Java class called HelloWorldBean in the package com.practicaljava.lesson32.ejb by selecting File New Other EJB Session Bean (EJB 3.x). Do not select any local or remote business interfaces, and uncheck (when possible) the objects related to EJB 2.x. Eclipse will create a class with a default constructor. Add to this class the method sayHello() shown in Listing 32-2, and the EJB is finished.

The next step is to inject the HelloWorldBean into the servlet code with the @EJB annotation:

@EJB HelloWorldBean myBean;

Eclipse will mark this line with a red error bullet. Right-click it and select Quick Fix to automatically insert two import statements — one for the @EJB annotation and the other for HelloWorldBean.

Using JNDI remains an alternative to injecting the bean into the client. Java EE 6 supports portable JNDI names that don’t depend on the application server’s implementation. Instead of the @EJB annotation you could use the following code:

Context ctx = new InitialContext();
HelloWorldBean myBean = (HelloWorldBean)
                       ctx.lookup("java:global/Lesson32/HelloWorldBean");

Now add the following two lines in the doGet() method of the servlet to make it invoke the method sayHello() on the EJB:

PrintWriter out = response.getWriter();
out.println(myBean.sayHello()); 

That’s all there is to it. The complete code of the servlet is shown in Listing 32-3.

download.eps

Listing 32-3: Servlet client for HelloWorldBean

import java.io.IOException;
import java.io.PrintWriter;
 
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.practicaljava.lesson32.ejb.HelloWorldBean;
 
@WebServlet("/HelloWorldServlet")
public class HelloWorldServlet extends HttpServlet {
    
  @EJB HelloWorldBean myBean;
 
  protected void doGet(HttpServletRequest request, 
        HttpServletResponse response) throws ServletException, IOException {
 
       PrintWriter out = response.getWriter();
       out.println(myBean.sayHello());       
  }
}

Local Beans

If you’d like to expose the business method to local clients, you can declare the interface marked with the @Local annotation and the bean will implement it, as in Listing 32-4.

download.eps

Listing 32-4: Local interface and bean

@Local
public interface Greeting {
   public String sayHello();
}
 
@Stateless
public class HelloWorldBeanL implements Greeting{
 
    public String sayHello(){
        return "Hello World!";
    }    
}

Remote Beans

For clients that may access the bean remotely, declare an interface marked with @Remote and have your bean class implement it. An EJB can implement both remote and local interfaces, and you can expose different methods for remote and local clients too.

download.eps

Listing 32-5: The Greeting interface

@Remote
public interface Greeting {   public String sayHello();
}
 
@Stateless
public class HelloWorldBeanL implements Greeting{
 
    public String sayHello(){
        return "Hello World!";
    }    
}

The clients find remote beans by performing JNDI lookups. Because the client and the server may be running in different JVMs, all arguments of the remote methods must be serializable.

The Server

Right-click the GlassFish server in the Servers view and add the project Lesson32 to deploy this Web application. You may also want to turn on automatic redeployments in GlassFish. To do this, double-click the GlassFish server in the Servers view and change the Publishing preferences to Automatically publish when resources change, as in Figure 32-1. This will eliminate the need for manual redeployment of the application every time the code changes.

Start GlassFish if it’s not running and run HelloWorldServlet. You’ll see the output produced by the EJB, as in Figure 32-2; you can get the same output by entering http://localhost:8080/Lesson32/HelloWorldServlet in your web browser. Even though this is a tiny application, nevertheless it demonstrates communication between objects from the presentation and business tiers. Real-world applications have more business logic, but from an architectural perspective they are the same.

Asynchronous Methods

There is one more feature in stateless beans that I’d like you to be aware of: asynchronous methods. Imagine that sayHello() is a long-running method performing some lengthy calculations and you’d like to call it and continue with other operations without waiting for it to complete. In a Core Java application you would start a thread that would eventually return the Future object, as explained in Lesson 21.

In the EJB container, however, you shouldn’t create and start threads from the application code, because EJB container takes care of all multi-threading issues for you. The good news is that asynchronous methods offer a solution when you need to introduce parallel processing from EJBs.

Just mark the method with the @Asynchronous annotation and have it return an object of type javax.ejb.AsyncResult, which is an implementation of the Future interface:

@Asynchronous 
public <Future>String sayHello(){
 
        // Some lengthy calculations go here
        //... 
 
        return new AsyncResult<String>("Hello World!");
    }

The client would make a call to sayHello(), then execute some other code without waiting for sayHello() to complete, and at some point would request Future by making a blocking call, get(), as in the following code snippet:

//Asynchronous call
Future<String> myFutureGreeting = myBean.sayHello();
// Some other code that is executed immediately after the above line goes here
//...
}
 
// Sometime later you can make a blocking call and start 
// waiting for the result of sayHello()
String myGreeting = myFutureGreeting.get();

You can also use asynchronous methods if a client needs to start more than one method on the EJB to run in parallel threads. The methods don’t even need to return values; say one is generating large PDF files and the other prepares shipments based on today’s orders. In this case you don’t even need to process returned values; just declare the methods as asynchronous and invoke them from the client. They’ll start immediately and will run in parallel.

Stateful Session Beans

Although stateless session beans are given to the client just for the time one method execution takes, stateful beans are allocated to the client for longer. They remember the results of the execution of previous method(s). For example, you can use a stateful bean to implement shopping cart functionality, enabling the user to add more than one item to the cart while browsing the company’s catalog. When the client ends the session the stateful bean can be allocated to another client.

Suppose you have a stateful EJB called MyShoppingCart. The client looks up this bean using JNDI and makes the first call to the method addItem(). Then the client continues browsing and adds another item to the shopping cart. Then the client decides to check out and calls the method placeOrder(). All these method invocations are done on the same instance of the stateful bean MyShoppingCart.

MyShoppingCart myCart = (MyShoppingCart)
                       ctx.lookup("java:global/OnlineStore/MyShoppingCart");
 
// The client is browsing the catalog and finds the first item to buy
...
myCart.addItem(myFirstItem);
 
// The client continue browsing the catalog and finds the second item to buy
...
myCart.addItem(mySecondItem);
 
// The client is ready to check out
...
 
myCart.placeOrder();

To complete the shopping process and release the stateful bean for other clients you need to call one of the bean’s MyShoppingCart methods marked with the @Remove annotation. In the preceding example the method placeOrder() should be marked with this annotation. You should also provide another @Remove method on the bean to allow the client to cancel the order and release the bean.

There is one more way to release a stateful bean, by using the @StatefulTimeout annotation, which enables you to specify how long a bean can stay allocated to the client without any activity. When this time expires the session times out and the bean is released.

Singleton Beans

Pretty often an application needs a place to keep data that is accessible to and shared by all the beans. Another use case for a singleton is to control access to some external resources. For example, if a limited number of connections are available to some external web service, you can create a singleton that will implement throttling for EJBs that need these connections. A singleton bean can be used as global storage (or a cache) for the application — the state of this bean is shared among clients.

Only one singleton bean with any given name can exist per JVM per application. If you need several singletons in an application, give them different names. An EJB container doesn’t create pools of singleton beans. To create a singleton EJB just mark it with the @Singleton annotation:

@Singleton
public class MyGlobalStorage {
...
}  

When is the singleton bean created? It’s up to the EJB container to decide, unless you specifically want to request that this bean be created on application startup. This is called eager initialization and there is a special annotation, @Startup, for it:

@Startup
@Singleton
public class MyGlobalStorage {
 
   private Map<String, Object> = new HashMap<String, Object>();
...
   addToStorage(String key, Object objToStore){...}
 
   removeFromStorage(String key){...} 
}

Let’s say that you’d like to create a program that at certain times sends some messages into a queue. Write a singleton bean, request that the EJB container instantiate it on application start-up, and start pushing the messages immediately after the singleton has been constructed. There is another handy annotation, @PostConstruct, that will cause the container to invoke a method immediately after the bean’s constructor is finished:

@Startup
@Singleton
public class MyStockQuoteServer {
...
 @PostConstruct
 void sendPriceQuotes(){
   // The code connecting to the stock prices feed and 
   // sending messages to a queue goes here
  
 }
}

To get access to the business methods of a singleton, the client Java classes will need to call the public static method getInstance() on the specific singleton, as shown in the following code snippet. If you’ll be implementing the singleton design pattern manually you need to declare a private constructor and a public static getInstance() in the class, but otherwise the EJB container will take care of this.

MyGlobalStorage.getInstance().addToStorage("emplOfTheMonth", bestEmployee);

The EJB container allows concurrent access to singleton beans, and by default it applies the container-managed concurrency policy, sparing the developer worry about race conditions and such. You just need to use the @Lock annotation, specifying whether you want a resource to be locked during the read or write operation. If you prefer to write thread-synchronization code by yourself, switch to bean-managed concurrency. You set the type of concurrency using the @ConcurrencyManagement annotation, as shown here:

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MyGlobalStorage {
...
}

Deploying EJB

Before deployment to any application server, EJBs are usually packaged into one archive file, which could be a jar, an Enterprise Archive (ear), which is a jar with a .ear file name extension, or a war. Even such a simple two-class web application as our current example should be compressed and deployed as one file. If your client is a web application it’s convenient to keep the EJB inside a war file. Let’s create one.

Right-click the Deployment Descriptor section in the Eclipse project Lesson32 and select Export WAR file. In a second you’ll get the file Lesson32.war that contains both the servlet and the EJB. This file can be deployed in any Java EE 6–compliant application server. This war is really small: under 4 KB! Java EE 6 makes EJB components really lightweight. If you have multiple EJB classes, put them in one or more jars in the WEB-INF/lib directory.

If your client is not a small web application, or you want to keep EJBs deployed separately, you can package them inside a separate jar or ear file. The root directory that you’ll be using to create ears has to have all compiled Java classes and, if applicable, the optional ejb-jar.xml file in the META-INF directory. As a matter of fact, you can create an ear file that will contain not only your EJBs, but also the war file.

Message-Driven Beans

MDBs (message-driven beans) perform only one function: They retrieve messages from queues and topics via the JMS API. The clients never need to look them up or invoke their methods. The client just needs to drop a message in a queue or publish it to a topic, and the MDBs that were listening to the messages at these destinations will get invoked automatically.

MDBs must implement the MessageListener interface. All configuration parameters can be specified in the parameters of the @MessageDriven annotation, as shown in Listing 32-6.

download.eps

Listing 32-6: MDB MyMessageBean

@MessageDriven(mappedName="jms/testQueue", activationConfig =  {
        @ActivationConfigProperty(propertyName = "acknowledgeMode",
                                  propertyValue = "Auto-acknowledge"),
        @ActivationConfigProperty(propertyName = "destinationType",
                                  propertyValue = "javax.jms.Queue")
    })
public class MyMessageBean implements MessageListener {
 
MessageDrivenContext ctx;
 
  // A no-argument  constructor is required 
  public MyListener() {}
 
  public void onMessage(Message message){
     // The business logic is implemented here. 
  }
}

When a message appears in the queue named testQueue the EJB container picks one of the MDBs from the pool and invokes its method onMessage(), passing the message from the queue as an argument. Unlike with the stand-alone message receivers described in Lesson 30, with MDBs the container gives you excellent freebies: distributed transaction processing, automatic pooling, co-location of receivers and other beans, and simple assignment of queues or topics to the receivers in deployment descriptors. In addition, you can easily configure the number of receivers by specifying the pool size in the deployment descriptor.

You can use any client to send a message in a queue. It can be a stand-alone client, as shown in Listing 31-2, a servlet, an EJB, etc.

Timer Service

Many enterprise applications require the use of schedulers to perform certain repetitive tasks at certain times. Cron is a widely used scheduler for Unix-based applications. Windows also has a task scheduler (Control Panel Scheduled Tasks). The open-source Quartz Scheduler is also popular among Java developers.

EJB 3.1 includes the @Schedule annotation, which takes a calendar-based expression so you can schedule the execution of required functionality in your beans. For example, you can create expressions that will invoke some business method every second, minute, hour, Monday, weekday midnight, etc.

The next code snippet shows how you can create a timer that will invoke the method getPriceQuotes() every second during weekdays from 9:30 a.m. to 4 p.m.:

@Stateless
public class MyStockQuoteFeed {
    
   @Schedule(second="*", minute="*", hour="9:30-16:00", dayOfWeek="Mon-Fri")
   public List getPriceQuotes(){
      // The code to connect to price quote feed goes here
      ...
   }
}

You can also create timers programmatically using the TimeService class and its methods createTimer(), createSingleActionTimer(), createIntervalTimer(), and createCalendarTimer().

In addition to using @Schedule and programmatic timers, you can configure timers in the deployment descriptor ejb-jar.xml.

Summary

EJB 3.1 is a powerful solution for creating a scalable, easy-to-develop and -deploy, and lightweight solution for enterprise applications. Even a small-scale application can benefit from EJB 3.1. If your application doesn’t need all the features mandated by the EJB specification, consider using EJB 3.1 Lite, which is a subset of the full specification.

It’s not possible to cover all the features offered by EJB containers in one short lesson. I’ve introduced you to the main EJB concepts, but if you’d like more in-depth coverage, read Andrew Lee Rubinger and Bill Burke’s Enterprise Java Beans 3.1, O’Reilly, 2010, ISBN: 0-596-15802-5.

Try It

The assignment for this lesson is to implement the StockServer class as an EJB and to use the timer to automatically generate and print stock price quotes every second. The new quotes should be sent to testQueue via the JMS API and consumed by a message-driven bean. Reuse the code of the sample stock server application of the StockServerImpl from Listing 25-2 to generate the price quotes.

Lesson Requirements

You should have Java and GlassFish v3 installed.

note.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson32 folder in the download.

Hint

If you want to push the stock prices to the end users, consider creating a JMS topic (e.g., PriceQuotes) to which the method getQuote() will publish the latest prices. The Java client should subscribe to this topic to get the fresh quotes every second.

Step-by-Step

1. In Eclipse project Lesson32 create a new stateless session bean, StockServerBean, that includes the method getQuote(). The initial version of the bean may look as follows:

@Stateless
public class StockServerBean {
  private String price=null;
  private ArrayList<String> nasdaqSymbols = new ArrayList<String>();
 
  public StockServerBean(){
   
    // Define some hard-coded NASDAQ symbols 
    nasdaqSymbols.add("AAPL");
    nasdaqSymbols.add("MSFT");
    nasdaqSymbols.add("YHOO");
    nasdaqSymbols.add("AMZN");
    nasdaqSymbols.add("MOT");
  }
 
  public void getQuote(String symbol){
 
    if(nasdaqSymbols.indexOf(symbol.toUpperCase()) != -1) {
 
        // Generate a random price for valid symbols   
        price = (new Double(Math.random()*100)).toString();
    }
    System.out.println("The price of "+ symbol + " is " + price);
  }
}

2. Use the @Schedule annotation to have the getQuote() method invoked every second.

3. Replace the println() statement in the method getQuote() with the code sending a text message with the generated price quote to the queue MyJMSTestQueue configured in Lesson 31.

4. Create an MDB called MyPriceConsumer to retrieve and print messages from the queue MyJMSTestQueue.

5. Deploy this application in GlassFish and test it.

cd.ai

Please select Lesson 32 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.17.230