Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (15.81 MB, 597 trang )
Chapter 7 ■ Enterprise JavaBeans
•
Restriction to obtain the current class loader has been removed, and the use of the java.io
package is now allowed.
•
Allignment of JMS 2.0.
•
Embeddable container implements Autocloseable to fit Java SE 7.
•
RMI/IIOP has been pruned in this release. This means that it might be marked as optional
in Java EE 8. Remote invocation would then be done with just RMI (without the IIOP
interoperability).
Table 7-2 lists the main packages defined in EJB 3.2 today.
Table 7-2. Main EJB Packages
Package
Description
javax.ejb
Classes and interfaces that define the contracts between the EJB and its clients and
between the EJB and the container
javax.ejb.embeddable
Classes for the embeddable API
javax.ejb.spi
Interfaces that are implemented by the EJB container
Reference Implementation
GlassFish is an open source application server project led by Oracle for the Java EE platform. Sun launched the project
in 2005 and it became the reference implementation of Java EE 5 in 2006. Today, GlassFish v4 includes the reference
implementation for EJB 3.2. Internally, the product is built around modularity (based on the Apache Felix OSGi
runtime), allowing a very fast startup time and the use of various application containers (Java EE 7, of course, but also
Ruby, PHP, etc.).
At the time of writing this book GlassFish is the only EJB 3.2 compliant implementation. But others will soon
follow: OpenEJB, JBoss, Weblogic, Websphere . . .
Writing Enterprise Java Beans
Session beans encapsulate business logic, are transactional, and rely on a container that does pooling, multithreading,
security, and so on. What artifacts do we need to create such a powerful component? One Java class and one
annotation—that’s all. Listing 7-1 shows how simple it is for a container to recognize that a class is a session bean and
apply all the enterprise services.
Listing 7-1. A Simple Stateless EJB
@Stateless
public class BookEJB {
@PersistenceContext(unitName = "chapter07PU")
private EntityManager em;
public Book findBookById(Long id) {
return em.find(Book.class, id);
}
233
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
public Book createBook(Book book) {
em.persist(book);
return book;
}
}
Previous versions of J2EE required developers to create several artifacts in order to create a session bean: a local
or remote interface (or both), a local home or a remote home interface (or both), and a deployment descriptor.
Java EE 5 and EJB 3.0 drastically simplified the model to the point where only one class and one or more business
interfaces are sufficient and you don’t need any XML configuration. As shown in Listing 7-1, since EJB 3.1 the class
doesn’t even have to implement any interface. We use only one annotation to turn a Java class into a transactional
and secure component: @Stateless. Then, using the entity manager (as seen in the previous chapters), the BookEJB
creates and retrieves books from the database in a simple yet powerful manner.
Anatomy of an EJB
Listing 7-1 shows the easiest programming model for session beans: an annotated POJO with no interface. But,
depending on your needs, session beans can give you a much richer model, allowing you to perform remote calls,
dependency injection, or asynchronous calls. An EJB is made of the following elements:
•
A bean class: The bean class contains the business method implementation and can
implement zero or several business interfaces. The session bean must be annotated with
@Stateless, @Stateful, or @Singleton depending on its type.
•
Business interfaces: These interfaces contain the declaration of business methods that are
visible to the client and implemented by the bean class. A session bean can have local
interfaces, remote interfaces, or no interface at all (a no-interface view with local access only).
As shown in Figure 7-2, a client application can access a session bean by one of its interfaces (local or remote) or
directly by invoking the bean class itself.
Figure 7-2. Bean class has several types of business interfaces
Bean Class
A session bean class is any standard Java class that implements business logic. The requirements to develop a session
bean class are as follows:
•
The class must be annotated with @Stateless, @Stateful, @Singleton, or the XML equivalent
in a deployment descriptor.
•
It must implement the methods of its interfaces, if any.
234
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
•
The class must be defined as public, and must not be final or abstract.
•
The class must have a public no-arg constructor that the container will use to create instances.
•
The class must not define the finalize() method.
•
Business method names must not start with ejb, and they cannot be final or static.
•
The argument and return value of a remote method must be legal RMI types.
Remote, Local, and No-Interface Views
Depending from where a client invokes a session bean, the bean class will have to implement remote or local
interfaces, or no interface at all. If your architecture has clients residing outside the EJB container’s JVM instance,
they must use a remote interface. As shown in Figure 7-3, this applies for clients running in a separate JVM
(e.g., a rich client), in an application client container (ACC), or in an external web or EJB container. In this case, clients
will have to invoke session bean methods through Remote Method Invocation (RMI). You can use local invocation
when the bean and the client are running in the same JVM. That can be an EJB invoking another EJB or a web
component (Servlet, JSF) running in a web container in the same JVM. It is also possible for your application to use
both remote and local calls on the same session bean.
Figure 7-3. Session beans invoked by several types of client
A session bean can implement several interfaces or none. A business interface is a standard Java interface that
does not extend any EJB-specific interfaces. Like any Java interface, business interfaces define a list of methods that
will be available for the client application. They can use the following annotations:
•
@Remote: Denotes a remote business interface. Method parameters are passed by value and
need to be serializable as part of the RMI protocol.
•
@Local: Denotes a local business interface. Method parameters are passed by reference from
the client to the bean.
You cannot mark the same interface with more than one annotation. The session beans that you have seen so far
in this chapter have no interface. The no-interface view is a variation of the local view that exposes all public business
methods of the bean class locally without the use of a separate business interface.
235
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
Listing 7-2 shows a local interface (ItemLocal) and a remote interface (ItemRemote) implemented by the ItemEJB
stateless session bean. With this code, clients will be able to invoke the findCDs() method locally or remotely as it is
defined in both interfaces. The createCd() will only be accessible remotely through RMI.
Listing 7-2. Stateless Session Bean Implementing a Remote and Local Interface
@Local
public interface ItemLocal {
List
List
}
@Remote
public interface ItemRemote {
List
List
Book createBook(Book book);
CD createCD(CD cd);
}
@Stateless
public class ItemEJB implements ItemLocal, ItemRemote {
// ...
}
Alternatively to the code in Listing 7-2, you might specify the interface in the bean’s class. In this case, you would
have to include the name of the interface in the @Local and @Remote annotations as shown in Listing 7-3. This is
handy when you have legacy interfaces where you can’t add annotations and need to use them in your session bean.
Listing 7-3. A Bean Class Defining a Remote, Local and No Interface
public interface ItemLocal {
List
List
}
public interface ItemRemote {
List
List
Book createBook(Book book);
CD createCD(CD cd);
}
@Stateless
@Remote(ItemRemote.class)
@Local(ItemLocal.class)
@LocalBean
public class ItemEJB implements ItemLocal, ItemRemote {
// ...
}
236
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
If the bean exposes at least one interface (local or remote) it automatically loses the no-interface view. It then
needs to explicitly specify that it exposes a no-interface view by using the @LocalBean annotation on the bean class.
As you can see in Listing 7-3 the ItemEJB now has a local, remote, and no interface.
Web Services Interface
In addition to remote invocation through RMI, stateless beans can also be invoked remotely as SOAP web services
or RESTful web services. Chapters 14 and 15 are dedicated to web services, so I won’t describe them here. I just want
to show you how a stateless session bean can be accessed in various forms just by implementing different annotated
interfaces. Listing 7-4 shows a stateless bean with a local interface, a SOAP web services endpoint (@WebService), and
a RESTful web service endpoint (@Path). Note that these annotations come, respectively, from JAX-WS (Chapter 14)
and JAX-RS (Chapter 15) and are not part of EJB.
Listing 7-4. A Stateless Session Bean Implementing Several Interfaces
@Local
public interface ItemLocal {
List
List
}
@WebService
public interface ItemSOAP {
List
List
Book createBook(Book book);
CD createCD(CD cd);
}
@Path(/items)
public interface ItemRest {
List
}
@Stateless
public class ItemEJB implements ItemLocal, ItemSOAP, ItemRest {
// ...
}
Portable JNDI Name
JNDI has been around for a long time. Its API is specified and is portable across application servers. But this wasn’t
the case with the JNDI name, which was implementation specific. When an EJB in GlassFish or JBoss was deployed,
the name of the EJB in the directory service was different and thus not portable. A client would have to look up an
EJB using one name for GlassFish, and another name for JBoss. Since EJB 3.1, JNDI names have been specified so the
code could be portable. So now each time a session bean with its interfaces is deployed to the container, each bean/
interface is automatically bound to a portable JNDI name. The Java EE specification defines portable JNDI names with
the following syntax:
java:
237
www.it-ebooks.info
Chapter 7 ■ enterprise JavaBeans
Each portion of the JNDI name has the following meaning:
•
application:
•
global: The java:global prefix allows a component executing outside a Java EE
application to access a global namespace.
•
app: The java:app prefix allows a component executing within a Java EE application to
access an application-specific namespace.
•
module: The java:module prefix allows a component executing within a Java EE
application to access a module-specific namespace.
•
comp: The java:comp prefix is a private component-specific namespace and is not
accessible by other components.
•
the case, the
file extension).
•
EJB module in a stand-alone jar file or a web module in a war file. The
to the base name of the archive with no file extension.
•
•
interface. For the no-interface view, the name can be the fully qualified bean class name.
To illustrate this naming convention, let’s take the example of an ItemEJB (defined in Listing 7-5), which has
a remote interface, a local interface, and a no-interface view (using the @LocalBean annotation). All these classes and
interfaces belong to the org.agoncal.book.javaee7 package. ItemEJB is the
cdbookstore.jar (the
Listing 7-5. A Stateless Session Bean Implementing Several Interfaces
package org.agoncal.book.javaee7;
@Stateless
@Remote(ItemRemote.class)
@Local(ItemLocal.class)
@LocalBean
public class ItemEJB implements ItemLocal, ItemRemote {
// ...
}
Once deployed, the container will create three JNDI names so an external component will be able to access the
ItemEJB using the following global JNDI names:
java:global/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemRemote
java:global/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemLocal
java:global/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemEJB
238
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
Note that, if the ItemEJB was deployed within an ear file (e.g., myapplication.ear), you would have to use the
java:global/myapplication/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemRemote
java:global/myapplication/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemLocal
java:global/myapplication/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemEJB
The container is also required to make JNDI names available through the java:app and java:module
namespaces. So a component deployed in the same application as the ItemEJB will be able to look it up using the
following JNDI names:
java:app/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemRemote
java:app/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemLocal
java:app/cdbookstore/ItemEJB!org.agoncal.book.javaee7.ItemEJB
java:module/ItemEJB!org.agoncal.book.javaee7.ItemRemote
java:module/ItemEJB!org.agoncal.book.javaee7.ItemLocal
java:module/ItemEJB!org.agoncal.book.javaee7.ItemEJB
This portable JNDI name can be applied to all session beans: stateless, stateful, and singleton.
Stateless Beans
In Java EE applications, stateless beans are the most popular session bean components. They are simple, powerful,
and efficient and respond to the common task of doing stateless business processing. What does stateless mean? It
means that a task has to be completed in a single method call.
As an example, we can go back to the roots of object-oriented programming where an object encapsulates its
state and behavior. In object modeling, to persist a book to a database, you would do something like this: create an
instance of a Book object (using the new keyword), set some values, and call a method so it could persist itself to a
database (book.persistToDatabase()). In the following code, you can see that, from the very first line to the last one,
the book object is called several times and keeps its state:
Book book = new Book();
book.setTitle("The Hitchhiker's Guide to the Galaxy");
book.setPrice(12.5F);
book.setDescription("Science fiction comedy series created by Douglas Adams.");
book.setIsbn("1-84023-742-2");
book.setNbOfPage(354);
book.persistToDatabase();
In a service architecture, you would delegate the business logic to an external service. Stateless services are
ideal when you need to implement a task that can be concluded with a single method call (passing all the needed
parameters). Stateless services are independent, are self-contained, and do not require information or state from
one request to another. So, if you take the preceding code and introduce a stateless service, you need to create a Book
object, set some values, and then use a stateless service to invoke a method that will persist the book on its behalf,
in a single call. The state is maintained by Book but not by the stateless service:
Book book = new Book();
book.setTitle("The Hitchhiker's Guide to the Galaxy");
book.setPrice(12.5F);
book.setDescription("Science fiction comedy series created by Douglas Adams.");
239
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
book.setIsbn("1-84023-742-2");
book.setNbOfPage(354);
statelessService.persistToDatabase(book);
Stateless session beans follow the stateless service architecture and are the most efficient component model
because they can be pooled and shared by several clients. This means that, for each stateless EJB, the container keeps
a certain number of instances in memory (i.e., a pool) and shares them between clients. Because stateless beans have
no client state, all instances are equivalent. When a client invokes a method on a stateless bean, the container picks up
an instance from the pool and assigns it to the client. When the client request finishes, the instance returns to the pool
to be reused. This means you need only a small number of beans to handle several clients, as shown in Figure 7-4.
The container doesn’t guarantee the same instance for the same client.
Figure 7-4. Clients accessing stateless beans in a pool
Listing 7-5 shows what a stateless EJB could look like: a standard Java class with just a single @Stateless
annotation. Because it lives in a container, it can use any container-managed service, one of which is dependency
injection. We use the @PersistenceContext annotation to inject a reference of an entity manager. For stateless session
beans, the persistence context is transactional, which means that any method invoked in this EJB (createBook(),
createCD(), etc.) is transactional. Chapter 9 explains this process in more detail. Notice that all methods have the
needed parameters to process business logic in one single call. For example, the createBook() method takes a Book
as a parameter and persists it without relying on any other state.
Listing 7-5. Stateless Session Bean ItemEJB
@Stateless
public class ItemEJB {
@PersistenceContext(unitName = "chapter07PU")
private EntityManager em;
public List
TypedQuery
return query.getResultList();
}
240
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
public List
TypedQuery
return query.getResultList();
}
public Book createBook(Book book) {
em.persist(book);
return book;
}
public CD createCD(CD cd) {
em.persist(cd);
return cd;
}
}
Stateless session beans often contain several closely related business methods. For example, the ItemEJB bean in
Listing 7-5 defines methods related to items sold by the CD-BookStore application. So you will find methods to create,
update, or find books and CDs, as well as other related business logic.
The @Stateless annotation marks the ItemEJB POJO as a stateless session bean, thus turning a simple Java class
into a container-aware component. Listing 7-6 describes the specification of the @javax.ejb.Stateless annotation.
Listing 7-6. @Stateless Annotation API
@Target({TYPE}) @Retention(RUNTIME)
public @interface Stateless {
String name() default "";
String mappedName() default "";
String description() default "";
}
The name parameter specifies the name of the bean and by default is the name of the class (ItemEJB in the
example in Listing 7-5). This parameter can be used to look up an EJB with JNDI, for example. The description
parameter is a String that can be used to describe the EJB. The mappedName attribute is the global JNDI name assigned
by the container. Note that this JNDI name is vendor specific and is therefore not portable. mappedName has no
relationship with the portable global JNDI name, which I described earlier.
Stateless session beans can support a large number of clients, minimizing any needed resources. Having stateless
applications is one way to improve scalability (as the container doesn’t have to store and manage state).
Stateful Beans
Stateless beans provide business methods to their clients but don’t maintain a conversational state with them. Stateful
session beans, on the other hand, preserve conversational state. They are useful for tasks that have to be done in
several steps, each of which relies on the state maintained in a previous step. Let’s take the example of a shopping
cart in an e-commerce web site. A customer logs on (her session starts), chooses a first book, adds it to her shopping cart,
chooses a second book, and adds it to her cart. At the end, the customer checks out the books, pays for them, and logs out
241
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
(the session ends). The shopping cart keeps the state of how many books the customer has chosen throughout the
interaction (which can take some time, specifically the time of the client’s session). This interaction with a stateful
component could be written as follows:
Book book = new Book();
book.setTitle("The Hitchhiker's Guide to the Galaxy");
book.setPrice(12.5F);
book.setDescription("Science fiction comedy series created by Douglas Adams.");
book.setIsbn("1-84023-742-2");
book.setNbOfPage(354);
statefulComponent.addBookToShoppingCart(book);
book.setTitle("The Robots of Dawn");
book.setPrice(18.25F);
book.setDescription("Isaac Asimov's Robot Series");
book.setIsbn("0-553-29949-2");
book.setNbOfPage(276);
statefulComponent.addBookToShoppingCart(book);
statefulComponent.checkOutShoppingCart();
The preceding code shows exactly how a stateful session bean works. Two books are created and added to
a shopping cart of a stateful component. At the end, the checkOutShoppingCart() method relies on the maintained
state and can check out the two books.
When a client invokes a stateful session bean in the server, the EJB container needs to provide the same instance
for each subsequent method invocation. Stateful beans cannot be reused by other clients. Figure 7-5 shows the
one-to-one correlation between a bean instance and a client. As far as the developer is concerned, no extra code is
needed, as the EJB container automatically manages this one-to-one correlation.
Figure 7-5. Clients accessing stateful beans
The one-to-one correlation comes at a price because, as you might have guessed, if you have one million clients, you
will get one million stateful beans in memory. To avoid such a big memory footprint, the container temporarily clears
stateful beans from memory before the next request from the client brings them back. This technique is called passivation
and activation. Passivation is the process of removing an instance from memory and saving it to a persistent location
(a file on a disk, a database, etc.). It helps you to free memory and release resources (a database or JMS connections, etc.).
Activation is the inverse process of restoring the state and applying it to an instance. Passivation and activation are
done automatically by the container; you shouldn’t worry about doing it yourself, as it’s a container service. What you
should worry about is freeing any resource (e.g., database connection, JMS factories connection, etc.) before the bean
is passivated. Since EJB 3.2, you can also disable passivation as you’ll see in the next chapter with life-cycle and callback
annotations.
242
www.it-ebooks.info
Chapter 7 ■ Enterprise JavaBeans
Let’s return to the shopping-cart example and apply it to a stateful bean (see Listing 7-7). A customer logs on to
a web site, browses the catalog of items, and adds two books to the shopping cart (addItem() method). The cartItems
attribute holds the content of the cart. Then the customer decides to get a coffee at a coffee machine. During this time,
the container might passivate the instance to free some memory, which in turn saves the shopping content to permanent
storage. A few minutes later, the customer comes back and wants to know the total price (getTotal() method) of his
shopping cart before buying anything. The container activates the EJB and restores the data to the shopping cart.
The customer can then check out (checkout() method) and buy the books. Once the customer logs off, the customer’s
session ends, and the container frees memory by permanently removing the instance of the stateful bean.
Listing 7-7. Stateful Session Bean ShoppingCartEJB
@Stateful
@StatefulTimeout(value = 20, unit = TimeUnit.SECONDS)
public class ShoppingCartEJB {
private List
public void addItem(Item item) {
if (!cartItems.contains(item))
cartItems.add(item);
}
public void removeItem(Item item) {
if (cartItems.contains(item))
cartItems.remove(item);
}
public Integer getNumberOfItems() {
if (cartItems == null || cartItems.isEmpty())
return 0;
return cartItems.size();
}
public Float getTotal() {
if (cartItems == null || cartItems.isEmpty())
return 0f;
Float total = 0f;
for (Item cartItem : cartItems) {
total += (cartItem.getPrice());
}
return total;
}
public void empty() {
cartItems.clear();
}
@Remove
public void checkout() {
// Do some business logic
cartItems.clear();
}
}
www.it-ebooks.info
243