Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Mastering Enterprise JavaBeans™ and the Java 2 Platform, Enterprise Edition - Roman E

..pdf
Скачиваний:
42
Добавлен:
24.05.2014
Размер:
6.28 Mб
Скачать

J2EE in the Real World: Implementing Our E-Commerce Session Beans 421

if (orderHome == null)

orderHome = (OrderHome) findHome("Ecommerce.OrderHome");

}

catch (Exception e) {

throw new RemoteException(e.toString());

}

/*

* Make a unique ID for the order */

String orderID = makeUniqueID();

/*

* Make a new persistent Order. */

Order order = null; try {

order = orderHome.create(orderID, customer); order.setSubtotal(subTotal); order.setTaxes(taxes);

}

catch (Exception e) { e.printStackTrace();

throw new QuoteException("Error generating an order: " + e.toString());

}

/*

*Convert each of our quote's line

*items into permanent, persistent

*order line items.

*/ try {

Vector orderLineItems = new Vector();

/*

* For each quote line item...

*/

for (int i=0; i < lineItems.size(); i++) {

/*

*Extract the fields from

*the Quote Line Item...

*/

QuoteLineItem qli =

(QuoteLineItem) lineItems.elementAt(i); Product prod = qli.getProduct();

int quantity = qli.getQuantity(); double discount = qli.getDiscount();

Source 14.5 QuoteBean.java (continues).

Go back to the first page for a quick link to buy this book online!

422 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

/*

*And shove the fields into

*a new Order Line Item.

*/

String id = makeUniqueID();

OrderLineItem oli = oliHome.create(id, order, prod, quantity, discount);

}

}

catch (Exception e) { e.printStackTrace();

throw new QuoteException("Error generating an order line items: " + e.toString());

}

return order;

}

//------------------------------------------------

// End public business methods //------------------------------------------------

//------------------------------------------------

//Begin EJB-Required methods. The methods below

//are called by the Container, and never called by

//client code.

//------------------------------------------------

/**

*Associates this Bean instance with a particular

*context.

*/

public void setSessionContext(SessionContext ctx) throws RemoteException { System.out.println("Quote.setSessionContext called");

this.ctx = ctx;

/*

*Get the QuoteLineItemHome Home Object

*so we can create Line Items.

*/ try {

this.qliHome = (QuoteLineItemHome) findHome("Ecommerce.QuoteLineItemHome");

}

catch (Exception e) {

throw new RemoteException(

"Could not retrieve Quote Line Item Home Object: " + e.toString());

}

}

Source 14.5 QuoteBean.java (continues).

Go back to the first page for a quick link to buy this book online!

J2EE in the Real World: Implementing Our E-Commerce Session Beans 423

/**

*The container calls this method directly after

*activating this instance. You should acquire

*any needed resources.

*/

public void ejbActivate() throws RemoteException { System.out.println("Quote.ejbActivate() called.");

}

/**

*The container calls this method directly before

*passivating this instance. You should release

*resources acquired during ejbActivate().

*/

public void ejbPassivate() throws RemoteException { System.out.println("Quote.ejbPassivate() called.");

}

/**

*This is the initialization method that

*corresponds to the create() method in the

*Home Interface.

*

*When the client calls the Home Object's

*create() method, the Home Object then calls

*this ejbCreate() method.

*/

public void ejbCreate(Customer customer) throws CreateException, RemoteException {

System.out.println("Quote.ejbCreate(" + customer + ") called");

this.customer = customer; this.lineItems = new Vector(); this.subTotal = 0;

this.taxes = 0;

}

/**

*Removes this quote, and hence all client-

*specific state.

*

*In our example, there is really no way for

*the client to 'abort' the quote, so the EJB

*Container will call this when the Session

*times out.

*/

public void ejbRemove() throws RemoteException { System.out.println("Quote.ejbRemove() called.");

Source 14.5 QuoteBean.java (continues).

Go back to the first page for a quick link to buy this book online!

424 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

/*

*Remove all the Line Items that are

*part of the quote. We want to remove

*the Line Items because an Quote is

*made up of unique Line Items, which

*other quotes cannot use.

*

*(For you UML fans, this is

*Composition, rather than

*Aggregation).

*/

Enumeration e = lineItems.elements(); while (e.hasMoreElements()) {

try {

QuoteLineItem li = (QuoteLineItem) e.nextElement(); li.remove();

}

catch (Exception ex) {

//Ignore exceptions. EJB Container may

//have removed the Line Items already

//due to Session timeout.

}

}

/*

*Don't remove the Customer, since the

*Customer can be re-used over and over

*again in different Quotes.

*

*(For you UML fans, this is

*Aggregation, rather than

*Composition).

*/

}

//------------------------------------------------

// End EJB-Required methods //------------------------------------------------

}

Source 14.5 QuoteBean.java (continued).

The first thing to notice is our three transient Home Object fields. These fields will not be persisted as part of the conversational state. The fields are as follows:

■■A Quote Line Item Home Object for creating new Quote Line Items. We need to create a Quote Line Item whenever the client adds a Product to our Quote for which we don’t already have a Line Item. If we already have a Line Item, we’ll increase that Line Item’s quantity.

Go back to the first page for a quick link to buy this book online!

J2EE in the Real World: Implementing Our E-Commerce Session Beans 425

■■An Order Line Item Home Object for transforming the Quote Line Items into permanent Order Line Items in the purchase() method.

■■An Order Home Object for transforming the Quote into a permanent Order in the purchase() method.

When our bean is context switched in via ejbActivate(), these three fields will not be set by the container. We must manually refind the Home Objects. This is done via the private findHome() method—a convenience method we write that will find a generic Home Object for us.

As with the Order entity bean, when our Quote is removed via ejbRemove(), all the line items are removed. This same process happens when the Quote’s clear() method is called to clear out the quote. Note that we don’t remove the Customer referenced by the Quote because the Customer can be reused over and over again for many different quotes.

QuoteHome.java

Next, we’ll write the home interface used to create new quotes or find existing quotes. Our home interface is in QuoteHome.java, and it is shown in Source 14.6.

package com.wiley.compBooks.ecommerce;

import javax.ejb.*;

import java.rmi.RemoteException; import java.util.Enumeration; import java.util.Date;

/**

*This is the home interface for Quote. This interface

*is implemented by the EJB Server's glue-code tools -

*the implemented object is called the Home Object and

*serves as a factory for EJB Objects.

*

*One create() method is in this Home Interface, which

*corresponds to the ejbCreate() method in the Quote file. */

public interface QuoteHome extends EJBHome {

/*

*This method creates the EJB Object.

*Notice that the Home Interface returns an

*EJB Object, whereas the Bean returns void.

*This is because the EJB Container is responsible

Source 14.6 QuoteHome.java (continues).

Go back to the first page for a quick link to buy this book online!

426 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

*for generating the EJB Object, whereas the Bean

*is responsible for initialization.

*

*@param customer The Customer for this Quote

*@return The newly created EJB Object.

*/

public Quote create(Customer customer) throws CreateException, RemoteException;

}

Source 14.6 QuoteHome.java (continued).

Our home interface exposes a single method to create a new Quote EJB Object. It takes a customer as a parameter because the quote (aka shopping cart) needs to be associated with a particular customer. Our login servlet will call this create() method when the user first logs in, and that quote will stay with the user throughout his or her visit to our store.

QuoteException.java

Finally, we have a custom exception class, QuoteException.java, for indicating problems that occur in our Quote bean. The code is in Source 14.7.

package com.wiley.compBooks.ecommerce;

/**

* Exceptions thrown by Quote */

public class QuoteException extends Exception {

public QuoteException() { super();

}

public QuoteException(Exception e) { super(e.toString());

}

public QuoteException(String s) { super(s);

}

}

Source 14.7 QuoteException.java.

Go back to the first page for a quick link to buy this book online!

J2EE in the Real World: Implementing Our E-Commerce Session Beans 427

Table 14.2 Deployment Descriptor Settings for QuoteBean

DEPLOYMENT

DESCRIPTOR SETTING

VALUE

Bean home name

Ecommerce.QuoteHome

Enterprise bean class name

com.wiley.compBooks.ecommerce.QuoteBean

Home interface class name

com.wiley.compBooks.ecommerce.QuoteHome

Remote interface class name

com.wiley.compBooks.ecommerce.Quote

Environment properties

<empty>

Re-entrant

false

Stateful or stateless

STATEFUL_SESSION

Session timeout

15 minutes

Transaction isolation level

TRANSACTION_READ_UNCOMMITTED

Transaction attribute

TX_SUPPORTS

Run-as mode

CLIENT_IDENTITY

 

 

The Deployment Descriptor

Now that we’ve written our bean code, we need to specify our bean’s declarative properties in a deployment descriptor. The descriptor values are in Table 14.2.

As with our Quote Line Item bean, we do not perform any persistent operations, and we do not anticipate any concurrency issues. Hence we have no need for transactional support. Hence, we use the weakest isolation level, TRANSACTION_ READ_UNCOMMITTED, and support transactions if they are already occurring but do not require transactions.

The Pricer Stateless Session Bean

One of the requirements that we laid out for our e-commerce architecture in Chapter 12 is support for customized pricing. A customized price is a specialized price computed from discounts, bulk purchases, and other factors.

The best way to have pricing behavior in an extensible manner is to externalize the entire pricing logic into its own enterprise bean. Thus, we have the concept of a Pricer—a component that takes a price quote as input and calculates the price of that quote based on a set of pricing rules. A pricing rule might be “Customer

Go back to the first page for a quick link to buy this book online!

428 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

X gets a 5% discount” or “If you purchase 10 motherboards you get a 15% discount.” These pricing rules could be read in from a database, or set via properties.

Internally, our Pricer will take a Quote stateful session bean as input and compute the subtotal (before taxes) of that Quote. It figures out the subtotal by computing a discount for each Quote Line Item and subtracting the discounts from the total price.

Our Pricer bean is a business logic provider—it will work on any Quote, and it holds no client-specific state. It is not a persistent object (it would not make sense to save a Pricer because a Pricer simply performs logic and holds no state). This means our Pricer fits into the EJB world best as a stateless session bean.

Let’s take a look at the implementation.

Pricer.java

First, we’ll design our Pricer’s remote interface, shown in Source 14.8 as Pricer.java.

We have one business method—price()—that takes a Quote and prices it. price() will set the individual pricing fields of the Quote for us. Therefore, it returns nothing.

package com.wiley.compBooks.ecommerce;

import javax.ejb.*;

import java.rmi.RemoteException; import java.rmi.Remote;

/**

*These are the business logic methods exposed publicly

*by PricerBean.

*

*This interface is what clients operate on when they

*interact with beans. The container vendor will

*implement this interface; the implementation object is

*called the EJB Object, which delegates invocations to

*the actual bean.

*/

public interface Pricer extends EJBObject {

/**

* Computes the price of a set of goods */

public void price(Quote quote) throws RemoteException, PricerException;

}

Source 14.8 Pricer.java.

Go back to the first page for a quick link to buy this book online!

J2EE in the Real World: Implementing Our E-Commerce Session Beans 429

PricerBean.java

Next, we’ll write the implementation of our Pricer’s business rules. The code for the bean implementation is in PricerBean.java, depicted in Source 14.9.

Because our bean holds no state whatsoever, most of our EJB required methods are blank. We have one public business logic method—price()—which computes the price of a quote. price() calls two private helper methods, priceSubtotal() and priceTaxes().

package com.wiley.compBooks.ecommerce;

import javax.ejb.*;

import java.rmi.RemoteException; import java.util.*;

/**

*Stateless Session Bean that computes prices based

*on a set of pricing rules. The pricing rules are

*deployed with the bean as environment properties. */

public class PricerBean implements SessionBean {

// Constants used for reading properties

public static final String ENV_DISCOUNT = "DISCOUNT_"; public static final String ENV_TAX_RATE = "TAX_RATE";

/*

*Although this is a stateless session bean, we

*do have state - the session context. Remember

*that stateless session beans can store state; they

*just can't store state on behalf of particular

*clients.

*/

private SessionContext ctx;

//----------------------------------------------------

// Begin business methods //----------------------------------------------------

/**

*Computes the total price of a quote. This includes

*subtotal and taxes. Sets the appropriate quote

*fields. We expose this coarse-grained method to

*avoid network round trips for pricing individual

*parts of a quote.

*/

Source 14.9 PricerBean.java (continues).

Go back to the first page for a quick link to buy this book online!

430 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

public void price(Quote quote) throws PricerException, RemoteException { priceSubtotal(quote);

priceTaxes(quote);

}

/**

*Computes the subtotal price for a set of products the

*customer is interested in. The subtotal takes into

*account the price of each product the customer wants,

*the quantity of each product, and any discounts the

*customer gets. However, the subtotal ignores taxes.

*@param quote All the data needed to compute the

*subtotal is in this parameter.

*/

private void priceSubtotal(Quote quote) throws PricerException, RemoteException { System.out.println("PricerBean.priceSubtotal() called");

/*

*Customers with certain names get discounts.

*The discount rules are stored in the

*environment properties that the bean is

*deployed with.

*

* Get the name of this customer. */

Customer customer = quote.getCustomer();

String customerName = customer.getName();

double percentDiscount = 0; try {

for (int i=0; ; i++) {

/*

*Get the next discount equation from

*the environment properties. A

*discount equation has the form

*"<name>=<% discount>". For example,

*"Ed Roman=50" means that the

*Customer whose name is "Ed Roman"

*gets a 50% discount.

*/

String discount = (String) ctx.getEnvironment().get(ENV_DISCOUNT + i);

/*

* Break the equation up into

Source 14.9 PricerBean.java (continues).

Go back to the first page for a quick link to buy this book online!