Example: explicit invalidation in the Persistence Manager Cache
Note: This page applies to WAS Version 5.0.2 and later only.
The scenario of use for this feature begins with configuring one or more bean types to be long-lifetime beans, and configuring the necessary Java Message Service (JMS) resources. After this is done, the server is started. The scenario continues as follows:
- Assume that a CMP entity bean of type Department has been configured to be a long-lifetime bean.
- 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.
- Transaction 2 begins. Application code calls findByPrimaryKey("dept01") again. Because this is not the first finder to return Department dept01, there is a cache hit and no database trip is made. Transaction 2 ends.
- Another application, which does not use the Department CMP bean, is run. This application may or may not be run on the WAS; 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 WAS. After performing the update, the application sends an invalidate request message to invalidate the Department bean dept01.
- Transaction 3 begins. The 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 Persistence Manager 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.
The public class PMCacheInvalidationRequest is central to the API, so a portion of its code is included for illustration purposes (if you see any differences between this illustration and the actual class, the class is to be considered correct):
package com.ibm.websphere.ejbpersistence; /** *An instance of this class represents a request to invalidate one or more *CMP beans in the PMcache.When an invalidation occurs,cached data for 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 *removes 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) { . . . } }The JMS resources used to make an invalidation request (TopicConnectionFactory, TopicDestination, and so forth) must be configured by the user 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 must names unique to the WAS namespace to avoid name conflict with customer JMS resources.
Configure the required JMS resources
The following are the names that must be used when the user configures the JMS resources required for enabling Persistence Manager cache invalidation. These resources must be configured for the WebSphere JMS Provider.
- Configure a new topic connection factory with the following properties:
- Name: InvalidateTCF
- JNDI name: com.ibm.websphere.ejbpersistence.InvalidateTCF
- Port: DIRECT
- XA Enabled: uncheck the checkbox
- Configure a new topic with the following properties:
- Name: invalidate
- JNDI name: com.ibm.websphere.ejbpersistence.invalidate
- Topic: com.ibm.websphere.ejbpersistence.invalidate
Finally, ensure that the com.ibm.ws.ejbpersistence.cacheinvalidatation=true property has been set to enable Persistence Manager cache invalidation.
Code example for sending a Persistence Manager cache invalidation request:
The following example code illustrates how to create a Persistence Manager cache invalidation request and how to send it using JMS. Exception handling has been ommitted from this example for simplicity.
// 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"; // Look up the TopicConnectionFactory InitialContext ic = new InitialContext(); TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory)ic.lookup(topicConnectionFactoryJNDIName); // Look up the Topic Topic topic = (Topic)ic.lookup(topicDestinationJNDIName); // Create a topic connection TopicConnection connection = topicConnectionFactory.createTopicConnection(); // Create a topic session boolean transacted = false; TopicSession session = connection.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE); // Create a topic publisher and enable the connection TopicPublisher pub = session.createPublisher(topic); connection.start(); // Create the PMCacheInvalidationRequest. Note that MyEJBHome // is the home interface type for the EJB ObjectMessage outMessage = session.createObjectMessage(); PMCacheInvalidationRequest invalidationRequest = new PMCacheInvalidationRequest("MyEJBHome"); outMessage.setObject(invalidationRequest); outMessage.setJMSType("cycle"); // Publish the invalidation request pub.publish(outMessage); // Close the JMS resources pub.close(); session.close(); connection.close;Note that 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, 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 the WAS Application Console.
Receiving acknowlegement of the invalidation JMS message
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. 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.