ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

J2EE Design Patterns: CMP-to-BMP Pattern
Pages: 1, 2

Vendor Specific: weblogic-ejb-jar.xml:

We define the vendor-specific information first in the weblogic-ejb-jar.xml. Here we tell the server where to place the home stub in JNDI, and where to look for the CMP mapping.



Persistence Mapping Info:

<persistence-type>
  <type-identifier>WebLogic_CMP_RDBMS</type-identifier>
  <type-version>6.0</type-version>
  <type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
</persistence-type>

Local JNDI Name:

<local-jndi-name>InventoryHome</local-jndi-name>

Vendor Specific: weblogic-cmp-rdbms-jar.xml

Datasource name:

(This datasource has to be created in the server configuration!)

<data-source-name>InventoryDB</data-source-name>

Table name:

<table-name>inventory</table-name>

Field mapping:

<field-map>
  <cmp-field>stock</cmp-field>
  <dbms-column>stock</dbms-column>
</field-map>
<field-map>
  <cmp-field>item</cmp-field>
  <dbms-column>item</dbms-column>
</field-map>
<field-map>
  <cmp-field>price</cmp-field>
  <dbms-column>price</dbms-column>
</field-map>

Now we have everything that we need. The container will take the abstract class and the deployment information to get everything working.

For some reason, we may want to take this CMP bean and migrate it to BMP. A few reasons why we would want to do this are:

  • Portability: Currently, it only works on WebLogic 6.1, and we don't want to learn all of the other tools. This is common if we want to sell this DB component -- it should run on any server.
  • Performance: If we wanted to fine tune the SQL that we use (e.g. use stored procedures, join across tables, etc.)
  • Alternate data sources: If we wanted to access a different data source, instead of a RDBMS.

Migrating the Bean to Use BMP

Here is where the pattern kicks in. We can migrate to BMP by "becoming the persistence manager." We saw that the persistence manager extends the abstract class that we created, where it implements the abstract methods and "does its stuff." We can do the same thing! The design will end up looking like Figure 2:

Diagram.
Figure 2. BMP design.

BMP Entity Bean

Let's walk through the bean class that will become the BMP bean.

Extend the abstract CMP bean class:

public class InventoryBeanBMP extends InventoryBean {

Create fields that hold the mapping:

public String item;
public float  price;
public int    stock;

Override the ejb*() methods:

To override the EJB methods, we need to remember what our responsibility is for each method in the BMP world.

EJB Method Responsibility
ejbLoad() Load the data from the database (SELECT)
ejbStore() Update the data back to the database (UPDATE)
ejbRemove() Delete the data from the database (DELETE)
ejbCreate() Insert a new row (INSERT)
ejbFindByPrimaryKey(primary key) Make sure the primary key exists
ejbFindAllPrices() Return a collection of primary keys for each item

Most of the methods look the same, so let's look at one of them (you can see the rest in the code download):

public String ejbCreate(String item, float price, int stock) throws CreateException {
   // insert row into database
   this.item  = item;
   this.price = price;
   this.stock = stock;

   // Insert database record
   try {
     Connection connection = getConnection();
     PreparedStatement statement = connection.prepareStatement
      ("INSERT INTO inventory (item, price, stock) VALUES (?, ?, ?)");
     statement.setString(1, item);
     statement.setFloat(2, price);
     statement.setInt(3, stock);
     if (statement.executeUpdate() != 1) {
      statement.close();
      connection.close();
      throw new CreateException("Could not create: " + item);
     }
     statement.close();
     connection.close();
     return item;
   }
   catch(SQLException e) {
     throw new EJBException("Could not create: " + item, e);
   }
}

Implement get and set methods:

public String getItem() {
   return this.item;
}
public void setItem(String item) {
   this.item = item;
}

public float getPrice() {
   return this.price;
}
public void setPrice(float price) {
   this.price = price;
}

public int getStock() {
   return this.stock;
}
public void setStock(int stock) {
   this.stock = stock;
}

Helper function to get a JDBC connection:

private Connection getConnection() throws SQLException {
   DataSource ds = null;

   try {
      Context ctx = new InitialContext();
      ds = (DataSource) ctx.lookup ("java:comp/env/jdbc/InventoryDB") ;
   } catch (NamingException exp) {
      exp.printStackTrace() ;
   }

   return (ds == null) ? null : ds.getConnection();
}

EJB Deployment Descriptors:

Standard ejb-jar.xml

Change settings from CMP:

The class points to the BMP class, not the abstract class, and we let the container know that we are managing the persistence:

<ejb-class>InventoryBeanBMP</ejb-class>
<persistence-type>Bean</persistence-type>

Add reference to JDBC data source: (used in getConnection() method above)

<resource-ref>
  <res-ref-name>jdbc/InventoryDB</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
</resource-ref>

Vendor Specific: weblogic-ejb-jar.xml

We don't have to do much with the vendor-specific files, as we did in the CMP version. We just need to point to the local JNDI name (same as before), and point the datasource to the real location, which is configured in the app server.

<reference-descriptor>
  <resource-description>
    <res-ref-name>jdbc/InventoryDB</res-ref-name>
    <jndi-name>inventoryDB</jndi-name>
  </resource-description>
</reference-descriptor>

And there we have it. We have reused the abstract class, overridden it to do the BMP implementation that we need, and then tweaked the deployment descriptors.

Conclusion

We have investigated a pattern that allows us to switch between CMP and BMP implementations in a nice clean manner. Our BMP implementation simply extends the CMP abstract bean class, and does the work! Notice that we could put in some logic in the get/set methods to make sure that we didn't do anything in ejbStore(), or did a tuned query. I still recommend that CMP beans be used when possible, and BMP used as a last resort.

Notes on Running the Sample Application

You can download the code for this sample pattern. There are two code sets, BMP and CMP (under directories of that name). A sample build script is provided (build.cmd) for compiling and deploying to BEA WebLogic 6.1. You may need to change a few directory paths in that script to get it working. To test the application, run the client via javac PricerClient. Check out the README.txt file for more information (setting up database table, etc.).

Dion Almaer is a Principal Technologist for The Middleware Company, and Chief Architect of TheServerSide.Com J2EE Community.


Return to ONJava.com.