Container Managed Models-Enterprise JavaBeans - Part 5
05 Dec 1998Container-Managed-Models
Developing an N-tier EJB Application - Developing Entity Beans
To develop the virtual Horses and Stable online bookstore example, the entity beans must be developed first. We develop an inventory entity bean to use later to build the shopping cart. Our inventory entity bean has methods to get and set the attributes of the various book titles our bookstore sells. It can also be queried for the price or other information on any given ISBN symbol.
Typically, when developing an entity bean, the steps to follow are as follows:
- Set up the data source.
- Define the EJB remote interface.
- Define the EJB home interface.
- Define the primary key class.
- Implement the enterprise bean.
- Compile the EJB classes.
- Declare the security and deployment properties.
- Generate the container classes using the tools supplied by the EJB server/container vendor, register the factory with the server, and deploy it.
- Write the client code.
- Compile the client code.
- Start the client.
Now we’ll examine each of these steps in greater detail:
Set Up Your Data Source
The example program requires a database table named BOOKS. The DML for the creation of such a table is given in Listing 5:
CREATE TABLE BOOKS
CODE Number
TITLE Text
AUTHORS Text
PRICE Currency
DISCOUNT Number
*primary key (CODE)
Listing 5: The DML for the BOOKS Table
Define Your EJB Home Interface
EJB clients do not work directly with the entity bean to create rows or access data but, rather, first create an instance of the home interface. The home interface extends javax.ejb.EJBHome and has methods that define how the entity bean is created and located in its container. It also follows the rules of RMI in that the arguments and return types for each method must be serializable and the methods should throw java.rmi.RemoteException as one of its exceptions. Listing 6 shows the BooksHome interface definition.
package com.gopalan.Shop.Books;
import javax.ejb.*;
import java.rmi.*;
public interface BooksHome extends EJBHome {
Books create (int isbn, String title, String authors,
double price, int discount)
throws CreateException, RemoteException;
Books findByPrimaryKey (BooksPK book)
throws FinderException, RemoteException;
}
Listing 6: The BooksHome interface definition
The create() method corresponds to the ejbCreate() method in the entity bean. The parameter sets of the two methods are identical. When the client calls BooksHome.create(), the container allocates an instance of the entity bean and calls BooksHome.ejbCreate(). For container-managed entity beans, ejbCreate()returns a void, unlike in the case of bean-managed persistence, where it returns a primary key object. The first thing a client does is to locate the home object for the required bean using JNDI. The BooksHome interface contains create() methods that will be invoked whenever a client requests a new bean. Note, this method is implemented in the EJBHome implementation and calls the ejbCreate() method in the bean class when invoked.
Define Your EJB Remote Interface
When the home interface implementation is instantiated, the EJB server also creates the remote interface implementation and enterprise bean instances. The methods in this interface are the external interfaces of BooksBean. The signatures of the methods in this external interface are identical to those of the bean, except these methods throw a java.rmi.RemoteException. The entity bean does not directly implement this interface. The corresponding container-generated EJBObject code implements this interface and delegates to the enterprise bean. The EJBObject acts as a proxy, passing method invocations through to the bean instance installed in the server. The remote interface is shown in Listing 7:
package com.gopalan.Shop.Books;
import javax.ejb.*;
import java.rmi.*;
public interface Books extends EJBObject {
public int getIsbn () throws RemoteException;
public String getTitle () throws RemoteException;
public String getAuthors () throws RemoteException;
public double getPrice () throws RemoteException;
public int getDiscount () throws RemoteException;
public double getDiscountedPrice () throws RemoteException;
public void setPrice (double cost) throws RemoteException;
public void setDiscount (int disc) throws RemoteException;
}
Listing 7: The Books remote interface definition
Notice the remote interface Books extends the javax.ejb.EJBObject and declares the BooksBean‘s get/set methods. It also follows the rules of RMI in that the arguments and return types for each method must be Serializable and the methods should throw java.rmi.RemoteException as one of its exception.
Define Your Primary Key Class
The EJB server requires an entity bean to have a primary key class with a public primary key data member (or data members, if using composite primary keys). You can have the container manage an enterprise bean or write the code to manage the bean yourself. In this example, the bean is container-managed. Listing 8 shows the BooksPK Primary Key Class definition.
package com.gopalan.Shop.Books;
public class BooksPK implements java.io.Serializable {
public int code;
public BooksPK () {
}
public BooksPK (int isbn) {
code = isbn;
}
}
Listing 8: The BooksPK Primary Key class definition
The primary key class is shown in the previous example. The primary key in the BOOKS table is CODE and so code is defined as a public data member in this class, which is assigned a value when the class is constructed.
Implement the Entity Bean
The enterprise bean implements javax.ejb.EntityBean and the developer-defined interface methods. It should follow the rules of RMI in that the arguments and return types for each method must be serializable and the methods should throw java.rmi.RemoteException as one of its exceptions. Listing 8.9 shows the BooksBean enterprise bean implementation
package com.gopalan.Shop.Books;
import java.rmi.*;
import javax.ejb.*;
public class BooksBean implements EntityBean {
EntityContext entityContext;
public int code; // CODE
public String title; // TITLE
public String authors; // AUTHORS
public double price; // PRICE
public int discount; // DISCOUNT
// Implementation for all the Remote Interface business methods
public int getIsbn () throws RemoteException {
return code;
}
public String getTitle () throws RemoteException {
return title;
}
public String getAuthors () throws RemoteException {
return authors;
}
public double getPrice () throws RemoteException {
return price;
}
public int getDiscount () throws RemoteException {
return discount;
}
public double getDiscountedPrice () throws RemoteException {
double deduct = ((double)discount)/100;
return ( price*(1-deduct) );
}
public void setPrice (double cost) throws RemoteException {
price = cost;
}
public void setDiscount (int disc) throws RemoteException {
discount = disc;
}
// Implementation for all the Home Interface methods
public void ejbCreate (int isbn, String bookTitle, String author,
double cost, int disc)
throws CreateException, RemoteException {
code = isbn;
title = bookTitle;
authors = author;
price = cost;
discount= disc;
}
public void ejbPostCreate (int isbn, String bookTitle, String author,
double cost, int disc) {
}
// Implement all the mandatory methods required by the EJB Spec
public void ejbActivate () throws RemoteException{}
public void ejbLoad () throws RemoteException{}
public void ejbPassivate () throws RemoteException{}
public void ejbRemove () throws RemoteException, RemoveException{}
public void ejbStore () throws RemoteException{}
public void setEntityContext (EntityContext context)
throws RemoteException {
entityContext = context;
}
public void unsetEntityContext () throws RemoteException {
entityContext = null;
}
}
Listing 9: The BooksBean enterprise bean implementation class
Much of the previous code should be self-explanatory. Notice all the business methods that constitute the remote interface and the creation and find methods that constitute the home interface have been implemented in the enterprise Bean.
Compile Your EJB Classes
You can now compile all the sources you developed. Go ahead and compile the code.
javac *.java
Declare the Deployment Descriptors
From here on, whatever is discussed is specific to the particular EJB server implementation. Iona’s HomeBase server requires we specify our deployment descriptors in XML. (You can get information on XML from any good book on the subject. A detailed explanation of XML is beyond the scope of this chapter.) These properties are used both in the container class generation and in deployment. We need to define the various properties that help you to generate the container classes. In a properties file (call it Shop.ejbml), type in the Listing 10:
<!---------------------------------------------------------->
<!------- Books Entity Bean ---------------->
<!---------------------------------------------------------->
<ejbml>
<entity-bean
name="Books"
descriptor="Books/BooksDeployment"
package="com.gopalan.Shop.Books"
home="com.gopalan.Shop.Books.BooksHome"
remote="com.gopalan.Shop.Books.Books"
bean="com.gopalan.Shop.Books.BooksBean"
primary-key="com.gopalan.Shop.Books.BooksPK"
tx-attribute="TX_SUPPORTS"
>
<property
name="databasePassword"
value="mouse"
/>
<property
name="dataSourceName"
value="Shop"
/>
<property
name="databaseUser"
value="user"
/>
<property
name="databaseTable"
value="books"
/>
<container-managed
storage-helper=
"com.ejbhome.generator.helpers.RelationalPersistenceCodeHelper"
table="books"
data-source="Shop"
user="user"
password="mouse"
>
<field name="discount" />
<field name="price" />
<field name="authors" />
<field name="title" />
<field name="code" />
</container-managed>
</entity-bean>
</ejbml>
Listing 10: The Deployment Descriptor for the Books EJB
The first few lines specify the name of our home and remote interfaces and that this is an entity bean. We also declare our Primary Key class in this file. Because this is a container-managed bean, we specify the fields in the database that are container managed. We also specify the enterprise bean supports transactions (TX_SUPPORTS) in its transaction attributes.
The different transaction attributes available that can be specified are shown in Table 3
TX_BEAN_MANAGED |
Enterprise bean starts and ends a transaction. It may use the javax.jts.UserTransaction interface to demarcate transaction boundaries. |
TX_MANDATORY |
Caller must start transaction. The bean is always invoked in the scope of the client’s transaction. If the client does not have one, a javax.transaction.TransactionRequiredException exception is raised. |
TX_REQUIRED |
Enterprise bean requires a transaction. If the client is associated with a transaction context, the bean is invoked in the same context. Otherwise, the container starts a new transaction before invoking methods on the bean and commits the transaction before returning from them. |
TX_REQUIRES_NEW |
Enterprise bean requires a new transaction be created for each method call. |
TX_NOT_SUPPORTED |
Caller’s transaction will be suspended before calling the enterprise bean. The bean is invoked without transactional scope. |
TX_SUPPORTS |
The caller’s transaction is simply passed on by the EJB Container. If the caller does not have a transaction context, the bean methods are invoked without a transaction context. |
Because the enterprise bean is container managed, we specify the fields that are container managed so the tools can generate appropriate JDBC code. We then specify the data source name, the table name, the user name, and the password, which may be helpful in any JDBC calls the enterprise bean makes.
Register and Deploy the Bean
This section is again specific to the EJB Server/container you are using. The EJBHome server defines a couple of files datasource.properties and ejbhome.properties in a directory named conf.
You need to modify the datasource.properties file to look Listing 11:
# This is a list of datasource names with their respective JDBC databases URLs.
#
# For example:
#
# To map a datasource name of jdbc/Inventory to an Oracle lite database called test,
# you would use the following:
#
# jdbc/Inventory=jdbc:polite:test
#
Books=jdbc:odbc:Books,user=user,password=mouse
Music=jdbc:odbc:Books,user=user,password=mouse
Listing 11: The datasource definition for deployment on HomeBase
Generate the Container Classes
The next step is to generate the container classes using the tools supplied by the EJB server/container vendor. When the entity and session beans are deployed, a number of container source and class files are generated with the following prefixes. A container is a set of classes generated by the deployment tool that manages an enterprise bean’s persistence, transactional properties, and security. Now that we have our Shop.ejbml ready, we can generate and compile the container classes. For this, we need to execute the HomeBase server’s deployer tool. We do this as follows:
java com.ejbhome.Deployer .\conf\Shop.ejbml
The following listing shows what our screen console would look like while we compile and deploy our files. Our commands are shown in boldface font.
E:\>javac cup\chap8\Shop\Books\*.java
E:\>java com.ejbhome.Deployer .\conf\Shop.ejbml
EJBHome EJB Deployer version 0.5.1 (Scunthorpe)
(c) Copyright IONA Technologies PLC 1999. All Rights Reserved.
Windows NT x86 4.0
A nonfatal internal JIT (3.00.072b(x)) error 'regvarHI' has occurred in :
'com/ejbhome/Deployer.<init> (Ljava/util/Properties;Ljava/util/Vector;)V': Interpreting method.
Please report this error in detail to http://java.sun.com/cgi-bin/bugreport.cgi
Deploying: Books...
Generating: IonaBooksHome...done.
Generating: IonaRemoteBooks...done.
Generating: IonaBooksBean...done.
Generating: IonaBooksContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaBooksHome.java...done.
Compiling: IonaRemoteBooks.java...done.
Compiling: IonaBooksBean.java...done.
Compiling: IonaBooksContext.java...done.
Compiling: IonaBooksHome_Stub.java...done.
Compiling: IonaBooksHome_Skel.java...done.
Compiling: IonaRemoteBooks_Stub.java...done.
Compiling: IonaRemoteBooks_Skel.java...done.
Deploying: Music...
Generating: IonaMusicHome...done.
Generating: IonaRemoteMusic...done.
Generating: IonaMusicBean...done.
Generating: IonaMusicContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaMusicHome.java...done.
Compiling: IonaRemoteMusic.java...done.
Compiling: IonaMusicBean.java...done.
Compiling: IonaMusicContext.java...done.
Compiling: IonaMusicHome_Stub.java...done.
Compiling: IonaMusicHome_Skel.java...done.
Compiling: IonaRemoteMusic_Stub.java...done.
Compiling: IonaRemoteMusic_Skel.java...done.
Deploying: Cart...
Generating: IonaCartHome...done.
Generating: IonaRemoteCart...done.
Generating: IonaCartBean...done.
Generating: IonaCartContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaCartHome.java...done.
Compiling: IonaRemoteCart.java...done.
Compiling: IonaCartBean.java...done.
Compiling: IonaCartContext.java...done.
Compiling: IonaCartHome_Stub.java...done.
Compiling: IonaCartHome_Skel.java...done.
Compiling: IonaRemoteCart_Stub.java...done.
Compiling: IonaRemoteCart_Skel.java...done.
E:\>
You can now start up the server with the command:
java com.ejbhome.Server
Write Your Client Code
The client code for the entity bean is listed in the following. The client generally goes ahead and creates a set of records in the database and modifies the prices.
The client first does a Naming.lookup()and gets a reference to the BooksHome interface. It then goes about creating a whole bunch of entity bean instances and these records are created in the database. The client also exercises the various business methods of our ShopBean like getting and setting the prices of the book titles. Listing 12 shows the BooksTest application, client to the Books entity bean.
package com.gopalan.Shop.Books;
import java.rmi.*;
import java.util.*;
public class BooksTest {
static final int NUMBOOKS = 4;
public static void main(String[] args) throws Exception {
BooksHome home = (BooksHome)Naming.lookup ("Books");
System.out.println ( "Naming.lookup successful..." );
if (home == null) {
System.out.println( "null BooksHome returned..." );
}
String title = "Book";
String author= "Author";
Vector v = new Vector ();
for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
System.out.println ("ISBN = " +(i+1) +
" Book = " +title+(i+1) +
" Author = " +author+(i+1) +
" Creating home.create...");
v.addElement (home.create ((i+1),title+(i+1),
author+(i+1),100.00,10));
}
for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
Books books= (Books) (v.elementAt (i));
books.setPrice (books.getPrice ()+1);
System.out.println ("Final Price of " + books.getTitle () +
" is "+ books.getPrice ());
}
System.out.println ("Books.setBooksPrice successful...");
for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
Books books= (Books)(home.findByPrimaryKey (new BooksPK (i+1)));
books.setPrice (books.getPrice ()+1);
System.out.println ("Final Price of " + books.getTitle () +
" is "+ books.getPrice ());
}
System.out.println ("Books.findByPrimaryKey successful...");
}
}
Listing 12: The BooksTest entity bean client application
Compile and run the client. The client will create four records in the database. When session beans are discussed, you see how to model the session bean to be a client to our entity bean.