Strategies for Enterprise JavaBeans

   

Designing EJB Classes and Interfaces

The following strategies apply to designing your enterprise bean implementation classes and the interfaces they expose to their clients . The core of an EJB application consists of the enterprise beans it's built on, so this is the first place to focus your efforts toward producing a good design.

Designing Coarse-Grained Enterprise Beans

Prior to EJB 2.0, it was clear that an enterprise bean was too heavy an implementation approach for some objects. As Chapter 3 pointed out, the overhead involved in making a remote method call can be quite expensive. These and other issues forced many EJB applications to limit the number of calls a client invoked on an EJB and it also became more efficient to package up larger amounts of data when communicating to and from enterprise beans. The term that was often used for this approach was coarse-grained. However, with the release of the EJB 2.0 Specification and the introduction of local interfaces, you now have a choice to make regarding whether you utilize coarse-grained or fine-grained access for your enterprise beans.

The decision is whether your enterprise beans need to be exposed to remote clients or whether local interfaces will suffice. Chapter 3 provided a few guidelines when attempting to decide between local and remote exposure. A good rule of thumb is to use remote interfaces for your session beans and local interfaces for entity beans. This rule will not work in all situations, but it is a good starting point.

Using a Business Method Interface

An enterprise bean class must implement the business methods declared in its component interface. That's a straightforward requirement fundamental to EJB, but there's no default mechanism in place to enforce this until a bean is deployed. A mismatch between a bean class and its component interface is not the kind of problem a deployer should have to face. This is an error that needs to be detected when a bean is compiled. As you know, the built-in way to force a class to implement a particular method in Java is to declare the class to implement an interface that includes the method. Given that, it makes sense that an interface needs to come into play here. Your first thought might be to declare the bean to implement its component interface given that its business methods are already declared there. There are actually two reasons why this isn't a recommended approach.

The first reason why a bean shouldn't implement its component interface is that every component interface either extends javax.ejb.EJBLocalObject or javax.ejb.EJBObject , depending on whether the bean is exposed to a local or remote client respectively. If the bean is exposed to a remote client, then its component interface must extend javax.ejb.EJBObject , which in turn extends java.rmi.Remote . These interfaces support remote access by a client and expose some of the helper functionality the container implements for you. If a bean class implemented EJBObject , it would have to implement methods such as getEJBHome and getHandle . Besides placing extra work on the programmer, the chief drawback with this option is that these method implementations would never be called. This is because the container's implementation of the remote interface intercepts all calls from clients, and it always invokes the container's implementation of the EJBObject methods when they're called. You could provide do-nothing implementations for them to save yourself some work, but that would just clutter your class declarations with useless methods.

If your bean supports local client access, its component interface must extend javax.ejb.EJBLocalObject . Even though this component interface doesn't extend java.rmi.Remote , there are still methods in this interface that the container is designed to implement. Regardless of the component interface type, if your enterprise bean class implemented the interface, it would have to provide unnecessary method implementations that would never be invoked.

The second reason has to do with the fact that a client should never access an enterprise bean directly. A client should always use a component interface reference to access an EJB. A component interface reference is associated with a class that is created by the container at deployment time to implement the bean's component interface. This is the means of access intended for a client regardless of whether the client is local or remote or whether the client is in the same or a different container. It's this separation between the reference held by a client and a bean instance that allows the container to passivate entity and stateful session bean objects and to reuse stateless session beans for multiple clients. Methods that return a reference to an EJB or accept one as a method parameter should always be declared using the bean's component interface type and not the implementation class. This practice prevents an instance of an enterprise bean from passing a reference to itself as an argument to a method or returning a this reference from a method. When a bean method needs to supply a reference to its associated component interface, you can get the reference you need by calling

 context.getEJBObject() 

or

 context.getEJBLocalObject() 

where context is either an EntityContext or a SessionContext object that has been associated with the enterprise bean.

This separation can break down if a bean class is declared to implement its component interface. With that type of declaration, the compiler would consider passing this just as acceptable as passing context.getEJBObject() wherever a remote interface reference is needed. By not implementing the component interface, the compiler can verify that an enterprise bean instance is never passed as an argument or returned from a method call directly.

Given that declaring an enterprise bean to implement its component interface isn't a good approach, you need a different way to ensure that a bean implements all the business methods declared in its component interface. Just to be clear, business methods are the functional methods declared by a bean (they don't include the container callback methods or the remote methods needed because of the distributed nature of the architecture). Using an interface is still the right approach here, but the component interface isn't the right one to use. What's instead recommended is to declare a new interface that includes only the business methods exposed by an EJB. This interface can then be extended by the bean's component interface and implemented by the bean class. This allows the compiler to verify that the bean class implements all the required methods but it doesn't impose any unnecessary requirements on the bean class. This approach is known as the Business Interface pattern. Listing 16.1 shows a basic business interface for a local client.

Listing 16.1 IEnglishAuction.java “A Local Business Interface for the English Auction Entity Bean
 /**  package com.que.ejb20.auction.model;   * Title:        IEnglishAuction<p>   * Description:  Local business method interface for the EnglishAuction   *               entity bean<p>   */  import java.sql.Timestamp;  import com.que.ejb20.auction.exceptions.InvalidAuctionStatusException;  import com.que.ejb20.auction.exceptions.InvalidBidException;  import com.que.ejb20.auction.view.AuctionDetailView;  import com.que.ejb20.auction.view.BidView;  import com.que.ejb20.item.model.Item;  public interface IEnglishAuction {   public Integer getId();    public void setName(String newName);    public String getName();    public void setDescription(String newDescription);    public String getDescription();    public void setStatus(String newStatus) throws InvalidAuctionStatusException;    public String getStatus();    public void setStartingBid(Double newStartingBid)      throws InvalidAuctionStatusException;    public Double getStartingBid();    public void setMinBidIncrement(Double newMinBidIncrement)      throws InvalidAuctionStatusException;    public Double getMinBidIncrement();    public void setReserveAmount(Double newReserveAmount)      throws InvalidAuctionStatusException;    public Double getReserveAmount();    public void setStartDateTime(Timestamp newStartDateTime)      throws InvalidAuctionStatusException;    public Timestamp getStartDateTime();    public void setScheduledEndDateTime(Timestamp newScheduledEndDateTime)      throws InvalidAuctionStatusException;    public Timestamp getScheduledEndDateTime();    public void setActualEndDateTime(Timestamp newActualEndDateTime);    public Timestamp getActualEndDateTime();    public void assignItem(Item newItem, int newQuantity)      throws InvalidAuctionStatusException;    public Item getItem();    public Integer getQuantity();    public void removeItem() throws InvalidAuctionStatusException;    /**     * Submit a bid to an open auction     *     * @param bidAmount the amount of the bid     * @param bidder the participant submitting the bid     * @return the automatically assigned bid transaction ID     * @throws InvalidBidException if the bid does not meet the criteria for     *   the next acceptable bid     * @throws InvalidAuctionStatusException if the auction is not open     */    public String submitBid(double bidAmount, Bidder bidder)      throws InvalidBidException, InvalidAuctionStatusException;    /**     * Determine the next required bid for an auction     *     * @return the next acceptable bid amount     */    public double computeNextBidAmount()      throws InvalidAuctionStatusException;    public BidView getLeadingBidView();    public BidView getWinningBidView();    public AuctionDetailView getAuctionDetail();    /**     * Get the time remaining before the auction closes     *     * @return the time remaining in msec     */    public long getTimeLeft();    /**     * Report whether or not the current leading bid satisfies the reserve     *     * @return true if the reserve has been met or there is no reserve and     *   at least one bid has been submitted     */    public boolean reserveMet();    /**      * Assign the current leading bid as the auction winner     *     * @throws InvalidAuctionStatusException if the auction is not Open     */    public void assignWinner() throws InvalidAuctionStatusException;  } 

A business interface declares only the business methods that are found in the component interface. These are the methods that a client would need to invoke on the bean. Notice that it doesn't contain any methods particular to remote invocation or anything else specific to EJB. This provides a clean separation between the business method declarations and the fact that they're intended for implementation by an enterprise bean.

The business interface in Listing 16.1 is designed for a local client. If the business interface were for a remote client instead, it would have to look a little different. This is because every method signature would have to include RemoteException in the throws clause. Every method in a remote interface has to include this exception, even if it's declared in a superinterface that's extended by the remote. This gives away the fact that a business interface is intended for remote calls, but that's a minor intrusion.

The declaration in Listing 16.1 used the naming convention of starting an interface name with the letter " I ." This is not required and is just an example of a convention that some developers use. Another common naming convention for business interfaces is to use the word " Business " somewhere in the name. You should be careful with this one because it can lead to very long names . Some developers don't like naming conventions such as these because they feel that exposing the fact that a declaration is for an interface (as opposed to a class) breaks encapsulation by providing too much information to other classes. There are no hard and fast rules here. All that really matters is that you choose the naming conventions your development team wants to use and you stick with them.

With a business interface defined, the component interface that would normally declare these methods can now just extend this new interface. Listing 16.2 shows the updated EnglishAuction interface.

Listing 16.2 EnglishAuction.java “Local Interface Extending the IEnglishAuction Business Interface
 package com.que.ejb20.auction.model;  /**   * Title:        EnglishAuction<p>   * Description:  Local interface for the EnglishAuction entity bean<p>   */  import javax.ejb.EJBLocalObject;  public interface EnglishAuction extends EJBLocalObject, IEnglishAuction {   // all business methods are declared in IEnglishAuction  } 

Notice that the EnglishAuction interface no longer declares any methods. That's because IEnglishAuction takes care of all the business methods and the EJBLocalObject interface takes care of the EJB side.

Tip

As shown in Listing 16.2, a Java interface can extend multiple interfaces even though a class can extend only a single parent.


Now you can declare the auction enterprise bean to implement the business interface associated with its component interface. The compiler can now ensure that all the business methods have implementations without requiring you to provide implementations for the EJBLocalObject methods. All that's required to do this is adding an implements clause to the EnglishAuctionBean declaration:

 public class EnglishAuctionBean implements EntityBean, IEnglishAuction {   // The rest of the enterprise bean code goes here    ...  } 

An extension to the business interface idea proposes using a regular Java class to implement the business interface and provide the corresponding business logic for the methods. The enterprise bean either extends this class or declares a reference to an instance of it and delegates calls to that instance. This business logic implementation creates another layer of separation and might be too thick for some. However, separating the business logic from the EJB that provides access to it could be considered a step toward adopting a rule engine approach.

Using an Abstract Superclass for Callback Methods

Inheritance is one of the fundamental concepts in object-oriented programming. The EJB 2.0 Specification does not support the concept of component inheritance, but it does allow for what it calls interface inheritance and implementation class inheritance.

Interface inheritance is defined as the normal Java mechanism of extending an interface with another interface. You can take advantage of this type of inheritance when declaring the home and component interfaces for an enterprise bean. For example, the business method strategy just described showed how the component interface for an enterprise bean could extend a Java interface that encapsulates its business methods. Home interfaces can also extend other Java interfaces to take advantage of common behavior, but this tends to be less useful than using inheritance with component interfaces.

As the name implies, implementation class inheritance applies to enterprise bean classes instead of their interfaces. For example, you could declare a SealedBidAuctionBean class that extends EnglishAuctionBean to add some special behavior to the business logic. This is the normal mechanism of subclassing.

Although these two types of inheritance are supported with enterprise beans, true polymorphic inheritance can't be achieved. Home interfaces, primary key classes for entity beans, and the way persistence is handled between a superclass and subclass get in the way of actual component inheritance. As introduced in Chapter 5, "Entity Beans," there are still advantages to making some use of inheritance within EJBs, even with its limitations. In many cases, developers create abstract parent classes that are not enterprise beans, but regular Java classes. These Java classes provide default implementations for the container callback methods. Listing 16.3 shows an abstract controller class that could be extended by an application's session beans.

Listing 16.3 AbstractController.java “An Abstract Superclass for the Session Beans in the Auction Example
 /**   * Title:        AbstractController<p>   * Description:  An abstract parent class for session beans.<p>   */  package com.que.ejb20.common.ejb;  import javax.ejb.SessionBean;  import javax.ejb.SessionContext;  public class AbstractController implements SessionBean {   // Reference to gain access to the session context held by the container    private SessionContext sessionContext = null;    public void setSessionContext( SessionContext ctx ){     this.sessionContext = ctx;    }    public void ejbRemove(){     // Do nothing for this class    }    public void ejbPassivate(){     // Do nothing for this class    }    public void ejbActivate(){     // Do nothing for this class    }    public SessionContext getSessionContext() {     return this.sessionContext;    }  } 

The AbstractController class provides default implementations for the container callback methods that apply to session beans. With this approach, the concrete session beans must implement these methods only if they want to override the default behavior. This is useful, but support for inheritance is one aspect of the EJB specification that still needs some work.

For more information on inheritance within enterprise beans, see "Inheritance and Entity Beans," p. 141 .

Using Container-Managed Transactions

This is a simple point first discussed in Chapter 12, "Transactions," but it's worth repeating here while the subject of designing your bean classes is being addressed. When you implement an entity bean class, the container always manages the transaction demarcation that applies to the bean. You have a choice when it comes to session beans and message-driven beans, however. You can allow the container to start and commit transactions for you or you can code the transaction control yourself. One of the primary benefits offered by EJB is the declarative transaction support it provides. Using bean-managed transactions requires a lot more coding expertise from the bean provider and it opens the door for errors that could compromise data integrity. You should start every design with the intention of using container-managed transactions. If you absolutely have to have the flexibility offered by bean-managed transactions, the EJB architecture supports it fully while still discouraging its use.



Special Edition Using Enterprise JavaBeans 2.0
Special Edition Using Enterprise JavaBeans 2.0
ISBN: 0789725673
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net