Using JDO in Entity Beans with BMP


When an entity bean uses container-managed persistence (CMP), the bean developer has no use for JDO, since the container takes care of persistence. On the other hand, when the bean developer writes an entity bean that implements bean-managed persistence (BMP), the developer can use JDO as the persistence service.

When the bean developer uses JDO, the entity bean delegates to a corresponding persistent application data object. In essence, the entity bean wraps a persistent application data object. The bean developer may decide to wrap each application data object in a corresponding entity bean, or he may decide to make the entity bean a coarse-grained object that delegates to a composition of many related fine-grained application data objects.

Listing 6-7 shows an example entity bean that implements the QuoteServer business interface. Omitted from the code in Listing 6-7 are the private bean methods that remain the same as those found in Listing 6-6. The example is taken from the com.ysoft.jdo.book.sayings.service.entity.QuoteServerEJB class.

Listing 6-7: Example of a BMP Entity Bean That Uses JDO

start example
 public class QuoteServerEJB implements javax.ejb.EntityBean, QuoteServer    {    private EntityContext context;    private ConnectionFactory cFactory;    private PersistenceManager pm;    private Query query;    private QuoteManager qm;    public Integer ejbCreate() throws CreateException       {       PersistenceManager persistenceManager = null;       try          {          // get a persistence manager          persistenceManager = getPersistenceManager();          QuoteManagerOID key = new QuoteManagerOID(                QuoteManager.getSingletonKey());          // if we get a QuoteManager then we can't create one          if (getQuoteManager(persistenceManager, false, key) != null)             {             throw new CreateException(                   "The one and only QuoteServer already exists");             }          // create the one and only QuoteManager          getQuoteManager(persistenceManager, true, key);          return key.toInteger();          }       catch (CreateException e)          {          throw e;          }       catch (ResourceException e)          {          throw new CreateException("Caught exception in ejbCreate: " + e);          }       finally          {          if (persistenceManager != null)             persistenceManager.close();          }       }    public void ejbPostCreate()       {       }    public void ejbRemove() throws RemoveException       {       throw new RemoveException("remove not allowed");       }    public Integer ejbFindByPrimaryKey(Integer key) throws FinderException       {       QuoteManagerOID oid = new QuoteManagerOID(key);       PersistenceManager persistenceManager = null;       try          {          // get a persistence manager          persistenceManager = getPersistenceManager();          // if we can't get the QuoteManager we can't find the QuoteServer          if (getQuoteManager(persistenceManager, false, oid) == null)             throw new ObjectNotFoundException(                   "can't find the only QuoteServer by key: " + oid +                   "; use Integer(" + QuoteManager.getSingletonKey() +                   ") for key");          return oid.toInteger();          }       catch (ResourceException e)          {          throw new FinderException(             "Caught exception in ejbFindByPrimaryKey: " + e);          }       finally          {          if (persistenceManager != null)             persistenceManager.close();          }       }    public void setEntityContext(EntityContext ctx)       {       context = ctx;       try           {           cFactory = JndiLocator.getCF("java:/jdoCF");           }       catch (NamingException e)          {          throw new EJBException(             "Unable to get ConnectionFactory using \"java:/jdoCF\" name: ", e);          }       }    public void unsetEntityContext()       {       cFactory = null;       context = null;       }    public void ejbActivate()       {       }    public void ejbPassivate()       {       cleanup();       }    public void ejbLoad()       {       }    public void ejbStore()       {       cleanup();       }    public void addQuote(String quote, String source) throws QuoteServerException       {       quote = normalizeString(quote);       source = normalizeString(source);       setup();       Quote q = qm.newQuote(quote, source);       pm.makePersistent(q);       }    public Quote getQuote() throws QuoteServerException       {       setup();       return (Quote) EJBHelper.respond(getQuote(pm, qm, query));       }    private PersistenceManager getPersistenceManager() throws ResourceException       {       return (PersistenceManager) cFactory.getConnection();       }    private void setup()       {       if (pm == null)          {          try             {             PersistenceManager tPM = getPersistenceManager();             query = getQuery(tPM);             QuoteManagerOID oid = new QuoteManagerOID(                   (Integer) context.getPrimaryKey());             qm = getQuoteManager(tPM, false, oid);             pm = tPM; // flags that setup is complete             }          catch (ResourceException e)             {             throw new EJBException("Unable to get PM: ", e);             }          }       }    private void cleanup()       {       if (pm != null)          {          // clear bean's references to persistent objects          qm = null;          // close the pm if not closed          if (!pm.isClosed())             pm.close();          // clear bean's references to pm and its resources          query = null;          pm = null; // also the flag that cleanup has occurred          }       }    // private methods are omitted    // when identical to those in earlier listings    } 
end example

In this example, the singleton QuoteManager object and all the Quote objects together compose the singleton QuoteServer entity. As a result, the QuoteServer entity bean implements the same QuoteServer business interface as the session beans. Although atypical in some ways, the QuoteServer entity bean provides a good example of how to use JDO within a BMP entity bean.

The EJB's Entity Context Callback Methods

The container calls the entity bean's setEntityContext method soon after creating the bean. The QuoteServer uses this method to obtain the persistence manager factory. The bean retains this reference until the container calls the unsetEntityContext method. The container may never call the unsetEntityContext method.

The QuoteServer Bean's setup and cleanup Methods

The setup and cleanup methods in the QuoteServer entity bean are private methods created for the example. The setup method performs the housekeeping chore of associating a QuoteServer entity bean instance with a persistence manager, a JDO transaction, and its delegate persistent object. The cleanup method performs the complementary set of tasks to disassociate the bean instance from all three.

In Listing 6-7, the setup method acquires a persistence manager from the factory, creates a query, and finds the singleton QuoteManager object in the database. The setup method stores each of these objects in the bean's member fields. The setup method is a no-op if the bean is already initialized. The cleanup method performs the opposite set of tasks. It closes the persistence manager and clears the bean's member fields of their references. The cleanup method is a no-op if the bean is already deinitialized.

On the one hand, the client may call the entity bean's business methods any number of times within one transaction without the EJB container making an intervening call to one of the EJB-defined callback methods. On the other hand, the container may call some EJB callback methods multiple times within the same transaction. The loose coupling between the bean's callback methods, on the one hand, and the transactional boundaries and the client's calls to business methods, on the other, makes the definition of the programming model for entity beans challenging.

The ejbStore Callback Method

After the client has invoked a business method on the entity bean, the EJB container calls the ejbStore method when the transaction is about to commit. Within the ejbStore method, the application code must close the persistence manager and clear the bean's references to persistent objects. To do this in Listing 6-7, the QuoteServer bean calls the cleanup method.

When the container calls ejbStore, the transaction is still active. The next call to the bean may be to a business method in a different transactional context from a different client, and it may occur before the container has completed the last transaction. As a result, the container's call to the ejbStore method is the bean's only chance to reliably disassociate itself from the persistence manager and its managed objects.

The Responsibility to Flush When ejbStore Is Called

The container may also call the ejbStore method to ensure that any modified state is flushed to the database for visibility to other beans running in the same transactional context. In this case, the container calls the ejbStore method in the middle of a transaction.

A common case where the container expects the bean to flush its state occurs when the client calls an entity bean's finder method. In this case, the EJB container calls the ejbStore method on the entity bean instances that are enlisted in the same transaction to ensure that the finder method, which is invoked on yet another entity bean, sees the current transactional state.

Because JDO does not provide a way to force persistent state to flush, the application cannot fulfill the contractual responsibility to flush changes to the underlying datastore. The impacts of this failure depend on whether the EJB container is clustered or not. If the EJB container is clustered, then the details of the clustered configuration determine the impact.

When the EJB container is not clustered, there is only one JVM running the EJB server. As a result, the failure to flush presents no difficulties. Every bean that uses JDO within the same transactional context will acquire from the connection factory or persistence manager factory the same persistence manager along with its managed objects. As a result, in a nonclustered environment, every bean instance that uses JDO in the same transactional context has the same visibility on the transactional state because every one of them uses the same persistence manager.

When the EJB container is clustered, there are multiple JVMs that are each running an EJB server. In a clustered environment, the inability to force JDO to flush the changes of transactional objects to the datastore may mean that not all entity beans in the same transactional context see the same transactional state. The impact of this failure is most likely to occur when the application calls an entity bean's finder method after making changes in the transaction. In this case, the finder method may ignore the changes held by the transactional objects in other JVMs. The application can avoid this impact by avoiding calls to EJB finder methods after changes have been made within the transaction.

If possible, the clustered EJB container should be configured to place all entity beans of the same type that have the same transactional context in the same EJB server. When this can be done, JDO's caching makes up for the lack of flushing just as it does in a nonclustered configuration. In addition to the workarounds suggested here, your JDO vendor or EJB vendor may offer guidance or special features for clustered configurations that address JDO's shortcomings in this regard.

The Entity EJB's Business Methods

The QuoteServer business methods call the setup method. The setup method ensures that the bean has the persistence manager associated with the managed transaction and has stored in its member field the reference to the QuoteManager object that it uses in its business methods. After the bean is initialized, calling the setup method again is a no-op.

Entity beans are not required to store references to persistent objects in their member fields, and they can, like CMT session beans, acquire a persistence manager and close it in each business method. Storing the persistence manager and some persistent objects in the bean's member fields are optimizations.

The question arises whether it is safe to close the persistence manager when the transaction may continue on to the next invocation of the bean's business interface. The answer is simple. Closing the persistence manager and reacquiring one within the same transactional context must always work in EJBs. Even in CMT session beans, the next call to a business method may be within the same managed transaction. Although the details may differ, all JDO implementations that support managed transactions must effectively delay closing the persistence manager until the managed transaction ends. If the transactional context does not change, the factory methods, getPersistenceManager and getConnection, must recycle the persistence manager associated with the transaction. As a result, the objects in the persistence manager's cache remain as they were. When the business method finds these objects the second time within the same managed transaction, it finds them in the same state that they were in when it last used them.

The ejbLoad Callback Method

The ejbLoad method is called whenever the container wants the entity bean to reload its persistent state from the database. The container may call the bean's ejbLoad method after it has started a transaction, or it may not. It depends on what commit option the container uses for the bean. As a result, the ejbLoad method is not a reliable place to acquire the persistence manager since it may not be called.

Note

For more discussion of commit options in entity beans, see sections 12.1.9 and 12.4.4 of the Enterprise JavaBeans Specification Version 2.0, available from the Sun Microsystems Web site.

The example bean in Listing 6-7 does nothing in the ejbLoad method. Instead, it calls its setup method at the start of every business method to lazily acquire the persistence manager and the references to persistent objects. When the example bean's setup method is called more than once without an intervening call to its cleanup method, the redundant calls are no-ops.

The ejbPassivate Callback Method

The EJB container calls the ejbPassivate method whenever it wants to dissociate the entity bean from its persistent identity and return it to the pool of bean instances. Although the container often calls the ejbStore method before it calls the ejbPassivate method, there are cases where this sequence is not followed. For this reason, the ejbPassivate method in Listing 6-7 calls the cleanup method to clear the bean's member fields and close the persistence manager. When the QuoteServer bean's cleanup method is called more than once without an intervening call to the setup method, the redundant calls are no-ops.

The ejbActivate Callback Method

The application code performs no actions related to JDO in the ejbActivate callback method.

The ejbCreate Methods

In entity beans, each ejbCreate method creates a new persistent object and inserts its persistent state into the datastore. There may be several ejbCreate methods that take different parameters for initializing the new persistent object. All of the ejbCreate methods return an application-defined class called the primary key class. Typically, the primary key class is the application identity class for the persistent object that the entity bean delegates to.

The ejbCreate methods associate the entity bean with its primary key object. As a result, these methods can set up the bean for later use by business methods, and they can rely upon ejbPassivate or ejbStore to call the cleanup method. Since the QuoteServer entity bean's ejbCreate method is rarely called, this bean's ejbCreate method cleans up after itself.

In the example entity bean in Listing 6-7, there is only one ejbCreate method. It takes no parameters, and it creates the singleton QuoteManager object. Rather than return the QuoteManagerOID application identity class, the example bean returns an Integer object from its ejbCreate method. As a result, the bean maps between the QuoteManagerOID objects and the Integer objects as necessary.

The ejbRemove Method

The ejbRemove method in entity beans deletes the state of the persistent object from the datastore. In the typical case, the method would call the getPrimaryKey method in the EntityContext object. It would use the primary key to find the corresponding persistent object and delete it by calling the persistence manager's deletePersistent method.

In the example entity bean in Listing 6-7, the entity cannot be deleted. Instead, the ejbRemove method throws a javax.ejb.RemoveException.

The ejbFindByPrimaryKey Method

Every entity bean is associated with a primary key object. In the ejbFindByPrimaryKey method, the container or the client asks the bean to verify that a persistent object exists for the primary key passed. In the example entity bean in Listing 6-7, the code performs the lookup after mapping from the entity bean primary key to the application identity object.

EJB finder methods do not associate the entity bean instance with a primary key. For this reason, all finder methods should set up for themselves and clean up after themselves. Like the business methods of CMT session beans, they should acquire a persistence manager at their start and close it before they return.

The EJB Finder Methods

Entity beans may have any number of finder methods. These work in a manner similar to the ejbFindByPrimaryKey method. Each finder method looks for the persistent objects that are delegates for its type of entity bean and returns a primary key object for each of them.

In Listing 6-7, the example bean does not have any finder methods other than the ejbFindByPrimaryKey method.

Serializing Data Objects in the Business Method's Response

As with the CMT session beans, it is necessary, in the general case, to deal with the possibility that the transaction has ended and the persistence manager has closed before the EJB container serializes the business method's response to the client. The QuoteServer entity bean shown in Listing 6-7 uses the respond method in the com.ysoft.jdo.book.common.ejb.EJBHelper class to create a graph of unmanaged objects to return as the response for the getQuote business method.

If the entity bean is always used by a client that controls the managed transaction, then the need to call the respond method is eliminated. In these cases, the client receives the response from the business method before it decides whether to end the transaction. This would be the case, for example, if the entity bean was used by a session bean as part of the session facade pattern.

The Switch to a Connection Factory

By comparing Listing 6-6 to Listing 6-7, you can see the changes required to use a connection factory instead of a persistence manager factory. Because the connection factory's getConnection method throws the javax.resource.ResourceException, the bean's private getPersistenceManager method now throws this exception. The business methods now catch the ResourceException and throw an EJBException in its place.

An entity bean, like all other EJBs, can use either a persistence manager factory or a connection factory to acquire the persistence manager.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

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