[ Team LiB ] |
9.5 Enterprise JavaBeansRMI is a distributed object API. It specifies how to write objects so that they can talk to one another no matter where on the network they are found. I could write dozens of business objects that can, in principal, talk to your business objects using RMI. At its core, however, RMI is nothing more than an API to which your distributed objects must conform. RMI says nothing about other characteristics normally required of an enterprise-class distributed environment. For example, it says nothing about how a client might perform a search for RMI objects matching some criteria. It also says nothing about how those objects might work together to construct a single transaction. What is missing from the picture is a distributed component model. A component model is a standard that defines how components are written so that systems can be built from components by different authors with little or no customization. You may be familiar with the JavaBeans component model. It is a component model that defines how you write user interface components so that they may be plugged into third-party applications. The magic thing about JavaBeans is that there is very little API behind the specification—you neither implement nor extend any special classes and you need call no special methods. The force of JavaBeans is largely in conformance with an established naming convention. Enterprise JavaBeans is a more complex extension of this concept. While there are API elements behind Enterprise JavaBeans, it is much more than just an API. It is a standard way of writing distributed components so that the components I write can be used with the components you write in someone else's system. RMI does not support this ability for several reasons. Consider all of the following issues RMI does not address:
Enterprise JavaBeans addresses all of these points and more so that you can literally pick and choose the best designed business components from different vendors and make them work and play well with one another in the same environment. EJB is now the standard component model for capturing distributed business components. It hides from you the details you might have to worry about yourself if you were writing an RMI application. 9.5.1 EJB RolesOne of the benefits of the EJB approach is that it separates different application development roles into distinct parts so that everything one role does is usable by any possible player of any of the other roles. EJB specifically defines the following roles:[5]
An EJB provider captures each of the business components that model a business in Java code. The EJB specification breaks down each of these business components into three pieces: the home interface, the remote interface, and the bean implementation. Your job as the EJB provider is thus to write these three classes for each business component in your system. 9.5.2 Kinds of BeansEJB specifies two kinds of beans: entity beans and session beans. The distinction between the two is that entity beans are persistent and session beans are transient. In other words, entity beans save their states across time while session beans do not. Most business concepts will work best as entity beans—they are the entities that make up your business. Entity beans are shared by all clients.[6]
Session beans are unique to each client. They come to life only when requested by a client. When that client is done with them, they go away. An example of a session bean might be a Registration class that represents the registration of a person for some event. The Registration exists for a specific client to associate a person with an event. It manages the business logic associated with a registration, but it goes away once the registration is complete. The persistent data is in the Person and Event classes.
The home and remote interfaces for both kinds of beans are RMI remote interfaces. That is, they are indirectly derived from the java.rmi.Remote interface and are exported for remote access. The class extended by a remote interface is EJBObject. If, for example, you wanted to turn the Ball object from earlier in the chapter into an entity bean, you would create a BallHome interface, a Ball interface, and a BallBean implementation. The Ball interface would extend javax.ejb.EJBObject, which in turn extends java.rmi.Remote. The result might be a class that looks like this: public interface Ball extends javax.ejb.EJBObject { void hit( ) throws java.rmi.RemoteException; int getPosition( ) throws RemoteException; } This interface looks a lot like the RMI example from earlier in the chapter. In fact, the only difference is that this one extends Remote indirectly via EJBObject. The interface specifies only those methods that should be made available to the rest of the world. The home interface is where you go to find or create instances of the bean. It specifies different versions of create( ) or findXXX( )[7] methods that enable a client to create new instances of the bean or find existing instances. If you think about the problem of a banking system, they might have account beans, customer beans, and teller beans. When the bank attracts a new customer, its enterprise banking system needs to create a Customer bean to represent that customer. The bank manager's Windows application that enables the registration of new customers might have the following code for creating a new Customer bean:
InitialContext ctx = new InitialContext( ); CustomerHome custhome; Customer cust; custhome = (CustomerHome)ctx.lookup("CustomerHome"); cust = custhome.create(ssn); This code provides you with your first look at JNDI support in EJB. Using a JNDI initial context, you look up an implementation of the customer bean's home interface. That home interface, CustomerHome, provides a create( ) method that enables you to create a new Customer bean. In the preceding case, the create( ) method accepts a String representing the customer's Social Security number.[8] The EJB specification requires that a home interface specify create( ) signatures for each way to create an implementation of that bean. The CustomerHome interface might look like this:
public interface CustomerHome extends EJBHome { Customer create( ) throws CreateException, RemoteException; Customer create(String ssn) throws CreateException, RemoteException; Customer findByPrimaryKey(CustomerKey pk) throws FinderException, RemoteException; Customer findBySocialSecurityNumber(String ssn) throws FinderException, RemoteException; } The finder methods provide ways to look up Customer objects. All except the findByPrimaryKey( ) method can be named however you wish to name them, and they should return either a remote reference to the bean in question or a Collection. The findByPrimaryKey( ) method is a special finder for EJBs. Each entity bean instance has a primary key that uniquely identifies it. The primary key can be any serializable Java class you write. The only requirement is that the class must implement the equals( ) and hashCode( ) in an appropriate fashion. For example, if your beans have a unique numeric identifier, you might create your own CustomerKey class that stores the identifier as a long. If you do the latter, your CustomerKey class should look something like the following: public class CustomerKey implements Serializable { private long objectID = -1L; public CustomerKey(long l) { objectID = l; } public boolean equals(Object other) { if( other instanceof CustomerKey ) { return ((CustomerKey)other).objectID = = objectID; } return false; } public int hashCode( ) { return (new Long(objectID)).hashCode( ); } } You can even use primitive wrapper classes instead of custom primary key classes. The following example, for instance, could just as easily have used the Long class for its primary keys. You do not actually write the class that implements the Customer or CustomerHome interfaces—that is the task of the EJB container. Generally, the EJB container will have tools that enable a deployer to automatically create and compile implementation classes for the home and remote interfaces. These automatically generated classes handle issues such as security and then delegate to your bean implementation class. The bean is where you write your business logic. The bean class must implement the following methods:
Consider that the Customer remote interface for the previous home interface looks like this: public interface Customer extends EJBObject { String getSocialSecurityNumber( ) throws RemoteException; } A skeleton of the bean implementation might look something like this (minus the method bodies): public class CustomerBean implements EntityBean { private transient EntityContext context = null; private String ssn = null; public void ejbActivate( ) throws RemoteException { // you will mostly leave this method empty // activation of resources required by // an object of this type independent of the // customer it represents belong here // an example might be opening a file handle // for logging } public CustomerKey ejbCreate( ) throws CreateException { // this method creates a primary key for the // customer and inserts the customer into the // database } public CustomerKey ejbCreate(String ssn) throws CreateException { // this method works the same as ejbCreate( ) } public CustomerKey ejbFindByPrimaryKey(CustomerKey pk) throws FinderException, RemoteException { // this method goes to the database and performs // a SELECT and returns the PK if it is in the // database } public CustomerKey ejbFindBySocialSecurityNumber(String ssn) throws FinderException { // this method goes to the database and performs // a SELECT and returns the PK of the row // with a matching SSN } public void ejbLoad( ) throws RemoteException { // this method goes to the database and selects // the row that has this object's primary key // and then populates this object's fields } public void ejbPassivate( ) throws RemoteException { // this method is generally empty // you should release any system resources held // by this object here } public void ejbPostCreate( ) { // this is called to let you do any initialization // for this object after ejbCreate( ) is called and // a primary key is assigned to the object } public void ejbRemove( ) throws RemoteException { // this method goes to the database and deletes // the record with a primary key matching this // object's primary key } public void ejbStore( ) throws RemoteException { // this method goes to the database and saves // the state of this bean } public String getSocialSecurityNumber( ) { // this method is from the Customer remote interface return ssn; } public void setEntityContext(EntityContext ctx) throws RemoteException { // this method assigns an EntityContext to the // bean context = ctx; } public void unsetEntityContext( ) throws RemoteException { // this method removes the EntityContext assignment context = null; } } JDBC comes into play under the bean-managed persistence model in the ejbCreate( ), ejbFindXXX( ), ejbLoad( ), ejbStore( ), and ejbRemove( ) methods. Chapter 6 describes the details of bean-managed persistence. Under container-managed persistence, you do not worry about any persistence issues. EJB supports two distinct container-managed persistence models. The old model, EJB 1.x persistence, did not work well at all. The newer model, EJB 2.x persistence, is very promising though not yet widely implemented in production systems. Chapter 5 covers container-managed persistence. The book Enterprise JavaBeans (O'Reilly) by Richard Monson-Haefel contains a more complete discussion of EJB development. |
[ Team LiB ] |