Explicit invalidation in the Persistence Manager cache

 

Overview

 

CMP entity beans can be configured to have a long life by setting Lifetime In Cache Usage equal to a value other than the default OFF. This will cache bean data beyond the end of the transaction. When the specified time runs out, the cached data is no longer valid. Lifetime In Cache controls the basic length of time the cached data remains valid.

You can also explicitly invalidate cached data programatically. This is useful when apps that do not use CMP beans modify data that underlies a CMP bean, for example, in database. The app can inform WAS that any cached version of this bean data is stale and no longer matches what is in the database.

Because the PM Cache Invalidation mechanism does consume resources in exchange for its benefits, it is not enabled by default.

 

Enable PM Cache Invalidation

  1. Go to:

    Console | Servers | Application Servers | server | Process Definition | Java Virtual Machine

  2. Update the Generic JVM arguments with...

    -Dcom.ibm.ws.ejbpersistence.cacheinvalidation=true

 


Example: Explicit Invalidation in the Persistence Manager Cache

 

Usage Scenario

The scenario of use for this feature begins with configuring one or more bean types to be long-lifetime beans, and configuring the necessary JMS resources (described below). Once this is done, the server is started. The scenario continues as follows...

  1. Assume that a CMP entity bean of type Department has been configured to be a long-lifetime bean.

  2. Transaction 1 begins. Application code looks up Department's home and calls a finder method (such as findByPrimaryKey("dept01") ). As this is the first finder to return Department dept01, a trip is made to the database to obtain the data. Transaction 1 ends.

  3. Transaction 2 begins. Application code calls findByPrimaryKey("dept01") again. Because this is not the first finder to return Department dept01, we get a cache hit and no database trip is made. So far this is current WebSphere Application Server behavior for long-lifetime beans. Transaction 2 ends.

  4. Another application, which does not use the Department CMP bean, is executed. This application might or might not be run on the WebSphere Application Server; it could be a legacy application. The application updates the database table that is mapped to the Department bean, altering the row for dept01. For example, the budget column in the table (mapped to a Java double CMP attribute in the Deparment bean) is changed from $10,000.00 to $50,000.00. This application was designed to cooperate with WebSphere Application Server. After performing the update, the application sends an invalidate request message to invalidate the Department bean dept01.

  5. Transaction 3 begins. Application code looks up Department's home and calls a finder method (such as findByPrimaryKey("dept01") ). Because this is the first finder after Department dept01 is invalidated, a new database trip is made to obtain the data. Transaction 3 ends.

 

Persistence Manager cache invalidation API

The PM cache invalidation API is in the form of a JMS message that the client sends to a specially-named JMS topic using a connection from a specifically named JMS TopicConnectionFactory. The JMS message must be an ObjectMessage created by the client. The client code creates a PMCacheInvalidationRequest object that describes the bean data to invalidate. Client code places the PMCacheInvalidationRequest object in the ObjectMessage and publishes the ObjectMessage (for further details on the JMS objects and terms used here, please see the Java Message Service documentation).

The public class PMCacheInvalidationRequest is central to the API, so we include a portion of its code here for illustration purposes (if you see any differences between this illustration and the actual class, the class is to be considered correct)

packagecom.ibm.websphere.ejbpersistence;

/**
*An instance of this class represents a request to invalidate one or more
*CMP beans in the PMcache.When an invalidate occurs,cached datafor this
*bean is removed from the cache;the next time an application tries to find
*this bean,a fresh copy of the bean data is obtained from the data store.
*
*The ability to invalidate a bean means that a CMP bean may be configured
*as a long-lifetime bean and thus be cached across transactions for much
*greater performance on future attempts to find this bean.Yet when some
*outside mechanism updates the bean data,sending an invalidation request
*will remove stale data from the PMcache so applications do not behave falsely
*based on stale data.
*/
public class PMCacheInvalidationRequestimplementsSerializable{

. . .

/**
 * Constructor used to invalidate a single bean 
 * @param beanHomeJNDIName the JNDI name of the bean home. This is the same value
 * used to look up the bean home prior to calling findByPrimaryKey, for example.
 * @param beanKey the primary key of the bean to be invalidated. The actual 
 * object type must be the primary key type for this bean type.
 */
public PMCacheInvalidationRequest(String beanHomeJNDIName, Object beanKey)
 throws IOException {
. . .
}

/**
 * Constructor used to invalidate a Collection of beans  
 * @param beanHomeJNDIName java.lang.String the JNDI name of the bean home. 
 * This is the same value used to look up the bean home prior to calling 
 * findByPrimaryKey, for example.
 * @param beanKeys a Collection of the primary keys of the beans to be 
 * invalidated. The actual type of each object in the Collection must be the 
 * primary key type for this bean type.
*/
 public PMCacheInvalidationRequest(String beanHomeJNDIName, Collection beanKeys)
 throws IOException {
. . .
}

/**
 * Constructor used to invalidate all beans of a given type 
 * @param beanHomeJNDIName java.lang.String the JNDI name of the bean home.
 * This is the same value used to look up the bean home prior to calling 
 * findByPrimaryKey, for example.
 */
public PMCacheInvalidationRequest(String beanHomeJNDIName) {
 . . .
}

}

If the client wants to perform the invalidation in a synchronous way, it can opt to receive an acknowledgement JMS message when the invalidation is complete. To ask for an acknowledgement (ACK) message, the client sets a Topic of its own choosing in the JMSReplyTo field of the ObjectMessage for the invalidation request (see JMS documentation for further details). The client then waits (using the receive() method of JMS) on receipt of the acknowledgement message before continuing execution.

An ACK message enables the caller to insure there is not even a brief (seconds or less) window during which PM cache data is stale. The sending of an acknowledgement for each request does, of course, take a bit more time and so is recommended to be used only when needed.

The JMS resources used to make an invalidation request (TopicConnectionFactory, TopicDestination, and so forth) must be configured by the user (using the Administration console or other method) if they want to use PM Cache Invalidation. In this way the user can chose whichever JMS provider they prefer (as long as it supports pub-sub). The names that must be used for these resources are defined as part of the API, and use names unique to the WAS namespace to avoid name conflict with customer JMS resources.

The following are the names that must be used when the user configures the JMS resources (shown as Java constants for clarity)

// The JNDI name of the TopicConnectionFactory 
 private static final String topicConnectionFactoryJNDIName = "com.ibm.websphere.ejbpersistence.InvalidateTCF";
// The JNDI name of the TopicDestination
  private static final String topicDestinationJNDIName = "com.ibm.websphere.ejbpersistence.invalidate"; 
// The Topic name (part of the TopicDestination)
  private static final String topicString = "com.ibm.websphere.ejbpersistence.invalidate";


Here are examples of how these constants can be used in client code

// Look up the TopicConnectionFactory...
InitialContext ic = new InitialContext();
TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) ic.lookup(topicConnectionFactoryJNDIName);
...
// Look up the Topic
Topic topic = (Topic) ic.lookup(topicDestinationJNDIName);

JMS messages can be sent not only from J2EE application code (for example, a SessionBean or BMP entity bean method) but also from non-J2EE applications if your chosen JMS provider allows for this. For example, the IBM MQ provider, available in WAS as the Embedded Messaging feature (selectable during installation), supports the use of MQ classes (or structures in other languages) to create a topic connection and topic that are compatible with the TopicConnectionFactory and TopicDestination you configure using WAS Application Console.