Method Invocations in RosterApp

To show how the various components interact, this section describes the sequence of method invocations that occur for particular functions. The source code for the components is in the <INSTALL>/j2eetutorial14/examples/ejb/cmproster directory.

Creating a Player

1. RosterClient

The RosterClient invokes the createPlayer business method of the RosterBean session bean to create a new player. In the following line of code, the type of the myRoster object is Roster, the remote interface of RosterBean. The argument of the createPlayer method is a PlayerDetails object, which encapsulates information about a particular player.

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

2. RosterBean

The createPlayer method of the RosterBean session bean creates a new instance of the PlayerBean entity bean. Because the access of PlayerBean is local, the create method is defined in the local home interface, LocalPlayerHome. The type of the playerHome object is LocalPlayerHome. Here is the source code for the createPlayer method:

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());
  }
} 

3. PlayerBean

The ejbCreate method assigns the input arguments to the bean's persistent fields by calling the set access methods. At the end of the transaction that contains the create call, the container saves the persistent fields in the database by issuing an SQL INSERT statement. The code for the ejbCreate method follows.

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

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

Adding a Player to a Team

1. RosterClient

The RosterClient calls the addPlayer business method of the RosterBean session bean to add player P1 to team T1. The P1 and T1 parameters are the primary keys of the PlayerBean and TeamBean instances, respectively.

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

2. RosterBean

The addPlayer method performs two steps. First, it calls findByPrimaryKey to locate the PlayerBean and TeamBean instances. Second, it invokes the addPlayer business method of the TeamBean entity bean. Here is the source code for the addPlayer method of the RosterBean session bean:

public void addPlayer(String playerId, String teamId) { 

  try {
    LocalTeam team = teamHome.findByPrimaryKey(teamId);
    LocalPlayer player =
      playerHome.findByPrimaryKey(playerId);
    team.addPlayer(player);
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
} 

3. TeamBean

The TeamBean entity bean has a relationship field named players, a Collection that represents the players that belong to the team. The access methods for the players relationship field are as follows:

public abstract Collection getPlayers();
public abstract void setPlayers(Collection players); 

The addPlayer method of TeamBean invokes the getPlayers access method to fetch the Collection of related LocalPlayer objects. Next, the addPlayer method invokes the add method of the Collection interface. Here is the source code for the addPlayer method:

public void addPlayer(LocalPlayer player) {
  try {
    Collection players = getPlayers();
    players.add(player);
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
} 

Removing a Player

1. RosterClient

To remove player P4, the client would invoke the removePlayer method of the RosterBean session bean:

myRoster.removePlayer("P4"); 

2. RosterBean

The removePlayer method locates the PlayerBean instance by calling findBy-PrimaryKey and then invokes the remove method on the instance. This invocation signals the container to delete the row in the database that corresponds to the PlayerBean instance. The container also removes the item for this instance from the players relationship field in the TeamBean entity bean. By this removal, the container automatically updates the TeamBean-PlayerBean relationship. Here is the removePlayer method of the RosterBean session bean:

public void removePlayer(String playerId) { 
  try {
    LocalPlayer player =
      playerHome.findByPrimaryKey(playerId);
    player.remove();
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
} 

Dropping a Player from a Team

1. RosterClient

To drop player P2 from team T1, the client would call the dropPlayer method of the RosterBean session bean:

myRoster.dropPlayer("P2", "T1"); 

2. RosterBean

The dropPlayer method retrieves the PlayerBean and TeamBean instances by calling their findByPrimaryKey methods. Next, it invokes the dropPlayer business method of the TeamBean entity bean. The dropPlayer method of the RosterBean session bean follows:

public void dropPlayer(String playerId, String teamId) {

  try {
    LocalPlayer player =
      playerHome.findByPrimaryKey(playerId);
    LocalTeam team = teamHome.findByPrimaryKey(teamId);
    team.dropPlayer(player);
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
} 

3. TeamBean

The dropPlayer method updates the TeamBean-PlayerBean relationship. First, the method retrieves the Collection of LocalPlayer objects that correspond to the players relationship field. Next, it drops the target player by calling the remove method of the Collection interface. Here is the dropPlayer method of the TeamBean entity bean:

public void dropPlayer(LocalPlayer player) {

  try {
    Collection players = getPlayers();
    players.remove(player);
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
} 

Getting the Players of a Team

1. RosterClient

The client can fetch a team's players by calling the getPlayersOfTeam method of the RosterBean session bean. This method returns an ArrayList of PlayerDetails objects. A PlayerDetail object contains four variables--playerId, name, position, and salary--which are copies of the PlayerBean persistent fields. The RosterClient calls the getPlayersOfTeam method as follows:

playerList = myRoster.getPlayersOfTeam("T2"); 

2. RosterBean

The getPlayersOfTeam method of the RosterBean session bean locates the LocalTeam object of the target team by invoking the findByPrimaryKey method. Next, the getPlayersOfTeam method calls the getPlayers method of the TeamBean entity bean. Here is the source code for the getPlayersOfTeam method:

public ArrayList getPlayersOfTeam(String teamId) { 

  Collection players = null;

  try {
    LocalTeam team = teamHome.findByPrimaryKey(teamId);
    players = team.getPlayers();
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }

  return copyPlayersToDetails(players);
} 

The getPlayersOfTeam method returns the ArrayList of PlayerDetails objects that is generated by the copyPlayersToDetails method:

private ArrayList copyPlayersToDetails(Collection players) {

  ArrayList detailsList = new ArrayList();
  Iterator i = players.iterator();

  while (i.hasNext()) {
    LocalPlayer player = (LocalPlayer) i.next();
    PlayerDetails details = 
      new PlayerDetails(player.getPlayerId(),
        player.getName(), player.getPosition(),
        player.getSalary());
      detailsList.add(details);
  }

  return detailsList;
}  

3. TeamBean

The getPlayers method of the TeamBean entity bean is an access method of the players relationship field:

public abstract Collection getPlayers(); 

This method is exposed to local clients because it is defined in the local interface, LocalTeam:

public Collection getPlayers(); 

When invoked by a local client, a get access method returns a reference to the relationship field. If the local client alters the object returned by a get access method, it also alters the value of the relationship field inside the entity bean. For example, a local client of the TeamBean entity bean could drop a player from a team as follows:

LocalTeam team = teamHome.findByPrimaryKey(teamId);
Collection players = team.getPlayers();
players.remove(player); 

If you want to prevent a local client from modifying a relationship field in this manner, you should take the approach described in the next section.

Getting a Copy of a Team's Players

In contrast to the methods discussed in the preceding section, the methods in this section demonstrate the following techniques:

1. RosterClient

If you wanted to hide the salary of a player from a remote client, you would require the client to call the getPlayersOfTeamCopy method of the RosterBean session bean. Like the getPlayersOfTeam method, the getPlayersOfTeamCopy method returns an ArrayList of PlayerDetails objects. However, the objects returned by getPlayersOfTeamCopy are different: their salary variables have been set to zero. The RosterClient calls the getPlayersOfTeamCopy method as follows:

playerList = myRoster.getPlayersOfTeamCopy("T5"); 

2. RosterBean

Unlike the getPlayersOfTeam method, the getPlayersOfTeamCopy method does not invoke the getPlayers access method that is exposed in the LocalTeam interface. Instead, the getPlayersOfTeamCopy method retrieves a copy of the player information by invoking the getCopyOfPlayers business method that is defined in the LocalTeam interface. As a result, the getPlayersOfTeamCopy method cannot modify the players relationship field of TeamBean. Here is the source code for the getPlayersOfTeamCopy method of RosterBean:

public ArrayList getPlayersOfTeamCopy(String teamId) { 

  ArrayList playersList = null;

  try {
    LocalTeam team = teamHome.findByPrimaryKey(teamId);
    playersList = team.getCopyOfPlayers();
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }

  return playersList;
} 

3. TeamBean

The getCopyOfPlayers method of TeamBean returns an ArrayList of PlayerDetails objects. To create this ArrayList, the method iterates through the Collection of related LocalPlayer objects and copies information to the variables of the PlayerDetails objects. The method copies the values of PlayerBean persistent fields--except for the salary field, which it sets to zero. As a result, a player's salary is hidden from a client that invokes the getPlayersOfTeamCopy method. The source code for the getCopyOfPlayers method of TeamBean follows.

public ArrayList getCopyOfPlayers() {

  ArrayList playerList = new ArrayList();
  Collection players = getPlayers();

  Iterator i = players.iterator();
  while (i.hasNext()) {
    LocalPlayer player = (LocalPlayer) i.next();
    PlayerDetails details = 
      new PlayerDetails(player.getPlayerId(),
        player.getName(), player.getPosition(), 0.00);
      playerList.add(details);
  }

  return playerList;
} 

Finding the Players by Position

1. RosterClient

The client starts the procedure by invoking the getPlayersByPosition method of the RosterBean session bean:

playerList = myRoster.getPlayersByPosition("defender"); 

2. RosterBean

The getPlayersByPosition method retrieves the players list by invoking the findByPosition method of the PlayerBean entity bean:

public ArrayList getPlayersByPosition(String position) {

  Collection players = null;

  try {
    players = playerHome.findByPosition(position);
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }

  return copyPlayersToDetails(players);
} 

3. PlayerBean

The LocalPlayerHome interface defines the findByPosition method:

public Collection findByPosition(String position)
  throws FinderException; 

Because the PlayerBean entity bean uses container-managed persistence, the entity bean class (PlayerBean) does not implement its finder methods. To specify the queries associated with the finder methods, EJB QL queries must be defined in the bean's deployment descriptor. For example, the findByPosition method has this EJB QL query:

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

At runtime, when the container invokes the findByPosition method, it will execute the corresponding SQL SELECT statement.

For details about EJB QL, please refer to Chapter 29. To learn how to view and edit an EJB QL query in deploytool, see the section Finder/Select Methods Dialog Box (PlayerBean).

Getting the Sports of a Player

1. RosterClient

The client invokes the getSportsOfPlayer method of the RosterBean session bean:

sportList = myRoster.getSportsOfPlayer("P28"); 

2. RosterBean

The getSportsOfPlayer method returns an ArrayList of String objects that represent the sports of the specified player. It constructs the ArrayList from a Collection returned by the getSports business method of the PlayerBean entity bean. Here is the source code for the getSportsOfPlayer method of the RosterBean session bean:

public ArrayList getSportsOfPlayer(String playerId) { 

  ArrayList sportsList = new ArrayList();
  Collection sports = null;

  try {
    LocalPlayer player =
      playerHome.findByPrimaryKey(playerId);
    sports = player.getSports();
  } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
  }
  
  Iterator i = sports.iterator();
  while (i.hasNext()) {
    String sport = (String) i.next();
    sportsList.add(sport);
  }
  return sportsList;
} 

3. PlayerBean

The getSports method is a wrapper for the ejbSelectSports method. Because the parameter of the ejbSelectSports method is of type LocalPlayer, the getSports method passes along a reference to the entity bean instance. The PlayerBean class implements the getSports method as follows:

public Collection getSports() throws FinderException {

  LocalPlayer player = 
    (team.LocalPlayer)context.getEJBLocalObject();
  return ejbSelectSports(player);
} 

The PlayerBean class defines the ejbSelectSports method:

public abstract Collection ejbSelectSports(LocalPlayer player)
  throws FinderException; 

The bean's deployment descriptor specifies the following EJB QL query for the ejbSelectSports method:

SELECT DISTINCT t.league.sport
FROM Player p, IN (p.teams) AS t
WHERE p = ?1 

Because PlayerBean uses container-managed persistence, when the ejbSelectSports method is invoked the EJB container will execute its corresponding SQL SELECT statement.