Developing an Enterprise Bean with EJB 2.1 Technology







Let's examine what a developer does to create enterprise beans using EJB 2.1 technology. The examples presented here are taken from the J2EE 1.4 Tutorial. The source code for the examples is contained in the J2EE 1.4 tutorial bundle. If you download the tutorial bundle, you'll find the example source code below the /j2eetutorial14/examples/ejb directory, where <install_dir> is the directory where you installed the tutorial bundle.



Note that the remainder of this article focuses on session beans and enterprise beans. Message-driven beans will not be specifically covered. Some of the same development techniques described in this section for stateless session beans also apply to message-driven beans. Also, many of the development simplifications and new features provided in EJB 3.0 technology are also applicable to message-driven beans. For information on how to develop message-driven beans in EJB 2.1 technology see Chapter 28: A Message-Driven Bean Example in the J2EE 1.4 Tutorial.



A Stateless Session Bean



Let's start with an example of a stateless session bean called ConverterBean. This is an enterprise bean that can be accessed remotely. The bean converts currency -- from dollars to yen, and from yen to euros. The source code for ConverterBean is in the /j2eetutorial14/examples/ejb/converter/src directory.



To create the session bean, a developer needs to code:



  • A home interface that defines the life-cycle methods for the session bean that can be accessed
    by a remote client.

  • A remote interface that defines the business methods for the session bean that can be accessed by a remote client.

  • A bean class that contains the implementations of the home and remote interfaces.

  • A deployment descriptor that specifies information about the bean such as it's name and type.


Home Interface



Here's the home interface for ConverterBean:



import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface ConverterHome extends EJBHome {
Converter create() throws RemoteException, CreateException;
}


The home interface:



  • Extends the javax.ejb.EJBHome interface. All home interfaces must extend javax.ejb.EJBHome.

  • Defines a life-cycle method, create. A home interface must define one or more life cycle methods, such as create and remove, and these methods can have multiple signatures. In the case of a stateless session bean, the create method cannot have arguments. A remote client can invoke the create method to request creation of a session bean instance. In response, the EJB container creates the instance within the container (that is, on the server). The create method returns an object that is the remote interface type of the bean, in this example, Converter. This object runs locally on the client and acts as a proxy for the remote instance of the bean. A client can subsequently invoke business methods on the Converter object locally. In response, this invokes the same business methods on the remote session bean instance in the EJB container. The create method throws a RemoteException and a CreateException.


Remote Interface



Here's the remote interface for ConverterBean:



import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.math.*;

public interface Converter extends EJBObject {
public BigDecimal dollarToYen(BigDecimal dollars)
throws RemoteException;
public BigDecimal yenToEuro(BigDecimal yen)
throws RemoteException;
}


The remote interface:



  • Extends the javax.ejb.EJBObject interface. All remote interfaces must extend the javax.ejb.EJBObject interface.

  • Defines the business methods for the bean, in this case, dollarToYen and yenToEuro. These are the methods that the remote client can call on the session bean. However, as mentioned in the description of the home interface, these method calls actually go through a local proxy object that subsequently invokes analogous methods on the remote session bean instance in the EJB container. Each business method throws a RemoteException.


Bean Class



Here's the class for ConverterBean:



import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import java.math.*;

public class ConverterBean implements SessionBean {

BigDecimal yenRate = new BigDecimal("121.6000");
BigDecimal euroRate = new BigDecimal("0.0077");

public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}

public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2,BigDecimal.ROUND_UP);
}

public ConverterBean() {}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}

The bean class:



  • Implements the javax.ejb.SessionBean interface. Because the session bean implements this interface, it must also implement all of the methods in the interface: ejbRemove, ejbActivate, ejbPassivate, and setSessionContext. The class must implement these methods even if it doesn't use them, as is the case here because the methods are empty.

  • Defines a constructor with no parameters.

  • Implements an ejbCreate method. A session bean must implement at least one ejbCreate method. It could implement more, but each must have a different signature. For every ejbCreate method implemented in the class, there must be a corresponding create method defined in the home interface. Recall that a client can't directly call a session or entity bean's methods, and instead calls methods defined in the bean's interfaces. To create an instance of the session bean, a remote client calls the create method on the home interface. In response, the EJB container instantiates the session bean and then invokes the corresponding ejbCreate method in the bean class to initialize the instance's state. In this example, ejbCreate is empty so there is no initial setting of data in the bean instance.

  • Implements the business methods defined in the remote interface: dollarToYen and yenToEuro. A remote client invokes business methods on the remote interface (through the local proxy). In response, this invokes the same business methods on the remote session bean component in the EJB container. The container then runs the business method implementations in the bean class.


Deployment Descriptor



Here's the deployment descriptor for ConverterBean:





ConverterJAR


ConverterBean
converter.ConverterHome
converter.Converter
converter.ConverterBean
Stateless
Bean








The deployment descriptor is an XML file that specifies basic information about
the bean, such as it's name and the name of its interfaces. In fact, the descriptor's
purpose is mainly to associate the bean class with the interfaces. The descriptor in this
example also identifies this as a stateless session bean.


The Session Bean Client



To demonstrate how the bean is accessed, here's the remote client provided in the ConverterBean example inside the J2EE 1.4 tutorial bundle:



import converter.Converter;
import converter.ConverterHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.math.BigDecimal;


public class ConverterClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Context myEnv = (Context) initial.lookup("java:comp/env");
Object objref = myEnv.lookup("ejb/SimpleConverter");

ConverterHome home =
(ConverterHome) PortableRemoteObject.narrow(objref,
ConverterHome.class);

Converter currencyConverter = home.create();

BigDecimal param = new BigDecimal("100.00");
BigDecimal amount = currencyConverter.dollarToYen(param);

System.out.println(amount);
amount = currencyConverter.yenToEuro(param);
System.out.println(amount);

System.exit(0);
} catch (Exception ex) {
System.err.println("Caught an unexpected exception!");
ex.printStackTrace();
}
}
}


The initial part of the code creates an instance of the session bean. Recall that a client can't directly call a session bean's methods, and instead calls methods defined in the bean's interfaces. To create an instance of the session bean, the client needs to call the create method on the bean's home interface. To do that, the client first uses the Java Naming and Directory Interface (JNDI) to acquire an object that represents the ConverterHome interface. The client then invokes the create method on the ConverterHome object. In response, the EJB container creates the session bean instance and then invokes the corresponding ejbCreate method in the bean class to initialize the instance's state. In this example, ejbCreate is empty so there is no initial setting of data in the bean instance. The container returns a Converter object. To invoke a business method, the client calls the business method on the Converter object.



An Entity Bean with Container-Managed Persistence



For the second example, let's examine an entity bean called PlayerBean that represents a player on a sports team. PlayerBean uses container-managed persistence. In addition, it has relationships with other entity beans, such as TeamBean. The TeamBean entity bean represents a sports team. The relationship to TeamBean is many-to-many: a player can be on multiple teams (in different sports), and a team has multiple players. These relationships are maintained by the container. The source code for PlayerBean is in the /j2eetutorial14/examples/ejb/cmproster/src directory.


An entity bean like PlayerBean that is the target of a container-managed relationship must have a local interface. So PlayerBean is accessible by local clients only. To create the entity bean, a developer needs to code:



  • A local home interface that defines the life-cycle methods for the entity bean that can be invoked by local clients.

  • A local interface that defines the business and access methods for the entity bean that can be invoked by local clients.

  • A bean class that contains the implementation of the business logic for the local home and local interfaces.

  • A deployment descriptor that specifies information about the bean such as its name, its persistence type, and its abstract schema.


Local Home Interface



Here's an excerpt from the local home interface for PlayerBean:



package team;

import java.util.*;
import javax.ejb.*;

public interface LocalPlayerHome extends EJBLocalHome {

public LocalPlayer create (String id, String name,
String position, double salary)
throws CreateException;

public LocalPlayer findByPrimaryKey (String id)
throws FinderException;

public Collection findByPosition(String position)
throws FinderException;
...
public Collection findBySport(String sport)
throws FinderException;
...
}


The local home interface:



  • Extends the javax.ejb.EJBLocalHome interface. All local home interfaces must
    extend javax.ejb.EJBLocalHome.

  • Defines a life-cycle method, create. As is the case for home interfaces, a local home interface must define one or more life cycle methods, such as create and remove. However unlike methods for stateless sessions beans, a create method for an entity bean can have arguments. A client can invoke the create method to request creation of an entity bean instance. In response, the EJB container creates the instance within the container (that is, on the server). The create method returns an object that is the local interface type of the entity bean, in this example, LocalPlayer. It also throws a CreateException. (Because this is a local interface, the create method doesn't throw a RemoteException.)

  • Defines finder methods. These are methods whose name begins with find. A local home interface for an entity bean must define at least the findByPrimaryKey method. All finder methods except findByPrimaryKey use EJB QL queries (defined in the deployment descriptor for the bean) to find instances of an entity bean based on specific criteria. For example, findBySport uses an EJB QL query to find instances of PlayerBean that represent players in a specified sport. Finder methods return the local interface type of the entity bean or a collection of those types. They also throw a FinderException.



Although not shown in this example, a local home interface can also define home methods. A home method is similar to a business method in that it contains business logic, but unlike a business method, a home method applies to all entity beans of a particular class. (A business method applies to a single entity bean.)


Local Interface



Here's the local interface for PlayerBean:



package team;

import java.util.*;
import javax.ejb.*;

public interface LocalPlayer extends EJBLocalObject {

public String getPlayerId();
public String getName();
public String getPosition();
public double getSalary();
public Collection getTeams();

public Collection getLeagues() throws FinderException;
public Collection getSports() throws FinderException;
}

The local interface:



  • Extends the javax.ejb.EJBLocalObject interface. All local interfaces must extend the javax.ejb.EJBLocalObject interface.

  • Defines the access methods that a local client can invoke, in this case, getPlayerID, getName, getPosition, getSalary, and getTeams. These methods access the bean's persistent fields (playerID, name, position, and salary) and relationship field (teams) that are specified in the bean's deployment descriptor.

  • Defines business methods that a local client can invoke on the entity bean, in this case, getLeagues and getSports.


Bean Class


Here's an excerpt from the PlayerBean class:



public abstract class PlayerBean implements EntityBean {
private EntityContext context;


public abstract String getPlayerId();
public abstract void setPlayerId(String id);
...

public abstract Collection getTeams();
public abstract void setTeams(Collection teams);
...

public abstract Collection ejbSelectLeagues(LocalPlayer player)
throws FinderException;
...

public Collection getLeagues() throws FinderException {
LocalPlayer player =
(team.LocalPlayer)context.getEJBLocalObject();
return ejbSelectLeagues(player);
}
...

public String ejbCreate (String id, String name,
String position, double salary) throws CreateException {

setPlayerId(id);
setName(name);
setPosition(position);
setSalary(salary);
return null;
}

public void ejbPostCreate(String id, String name, String position,
double salary) throws CreateException {
}
...

The bean class:



  • Implements the javax.ejb.EntityBean interface. Because the entity bean implements this interface, it must also implement all of the methods in the interface: ejbRemove, ejbActivate, ejbPassivate, ejbLoad, ejbStore, setEntityContext and unsetEntityContext. The class must implement these methods even if it doesn't use them.

  • Defines access methods such as getPlayerID and setPlayerID that access the bean's persistent fields, and methods such as getTeams and setTeams that access the bean's relationship field. Entity bean fields are identified as persistent fields or relationship fields in the bean's deployment descriptor. Notice that the access methods are defined as abstract. That's because the EJB container actually implements the methods. In other words, the container actually gets and sets the data for these fields.

  • Defines select methods, such as ejbSelectLeagues. These are methods that begin with ejbSelect. Select methods are similar to finder methods. For example, each select method requires a corresponding EJB QL query in the deployment descriptor. However unlike finder methods, select methods cannot be invoked by a client. They can be invoked only by the methods implemented in the entity bean class. In the PlayerBean class, the select methods appear within business methods (which the local client can invoke). Unlike select methods, finder methods are not defined in the bean class.

  • Implements business methods, such as getLeagues. In the PlayerBean class, these methods are essentially wrappers for select methods.

  • Implements an ejbCreate method and an ejbPostCreate method. To create an instance of the entity bean, a local client calls the create method on the local home interface. In response, the EJB container instantiates the entity bean and then invokes the corresponding ejbCreate method in the bean class. The container initializes the instance's state by calling the set access methods in ejbCreate to assign the input arguments to the persistent fields. At the end of the transaction that contains the create call, the container saves the persistent fields by inserting a row into the database. The container also calls method ejbPostCreate after the component is created. This method can be used to do things such as set a relationship field to initialize the bean instance. However, in this example, the method is empty.


Deployment Descriptor


Here's an excerpt from the deployment descriptor for PlayerBean:





TeamJAR


PlayerBean
team.LocalPlayerHome
team.LocalPlayer
team.PlayerBean
Container
...
Player

no description
position

...
playerId
...


findAll


select object(p) from Player p

...


ejbSelectLeagues

team.LocalPlayer


Local
select distinct t.league
from Player p, in (p.teams) as t
where p = ?1


...





Many

TeamBean


players
java.util.Collection



Many

PlayerBean


teams
java.util.Collection




...






Clearly there's a lot more here than in the deployment descriptor for the stateless session bean in the previous example. In addition to the basic information it specifies about the entity bean, such as it's name and the name of its interfaces, the deployment descriptor specifies the bean's abstract schema. The abstract schema for the bean identifies the bean's container-managed persistence fields, such as position, and the bean's container-managed relationships, such as the many-to-many relationship between players and teams. The deployment descriptor also specifies EJB QL queries, such as select object(p) from Player p, and the finder method each query is associated with (in this case, findAll). Additionally, the deployment descriptor associates EJB QL queries with select methods.


The Entity Bean Client and Remote Session Bean



To demonstrate how the entity bean is accessed, here are some excerpts from the local client and from a remote session bean named RosterBean. The client and the session bean are provided in the ConverterBean example inside the J2EE 1.4 tutorial bundle. The client invokes methods on RosterBean, which works in conjunction with entity beans local to it, such as PlayerBean and TeamBean, to access needed data.


Here is the client:



import java.util.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import util.*;
import roster.*;


public class RosterClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Object objref = initial.lookup("java:comp/env/ejb/SimpleRoster");

RosterHome home =
(RosterHome) PortableRemoteObject.narrow(objref,
RosterHome.class);

Roster myRoster = home.create();



The client uses JNDI to acquire an object that represents the session bean's remote interface, RosterHome. The client then invokes the create method on the RosterHome object. In response, the EJB container creates the session bean instance, and invokes a corresponding ejbCreate method in the RosterBean class to initialize the instance's state.


The client invokes the createPlayer method of RosterBean to create a new player:



myRoster.createPlayer(new PlayerDetails("P1", "Phil Jones",
"goalkeeper", 100.00));

Here is the createPlayer method in RosterBean:



public void createPlayer(PlayerDetails details) {

try {
LocalPlayer player = playerHome.create(details.getId(),
details.getName(), details.getPosition(),
details.getSalary());
} catch (Exception ex) {
throw new EJBException(ex.getMessage());
}
}

The client then calls the addPlayer method of RosterBean to add player P1 to team T1:



myRoster.addPlayer("P1", "T1");

The P1 and T1 parameters are the primary keys of the PlayerBean and TeamBean instances.



The client invokes the getPlayersByPosition method of PlayerBean to create a new player.



playerList = myRoster.getPlayersByPosition("defender");


The local home interface for PlayerBean defines the finder method, findByPosition, as follows:



public Collection findByPosition(String position)
throws FinderException;


The EJB container then executes the EJB QL query associated with findByPosition in the bean's deployment descriptor. The EJB QL query is:



SELECT DISTINCT OBJECT(p) FROM Player p
WHERE p.position = ?1


So What's the Problem?



Looking at the EJB 2.1 examples, it's not too hard to understand why some developers see EJB technology as overly complex. Some of the things that feed this perception are:



  • The need to code a variety of classes, interfaces, and files for an enterprise bean. For even a "simple" enterprise bean, a developer needs to code two interfaces (a home and component interface), a bean class, and a deployment descriptor.

  • The size and complexity of the deployment descriptor. Notice especially the size and content of the deployment descriptor for the entity bean with container-managed persistence in the previous example. Beyond that, the deployment descriptor contains a lot of information that appears in the interfaces and class of the bean -- in other words, the deployment descriptor contains a lot of redundant information.

  • The many rules that need to be followed. EJB technology is quite prescriptive. Rules such as "all home interfaces must extend javax.ejb.EJBHome" pervade the technology.

  • The need to implement all interface methods. Because an enterprise bean class implements an interface, it must also implement all of the methods in the interface, such as ejbActivate and ejbPassivate. This is required even if the bean doesn't use the interface methods. This adds to what many developers see as "code clutter."

  • The use of JNDI to locate and acquire enterprise bean objects. Some developers view JNDI as clumsy and non-intuitive.



EJB 3.0 technology aims to address these and other problems that contribute to perceived complexity.

Posted by 아름프로
BLOG main image

카테고리

분류 전체보기 (539)
이야기방 (19)
토론/정보/사설 (16)
IBM Rational (9)
U-IT (0)
SOA/WS/ebXML (110)
개발방법론/모델링 (122)
J2SE (34)
J2EE (60)
DataBase (39)
Open Projects (30)
BP/표준화 (50)
Apache Projects (15)
Web/보안/OS (22)
Tools (7)
AJAX/WEB2.0 (1)
Linux/Unix (1)
영어 (0)
비공개방 (0)

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

달력

«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

글 보관함

Total :
Today : Yesterday :