4.2. Music Stateless Session Bean

Because read-only access is all we need with the Music Collection database, it makes sense to perform this task with a session bean. And because we do not need to keep track of client-specific data, a stateless session bean will suffice. Let's examine the code for the Music EJB now.

Home Interface

Listing 4.1 is the home interface (MusicHome) for the Music EJB. Since this is a stateless session bean, we need only a single create() method with no arguments. Note that this method returns the remote interface object, Music.

Listing 4.1. MusicHome.java
// MusicHome.java
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface MusicHome extends EJBHome {

    Music create() throws RemoteException, CreateException;
}

Remote Interface

The remote interface in Listing 4.2 contains the Music EJB's business methods. The getMusicList() method returns an ArrayList collection of recordings from the Music Collection database. Method getTrackList() accepts a RecordingVO object as an argument and returns an ArrayList collection of tracks for the specified recording. Both methods have the required RemoteException in their throws clauses. The getTrackList() method throws a user-defined NoTrackListException if a track list does not exist for a recording. This is used as a consistency check of the database.

Listing 4.2. Music.java
// Music.java
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.util.*;

public interface Music extends EJBObject {

   public ArrayList getMusicList() throws RemoteException;
   public ArrayList getTrackList(RecordingVO rec)
          throws NoTrackListException, RemoteException;
}

NoTrackListException is an example of an application exception. Recall that application exceptions are usually nonfatal errors produced by inconsistent user input or some other recoverable situation. The EJB container propagates the exception back to the client. While it's true that a recording without a track list may seem surprising to a client, the application could continue executing if the user selects a different recording.

NoTrackListException

Listing 4.3 shows the class definition for NoTrackListException. Since NoTrackListException extends Exception, we can catch this exception in any catch handler with an Exception signature. However, depending on the application, we might want to check for this specific exception type independently of other exceptions. We'll discuss this further when we present the application client later in the chapter.

Listing 4.3. NoTrackListException.java
// NoTrackListException.java
// Application Exception
public class NoTrackListException extends Exception {

  public NoTrackListException() { }
  public NoTrackListException(String msg) {
    super(msg);
  }
}

RecordingVO Class

In the previous chapter, our Loan EJB example used value objects LoanVO and PaymentVO to encapsulate data transmitted back and forth between a client and the Loan EJB. This handy J2EE design pattern can be applied here as well. We'll use two value objects: RecordingVO to encapsulate data associated with a recording, and TrackVO to encapsulate data associated with a track. Both classes implement the java.io.Serializable interface, since they'll be transmitted in remote calls to and from the Music EJB.

Listing 4.4 shows the code for the RecordingVO class, which defines only a constructor and getter methods to access the data. The class also implements an equals() method. This allows Java collection classes to compare two RecordingVO objects for equality by comparing recordID fields.

Listing 4.4. RecordingVO.java
// RecordingVO.java
public class RecordingVO implements java.io.Serializable
{
  private int recordID;
  private String title;
  private String artistName;
  private String musicCategory;
  private String label;
  private int numberOfTracks;

  public RecordingVO(int recordID, String title,
      String artistName, String musicCategory,
     String label, int numberOfTracks)
  {
    this.recordID = recordID;
    this.title = title;
    this.artistName = artistName;
    this.musicCategory = musicCategory;
    this.label = label;
    this.numberOfTracks = numberOfTracks;
  }

  public int getRecordID() { return recordID; }
  public String getTitle() { return title; }
  public String getArtistName() { return artistName; }
  public String getMusicCategory()
        { return musicCategory; }
  public String getLabel() { return label; }
  public int getNumberOfTracks() { return numberOfTracks; }
  // Implement equals; convenient way to determine if
  // 2 RecordingVO objects represent the same recording.
  // Used by Java Collection classes to indicate
  // successful removal from Collection.
  public boolean equals(Object rec) {
        return (recordID == ((RecordingVO)rec).recordID);
  }
}

If you return to Figure 4-2 on page 88, you'll note that our RecordingVO object does not appear in the diagram. That's because it's an abstraction of a class that selects data from three different tables. By encapsulating data from disparate sources into a single object, we group component information that belongs together. This abstraction hides the underlying database representation and creates a special kind of value object called a Composite Object.

Design Guideline

Composite objects make the Music EJB interface very simple and reduce the number of remote calls required to fetch data. The Music EJB returns all the recordings in one call, and a single call returns the track data associated with a specific recording.


TrackVO Class

The TrackVO class in Listing 4.5 has a constructor to store track information and getter methods to access the data.

Listing 4.5. TrackVO.java
// TrackVO.java
public class TrackVO implements java.io.Serializable
{
   private int trackNumber;
   private String title;
   private String trackLength;
   public TrackVO(int trackNumber, String title,
      String trackLength) {
         this.trackNumber = trackNumber;
         this.title = title;
         this.trackLength = trackLength;
    }

    public String getTitle() { return title; }
    public String getTrackLength() { return trackLength; }
    public int getTrackNumber() { return trackNumber; }
}

Bean Implementation Class

Now let's look at the heart of the Music Collection design, the implementation of the Music EJB. The MusicBean implementation in Listing 4.6 is divided into three sections. The first section contains the implementation of the getMusicList() and getTrackList() business methods. Both methods make calls to private helper functions that obtain connections to the database. By moving the database SQL code out of the business methods into private methods, we isolate database-dependent code. Furthermore, if we create a Data Access Object (DAO) to further isolate database access, there will be minimal impact to our bean implementation code. (We implement the DAO pattern later in this chapter; see “Data Access Object Pattern” on page 119.)

The second section of our bean implementation class contains the EJB methods, most of which are empty. The ejbCreate() method, however, performs the database name lookup and DataSource instantiation. Note that we save the DataSource instantiation in private instance variable ds for access in other methods. Doesn't this imply that our stateless session bean is maintaining state information? It's perfectly all right for stateless session beans to store state information as long as the data does not contain any client-specific information. A stateless session bean, therefore, can store client independent data if the information doesn't change. Furthermore, the EJB container may create copies of the data when instantiating multiple bean instances and this operation must be acceptable. Since we should always perform the lookup and instantiation once within the life cycle of an EJB, the ejbCreate() method is a good spot to perform these “once-only” initialization tasks.

The third section of the MusicBean class contains the private helper methods. We have one method to connect to the database and a second one to disconnect. We also have two database access methods, dbLoadMusicList() and dbLoadTrackList(). Each method connects to the database, defines an SQL PreparedStatement object, uses the JDBC API to read data from the database, builds the appropriate value object, and adds the value object to the ArrayList collection. Note that although we instantiate the DataSource just once, we wait and acquire the database connection inside the method that actually accesses the database. When we finish reading from the database, we release the database connection and close the open JDBC objects in finally blocks. We therefore keep the connection open only as long as we need it.

Listing 4.6. MusicBean.java
// MusicBean.java
import java.sql.*;
import javax.sql.*;
import java.util.*;
import javax.ejb.*;
import javax.naming.*;
import java.rmi.RemoteException;

public class MusicBean implements SessionBean {

  // Business methods

  public ArrayList getMusicList()
  {
    try {
      // Encapsulate database calls in separate method
      return (dbLoadMusicList());
    } catch (Exception ex) {
      throw new EJBException("getMusicList: " +
           ex.getMessage());
    }
  }

  public ArrayList getTrackList(RecordingVO rec)
    throws NoTrackListException {

    ArrayList trackList;
    try {
      // Encapsulate database calls in separate method
      trackList = dbLoadTrackList(rec);
    } catch (Exception ex) {
      throw new EJBException("getTrackList: " +
        ex.getMessage());
    }
    if (trackList.size() == 0) {
      throw new NoTrackListException(
        "No Track List found for RecordingID " +
        rec.getRecordID());
    }
    return trackList;
  }

  // JDBC Database Connection
  private Connection con = null;
  private DataSource ds;            // DataSource

  // EJB methods

  // Do the DataSource lookup just once
  // (put the lookup in ejbCreate() method)
  public void ejbCreate() {
    String dbName = "java:comp/env/jdbc/MusicDB";
    try {
      InitialContext ic = new InitialContext();
      ds = (DataSource) ic.lookup(dbName);

    } catch (Exception ex) {
      throw new EJBException("Cannot find DataSource: " +
         ex.getMessage());
    }
  }

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

  // Database methods

  // Obtain a JDBC Database connection
  private void getConnection()
  {
    try {
      con = ds.getConnection();
    } catch (Exception ex) {
      throw new EJBException(
         "Cannot connect to database: " + ex.getMessage());
    }
  }

  // Release JDBC Database connection
  private void disConnect()
  {
    try {
      if (con != null) con.close();
    } catch (SQLException ex) {
      throw new EJBException("disConnect: " +
         ex.getMessage());
    }
  }

  private ArrayList dbLoadMusicList() throws SQLException
  {
    ArrayList mList = new ArrayList();

    // Objects needed for the JDBC API SQL calls:
    PreparedStatement musicStmt = null;
    ResultSet rs = null;

    // The following query searches 3 tables in
    // the Music Collection database to create a composite
    // RecordingVO object.
    // We select all fields in the Recordings table,
    // the RecordingArtistName field in the
    // Recording Artists table, and the MusicCategory
    // field from the Music Categories table.
    // The Where clause matches the correct records from
    // the Recording Artists and Music Categories tables.
    String selectQuery = "Select Recordings.*, " +
      ""Recording Artists".RecordingArtistName, " +
      ""Music Categories".MusicCategory " +
      "From Recordings, "Recording Artists", " +
      " "Music Categories" " +
      "Where Recordings.RecordingArtistID = " +
      ""Recording Artists".RecordingArtistID and " +
      "Recordings.MusicCategoryID = " +
      ""Music Categories".MusicCategoryID ";

    try {
      // Obtain a database connection
      getConnection();
      musicStmt = con.prepareStatement(selectQuery);

     // Execute the query; results are in ResultSet rs
     rs = musicStmt.executeQuery();

     // Loop through ResultSet and create RecordingVO
     // object for each row
     while (rs.next())
     {
       // Create RecordingVO object and
       // add to mList ArrayList
       // Use getInt() and getString() to pull data from
       // ResultSet rs.
       // The parameter numbers match the order of the
       // field names in the query statement.

       mList.add(new RecordingVO(
         rs.getInt(1),       // RecordingID
         rs.getString(2),    // RecordingTitle
         rs.getString(9),    // RecordingArtistName
         rs.getString(10),   // MusicCategory
         rs.getString(5),    // RecordingLabel
         rs.getInt(7)));     // NumberofTracks
    }

  } catch (SQLException ex) {
    throw new EJBException(
      "SQLException reading recording data:
" +
      ex.getMessage());
    } finally {
      // Close the ResultSet and the
      // PreparedStatement objects
      if (rs != null) rs.close();
      if (musicStmt != null) musicStmt.close();
      // Release the database connection
      disConnect();
    }
    return mList;
  }

  private ArrayList dbLoadTrackList(RecordingVO rec)
        throws SQLException
  {
    ArrayList tList = new ArrayList();
    // Objects needed for the JDBC API SQL calls:
    PreparedStatement trackStmt = null;
    ResultSet rs = null;

    // Select all records from Tracks table
    // where RecordingID is the same as the In parameter
    // Order the records by TrackNumber field
    String selectQuery = "Select * From Tracks " +
      "Where RecordingID = ? Order By TrackNumber";

    try {
      // Obtain a database connection
      getConnection();
      // Get a PreparedStatement with query
      trackStmt = con.prepareStatement(selectQuery);
      // Set In parameter to RecordingID argument
      trackStmt.setInt(1, rec.getRecordID());
      // Execute query; return in ResultSet rs
      rs = trackStmt.executeQuery();

      while (rs.next())
      {
        // Loop through ResultSet, create TrackVO object
        // and add to tList ArrayList
        tList.add(new TrackVO(
        rs.getInt(2),         // trackNumber
        rs.getString(3),      // title
        rs.getString(4)));    // trackLength
      }

    } catch (SQLException ex) {
      throw new EJBException(
        "SQLException reading track data:
" +
        ex.getMessage());

    } finally {
      if (rs != null) rs.close();
      if (trackStmt != null) trackStmt.close();
      // Release the database connection
      disConnect();
    }
    return tList;
  }
} // MusicBean

Design Guideline

You should always access a database by obtaining a connection from the EJB container's DataSource. This allows the container to efficiently assign connections from a connection pool. Because obtaining the connection doesn't cost much, we acquire it just before accessing the database. After we're finished reading or writing the database, we release the connection. Note that the code to release our database connection and the JDBC objects is inside a finally block. This ensures that the connection resources are always released, even if the system throws exceptions.


Design Guideline

Whenever possible use the JDBC PreparedStatement instead of Statement. This allows the database to cache the “compiled” statement internally, avoiding multiple recompilations when the text of the query is unchanged. This provides increased performance when a database has a large number of client requests.


Deployment Descriptor

Since we've added database access to this enterprise bean, let's examine the effects on the deployment descriptor. Listing 4.7 shows the deployment descriptor for the Music EJB. The information describing the database is included within the tags <resource-ref> and </resource-ref>. (We show the tag values in bold.) The resource name is jdbc/MusicDB, its type is javax.sql.DataSource, and the EJB container provides authorization. The resource is shareable.

Listing 4.7. Deployment Descriptor for MusicEJB
<ejb-jar>
 <display-name>MusicSessionJAR</display-name>
 <enterprise-beans>

    <session>
      <display-name>MusicEJB</display-name>
      <ejb-name>MusicEJB</ejb-name>
      <home>MusicHome</home>
      <remote>Music</remote>
      <ejb-class>MusicBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Bean</transaction-type>

      <security-identity>
        <description></description>
        <use-caller-identity></use-caller-identity>
      </security-identity>

      <resource-ref>
        <res-ref-name>jdbc/MusicDB</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
      </resource-ref>
    </session>
 </enterprise-beans>
</ejb-jar>

..................Content has been hidden....................

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