Chapter 25. Exercises for Chapter 7

Exercise 7.1: Entity Relationships in CMP 2.0, Part 1

This exercise walks you through implementing a complex set of interrelated entity beans defined in Chapter 7 of the EJB book.

Start Up JBoss

If JBoss is not running, start it up. If it’s already running, there’s no reason to restart it.

Initialize the Database

The database table for this exercise will automatically be created in JBoss’s default database, HypersonicSQL, when the EJB JAR is deployed.

Build and Deploy the Example Programs

Perform the following steps:

  1. Open a command prompt or shell terminal and change to the ex07_1 directory created by the extraction process

  2. Set the JAVA_HOME and JBOSS_HOME environment variables to point to where your JDK and JBoss 4.0 are installed. Examples:

    Windows:C:workbookex07_1> set JAVA_HOME=C:jdk1.4.2 C:workbookex07_1> set JBOSS_HOME=C:jboss-4.0
    Unix:$ export JAVA_HOME=/usr/local/jdk1.4.2 $ export JBOSS_HOME=/usr/local/jboss-4.0
  3. Add ant to your execution path.

    Windows:C:workbookex07_1> set PATH=..antin;%PATH%
    Unix:$ export PATH=../ant/bin:$PATH
  4. Perform the build by typing ant.

As in the last exercise, you will see titan.jar rebuilt, copied to the JBoss deploy directory, and redeployed by the application server.

Examine the JBoss-Specific Files

This chapter introduces no new features in JBoss-specific files. Please review Exercise 6.1 to understand the JBoss-specific files in this example. Also, this chapter implements nonperformance-tuned entity beans and relies on the CMP 2.0 engine to create all database tables. To learn about JBoss’s extensive configuration options, please review the advanced CMP 2.0 documentation at http://www.jboss.org.

Examine and Run the Client Applications

From this chapter on, we no longer use remote entity bean interfaces (so the example code matches the code illustrated in the EJB section of this book). Accordingly, the Customer EJB switches to local-only interfaces:

  • CustomerHomeRemote becomes CustomerHomeLocal.

  • CustomerRemote becomes CustomerLocal.

  • Bean interface methods no longer throw RemoteExceptions.

  • The ejb-jar.xml descriptor changes to use local interfaces. Thus:

    <ejb-name>CustomerEJB</ejb-name>
    <home>com.titan.customer.CustomerHomeRemote</home>
    <remote>com.titan.customer.CustomerRemote</remote>
    <ejb-class>com.titan.customer.CustomerBean</ejb-class>

    changes to:

    <ejb-name>CustomerEJB</ejb-name>
    <local-home>com.titan.customer.CustomerHomeLocal</local-home>
    <local>com.titan.customer.CustomerLocal</local>
    <ejb-class>com.titan.customer.CustomerBean</ejb-class>
  • The JNDI binding in jboss.xml changes as well. Thus:

    <entity>
      <ejb-name>CustomerEJB</ejb-name>
      <jndi-name>CustomerHomeRemote</jndi-name>
    </entity>

    changes to:

    <entity>
      <ejb-name>CustomerEJB</ejb-name>
      <local-jndi-name>CustomerHomeLocal</local-jndi-name>
    </entity>

Because interfaces are now local, the example programs no longer need to use dependent value classes to set up relationships like Customer-Address. This change simplifies the code and allows you to pass local entity beans such as Address, Credit Card, and Phone to Customer EJB methods directly.

Another consequence is that remote clients can no longer invoke business logic on the entity beans implemented in this chapter. Instead, you’ll implement all example business logic in the methods of a stateless session bean. Also, EJB containers don’t allow the manipulation of a relationship collection (including iteration through the collection) outside the context of a transaction. In JBoss, all bean methods are Required by default, so all example test code will run within a transaction. Chapter 16 in the EJB book discusses transactions in more detail.

To execute these examples from the command line, implement separate, distinct remote clients that get a reference to the stateless test bean and invoke the appropriate test method.

Client_71a

The Client_71a example program reveals the unidirectional relationship between Customer and Address. The business logic for this example is implemented in com.titan.test.Test71Bean in the test71a( ) method.

In test71a( ), output is written to the PrintWriter created below. The method finishes by extracting a String from the PrintWriter and passing it back to the remote client for display:

public String test71a( ) throws RemoteException
{
   String output = null;
   StringWriter writer = new StringWriter( );
   PrintWriter out = new PrintWriter(writer);
   try
   {

The first part of test71a( ) simply fetches the home interfaces of Customer and Address from JNDI. It then creates both a Customer and an Address:

InitialContext jndiContext = getInitialContext( );
Object obj = jndiContext.lookup("CustomerHomeLocal");
CustomerHomeLocal customerhome = (CustomerHomeLocal)obj; 

obj = jndiContext.lookup("AddressHomeLocal");
AddressHomeLocal addresshome = (AddressHomeLocal)obj; 

out.println("Creating Customer 71");
      
Integer primaryKey = new Integer(71);
CustomerLocal customer = customerhome.create(primaryKey);
customer.setName( new Name("Smith","John") );

AddressLocal addr = customer.getHomeAddress( );

if (addr==null) 
{
   out.println("Address reference is NULL, Creating one and
               setting in Customer..");
   addr = addresshome.createAddress("333 North Washington"
                                    ,"Minneapolis"
                                    ,"MN","55401");

A call to customer.setHomeAddress( ) sets up the relationship:

   customer.setHomeAddress(addr);
 }
 ...

Next, modify the address directly with new information. Calling the Address object’s set methods is the correct way to modify a unidirectional relationship that has already been set up.

addr.setStreet("445 East Lake Street");
addr.setCity("Wayzata");
addr.setState("MN");
addr.setZip("55432");
...

The next bit of code shows the wrong way to modify a unidirectional relationship that’s already been created. Instead of modifying the existing Address entity, it creates a new one. Passing the new one to customer.setHomeAddress( ) orphans the old one, which thereafter just sits there in the database, unused and forgotten. The result is a database “leak:”

addr = addresshome.createAddress("700 Main Street"
                                       ,"St. Paul","MN","55302");
...
customer.setHomeAddress(addr);

Two different relationships can share the same entity. This code shares a single address between the Home Address and Billing Address relationships:

addr = customer.getHomeAddress( );
...
customer.setBillingAddress(addr);
         
AddressLocal billAddr = customer.getBillingAddress( );
AddressLocal homeAddr = customer.getHomeAddress( );

The Billing Address and Home Address now refer to the same bean:

if (billAddr.isIdentical(homeAddr))
{
   out.println("Billing and Home are the same!");
}
else
{
   out.println("Billing and Home are NOT the same! 
                BUG IN JBOSS!");
}
}
   catch (Exception ex)
   {
      ex.printStackTrace(out);
   }

Finally, test71a( ) closes the PrintWriter, extracts the output string, and returns it to the client for display:

   out.close( );
   output = writer.toString( );

   return output;
}

In order to run Client_71a, invoke the Ant task run.client_71a. Remember to set your JBOSS_HOME and PATH environment variables. The output should look something like this:

C:workbookex07_1>ant run.client_71a
Buildfile: build.xml

prepare:

compile:

run.client_71a:
     [java] Creating Customer 71
     [java] Address reference is NULL, Creating one and setting in Customer..
     [java] Address Info: 333 North Washington Minneapolis, MN 55401
     [java] Modifying Address through address reference
     [java] Address Info: 445 East Lake Street Wayzata, MN 55432
     [java] Creating New Address and calling setHomeAddress
     [java] Address Info: 700 Main Street St. Paul, MN 55302
     [java] Retrieving Address reference from Customer via getHomeAddress
     [java] Address Info: 700 Main Street St. Paul, MN 55302
     [java] Setting Billing address to be the same as Home address.
     [java] Testing that Billing and Home Address are the same Entity.
     [java] Billing and Home are the same!

Client_71b

The Client_71b program illustrates a simple one-to-one bidirectional relationship between a Customer bean and a Credit Card bean. The business logic for this example is implemented in com.titan.test.Test71Bean, in the test71b( ) method. Examine the code for this example.

You use the default JNDI context to obtain references to the local home interfaces of the Customer and Credit Card EJBs. The code also creates an instance of a Customer EJB:

// obtain CustomerHome
InitialContext jndiContext = getInitialContext( );
Object obj = jndiContext.lookup("CustomerHomeLocal");
CustomerHomeLocal customerhome = (CustomerHomeLocal)obj; 
      
obj = jndiContext.lookup("CreditCardHomeLocal");
CreditCardHomeLocal cardhome = (CreditCardHomeLocal)obj; 
Integer primaryKey = new Integer(71);
CustomerLocal customer = customerhome.create(primaryKey);
customer.setName( new Name("Smith","John") );

Next, create an instance of a Credit Card. Notice that you don’t need to pass in a primary key; the crude algorithm introduced in Exercise 6.3 generates one automatically:

// set Credit Card info
Calendar now = Calendar.getInstance( );
CreditCardLocal card = cardhome.create(now.getTime( ),
                   "370000000000001", "John Smith", "O'Reilly");

Then you establish the one-to-one bidirectional relationship between Customer and Credit Card simply by calling the Customer EJB’s setCreditCard( ) method:

 customer.setCreditCard(card);

The following code illustrates the bidirectional nature of the relationship by navigating from a Credit Card to a Customer and vice versa:

String cardname = customer.getCreditCard( ).getNameOnCard( );
out.println("customer.getCreditCard( ).getNameOnCard( )="
            + cardname);

Name name = card.getCustomer( ).getName( );
String custfullname = name.getFirstName( ) + " " +
                      name.getLastName( );
out.println("card.getCustomer( ).getName( )="+custfullname);

Finally, the code illustrates how to destroy the relationship between the Customer and Credit Card beans:

card.setCustomer(null);
      
CreditCardLocal newcardref = customer.getCreditCard( );
if (newcardref == null) 
{
   out.println
      ("Card is properly unlinked from customer bean");
} 
else 
{
   out.println("Whoops, customer still thinks it has a
               card!  BUG IN JBOSS!");
}

In order to run Client_71b, invoke the Ant task run.client_71b. Remember to set your JBOSS_HOME and PATH environment variables. The output should look something like this:

C:workbookex07_1>ant run.client_71b
Buildfile: build.xml

prepare:

compile:

run.client_71b:
     [java] Finding Customer 71
     [java] Creating CreditCard
     [java] Linking CreditCard and Customer
     [java] Testing both directions on relationship
     [java] customer.getCreditCard( ).getNameOnCard( )=John Smith
     [java] card.getCustomer( ).getName( )=John Smith
     [java] Unlink the beans using CreditCard, test Customer side
     [java] Card is properly unlinked from customer bean
     [java]

Client_71c

The Client_71c program illustrates the proper use of a one-to-many unidirectional relationship between customers and their phone numbers. The business logic for this example is implemented in com.titan.test.Test71Bean, in the test71c( ) method.

First, the test code locates the Customer home interface through JNDI, then finds the Customer that needs new phone numbers:

// obtain CustomerHome
InitialContext jndiContext = getInitialContext( );
Object obj = jndiContext.lookup("CustomerHomeLocal");
CustomerHomeLocal home = (CustomerHomeLocal)obj; 

// Find Customer 71
Integer primaryKey = new Integer(71);
CustomerLocal customer = home.findByPrimaryKey(primaryKey);

The next bit of code invokes the Customer helper method addPhoneNumber( ) to relate two phone numbers to the customer and outputs the contents of the customer-phone relationship after each addition:

// Display current phone numbers and types
out.println("Starting contents of phone list:");
ArrayList vv = customer.getPhoneList( );
for (int jj=0; jj<vv.size( ); jj++) 
{
   String ss = (String)(vv.get(jj));
   out.println(ss);
}

// add a new phone number
out.println("Adding a new type 1 phone number..");
customer.addPhoneNumber("612-555-1212",(byte)1);

out.println("New contents of phone list:");
vv = customer.getPhoneList( );
for (int jj=0; jj<vv.size( ); jj++) 
{
   String ss = (String)(vv.get(jj));
   out.println(ss);
}

// add a new phone number
out.println("Adding a new type 2 phone number..");
customer.addPhoneNumber("800-333-3333",(byte)2);
out.println("New contents of phone list:");
vv = customer.getPhoneList( );
for (int jj=0; jj<vv.size( ); jj++) 
{
   String ss = (String)(vv.get(jj));
   out.println(ss);
}

This code uses the updatePhoneNumber( ) helper method to modify an existing phone number:

// update a phone number
out.println("Updating type 1 phone numbers..");
customer.updatePhoneNumber("763-555-1212",(byte)1);

out.println("New contents of phone list:");
vv = customer.getPhoneList( );
for (int jj=0; jj<vv.size( ); jj++) 
{
   String ss = (String)(vv.get(jj));
   out.println(ss);
}

Finally, this code illustrates how to remove a member of a one-to-many unidirectional relationship:

// delete a phone number
out.println("Removing type 1 phone numbers from this
             Customer..");
customer.removePhoneNumber((byte)1);
    
out.println("Final contents of phone list:");
vv = customer.getPhoneList( );
for (int jj=0; jj<vv.size( ); jj++) 
      {
   String ss = (String)(vv.get(jj));
   out.println(ss);
}

Note that the phone entity hasn’t been destroyed. It’s still in the database; it’s just no longer related to this customer bean.

In order to run Client_71c, invoke the Ant task run.client_71c. Remember to set your JBOSS_HOME and PATH environment variables. The output should look something like this:

C:workbookex07_1>ant run.client_71c
Buildfile: build.xml

prepare:

compile:

run.client_71c:
     [java] Starting contents of phone list:
     [java] Adding a new type 1 phone number..
     [java] New contents of phone list:
     [java] Type=1  Number=612-555-1212
     [java] Adding a new type 2 phone number..
     [java] New contents of phone list:
     [java] Type=1  Number=612-555-1212
     [java] Type=2  Number=800-333-3333
     [java] Updating type 1 phone numbers..
     [java] New contents of phone list:
     [java] Type=1  Number=763-555-1212
     [java] Type=2  Number=800-333-3333
     [java] Removing type 1 phone numbers from this Customer..
     [java] Final contents of phone list:
     [java] Type=2  Number=800-333-3333
..................Content has been hidden....................

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