Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Object Grid

Download as pdf or txt
Download as pdf or txt
You are on page 1of 215

WebSphere WebSphere Extended Deployment Version 6.

ObjectGrid programming model guide

Note Before using this information, be sure to read the general information under Notices on page 207.

Compilation date: August 26, 2005 Copyright International Business Machines Corporation 2004, 2005. All rights reserved. US Government Users Restricted Rights Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.

Contents
How to send your comments . . . . . v Chapter 1. Getting started with ObjectGrid . . . . . . . . . . . . . 1
Running the ObjectGrid sample application on the command line . . . . . . . . . . . . . Importing and using the ObjectGrid sample application in Eclipse . . . . . . . . . . Loading and running the ObjectGrid sample application with WebSphere Extended Deployment . 1 . 3 . 5 Loader considerations . . . . . . ObjectTransformer plug-in . . . . . TransactionCallback plug-in . . . . OptimisticCallback interface . . . . ObjectGrid configuration . . . . . Basic ObjectGrid configuration . . Complete ObjectGrid configuration . Mixed mode ObjectGrid configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 104 108 115 118 119 119 130

Chapter 6. Integrating ObjectGrid with WebSphere Application Server . . . . 133


Integrating ObjectGrid in a Java 2 Platform, Enterprise Edition environment . . . . . . . Building ObjectGrid-enabled Java 2 Platform, Enterprise Edition applications . . . . . . Considerations for the integration of Java 2 Platform, Enterprise Edition applications and ObjectGrid . . . . . . . . . . . . . Monitoring ObjectGrid performance with WebSphere Application Server performance monitoring infrastructure (PMI) . . . . . . . ObjectGrid statistics . . . . . . . . . . Enabling ObjectGrid PMI . . . . . . . . Retrieving ObjectGrid PMI statistics . . . . . ObjectGrid distributed transaction propagation . . Requirements . . . . . . . . . . . . Overview. . . . . . . . . . . . . . Distributed transaction propagation configuration . . . . . . . . . . . . Distributed transaction propagation distribution mode options . . . . . . . . . . . . ObjectGrid and external transaction interaction . . Integrating ObjectGrid and the partitioning facility ObjectGrid and the partitioning facility . . . . Installing and running the ObjectGridPartitionCluster sample application . Building an integrated ObjectGrid and partitioning facility application . . . . . . Example: ObjectGrid and partitioning facility programming . . . . . . . . . . . . 133 135

Chapter 2. ObjectGrid . . . . . . . . . 7 Chapter 3. ObjectGrid tutorial : application programming model . . . . 11


System programming model overview . . . . System programming model overview: ObjectGrid interface plug points and features . System programming model overview: BackingMap interface plug points and features System programming model overview: Session interface features . . . . . . . . . . System programming model overview: ObjectMap interface features . . . . . . . . 13 . 15 . 17 . 24 . 25

136

Chapter 4. ObjectGrid samples . . . . 29 Chapter 5. ObjectGrid application programming interface overview . . . 33


ObjectGridManager interface . . . . . . . createObjectGrid methods . . . . . . . getObjectGrid methods . . . . . . . . removeObjectGrid methods . . . . . . . Use the ObjectGridManager interface to control the life cycle of an ObjectGrid instance . . . Trace ObjectGrid . . . . . . . . . . . ObjectGrid interface . . . . . . . . . . BackingMap interface . . . . . . . . . . Session interface . . . . . . . . . . . . ObjectMap and JavaMap interfaces . . . . . Keywords . . . . . . . . . . . . . . LogElement and LogSequence objects . . . . Locking . . . . . . . . . . . . . . Pessimistic locking . . . . . . . . . . Optimistic locking . . . . . . . . . . None . . . . . . . . . . . . . . ObjectGrid security . . . . . . . . . . . Authentication . . . . . . . . . . . ObjectGrid authorization . . . . . . . . Security integration with WebSphere Extended Deployment servers . . . . . . . . . Listeners . . . . . . . . . . . . . . Evictors . . . . . . . . . . . . . . Loaders. . . . . . . . . . . . . . .
Copyright IBM Corp. 2004, 2005

136 137 140 142 143 144 144 146 146 148 151 151 153 156 160

. . . . . . . . . . . . . . . . . . . . . . .

33 33 36 37 37 39 40 42 45 48 52 54 58 58 64 65 66 67 71 76 79 84 92

Chapter 7. ObjectGrid performance best practices . . . . . . . . . . . 173


Locking performance best practices . . . copyMode method best practices . . . . ObjectTransformer interface best practices . Plug-in evictor performance best practices . Default evictor best practices . . . . . . . . . . . . . . . . . . . . 173 174 178 179 181

Chapter 8. Distributing changes between peer Java virtual machines . 183


Java Message Service for distributing transaction changes . . . . . . . . . . . . . . . 186

iii

Chapter 9. Injection-based container integration . . . . . . . . . . . . 189 Chapter 10. Troubleshooting . . . . . 191


Intermittent and unexplained errors . General exception handling technique Specific exception handling techniques Optimistic collision exception . . . LockTimeoutException exception . . LockDeadlockException . . . . . XML configuration problem diagnosis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 191 192 192 193 195 198

Missing a required attribute . . Missing a required element . . . XML value of attribute is not valid Validating XML without support of implementation. . . . . . . ObjectGrid messages . . . . .

. . . an . .

. . . . .

. . . . .

. . . . .

. . . . .

. 199 . 200 . 201 . 202 . 203

Notices . . . . . . . . . . . . . . 207 Trademarks and service marks . . . . 209

iv

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

How to send your comments


Your feedback is important in helping to provide the most accurate and highest quality information. v To send comments on articles in the WebSphere Extended Deployment Information Center, available at: http://www.ibm.com/software/webservers/appserv/extend/library/ 1. Display the article in your Web browser and scroll to the end of the article. 2. Fill out the Feedback link at the bottom of the article and submit. v To send comments on this or another PDF books, you can e-mail your comments to: wasdoc@us.ibm.com. Be sure to include the document name and number, and, if applicable, the specific page, table, or figure number on which you are commenting. When you send information to IBM, you grant IBM a nonexclusive right to use or distribute the information in any way it believes appropriate without incurring any obligation to you.

Copyright IBM Corp. 2004, 2005

vi

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 1. Getting started with ObjectGrid


Use this topic to get started with ObjectGrid, a distributed computing framework that makes objects available to a set of applications. WebSphere Extended Deployment Version 6.0 and WebSphere Application Server Version 6.0.2 or higher must be installed on at least one machine in your environment. Restriction: You can use ObjectGrid in a WebSphere Extended Deployment Version 6.0 environment. You can also use ObjectGrid in a Java 2 Platform, Standard Edition (J2SE) Version 1.4.2 or higher environment or in a WebSphere Application Server Version 6.02 or higher environment with additional licensing arrangements. Contact your sales representative for details. If you want to develop ObjectGrid applications without accessing server machines that have WebSphere Extended Deployment installed, you can run them on your local machine. The local machine requires the installation of an IBM Software Developer Kit (SDK) or Eclipse. To develop ObjectGrid applications on your local machine, copy the <install_root>/optionalLibraries/ObjectGrid/objectgridSA.jar file and the <install_root>/optionalLibraries/ObjectGrid/objectgridSamples.jar file from the WebSphere Extended Deployment server to a working directory on your local machine. Use this task to run and step through ObjectGrid sample applications. You can run the applications in this task in a Java command line, Eclipse, or Java 2 Platform, Enterprise Edition (J2EE) environment. v To get the ObjectGrid sample application running on the command line, see Running the ObjectGrid sample application on the command line. v To run the ObjectGrid sample application in Eclipse, see Importing and using the ObjectGrid sample application in Eclipse. v To run the ObjectGrid sample application on WebSphere Extended Deployment, see Loading and running the ObjectGrid sample application with WebSphere Extended Deployment You got started with ObjectGrid by running the sample application and loading the sample into your development environment. Related concepts Chapter 2, ObjectGrid, on page 7 ObjectGrid is an extensible transactional object caching framework for Java 2 Platform, Standard Edition (J2SE) and Java 2 Platform, Enterprise Edition (J2EE) applications.

Running the ObjectGrid sample application on the command line


Use this topic to run ObjectGrid-enabled applications on a Java command line and test your ObjectGrid configuration.

Copyright IBM Corp. 2004, 2005

You must have a Software Development Kit (SDK) installed. You also must have access to the ObjectGrid sample applications. See Getting started with ObjectGrid for more information. Use this task to quickly run an application with ObjectGrid enabled. 1. Check your Software Development Kit (SDK) version. ObjectGrid requires an IBM SDK 1.4.2 or higher. To test your Java environment before running the ObjectGrid sample application, perform the following steps: a. Open a command-line prompt. b. Type the following command:
java -version

If the command runs correctly, text similar to the following example displays:
java version "1.4.2" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2) Classic VM (build 1.4.2, J2RE 1.4.2 IBM Windows 32 build cn142-20040820 (JIT enabled: jitc))

If an error displays, ensure that the SDK is installed and is in your CLASSPATH. 2. Run the ObjectGrid sample application. The sample application illustrates a simple case that involves employees, offices, and work locations. The sample application creates an ObjectGrid instance with maps for each object type. Each map has entries inserted and manipulated to demonstrate the ObjectGrid caching function. a. Open a command line and navigate to the working directory. If you are running on a local machine, copy the .jar files to a working directory. If you are using WebSphere Extended Deployment, you can navigate to the <install_root>/optionalLibraries/ObjectGrid directory. b. Issue the following command:
cd working_directory java -cp "objectgridSA.jar;objectgridSamples.jar" com.ibm.websphere.samples.objectgrid.basic.ObjectGridSample

The system displays output that is similar to the following text. This output has been shortened for publishing purposes:
Initializing ObjectGridSample ... resourcePath: METAINF/objectgriddefinition.xml objectgridUrl: jar:file:/C:/temp/objg/objectgridSample.jar!/ METAINF/objectgriddefinition.xml EmployeeOptimisticCallback returning version object for employee = Perry Cheng, version = 0 EmployeeOptimisticCallback returning version object for employee = Hao Lee, version = 0 EmployeeOptimisticCallback returning version object for employee = Ken Huang, version = 0 EmployeeOptimisticCallback returning version object for employee = Jerry Anderson, version = 0 EmployeeOptimisticCallback returning version object for employee = Kevin Bockhold, version = 0 com.ibm.websphere.samples.objectgrid.basic.ObjectGridSample status: ivObjectGrid Name = clusterObjectGrid ivObjectGrid = com.ibm.ws.objectgrid.ObjectGridImpl@187b81e4 ivSession = com.ibm.ws.objectgrid.SessionImpl@6b0d81e4 ivEmpMap = com.ibm.ws.objectgrid.ObjectMapImpl@6b1841e4

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

ivOfficeMap = com.ibm.ws.objectgrid.ObjectMapImpl@6ba081e4 ivSiteMap = com.ibm.ws.objectgrid.ObjectMapImpl@6bae01e4 ivCounterMap = com.ibm.ws.objectgrid.ObjectMapImpl@697b41e4 interactiveMode = false Action = populateMaps CounterOptimisticCallback returning version object for counter name = Counter1, version = 0 CounterOptimisticCallback returning version object for counter name = Counter2, version = 0 CounterOptimisticCallback returning version object for counter name = Counter3, version = 0 ivCounterMap operations committed ivOfficeMap operations committed ... ending with: CounterOptimisticCallback returning version object for counter name = Counter1, version = 0 EmployeeOptimisticCallback returning version object for employee = Ken Huang, version = 0 CounterOptimisticCallback returning version object for counter name = Counter2, version = 0 EmployeeOptimisticCallback returning version object for employee = Perry Cheng, version = 0 CounterOptimisticCallback returning version object for counter name = Counter3, version = 0 EmployeeOptimisticCallback returning version object for employee = Jerry Anderson, version = 0 CounterOptimisticCallback returning version object for counter name = Counter4, version = 0 EmployeeOptimisticCallback returning version object for employee = Hao Lee, version = 0 EmployeeOptimisticCallback returning version object for employee = Kevin Bockhold, version = 1 DONE cleanup

You ran the ObjectGrid sample application on a Java command line to test the ObjectGrid functionality. If you are interested in viewing the source for this sample, it is located in the objectgridSamples.jar file, specifically in the com\ibm\websphere\samples\objectgrid\basic\ObjectGridSample.java file.

Importing and using the ObjectGrid sample application in Eclipse


Use this task to import and use the ObjectGrid sample application in Eclipse. For this sample application, use Eclipse Version 3.0 or later to import and run the sample. You can obtain Eclipse from the Application Server Toolkit that is included with WebSphere Application Server, from installing Rational Application Developer, or by downloading it directly from Eclipse.org. By using Eclipse, you can easily debug your applications. You can perform a step-by-step walk through of the sample application. 1. Import the project into Eclipse: a. Run the Eclipse program. Use the eclipse.exe file in the Eclipse installation directory. b. Using Eclipse, create a new project. 1) Click File > New > Project > Java > Java Project. Click Next. 2) Type a project name. For example, type ObjectGridSamples. 3) Select Create new project in workspace.
Chapter 1. Getting started with ObjectGrid

4) 5) 6) 7) 8) 9)

In the Project Layout section, click Configure default. For the source and output folder, select Project and click OK. Click Next. Click the Libraries tab. Click Add External JARs. Navigate to the <install_root>/optionalLibraries/ObjectGrid folder and select the ObjectGridSA.jar file. Click Open in JAR selection wizard.

10) Click Finish. 2. Import the objectgridSamples.jar file into the Java Project. a. Right-click on the Java project and select Import. b. Select Zip file from the Import source list. c. Click Next. d. Click Browse to open the Import From Zip File wizard. e. Navigate to the <install_root>/optionalLibraries/ObjectGrid directory. Select the objectgridSamples.jar file and click Open. f. Verify that the check box of the root file tree is selected. g. Verify that the Into folder is the Java project that you created in the previous step, for example, the ObjectGridSamples project. h. Click Finish. 3. Check the properties of the Java Project. a. Open the Java Perspective. Click Window > Open Perspective > Java. b. Go to the console view. Click Window > Show view > Console. c. Verify that the Package Explorer view is available and selected. Click Window > Show View > Package Explorer. d. Right-click on the Java project and select Properties. e. Click Java Build Path on the left panel. f. Click the Source tab in the right panel. g. Verify that the project root is listed in the Source folders on the Build path panel. h. Click the Libraries tab in the right panel. i. Verify that the objectgridSA.jar file and a JRE System Library are listed in the JAR and class folders on the Build path panel. j. Click OK. 4. Run the ObjectGrid sample. a. From the Package Explorer view, expand the Java project. b. Expand the com.ibm.websphere.samples.objectgrid.basic package. c. Right-click on the ObjectGridSample.java file. Click Run > Java Application. d. The console displays similar output to when you run the application on the Java command line. For an example of the output, see Running the ObjectGrid sample application on the command line. You can also see the steps to load the project in Eclipse and run the debugger in the objectGridSamples.jar file, which is in the SamplesGuide.htm file. You can find the SamplesGuide.htm in the <install_root>/optionalLibraries/ObjectGrid/doc directory.

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Loading and running the ObjectGrid sample application with WebSphere Extended Deployment
Use this task to load and run the Java 2 Platform, Enterprise Edition (J2EE) ObjectGrid sample within WebSphere Extended Deployment. WebSphere Application Server and WebSphere Extended Deployment must be installed. Use this task to understand and test the integration of ObjectGrid with WebSphere Extended Deployment. For more information, see Chapter 6, Integrating ObjectGrid with WebSphere Application Server, on page 133. 1. Install the ObjectGridSample.ear file. You can install the enterprise archive (EAR) file on a single application server or a cluster. To install the ObjectGridSample.ear file in the administrative console, perform the following steps: a. In the administrative console, click Applications > Install New Application. b. On the Preparing for application installation page, specify the location of the ObjectGrid sample application. For example, browse to: <install_root>/installableApps/ObjectGridSample.ear. Click Next. c. On the second Preparing for application installation page, take the default settings and click Next. d. On the Select installation options page, take the default settings and click Next. e. On the Map modules to servers page, specify deployment targets where you want to install the modules that are contained in your application. Select a target server or cluster from the Clusters and servers list for every module. Select the Module check box to select all of the application modules or select individual modules. f. On the following pages, use the default values and click Finish. g. Click Save to Master Configuration after finishing the application installation. h. Click the Synchronize changes with Nodes option. On the Enterprise Applications > Save page, click Save. i. Click OK. 2. Check the HTTP port of the default_host of the servers and add a host alias. By default, Web modules are bound to the default_host virtual host name, unless you modify the host name during installation. If you are installing the application on a cluster, you must configure at least one host alias for the HTTP port of the default_host for each cluster member. You also must check the HTTP port of the default_host for each cluster member and add the corresponding host alias into the Host aliases list in the administrative console. To check the HTTP port of the default_host of a server, perform the following steps: a. In the administrative console, click Servers > Application Servers > server_name. b. Expand the ports in the Communication section. The WC_defaulthost port is the default_host virtual host name. To add a host alias, perform the following steps:

Chapter 1. Getting started with ObjectGrid

a. In the administrative console, click Environment > Virtual hosts > default_host > Host aliases > New. b. Use the default value of the host name and specify the port. c. Click OK. 3. Start the ObjectGrid sample application. v To start the application on a server, click Servers > Application servers. Select the server that has the ObjectGridSample.ear file installed. Click Start. v To start the application on a cluster, click Servers > Clusters. Select the cluster that has the ObjectGridSample.ear file installed. Click Start. After you start the application on a server or cluster, you can stop and start the application independently from the host server or cluster. To stop or start the ObjectGrid sample application, perform the following steps: a. In the administrative console, click Applications > Enterprise applications. b. Select the ObjectGrid sample application. c. Click Start or Stop. 4. Access the ObjectGrid sample. After you install the ObjectGridSample.ear file on a single server or cluster and start the application, you can access the ObjectGrid sample at the following Web address:
http://hostname:port/ObjectGridSample

For example, if your host name is localhost and the port value is 9080, use the http://localhost:9080/ObjectGridSample Web address. You installed and configured the ObjectGrid sample application on WebSphere Extended Deployment. After you install the application on a server or cluster, you can access the sample documentation after starting the application at the following Web address:
http://hostname:port/ObjectGridSample/docs/introduction.html

For example, if your hostname is localhost and the port value is 9080, use the http://localhost:9080/ObjectGridSample/docs/introduction.html Web address.

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 2. ObjectGrid
ObjectGrid is an extensible transactional object caching framework for Java 2 Platform, Standard Edition (J2SE) and Java 2 Platform, Enterprise Edition (J2EE) applications. You can use the ObjectGrid API when developing your applications to retrieve, store, delete, and update objects in the ObjectGrid framework. You can also implement customized plug-ins that monitor updates to the cache, retrieve and store data with external data sources, manage eviction of entries from the cache, and handle background cache functionality for your own ObjectGrid application environment. Map-based API The ObjectGrid provides an API that is based on the Java Map interface. The API is extended to support the grouping of operations into transactional blocks. You can associate a set of keywords with a key and all the objects that are associated with a keyword can be evicted from the Map instance at any time. This interface is a superset of the Map interface and adds support for batch operations, invalidation, keyword association, and explicit insert and update. The Java Map semantics are enhanced with extension points so that you can implement the following enhancements: v Cache evictors to fine-tune cache entry lifetimes v Transaction callback interfaces to carefully control transaction management v Loader implementations that get data from and store data to remote data sources v Listener interface that can provide information about all committed transactions as they occur and are applied towards the ObjectGrid framework as a whole or are applied for particular Map instances. The ObjectGrid environment Use the ObjectGrid framework with or without WebSphere Application Server or WebSphere Extended Deployment. You can use ObjectGrid with a J2SE Version 1.4.2 or higher environment. You can also use ObjectGrid in a WebSphere Application Server Version 6.0.2 or higher environment. When you use WebSphere Application Server support, additional distributed transaction support can be exploited. You can also exploit distributed transactional support if you use any environment that has a reliable publish and subscribe messaging system, such as a Java Message Service (JMS) provider. Restriction: You can use ObjectGrid in a WebSphere Extended Deployment Version 6.0 environment. You can also use ObjectGrid in a Java 2 Platform, Standard Edition (J2SE) Version 1.4.2 or higher environment or in a WebSphere Application Server Version 6.02 or higher environment with additional licensing arrangements. Contact your sales representative for details. Simple installation

Copyright IBM Corp. 2004, 2005

You can install and configure ObjectGrid in a few simple steps. These steps include copying the Java archive (JAR) files to your class path and defining a few configuration directives. Transactional changes All changes are made in the context of a transaction to ensure a robust programmatic interface. The transaction can either be explicitly controlled within the application, or the application can use the automatic commit programming mode. These transactional changes can also be propagated across application servers for ObjectGrid peer synchronization. You can scale ObjectGrid from a simple grid running in a single Java virtual machine (JVM) to a grid of Java virtual machines that can share a set of objects and watch for changes to the objects that are made by peer Java virtual machines. Synchronize ObjectGrid between WebSphere Application Server instances using the distributed transaction support or by implementing a similar approach with a distributed message passing solution, such as Java Message Service (JMS). With the distributed transactional support, peers can be notified of changes by using the message transport. You can also configure the Java virtual machines to invalidate objects that are modified by peer machines or to have a local copy replaced with changes that are on a remote machine. See ObjectGrid distributed transaction propagation on page 143 for more information. Injection container compatible APIs Configure the ObjectGrid using a simple XML file or programmatically using Java APIs. The Java APIs are designed to also work in environments where you are using injectionbased frameworks to configure your applications. The APIs and interfaces of the ObjectGrid objects can also be invoked by an Inversion of Control (IoC) container and then references to key ObjectGrid objects can be injected into the application. Distributed cache The ObjectGrid supports a simple push-based model for change propagation in its initial version. When a transaction is committed to the ObjectGrid, this set of changes can be propagated to peer ObjectGrids using a message transport. The changes can be propagated as an invalidation type message or the actual new values can be pushed to the peer grids. It can be configured to push changes for certain Maps only. The message transport can be the high availability manager on WebSphere Application Server Version 6.x or on any message transport that is available to the customer on other environments. These updates can be applied to peer caches or can be conditionally applied to avoid overwriting or invalidating a more current copy in that peer cache. Extensible architecture You can extend most elements of the ObjectGrid framework by developing plug-ins. You can tune the ObjectGrid to allow an application to make trade-off decisions between consistency and performance. Plug-in customized code can also support the following application-specific behaviors: v Listen to ObjectGrid instance events for initialization, transaction begin, transaction end, and destroy.

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v Monitor transaction commit operations with per-map listener interfaces. v Invoke transaction callbacks to enable transaction-specific processing. v Implement specific common transaction policies with generic ObjectGrid transactions. v Use loaders for transparent and common entry and exit points to external data stores and other information repositories. v Handle non-serializable objects in a specific way with ObjectTransformer interfaces. You can implement each of these behaviors without affecting the use of the basic ObjectGrid cache API interfaces. With this transparency, applications that are using the cache infrastructure can have data stores and transaction processing greatly changed without affecting these applications. Use ObjectGrid as a primary API or second-level cache The ObjectGrid APIs can be used directly by the application as a lookaside cache or as a write through cache. In write through mode, the application plugs in a Loader object so that the ObjectGrid can apply changes and fetch data directly and transparently to the application. ObjectGrid can also be used as a second-level cache for popular object relational mappers by writing an adapter. The cache is invisible to the application in this mode because the application uses the APIs from the object relational mapper as the primary API for accessing the data. To begin using and developing ObjectGrid applications, see Getting started with ObjectGrid. Related tasks Getting started with ObjectGrid Use this topic to get started with ObjectGrid, a distributed computing framework that makes objects available to a set of applications.

Chapter 2. ObjectGrid

10

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 3. ObjectGrid tutorial : application programming model


Use this task to learn about the ObjectGrid application programming model. Prepare your environment to run ObjectGrid applications. See Chapter 1, Getting started with ObjectGrid, on page 1 to learn about the Java archive (JAR) file locations, Java requirements, and how to run a simple file to verify that your environment is set up properly. Decide what programming environment to use for this task. You can use an integrated development environment (IDE) such as Eclipse, but the command line Java environment works also. Incorporate the ObjectGrid into enterprise beans and servlets after you are more familiar with ObjectGrid. The examples in the Tutorial do not assume any particular Java environment, so you can use any familiar environment. At its most basic definition, ObjectGrid is a cache and an in-memory repository for objects. Using a java.util.Map map to store and access objects is similar to using ObjectGrid. At the same time, ObjectGrid is more than just a cache. By exploring the various features and plug-ins in this task, you discover that ObjectGrid is very extensible and flexible. You can use ObjectGrid as a simple look-aside cache or a more elaborate cache backed by a resource manager. The examples in this tutorial are not complete programs. Imports, exception processing, and even some of the variables are not fully declared in every example. You can use the samples to write your own programs. Use this task to use ObjectGrid from a Java program. 1. Locate the ObjectGrid APIs and exceptions. All of the public ObjectGrid APIs and Exceptions are contained in the com.ibm.websphere.objectgrid package. For more advanced system or configuration topics, see the additional APIs and Exceptions in the com.ibm.websphere.objectgrid.plugins package. Where there are provided plug-in implementations, locate those classes in the com.ibm.websphere.objectgrid.plugins.builtins package. For the ObjectGrid security features, look for packages with security in the name, such as com.ibm.websphere.objectgrid.security, com.ibm.websphere.objectgrid.security.plugins, and com.ibm.websphere.objectgrid.security.plugins.builtins. This task focuses on the APIs that are in the com.ibm.websphere.objectgrid package. The complete JavaDoc for ObjectGrid can be found at the following location: <install_root>/web/xd/apidocs.
com.ibm.websphere.objectgrid com.ibm.websphere.objectgrid.plugins com.ibm.websphere.objectgrid.plugins.builtins com.ibm.websphere.objectgrid.security com.ibm.websphere.objectgrid.security.plugins com.ibm.websphere.objectgrid.security.plugins.builtins

2. Get or create an ObjectGrid instance. Use the ObjectGridManagerFactory to get the ObjectGridManager singleton instance. Then, create an ObjectGrid instance with the following statements:

Copyright IBM Corp. 2004, 2005

11

ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid");

The ObjectGridManager interface has several methods for creating, retrieving, and removing ObjectGrid instances. See the ObjectGridManager interface on page 33 topic to choose a variation for your situation. You can also set trace settings with the ObjectGridManager interface. If you are running within WebSphere Extended Deployment or WebSphere Application Server, these methods are not necessary because trace is managed by the included facilities. If you are running outside of WebSphere Application Server, these methods can be useful. See the Trace ObjectGrid on page 39 topic for more complete information for these methods. 3. Initialize the ObjectGrid. a. Set a name for the ObjectGrid, if you did not set the name with the create methods. b. Define the BackingMaps, by using the default configuration for a BackingMap for your initial applications. c. After you have defined your BackingMaps, initialize the ObjectGrid. Initializing the ObjectGrid signals that all of the configuration is complete and you want to start using the ObjectGrid. d. After the ObjectGrid has been initialized, get a Session object. Reference ObjectGrid interface on page 40 and the JavaDoc for more information. Use the following example as guidance in this step:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); objectGrid.defineMap("someMap"); objectGrid.initialize(); Session session = objectGrid.getSession();

4. Use sessions to manage transactional operations. All access to an ObjectGrid cache is transactional: multiple accesses, inserts, updates, and removals of Objects from the cache are contained within a single unit of work, referred to as a session. At the end of a session, you can either commit all of the changes within this unit of work, or roll back and forget all of the changes within the unit of work. You can also use automatic commit for single atomic operations against the cache. In the absence of an active session context, individual accesses to the cache contents are enclosed in their own automatically committed sessions. Another important aspect of the Session interface is to get transactional access, or handle, to the BackingMap with the ObjectMap interface. You can use the getMap method to create an ObjectMap handle to a predefined BackingMap. All operations against the cache, such as inserts, updates, deletes, are completed with the ObjectMap instance. Reference the Session interface on page 45 topic for more information. Use the following example to obtain and manage a session:
Session session = objectGrid.getSession(); ObjectMap objectMap = session.getMap("someMap"); session.begin(); objectMap.insert("key1", "value1"); objectMap.insert("key2", "value2"); session.commit(); objectMap.insert("key3", "value3"); // autocommit

12

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

5. Use the ObjectMap interface to access and update the cache. As you look at the ObjectMap interface, notice several methods for accessing and updating the cache. the ObjectMap interface is modeled as a map-like interface. However, checked exceptions are introduced as an aid with developing ObjectGrid applications with an IDE, such as Eclipse. If you want to use a java.util.Map interface without checked exceptions, you can use the getJavaMap method. See ObjectMap and JavaMap interfaces on page 48 for more information. Explicit insert and update methods get around the vague put operation. You can still use the put method, but using the explicit insert and update methods convey your intent much more clearly. The use of the put method is clarified by defining a put method without a preceding get operation as an insert method. If a preceding get operation is attempted before the put operation, then the put operation is treated as an insert or an update depending on whether the entry exists in the cache. You can perform the following basic ObjectMap operations: get, put, insert, update, remove, touch, invalidate, and containsKey. Various details and variations can be found in the System programming model overview topic or the ObjectMap API documentation. The following example demonstrates the use of the ObjectMap to modify the cache:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); objectGrid.defineMap("someMap"); objectGrid.initialize(); Session session = objectGrid.getSession(); ObjectMap objectMap = session.getMap("someMap"); // Start a transaction/session... session.begin(); objectMap.insert("key1", "value1"); objectMap.put("key2", "value2"); session.commit(); // Verify changes did commit String value1 = (String)objectMap.get("key1"); String value2 = (String)objectMap.get("key2"); System.out.println("key1 = " + value1 + ", key2 = " + value2); //Start a new transaction/session... session.begin(); objectMap.update("key2", "newValue2"); objectMap.remove("key1"); session.rollback(); // Verify changes didnt commit String newValue1 = (String)objectMap.get("key1"); String newValue2 = (String)objectMap.get("key2"); System.out.println("key1 = " + newValue1 + ", key2 = " + newValue2);

As you finish reading this section and experiment with the example code, you become more comfortable with the essential ObjectGrid programming model. For more specific information, see the Chapter 5, ObjectGrid application programming interface overview, on page 33.

System programming model overview


The system programming model provides several additional features and extension points for the ObjectGrid.

Chapter 3. ObjectGrid tutorial : application programming model

13

The following diagram illustrates how the system programming model provides several additional features and extension points.
Application

ObjectGrid Features Keyword processing BackingMap access Slot processing Security Session Features No Write Through mode Pushing data to the loader only Logsequence processing Performance Monitoring Features JavaMap and Java.util.map Map extensions Keyword processing CopyMode Evictor settings ObjectMap

Plugins ObjectgridEventListener TransactionCallback SubjectSource SubjectValidation MapAuthorization

Plugins Evictor Loader MapEventListener ObjectTransformer OptimisticCallback

Features readOnly numberOfBuckets preloadMode lockStrategy numberOfLockBuckets lockTimeout copyMode valueinterfaceClassName copyKey nullValuesSupported itlEvictorType timeToLive

BackingMap

Figure 1. ObjectGrid overview

A plug-in in ObjectGrid is a component that provides a certain type of function to the pluggable ObjectGrid components that include ObjectGrid and BackingMap. A feature represents a specific function or characteristic of an ObjectGrid component, including ObjectGrid, Session, BackingMap, ObjectMap, and so on. If a feature represents a function, it can be used to achieve a specific computing objective. If a feature is a characteristic, it can be used to tune the behavior of the ObjectGrid components. Each of the following sections describes some of the features and extensions that are illustrated in the preceding diagram: v System programming model overview: ObjectGrid interface plug points and features on page 15 The ObjectGrid interface has several plug points and features for more extensible interactions with the ObjectGrid. v System programming model overview: BackingMap interface plug points and features on page 17 The BackingMap interface has several optional plug points and features for more extensible interactions with the ObjectGrid. v System programming model overview: Session interface features on page 24

14

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

The Session interface has several features for more extensible interactions with the ObjectGrid. Each of the sections in this topic describe the feature and provide some brief code snippets for the usage scenario. v System programming model overview: ObjectMap interface features on page 25 The ObjectMap interface has several features for more extensible interactions with the ObjectGrid. Each of the sections in this topic describe the feature and provide some brief code snippets for the usage scenario. Fore more information about the individual features and plug-ins, see Chapter 5, ObjectGrid application programming interface overview, on page 33.

System programming model overview: ObjectGrid interface plug points and features
The ObjectGrid interface has several plug points and features for more extensible interactions with the ObjectGrid. Each of the following sections describe the feature and provide some brief code snippets for the usage scenario. Where appropriate, an XML snippet is provided to show the alternative XML configuration. For ore extensive information see the ObjectGrid interface on page 40 and ObjectGrid configuration on page 118 topics. Keyword processing The ObjectGrid interface provides a flexible invalidation mechanism that is based around keywords. A keyword is a non-null instance of any serializable object. You are free to associate keywords with BackingMap entries in any way. Most of the keyword processing is performed at the ObjectMap level, but the association of one keyword to another keyword to form a hierarchical tree of keywords is performed at the ObjectGrid level. The associateKeyword(java.io.Serializable parent, java.io.Serializable child) method links the two keywords together in a directional relationship. If a parent is invalidated, then the child is also invalidated. Invalidating the child has no impact on the parent. For example, this method is used to add a New York map entry as a child of the USA map entry, so that if USA is invalidated then all New York entries are also invalidated. See the following code sample:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); // associate several cities with "USA" keyword objectGrid.associateKeyword("USA", "New York"); objectGrid.associateKeyword("USA", "Rochester"); objectGrid.associateKeyword("USA", "Raleigh"); : // insert several entries with various keywords objectMap.insert("key1", "value1", "New York"); objectMap.insert("key2", "value2", "Mexico"); objectMap.insert("key3", "value3", "Raleigh"); objectMap.insert("key4", "value4", "USA"); objectMap.insert("key5", "value5", "Rochester"); objectMap.insert("key6", "value6", "France"); : // invalidate all entries associated with "USA" keyword, leaving // "key2" and "key6" entries objectMap.invalidateUsingKeyword("USA", true); :

Chapter 3. ObjectGrid tutorial : application programming model

15

For more information, see Keywords on page 52. BackingMap access The ObjectGrid provides access to the BackingMap objects. You can get access to a BackingMap with either the defineMap or getMap methods. See BackingMap interface on page 42 for more information. The following example creates two BackingMap references:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); BackingMap newBackingMap = objectGrid.defineMap("newMap"); :

Slot processing You can reserve a slot for storing objects that are used in the course of the transaction, such as the transaction ID object (TxID) or a database connection object (Connection). These stored objects are then referenced with a specific index, which is provided by the reserveSlot method. You can find additional information about using slots in the Loaders on page 92 and TransactionCallback plug-in on page 108 topics. The following code snippet demonstrates slot processing:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); int index = objectGrid.reserveSlot (com.ibm.websphere.objectgrid.TxID.SLOT_NAME); : // Use the index later when storing or retrieving objects from //the TxID object ... TxID tx = session.getTxID(); tx.putSlot(index, someObject); : Object theTxObject = tx.getSlot(index); :

Security processing Maps can be protected using security mechanisms. The following methods are available on an ObjectGrid for configuring and using the security features. v getSession(Subject) v SubjectSource v SubjectValidation v AuthorizationMechanism v MapAuthorization v PermissionCheckPeriod See ObjectGrid security on page 66 for more information on the available security mechanisms. ObjectGridEventListener The ObjectGridEventListener listener provides a way for applications to receive notification in the event of a transaction begin or commit. An instance of an ObjectGridEventListener can be set on the ObjectGrid. Reference the Listeners on page 79 topic for more information. Following is an example of how to implement the ObjectGridEventListener interface programmatically:

16

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

class MyObjectGridEventListener implements com.ibm.websphere.objectgrid.plugins.ObjectGridEventListener { ... } : ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); objectGrid.addEventListener(new MyObjectGridEventListener()); :

You can also perform the same configuration with XML:


: <objectGrids> <objectGrid name="someGrid"> <bean id="ObjectGridEventListner" className= "com.somecompany.MyObjectGridEventListener" /> : </objectGrid> </objectGrids> :

TransactionCallback plug-in Calling methods on the session sends corresponding events to the TransactionCallback plug-in. An ObjectGrid can have zero or one TransactionCallback plug-ins. BackingMaps that are defined on an ObjectGrid with a TransactionCallback plug-in must have a corresponding Loader. See TransactionCallback plug-in on page 108 for more information. The following code snippet demonstrates how to implement the TransactionCallback plug-in programmatically:
class MyTransactionCallback implements com.ibm.websphere.objectgrid.plugins.TransactionCallback { ... } : ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); objectGrid.setTransactionCallback(new MyTransactionCallback()); :

You can perform the same configuration with XML:


: <objectGrids> <objectGrid name="someGrid"> <bean id="TransactionCallback" className= "com.somecompany.MyTransactionCallback" /> </objectGrid> </objectGrids> :

System programming model overview: BackingMap interface plug points and features
The BackingMap interface has several optional plug points for more extensible interactions with the ObjectGrid. Each of the following sections describe the feature and provide some brief code snippets for the usage scenario. Where appropriate, an XML snippet is provided to show the alternative XML configuration. More extensive information is the BackingMap interface on page 42 and ObjectGrid configuration on page 118 topics or in the API documentation.

Configuration attributes
Several configuration items are associated with BackingMaps:

Chapter 3. ObjectGrid tutorial : application programming model

17

v ReadOnly (defaults to false): Setting this attribute to true makes the backing map read-only. Setting to false will makes the backing map a read and write. If you do not specify a value, the default of read and write results. v NullValuesSupported (defaults to true): Supporting null value means a null value can be put in a map. If this attribute is set to true, null values are supported in the ObjectMap; otherwise null values are not supported. If null values are supported, a get operation that returns null can mean that the value is null or the map does not contain the passed-in key. v NumberOfBuckets (defaults to 503): Specifies the number of buckets that are used by this BackingMap. The BackingMap implementation uses a hash map for its implementation. If many entries exist in the BackingMap then more buckets lead to better performance because the risk of collisions is lower as the number of buckets grows. More buckets also lead to more concurrency. v NumberOfLockBuckets (defaults to 383): Specifies the number of lock buckets that are used by the lock manager for this BackingMap. When the lockStrategy attribute is set to OPTIMISTIC or PESSIMISTIC, a lock manager is created for the BackingMap. The lock manager uses a hash map to keep track of entries that are locked by one or more transactions. If many entries exist in the hash map, then more lock buckets lead to better performance as the risk of collisions is lower as the number of buckets grows. More lock buckets also means more concurrency. When the lockStrategy is NONE, no lock manager is used by this BackingMap. In this case, setting the numberOfLockBuckets attribute has no effect. Programmatic configuration example The following example configures properties on a backing map:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); // override default of read/write backingMap.setReadOnly(true); // override default of allowing Null values backingMap.setNullValuesSupported(false); // override default (prime numbers work best) backingMap.setNumberOfBuckets(251); // override defualt (prime numbers work best) backingMap.setNumberOfLockBuckets(251); :

XML configuration example The following XML configuration example configures the same properties that are demonstrated in the preceding programmatic sample.
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" readOnly="true" nullValuesSupported="false" numberOfBuckets="251" numberOfLockBuckets="251" /> </objectGrid> </objectGrids> :

Lock strategy
When the lock strategy is set to OPTIMISTIC or PESSIMISTIC, a lock manager is created for the BackingMap. To prevent deadlocks from occurring, the lock manager has a default timeout value for waiting for a lock to be granted. If this

18

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

timeout limit is exceeded, a LockTimeoutException exception results. The default value of 15 seconds is sufficient for most applications, but on a heavily loaded system, a timeout might occur when no actual deadlock exists. In that case, the setLockTimeout method can be used to increase the lock timeout value from the default to whatever is needed to prevent false timeout exceptions from occurring. When the lock strategy is NONE, no lock manager is used by this BackingMap. In this case, setting the lockTimeout attribute has no effect. For more information, see the Locking on page 58 topic. Programmatic configuration example The following example sets the lock strategy:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); // override default value of OPTIMISTIC backingMap.setLockStrategy(LockStrategy.PESSIMISTIC); backingMap.setLockTimeout(30); // sets lock timeout to 30 seconds :

XML configuration example The following example sets the same lock strategy that is defined in the preceding programmatic example.
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" lockStrategy="PESSIMISTIC" lockTimeout="30" /> </objectGrid> </objectGrids> :

Copy keys and values


Making copies of keys and values can be expensive, both from a resource and performance perspective. Without the capability to make these copies, strange and difficult-to-debug problems can occur. ObjectGrid has provided the ability to configure whether to and when to make copies of keys or values. Normally, keys are considered immutable so there is no need to copy the key objects. The default mode for key objects is not to make copies. Value objects are more likely to be modified by the application. When to provide a copy of the Value object versus the actual reference to the Value object is a configurable option. Reference the Chapter 7, ObjectGrid performance best practices, on page 173 topic and the JavaDoc for additional details on the CopyKey and CopyMode settings. Programmatic configuration example Following is an example of setting the copy mode and copy key settings:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); backingMap.setCopyKey(true); // make a copy of each new key backingMap.setCopyMode(NO_COPY); // Most efficient trust the application :

XML configuration example

Chapter 3. ObjectGrid tutorial : application programming model

19

The following example results in the same configuration as in the preceding programmatic configuration example:
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" copyKey="true" copyMode="NO_COPY" /> </objectGrid> </objectGrids> :

Evictors
Evictors are used to periodically clean out unnecessary entries in the map. The entries that are removed are defined by the Evictor. The built-in Evictors are time-based, so the eviction strategy is based on the amount of time that an entry has been alive in the map. Other eviction strategies are based on usage, size, or a combination of factors. v Built-in Time To Live (TTL) Evictor: The built-in Time To Live evictor provides for a couple of configuration items that are set on the BackingMap with the setTtlEvictorType and setTimeToLive methods. By default, this built-in TimeToLive evictor is not active. You can activate it by calling the setTtlEvictorType method with one of three values: CREATION_TIME, LAST_ACCESS_TIME, or NONE (default). Then, depending on the type of TimeToLive evictor selected, the value for the setTimeToLive method is used to set the lifetime for each map entry. v Evictor plug-ins: In addition to the built-in Time To Live Evictor, an application can provide its own Evictor implementation plug-in. You can use any algorithm periodically to invalidate map entries. Programmatic configuration The following class creates an evictor:
class MyEvictor implements com.ibm.websphere.objectgrid.plugins.Evictor { ... } : ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); // timer starts when entry is first created backingMap.setTtlEvictorType(CREATION_TIME); // Allow each map entry to live 30 seconds before invalidation backingMap.setTimeToLive(30); // Both builtin and custom Evictors will be active backingMap.setEvictor(new MyEvictor()); :

XML configuration The following XML code creates a configuration that is identical to the preceding programmatic configuration:
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" pluginCollection="default" ttlEvictorType="CREATION_TIME" timeToLive="30" /> </objectGrid> </objectGrids> : <backingMapPluginCollections> <backingMapPluginCollection id="default"> <bean id="Evictor" className="com.somecompany.MyEvictor" />

20

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

</backingMapPluginCollection> </backingMapPluginCollections> :

For more information, see Evictors on page 84.

Loaders
An ObjectGrid Loader is a pluggable component that enables an ObjectGrid map to behave as a memory cache for data that is normally kept in a persistent store on either the same system or another system. Typically, a database or file system is used as the persistent store. A loader has the logic for reading and writing data from and to persistent store. A Loader is an optional plug-in for an ObjectGrid backing map. Only one Loader can ever be associated with a given backing map and each backing map has its own Loader instance. The backing map requests any data that it does not have from its Loader. Any changes to the map are pushed out to the Loader. The Loader plug-in provides a way for the backing map to move data between the map and its persistent store. Programmatic configuration Following is an example of a loader implementation:
class MyLoader implements com.ibm.websphere.objectgrid.plugins.Loader { .. } : Loader myLoader = new MyLoader(); myLoader.setDataBaseName("testdb"); myLoader.setIsolationLevel("ReadCommitted"); ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); backingMap.setLoader(myLoader); backingMap.setPreloadMode(true); :

XML configuration The following XML sample results in the same configuration as the preceding programmatic example:
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" pluginCollectionRef="default" preloadMode="true" /> </objectGrid> </objectGrids> : <backingMapPluginCollections> <backingMapPluginCollection id="default"> <bean id="Loader" classname="com.somecompany.MyLoader"> <property name="dataBaseName" type="java.lang.String" value="testdb" /> <property name="isolationLevel" type="java.lang.String" value="ReadCommitted" /> </bean> </backingMapPluginCollection> </backingMapPluginCollections> :

For more information, see the Loaders on page 92 topic.

Chapter 3. ObjectGrid tutorial : application programming model

21

MapEventListener interface
The MapEventListener callback interface is implemented by the application when it wants to receive events about a Map such as the eviction of a map entry or data preload completion. The following code example demonstrates how to set a MapEventListener instance on a BackingMap instance: Programmatic configuration
class MyMapEventListener implements com.ibm.websphere.objectgrid.plugins.MapEventListener { ... } : ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); backingMap.addMapEventListener(new MyMapEventListener() );

XML configuration The following example results in the same configuration as the preceding programmatic example:
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" pluginCollectionRef="default" /> </objectGrid> </objectGrids> : <backingMapPluginCollections> <backingMapPluginCollection id="default"> <bean id="MapEventListener" classname="com.somecompany.MyMapEventListener" /> </backingMapPluginCollection> </backingMapPluginCollections> :

See the Listeners on page 79 topic for more information.

ObjectTransformer interface
The ObjectTransformer can be used to serialize cache entry keys and values that are not defined as serializable so that you can define your own serialization scheme without extending or implementing the Serializable interface directly. This interface also provides methods for performing the copy function on keys and values. Following is a class that implements the ObjectTransformer interface: Programmatic configuration
class MyObjectTransformer implements com.ibm.websphere.objectgrid.plugins.ObjectTransformer { ... } : ObjectTransformer myObjectTransformer = new MyObjectTransformer(); myObjectTransformer.setTransformType("full"); ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); BackingMap backingMap = objectGrid.getMap("someMap"); backingMap.setObjectTransformer(myObjectTransformer); :

XML configuration The following XML example results in the same configuration as the preceding programmatic sample:

22

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" pluginCollectionRef="default" /> </objectGrid> </objectGrids> : <backingMapPluginCollections> <backingMapPluginCollection id="default"> <bean id="ObjectTransformer" className="com.somecompany.MyObjectTransformer"> <property name="transformType" type="java.lang.String" value="full" description="..." /> </bean> </backingMapCollection> </backingMapCollections> :

For more information, see the ObjectTransformer plug-in on page 104 topic.

OptimisticCallback
The OptimisticCallback interface can be used to create and process a version field that is associated with a given Value object. In many cases, using the Value object directly to determine if another cache client has modified the value since it was retrieved is very inefficient and error-prone. An alternative is to provide another field that represents the state of the Value object. That is the intent of this OptimisticCallback - to provide an alternative Versioned Value object that represents the Value object. Following is a sample configuration of OptimisticCallback: Programmatic configuration
class MyOptimisticCallback implements com.ibm.websphere.objectgrid.plugins.OptimisticCallback { ... } : OptimisticCallback myOptimisticCallback = new MyOptimisticCallback(); myOptimisticCallback.setVersionType("Integer"); backingMap.setOptimisticCallback(myOptimisticCallback); :

XML configuration The following example results in the same configuration as the preceding programmatic example:
: <objectGrids> <objectGrid name="someGrid"> <backingMap name="someMap" pluginCollectionRef="default" /> </objectGrid> </objectGrids> : <backingMapPluginCollections> <backingMapPluginCollection id="default"> <bean id="OptimisticCallBack" classname="com.somecompany.MyOptimisticCallback"> <property name="versionType" type="java.lang.string" value="Integer" description="..." /> </bean> </backingMapPluginCollection> </backingMapPluginCollections> :

Chapter 3. ObjectGrid tutorial : application programming model

23

System programming model overview: Session interface features


The Session interface has several features for more extensible interactions with the ObjectGrid. Each of the following sections describe the feature and provide some brief code snippets for the usage scenario. For more information about the Session interface, see Session interface on page 45.

No write through mode


Sometimes, applications just want to apply changes to the base map but not the Loader. The beginNoWriteThrough method of Session interface is designed to achieve this objective. The isWriteThroughEnabled method of Session interface can be used to verify if the current session is writing to the back end Loader. This might be useful to other users of the Session object to know what type of session is currently being processed. The following example enables the no write through mode:
: ObjectGrid objectGrid = objectGridManager.createObjectGrid("someGrid"); objectGrid.defineMap("someMap"); objectGrid.initialize(); Session session = objectGrid.getSession(); session.beginNoWriteThrough(); boolean isWriteThroughEnabled = session.isWriteThroughEnabled(); // make updates to the map ... session.commit(); :

Push data to the Loader only


Applications can apply local changes in the session to the Loader without committing these changes permanently by invoking the flush method, as in the following example:
: Session session = objectGrid.getSession(); session.begin(); // make some changes ... session.flush(); // push these changes to the Loader, but dont commit yet // make some more changes ... session.commit(); :

processLogSequence method
The processLogSequence method is used to process a LogSequence. Each LogElement within the LogSequence is examined and the appropriate operation, such as the insert, update, invalidate operations, is performed against the BackingMap identified by the LogSequence MapName. An ObjectGrid Session must be active before this method is invoked. The caller is then responsible for issuing the appropriate commit or rollback calls to complete the Session. Autocommit processing is not available for this method invocation. The main use of this method is to process a LogSequence that was received by a remote JVM. For example, using the distributed commit support, the LogSequences that are associated with a given committed session are then distributed to other listening ObjectGrids in other Java virtual machines (JVM). After receiving the

24

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

LogSequences at the remote JVM, the listener can start a Session using the beginNoWriteThrough method, invoke this processLogSequence method, and then perform the commit method on the Session. An example follows:
: session.beginNoWriteThrough(); try { session.processLogSequence(inputSequence); } catch (Exception e) { session.rollback(); throw e; } session.commit(); :

Performance monitoring
Maps can optionally be instrumented for performance monitoring while running within WebSphere Application Server. The setTransactionType method is available on a Session for configuring and using the performance monitoring features. See Monitoring ObjectGrid performance with WebSphere Application Server performance monitoring infrastructure (PMI) on page 136 for more information.

System programming model overview: ObjectMap interface features


The ObjectMap interface has several features for more extensible interactions with the ObjectGrid. Each of the following sections will describe the feature and provide some brief code snippets for the usage scenario. For more information about the ObjectMap interface, see ObjectMap and JavaMap interfaces on page 48.

JavaMap and java.util.Map interface


For applications that want to use the java.util.Map interface, the ObjectMap has the getJavaMap method, so that applications can get the implementation of the java.util.Map interface that is backed by the ObjectMap. The returned Map instance can then be cast to the JavaMap interface, which extends the Map interface. The JavaMap interface has the same method signatures as ObjectMap, but with different exception handling. The JavaMap interface extends the java.util.Map interface, so all exceptions are instances of the java.lang.RuntimeException class. Because the JavaMap interface extends the java.util.Map interface, it is easy to quickly use ObjectGrid with an existing application that uses a java.util.Map interface for object caching. A code snippet follows:
: JavaMap javaMap = (JavaMap)objectMap.getJavaMap(); :

Map extensions
The ObjectMap interface also provides additional functional capabilities in addition to the checked exceptions capabilities. For example, a user can specify that a given map entry is updated with the getForUpdate method, which indicates to the ObjectGrid runtime and Loader plug-in that the entry can be locked during the processing, if appropriate. Batch processing is another additional capability with the getAll, putAll, and removeAll methods. For more information about these methods, see the API documentation.
Chapter 3. ObjectGrid tutorial : application programming model

25

Keyword processing
Most map operations have the keyword parameter version, such as insert, get, getForUpdate, put, remove, and invalidate. For ease of use, the setDefaultKeyword method is also provided. This method associates entries with a keyword without using the keyword version of the map operation. A keyword example follows:
: // setDefaultKeyword session.begin(); objectMap.setDefaultKeyword("New York"); Person p = (Person) objectMap.get("Billy"); // "Billy" entry has "New York" keyword p = (Person) objectMap.get("Bob", "Los Angeles"); // "Bob" entry //has "Los Angeles" keyword objectMap.setDefaultKeyword(null); p = (Person) objectMap.get("Jimmy"); // "Jimmy" entry has no keyword session.commit(); : // keyword parameter version of insert operation session.begin(); Person person = new Person("Joe", "Bloggs", "Manhattan"); objectMap.insert("BillyBob", person, "Rochester"); // "BillyBob" has //"Rochester" keyword session.commit(); :

See Keywords on page 52 for more information.

Copy mode method


The setCopyMode method allows the copy mode for the Map to be overridden on this map for this session or transaction only. This method allows an application to use an optimal copy mode on a per session basis, as its needs dictate. The copy mode cannot be changed during an active session. A corresponding clearCopyMode method exists that resets the copy mode back to the one defined on the BackingMap. You can call this method only when no active sessions exist.An example of setting the copy mode follows:
: objectMap.setCopyMode(CopyMode.COPY_ON_READ, null); session.begin(); // modify objectMap ... session.commit(); objectMap.clearCopyMode(); // reset CopyMode to BackingMap setting session.begin(); // modify objectMap ... session.commit(); :

For more information, see the ObjectTransformer plug-in on page 104 and Chapter 7, ObjectGrid performance best practices, on page 173 topics.

Evictor settings
You can override the TimeToLive timeout value for the built-in TimeToLive evictor at the ObjectMap level. The setTimeToLive method establishes the number of seconds that any given cache entry can live. When modified, the previous TimeToLive value is returned. This TimeToLive value is the minimum time an entry remains in the cache before being considered for eviction and indicates to the built-in TimeToLive evictor how long an entry should remain after last access time. The new TimeToLive value only applies to ObjectMap entries that are accessed by the transaction started by the Session object that was used to obtain the ObjectMap

26

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

instance. The new TimeToLive value applies to any transaction that is in progress for the Session and future transactions that are run by the Session. The new TimeToLive value does not affect entries of an ObjectMap instance that are accessed by a transaction started by some other Session. By calling this method on the ObjectMap, any previous value set by the setTimeToLive method on the BackingMap is overridden for this ObjectMap instance. An example follows:
: session.begin(); int oldTTL = objectMap.setTimeToLive(60); // set TTL to 60 seconds Person person = new Person("Joe", "Bloggs", "Manhattan"); objectMap.insert("BillyBob", person); // "BillyBob" entry will have a TTL //of 60 seconds session.commit(); : objectMap.setTimeToLive(oldTTL); // reset TTL to original value Person person2 = new Person("Angelina", "Jolie", "somewhere"); objectMap.insert("Brad", person2); // "Brad" entry will use original TTL value :

For more information, see the Evictors on page 84 topic.

Chapter 3. ObjectGrid tutorial : application programming model

27

28

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 4. ObjectGrid samples


This topic describes the ObjectGrid samples that are provided when installing the WebSphere Extended Deployment product.

Overview
Several ObjectGrid-specific and ObjectGrid samples illustrate the integration with Java 2 Platform, Enterprise Edition (J2EE) applications and the partitioning facility (WPF). This topic describes each of the samples, the features that each sample demonstrates, the location of each sample, and the environments where the sample runs. This topic describes samples that are provided when installing WebSphere Extended Deployment. Other samples will be provided related to using Java Message Service (JMS) integration and integration of ObjectGrid with other open source frameworks at the following Web address:http://www1.ibm.com/support/docview.wss?uid=swg27006432 .

Samples
v ObjectGridSamplesSA : This sample is a set of Java 2 Platform, Standard Edition (J2SE) examples that are packaged in the objectgridSamples.jar file for demonstrating the ObjectGrid functions. These J2SE samples can be run in a J2SE environment. v ObjectGridSample : This sample is a J2EE example that demonstrates how servlets and Session enterprise beans use the ObjectGrid functions. This sample is shipped in an EAR file. v ObjectGridPartitionCluster : This sample is a J2EE sample for demonstrating how the WPF and ObjectGrid work together and how to use the ObjectGridEvent to propagate object changes and how to enable context-based routing to maintain ObjectGrid integrity and consistency. This sample is shipped in an EAR file. v ObjectGridJMSSamples: This is a set of J2EE samples packaged in the ObjectGridJMSSamples.zip file that demonstrate how to use JMS function to transmit changes in one ObjectGrid instance to another ObjectGrid instance in a single JVM or a cluster environment. These J2EE samples are only available on the Web at the following Web address:http://www1.ibm.com/support/docview.wss?uid=swg27006432 .

Sample functionality
Table 1. Sample functionalities Functional Area ObjectGrid SamplesSA sample ObjectGrid EventListener Transaction callback Loader x x x x ObjectGrid Sample sample ObjectGridPartition Cluster sample x x x ObjectGrid JMSSamples sample x

Copyright IBM Corp. 2004, 2005

29

Table 1. Sample functionalities (continued) Functional Area ObjectGrid SamplesSA sample MapEvent Listener Object Transformer Optimistic callback BackingMap copy mode Distributed invalidation Distributed update LogSequence processing Partitioning facility (WPF) Java Message Service (JMS) x x x x x x x x x x x x x x x x x ObjectGrid Sample sample ObjectGridPartition Cluster sample ObjectGrid JMSSamples sample

Location
After WebSphere Extended Deployment has been installed, the following .jar files are located in the following directories:
Table 2. Sample locations Sample ObjectGridSamplesSA ObjectGridSample ObjectGridPartitionCluster Location install_root\optionalLibraries\ObjectGrid install_root\optionalLibraries\ObjectGrid install_root\installableApps

The other examples, such as ObjectGridJMSSamples, can be found on the Web at the following Web address:http://www1.ibm.com/support/docview.wss?uid=swg27006432 . You can also find articles on IBM DeveloperWorks that describe topics of interest at the following Web address: http://www.ibm.com/developerworks. Search for ObjectGrid.

Sample environments
Some samples can run in a J2SE environment, but some have to run in a J2EE environment. Some can run in a single server instance, others have to run in a cluster. The following table shows the running environment of the samples. Restriction: You can use ObjectGrid in a WebSphere Extended Deployment Version 6.0 environment. You can also use ObjectGrid in a Java 2 Platform, Standard Edition (J2SE) Version 1.4.2 or higher environment or in a WebSphere Application Server Version 6.02 or higher environment with additional licensing arrangements. Contact your sales

30

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

representative for details.


Table 3. Sample running environments ObjectGrid SamplesSA J2SE Eclipse command line WebSphere single server Application cluster Sever Version Rational 6.0.x Application Developer unit test environment (UTE) WebSphere Extended Deployment Version 6.0 single server cluster x x x x x x x ObjectGrid Sample ObjectGrid Partition Cluster ObjectGrid JMSSamples

x x x

x x

Chapter 4. ObjectGrid samples

31

32

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 5. ObjectGrid application programming interface overview


This section discusses the how to configure the ObjectGrid with XML or through programmatic interfaces. In addition, information is included to implement the external interfaces that ObjectGrid provides. In all cases, an overview, API interfaces, and examples are described.

API documentation
The JavaDoc for ObjectGrid is the definitive source of information about the APIs. Find the JavaDoc in the following directory of your WebSphere Extended Deployment installation: install_root\web\xd\apidocs

ObjectGridManager interface
The ObjectGridManagerFactory class and the ObjectGridManager interface provide a mechanism to create, access, and cache ObjectGrid instances. The ObjectGridManagerFactory class is a static helper class to access the ObjectGridManager interface, a singleton. The ObjectGridManager interface includes several convenience methods to create instances of an ObjectGrid object. The ObjectGridManager interface also facilitates creation and caching of ObjectGrid instances that can be accessed by several users.

createObjectGrid methods
Use this topic to learn about the seven createObjectGrid methods that are in the ObjectGridManager interface.

createObjectGrid methods
The ObjectGridManager interface has seven createObjectGrid methods. Following is a simple scenario: Simple case with default configuration Following is a simple case of creating an ObjectGrid to share among many users.
import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.ObjectGridException; import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; import com.ibm.websphere.objectgrid.ObjectGridManager; final ObjectGridManager oGridManager= ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid employees = oGridManager.createObjectGrid("Employees",true); employees.initialize(); employees. /*sample continues..*/

The preceding Java code snippet creates and caches the Employees ObjectGrid. The Employees ObjectGrid is initialized with the default configuration and is ready to be used. The second parameter in the createObjectGrid method is set to true, which instructs the ObjectGridManager to cache the ObjectGrid instance it creates. If this parameter is set to false, the instance is not cached. Every ObjectGrid

Copyright IBM Corp. 2004, 2005

33

instance has a name, and the instance can be shared among many clients or users based on that name.

XML configuration
ObjectGrid is highly configurable. The previous example demonstrated how to create a simple ObjectGrid without any configuration. With this example, you can create a pre-configured ObjectGrid instance that is based on an XML configuration file. Two ways exist to configure an ObjectGrid instance: programmatically or using an XML-based configuration file. You can also configure ObjectGrid using a combination of both approaches. The ObjectGridManager interface allows creation of an ObjectGrid instance based on the XML configuration. The ObjectGridManager interface has several methods that take a URL as an argument. Every XML file that is passed into the ObjectGridManager must be validated against the schema. XML validation can be disabled only when the file has been previously validated and no changes have been made to the file since its last validation. Disabling validation saves a small amount of overhead but introduces the possibility of using an invalid XML file. The IBM Java Developer Kit (JDK) 1.4.2 has support for XML validation. When using a JDK that does not have this support, Apache Xerces might be required to validate the XML. The following Java code snippet demonstrates how to pass in an XML configuration file to create an ObjectGrid.
import java.net.MalformedURLException; import java.net.URL; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.ObjectGridException; import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; boolean validateXML = true; // turn XML validation on boolean cacheInstance = true; // Cache the instance String objectGridName="Employees"; // Name of Object Grid URL allObjectGrids = new URL("file:test/myObjectGrid.xml"); final ObjectGridManager oGridManager= ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid employees = oGridManager.createObjectGrid(objectGridName, allObjectGrids, validateXML, cacheInstance);

The XML file can contain configuration information for several ObjectGrids. The previous code snippet specifically returns ObjectGrid Employees, assuming that the Employees configuration is defined in the file. Seven createObjectGrid methods exist. The methods are documented in the following code block.
/** * A simple factory method to return an instance of an * Object Grid. A unique name is assigned. * The instance of ObjectGrid is not cached. * Users can then use {@link ObjectGrid#setName(String)} to change the * ObjectGrid name. * * @return ObjectGrid an instance of ObjectGrid with a unique name assigned * @throws ObjectGridException any error encountered during the ObjectGrid creation * @ibmapi */ public ObjectGrid createObjectGrid() throws ObjectGridException;

34

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

/** * A simple factory method to return an instance of an ObjectGrid with the * specified name. The instances of ObjectGrid can be cached. If an ObjectGrid * with the this name has already been cached, an ObjectGridException * will be thrown. * * @param objectGridName the name of the ObjectGrid to be created. * @param cacheInstance true, if the ObjectGrid instance should be cached * @return an ObjectGrid instance * @this name has already been cached or * any error during the ObjectGrid creation. * @ibmapi */ public ObjectGrid createObjectGrid(String objectGridName, boolean cacheInstance) throws ObjectGridException; /** * Create an ObjectGrid instance with the specified ObjectGrid name. The * ObjectGrid instance created will be cached. * @param objectGridName the Name of the ObjectGrid instance to be created. * @return an ObjectGrid instance * @throws ObjectGridException if an ObjectGrid with this name has already * been cached, or any error encountered during the ObjectGrid creation * @ibmapi */ public ObjectGrid createObjectGrid(String objectGridName) throws ObjectGridException; /** * Create an ObjectGrid instance based on the specified ObjectGrid name and the * XML file. The ObjectGrid instance defined in the XML file with the specified * ObjectGrid name will be created and returned. If such an ObjectGrid * cannot be found in the xml file, an exception will be thrown. * * This ObjecGrid instance can be cached. * * If the URL is null, it will be simply ignored. In this case, this method behaves * the same as {@link #createObjectGrid(String, boolean)}. * * @param objectGridName the Name of the ObjectGrid instance to be returned. It * must not be null. * @param xmlFile a URL to a wellformed xml file based on the ObjectGrid schema. * @param enableXmlValidation if true the XML is validated * @param cacheInstance a boolean value indicating whether the ObjectGrid * instance(s) * defined in the XML will be cached or not. If true, the instance(s) will * be cached. * * @throws ObjectGridException if an ObjectGrid with the same name * has been previously cached, no ObjectGrid name can be found in the xml file, * or any other error during the ObjectGrid creation. * @return an ObjectGrid instance * @see ObjectGrid * @ibmapi */ public ObjectGrid createObjectGrid(String objectGridName, final URL xmlFile, final boolean enableXmlValidation, boolean cacheInstance) throws ObjectGridException; /** * Process an XML file and create a List of ObjectGrid objects based * upon the file. * These ObjecGrid instances can be cached. * An ObjectGridException will be thrown when attempting to cache a * newly created ObjectGrid * that has the same name as an ObjectGrid that has already been cached. * * @param xmlFile the file that defines an ObjectGrid or multiple * ObjectGrids * @param enableXmlValidation setting to true will validate the XML
Chapter 5. ObjectGrid application programming interface overview

35

* file against the schema * @param cacheInstances set to true to cache all ObjectGrid instances * created based on the file * @return an ObjectGrid instance * @throws ObjectGridException if attempting to create and cache an * ObjectGrid with the same name as * an ObjectGrid that has already been cached, or any other error * occurred during the * ObjectGrid creation * @ibmapi */ public List createObjectGrids(final URL xmlFile, final boolean enableXmlValidation, boolean cacheInstances) throws ObjectGridException; /*** Create all ObjectGrids that are found in the XML file. The XML file will be * validated against the schema. Each ObjectGrid instance that is created will * be cached. An ObjectGridException will be thrown when attempting to cache a * newly created ObjectGrid that has the same name as an ObjectGrid that has * already been cached. * @param xmlFile The XML file to process. ObjectGrids will be created based * on what is in the file. * @return A List of ObjectGrid instances that have been created. * @throws ObjectGridException if an ObjectGrid with the same name as any of * those found in the XML has already been cached, or * any other error encounterred during ObjectGrid creation. * @ibmapi */ public List createObjectGrids(final URL xmlFile) throws ObjectGridException; /** * Process the XML file and create a single ObjectGrid instance with the * objectGridName specified only if an ObjectGrid with that name is found in * the file. If there is no ObjectGrid with this name defined in the XML file, * an ObjectGridException * will be thrown. The ObjectGrid instance created will be cached. * @param objectGridName name of the ObjectGrid to create. This ObjectGrid * should be defined in the XML file. * @param xmlFile the XML file to process * @return A newly created ObjectGrid * @throws ObjectGridException if an ObjectGrid with the same name has been * previously cached, no ObjectGrid name can be found in the xml file, * or any other error during the ObjectGrid creation. * @ibmapi */ public ObjectGrid createObjectGrid(String objectGridName, URL xmlFile) throws ObjectGridException;

getObjectGrid methods
Use the getObjectGrid methods to retrieve cached instances.

Retrieve a cached instance


Since the Employees ObjectGrid instance was cached by the ObjectGridManager interface, any other user can access it with the following code snippet:
ObjectGrid myEmployees = oGridManager.getObjectGrid("Employees");

Following are the two getObjectGrid methods that return cached ObjectGrid instances.
/** * Get a List of the ObjectGrid instances that have been previously cached. * Returns null if no ObjectGrid instances have been cached. * @return a List of ObjectGrid instances that have been previously cached * @ibmapi */

36

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

public List getObjectGrids(); /** * Use this if a ObjectGrid already exists. It returns a cached * ObjectGrid instance by name. This method returns null if no * ObjectGrid with this objectGridName has been cached. * * @param objectGridName the cached objectgrid name. * @return a cached ObjectGrid which currently exists. * * @since WAS XD 6.0 * @ibmapi * */ public ObjectGrid getObjectGrid(String objectGridName);

removeObjectGrid methods
This topic describes how to use the two removeObjectGrid methods.

Remove an ObjectGrid method


To remove ObjectGrid instances from the cache, use one of the removeObjectGrid methods. The ObjectGridManager no longer keeps a reference of the instances that are removed. Two remove methods exist. One method takes a boolean parameter. If the boolean parameter is set to true, the destroy method is called on the ObjectGrid. The call to the destroy method on the ObjectGrid shuts down the ObjectGrid and frees up any resources it is using. Following is a description of how to use the two removeObjectGrid methods.
/** * Remove an ObjectGrid from the cache of ObjectGrid instances * @param objectGridName the name of the ObjectGrid instance to remove * from the cache * @throws ObjectGridException if an ObjectGrid with the objectGridName * was not found in the cache * @ibmapi */ public void removeObjectGrid(String objectGridName) throws ObjectGridException; /** * Remove an ObjectGrid from the cache of ObjectGrid instances and * destroy its associated resources * @param objectGridName the name of the ObjectGrid instance to remove * from the cache * @param destroy destroy the objectgrid instance and its associated * resources * @throws ObjectGridException if an ObjectGrid with the objectGridName * was not found in the cache * @ibmapi */ public void removeObjectGrid(String objectGridName, boolean destroy) throws ObjectGridException;

Use the ObjectGridManager interface to control the life cycle of an ObjectGrid instance
This topic demonstrates how the ObjectGridManager interface can be used to control the life cycle of an ObjectGrid instance using startup beans and a servlet.

Manage an ObjectGrid instance life cycle in a startup bean


A startup bean can be used to control the life cycle of an ObjectGrid instance. A startup bean loads when an application starts. With a startup bean, code can run whenever an application starts or stops as expected. To create a startup bean, use
Chapter 5. ObjectGrid application programming interface overview

37

the home com.ibm.websphere.startupservice.AppStartUpHome interface and use the remote com.ibm.websphere.startupservice.AppStartUp interface. Implement the start and stop methods on the bean. The start method is invoked whenever the application starts up. The stop method is invoked when the application shuts down. The start method can be used to create ObjectGrid instances. Thestop method can be used to destroy ObjectGrid instances. Following is a code snippet that demonstrates this ObjectGrid life cycle management in a startup bean.
public class MyStartupBean implements javax.ejb.SessionBean { private ObjectGridManager objectGridManager; /* * The methods on the SessionBean interface have been * left out of this example for the sake of brevity */ public boolean start(){ // Starting the startup bean // This method is called when the application starts objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); try { // create 2 ObjectGrids and cache these instances ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", true); bookstoreGrid.defineMap("book"); ObjectGrid videostoreGrid = objectGridManager.createObjectGrid("videostore", true); // within the JVM, // these ObjectGrids can now be retrieved from the //ObjectGridManager using the getObjectGrid(String) method } catch (ObjectGridException e) { e.printStackTrace(); return false; } return true; } public void stop(){ // Stopping the startup bean // This method is called when the application is stopped try { // remove the cached ObjectGrids and destroy them objectGridManager.removeObjectGrid("bookstore", true); objectGridManager.removeObjectGrid("videostore", true); } catch (ObjectGridException e) { e.printStackTrace(); } } }

After the start method is called, the newly created ObjectGrid instances can be retrieved from the ObjectGridManager. For example, if a servlet is included in the application, the servlet can access these ObjectGrids using the following code snippet:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.getObjectGrid("bookstore"); ObjectGrid videostoreGrid = objectGridManager.getObjectGrid("videostore");

Managing an ObjectGrid life cycle in a servlet


One method to manage the life cycle of an ObjectGrid in a Servlet is to create the ObjectGrid instance in the init method and destroy the ObjectGrid in the destroy method. If the ObjectGrid instance is cached, it can be retrieved and manipulated in the servlet code. Following is some sample code that demonstrates ObjectGrid creation, manipulation, and destruction within a servlet.

38

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

public class MyObjectGridServlet extends HttpServlet implements Servlet { private ObjectGridManager objectGridManager; public MyObjectGridServlet() { super(); } public void init(ServletConfig arg0) throws ServletException { super.init(); objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); try { // create and cache an ObjectGrid named bookstore ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", true); bookstoreGrid.defineMap("book"); } catch (ObjectGridException e) { e.printStackTrace(); } } protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ObjectGrid bookstoreGrid = objectGridManager.getObjectGrid("bookstore"); BackingMap bookMap = bookstoreGrid.getMap("book"); // perform operations on the cached ObjectGrid // ... } public void destroy() { super.destroy(); try { // remove and destroy the cached bookstore ObjectGrid objectGridManager.removeObjectGrid("bookstore", true); } catch (ObjectGridException e) { e.printStackTrace(); } } }

Trace ObjectGrid
This topic explains how to set up tracing for ObjectGrid.

Java 2 Platform, Standard Edition (J2SE) environment


When it is necessary to send debug information to IBM, use the tracing mechanism to get the debug trace. Following is an example of how to get the debug trace in a J2SE environment:
oGridManager.setTraceFileName("debug.log"); oGridManager.setTraceSpecification("ObjectGrid=all=enabled");

The previous example does not include tracing of built-in evictor plug-ins for ObjectGrid. If you are using one or more of the evictor plug-ins that are provided by ObjectGrid and you are having problems that might be related to eviction, enable tracing for both ObjectGrid plus the evictors of ObjectGrid as the following example illustrates:
oGridManager.setTraceFileName("debug.log"); oGridManager.setTraceSpecification ("ObjectGridEvictors=all=enabled:ObjectGrid=all=enabled");

WebSphere Application Server environment


It is not necessary to use ObjectGridManager to set the trace within an WebSphere Application Server environment. You can use the administrative console to set the trace specification.

Chapter 5. ObjectGrid application programming interface overview

39

ObjectGrid interface
Use this topic to reference the methods that are needed to modify an ObjectGrid.

Introduction
ObjectGrid is an extensible, transactional object caching framework that is based on the Java Map interface. The ObjectGrid API operations are grouped into a transactional unit of work and allow extensibility through custom designed plug-in support. ObjectGrid is a named logical container that contains a number of BackingMaps. For more information about backing maps, see BackingMap interface on page 42.

Create and initialize


See the ObjectGridManager interface topic for the steps that are required for creating an ObjectGrid instance. Two distinct methods exist to create an ObjectGrid: programmatically or with XML configuration files. See ObjectGridManager interface on page 33 for more information.

Get or set and factory methods


The ObjectGrid interface contains the following methods:
Table 4. ObjectGrid interface methods Method BackingMap defineMap(String name); Description defineMap: is a factory method to define a uniquely named BackingMap. For more information about backing maps, see BackingMap interface on page 42. getMap: Returns a BackingMap previously defined by calling defineMap. This allows for configuring the BackingMap, if not already configured via XML configuration getSession: Returns a Session, which provides begin, commit, rollback functionality for a Unit of Work. For more information about Session objects, see Session interface on page 45. initialize: ObjectGrid is initialized and available for general use. This method is called implicitly when the getSession method is called, if the ObjectGrid is not in an initialized state. destroy: The framework is disassembled and cannot be used after this method is called.

BackingMap getMap(String name);

public Session getSession() throws ObjectGridException, TransactionCallbackException;

void initialize() throws ObjectGridException;

void destroy(); //Keywords.

40

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Table 4. ObjectGrid interface methods (continued) Method void associateKeyword(Serializable parent, Serializable child); Description associateKeyword: ObjectGrid keyword provides a flexible invalidation mechanism based on keywords. For more information about keywords, see Keywords on page 52. This method links the two keywords together in a directional relationship. If parent is invalidated, then the child is also invalidated. Invalidating the child has no impact on the parent.

//Security void setSecurityEnabled() void setPermissionCheckPeriod(long period); setSecurityEnabled: Enables security. Security is disabled by default. setPermissionCheckPeriod: This method takes a single parameter that indicates how often to check the permission that is used to allow a client access. If the parameter is 0, all methods ask the authorization mechanism, either JAAS authorization or custom authorization, to check if the current subject has permission. This strategy might cause performance issues depending on the authorization implementation. However, this type of authorization is available if it is required. Alternatively, if the parameter is less than 0, it indicates the number of milliseconds to cache a set of permissions before returning to the authorization mechanism to refresh them. This parameter provides much better performance, but if the backend permissions are changed during this time the ObjectGrid might allow or prevent access even though the backend security provider has been modified. setAuthorizationMechanism: Set the authorization mechanism. The default is SecurityConstants.JAAS_AUTHORIZATION.

void setAuthorizationMechanism(int authMechanism);

ObjectGrid interface: plug-ins


ObjectGrid interface has several optional plug-in points for more extensible interactions.
void addEventListener(ObjectGridEventListener cb); void removeEventListener(ObjectGridEventListener cb); void setTransactionCallback(TransactionCallback callback); // Security related plug-ins void setSubjectValidation(SubjectValidation subjectValidation); void setSubjectSource(SubjectSource source); void setMapAuthorization(MapAuthorization mapAuthorization);

v ObjectGridEventListener: An ObjectGridEventListener interface is used to receive notifications when significant events occur on the ObjectGrid. These events include ObjectGrid initialization, beginning of a transaction, ending a transaction, and destroying an ObjectGrid. To listen for these events, create a class that implements the ObjectGridEventListener interface and add it to the

Chapter 5. ObjectGrid application programming interface overview

41

v v v

ObjectGrid. These listeners are added and removed from the Session. See Listeners on page 79 and Session interface on page 45 for more information. TransactionCallback: A TransactionCallback listener interface allows transactional events such as begin, commit and rollback signals to send to this interface. Typically, a TransactionCallback listener interface is used with a Loader. For more information, see TransactionCallback plug-in on page 108 and Loaders on page 92. These events can then be used to coordinate transactions with an external resource or within multiple loaders. SubjectValidation. If security is enabled, this plug-in can be used to validate a javax.security.auth.Subject class that is passed to the ObjectGrid. MapAuthorization. If security is enabled, this plug-in can be used to authorize ObjectMap accesses to the principals that are represented by the Subject object. SubjectSource If security is enabled, this plug-in can be used to get a Subject object that represents the ObjectGrid client. This subject is then used for ObjectGrid authorization.

BackingMap interface
Each ObjectGrid instance contains a collection of BackingMap objects. Each BackingMap is named and is added to an ObjectGrid instance by using the defineMap method of the ObjectGrid interface. The defineMap method returns a BackingMap instance that is then used to define the behavior of an individual Map. The Session interface is used to begin a transaction and to obtain the ObjectMap or JavaMap that is required for performing transactional interaction between an application and a BackingMap object. However, the transaction changes are not applied to the BackingMap object until the transaction is committed. A BackingMap can be considered as a in-memory cache of committed data for an individual Map. For more information about the Session interface, see Session interface on page 45. The com.ibm.websphere.objectgrid.BackingMap interface provides methods for setting BackingMap attributes. Some of the set methods allow extensibility of a BackingMap through several custom designed plug-ins. Following is a list of the set methods for setting attributes and providing custom designed plug-in support:
// For setting BackingMap attributes. public void setReadOnly(boolean readOnlyEnabled); public void setNullValuesSupported(boolean nullValuesSupported); public void setLockStrategy( LockStrategy lockStrategy ); public void setCopyMode(CopyMode mode, Class valueInterface); public void setNumberOfBuckets(int numBuckets); public void setNumberOfLockBuckets(int numBuckets); public void setLockTimeout(int seconds); public void setTimeToLive(int seconds); public void setTtlEvictorType(TTLType type); // For public public public public public public public public setting an optional custom plug-in provided by application. abstract void setObjectTransformer(ObjectTransformer t); abstract void setOptimisticCallback(OptimisticCallback checker); abstract void setLoader(Loader loader); abstract void setPreloadMode(boolean async); abstract void setEvictor(Evictor e); void setMapEventListeners( List /*MapEventListener*/ eventListenerList ); void addMapEventListener(MapEventListener eventListener ); void removeMapEventListener(MapEventListener eventListener );

42

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

A corresponding get method exists for each of the set methods listed.

BackingMap attributes
Each BackingMap has the following attributes that can be set to modify or control the BackingMap behavior: v ReadOnly attribute. This attribute indicates if the Map is a read-only Map or a read and write Map. If this attribute is never set for the Map, then the Map is defaulted to be a read and write Map. When a BackingMap is set to be read only, ObjectGrid optimizes performance for read only when possible. v NullValuesSupported attribute. This attribute indicates if a null value can be put into the Map. If this attribute is never set, the Map does not support null values. If null values are supported by the Map, a get operation that returns null can mean that either the value is null or the map does not contain the key specified by the get operation. v LockStrategy attribute. This attribute determines if a lock manager is used by this BackingMap. If a lock manager is used, then the LockStrategy attribute is used to indicate whether an optimistic locking or pessimistic locking approach is used for locking the map entries. If this attribute is not set, then the optimistic LockStrategy is used. See the Locking on page 58 topic for details on the supported lock strategies. v CopyMode attribute. This attribute determines if a copy of a value object is made by the BackingMap when a value is read from the map or is put into the BackingMap during the commit cycle of a transaction. Various copy modes are supported to allow the application to make the trade-off between performance and data integrity. If this attribute is not set, then the COPY_ON_READ_AND_COMMIT copy mode is used. This copy mode does not have the best performance, but it has the greatest protection against data integrity problems. For more information about the copy modes, see copyMode method best practices on page 174. v NumberOfBuckets attribute. This attribute indicates the number of hash buckets to be used by the BackingMap. The BackingMap implementation uses a hash map for its implementation. If a lot of entries exist in the BackingMap, then more buckets means better performance. The number of keys that have the same bucket becomes lower as the number of buckets grows. More buckets also mean more concurrency. This attribute is useful for fine tuning performance. A default value of 503 is used if the application does not set the NumberOfBuckets attribute. v NumberOfLockBuckets attibute. This attribute indicates the number of lock buckets that are be used by the lock manager for this BackingMap. When the LockStrategy is set to OPTIMISTIC or PESSIMISTIC, a lock manager is created for the BackingMap. The lock manager uses a hash map to keep track of entries that are locked by one or more transactions. If a lot of entries exist in the hash map, more lock buckets lead to better performance because the number of keys that collide on the same bucket is lower as the number of buckets grows. More lock buckets also means more concurrency. When the LockStrategy attribute is set to NONE, no lock manager is used by this BackingMap. In this case, setting numberOfLockBuckets has no effect. If this attribute is not set, a default value of 383 is used . v LockTimeout attribute. This attribute is used when the BackingMap is using a lock manager. The BackingMap uses a lock manager when the the LockStrategy attribute is set to either OPTIMISTIC or PESSIMISTIC. The attribute value is in seconds and determines how long the lock manager waits for a lock to be

Chapter 5. ObjectGrid application programming interface overview

43

granted. If this attribute is not set, then 15 seconds is used a the LockTimeout value. See Pessimistic locking on page 58 for details regarding the lock wait timeout exceptions that can occur. v TtlEvictorType attribute. Every BackingMap has its own built in time to live evictor that uses a time-based algorithm to determine which map entries to evict. By default, the built in time to live evictor is not active. You can activate the time to live evictor by calling the setTtlEvictorType method with one of three values: CREATION_TIME, LAST_ACCESS_TIME, or NONE. A value of CREATION_TIME indicates that the evictor adds the TimeToLive attribute to the time that the map entry was created in the BackingMap to determine when the evictor should evict the map entry from the BackingMap. A value of LAST_ACCESS_TIME indicates that the evictor adds the TimeToLive attribute to the time that the map entry was last accessed by some transaction that the application is running to determine when evictor should evict the map entry. The map entry is evicted only if a map entry is never accessed by any transaction for a period of time that is specified by the TimeToLive attribute. A value of NONE indicates the evictor should remain inactive and never evict any of the map entries. If this attribute is never set, then NONE is used as the default and the time to live evictor is not active. See Evictors on page 84 for details regarding the built-in time to live evictor. v TimeToLive attribute. This attribute is used to specify the number of seconds that the built in time to live evictor needs to add to the creation or last access time for each as described previously for the TtlEvictorType attribute. If this attribute is never set, then the special value of zero is used to indicate the time to live is infinity. If set to infinity, map entries are never evicted by the evictor. The following example illustrates defining The someMap BackingMap in the someGrid ObjectGrid instance and setting various attributes of the BackingMap by using the set methods of the BackingMap interface:
import import import import ... ObjectGrid og = ObjectGridManagerFactory.getObjectGridManager().createObjectGrid("someGrid"); BackingMap bm = objectGrid.getMap("someMap"); bm.setReadOnly( true ); // override default of read/write bm.setNullValuesSupported(false); // override default of allowing Null values bm.setLockStrategy( LockStrategy.PESSIMISTIC ); // override default of OPTIMISTIC bm.setLockTimeout( 60 ); // override default of 15 seconds. bm.setNumberOfBuckets(251); // override default (prime numbers work best) bm.setNumberOfLockBuckets(251); // override default (prime numbers work best) ... com.ibm.websphere.objectgrid.BackingMap; com.ibm.websphere.objectgrid.LockStrategy; com.ibm.websphere.objectgrid.ObjectGrid; com.ibm.websphere.objectgrid.ObjectGridManagerFactory;

BackingMap plug-ins
The BackingMap interface has several optional plug points for more extensible interactions with the ObjectGrid: v ObjectTransformer plug-in: For some map operations, a BackingMap might need to serialize, deserialize, or copy a key or value of an entry in the BackingMap. The BackingMap can perform these actions by providing a default implementation of the ObjectTransformer interface. An application can improve performance by providing a custom designed ObjectTransformer plug-in that is used by the BackingMap to serialize, deserialize, or copy a key or value of an entry in the BackingMap.

44

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v Evictor plug-in: The built in time to live evictor uses a time-based algorithm to decide when an entry in BackingMap must be evicted. Some applications might need to use a different algorithm for deciding when an entry in a BackingMap needs to be evicted. The Evictor plug-in makes a custom designed Evictor available to the BackingMap to use. The Evictor plug-in is in addition to the built in time to live evictor. It does not replace the time to live evictor. ObjectGrid provides a custom Evictor plug-in that implements well-known algorithms such as least recently used or least frequently used. Applications can either plug-in one of the provided Evictor plug-ins or it can provide its own Evictor plug-in. v MapEventListener plug-in: An application might want to know about BackingMap events such as a map entry eviction or a preload of a BackingMap completion. A BackingMap calls methods on the MapEventListener plug-in to notify an application of BackingMap events. An application can receive notification of various BackingMap events by using the setMapEventListener method to provide one or more custom designed MapEventListener plug-ins to the BackingMap. The application can modify the listed MapEventListener objects by using the addMapEventListener method or the removeMapEventListener method. v Loader plug-in: A BackingMap is an in-memory cache of a Map. A Loader plug-in is an option that is used by the BackingMap to move data between memory and is used for a persistent store for the BackingMap. For example, a Java database connectivity (JDBC) Loader can be used to move data in and out of a BackingMap and one or more relational tables of a relational database. A relational database does not need to be used as the persistent store for a BackingMap. The Loader can also be used to moved data between a BackingMap and a file, between a BackingMap and a Hibernate map, between a BackingMap and a Java 2 Platform, Enterprise Edition (J2EE) entity bean, between a BackingMap and another application server, and so on. The application must provide a custom-designed Loader plug-in to move data between the BackingMap and the persistent store for every technology that is used. If a Loader is not provided, the BackingMap becomes a simple in-memory cache. v OptimisticCallback plug-in: When the LockStrategy attribute for a BackingMap is set to OPTIMISTIC, either the BackingMap or a Loader plug-in must perform comparison operations for the values of the map. The OptimisticCallback plug-in is used by the BackingMap and the Loader to perform the optimistic versioning comparison operations.

Session interface
This section describes how applications begin and end transactions using the Session interface. The Session interface also provides access to the application based ObjectMap and JavaMap interfaces.

Introduction
Each ObjectMap or JavaMap instance is directly tied to a specific Session object. Each thread that wants access to an ObjectGrid must first obtain a Session from the ObjectGrid object. A Session instance cannot be shared concurrently between threads. ObjectGrid does not use any thread local storage, but platform restrictions might limit the opportunity to pass a Session from one thread to another.

Chapter 5. ObjectGrid application programming interface overview

45

Methods
The following methods are available with the Session interface. See the API documentation for more information about the following methods:
public interface Session { ObjectMap getMap(String cacheName) throws UndefinedMapException; void begin() throws TransactionAlreadyActiveException, TransactionException; void beginNoWriteThrough() throws TransactionAlreadyActiveException, TransactionException; public void commit() throws NoActiveTransactionException, TransactionException; public void rollback() throws NoActiveTransactionException, TransactionException; public void flush() throws TransactionException; ObjectGrid getObjectGrid(); TxID getTxID() throws NoActiveTransactionException; boolean isWriteThroughEnabled(); void setTransactionType(String tranType); public void processLogSequence(LogSequence logSequence) throws NoActiveTransactionException, UndefinedMapException, ObjectGridException; }

Obtain a session
An application obtains a Session instance from an ObjectGrid object using the ObjectGrid.getSession method. The following code snippet shows how to obtain a Session instance:
ObjectGrid objectGrid = ...; Session sess = objectGrid.getSession();

After a Session is obtained, the thread keeps a reference to the session for its own use. Calling the getSession method multiple times returns a new Session object each time.

Transactions and sessions


A Session can be used to begin, commit, or rollback transactions. Operations against BackingMaps using ObjectMaps and JavaMaps are most efficiently performed within a Session transaction. After a transaction has started, any changes to one or more BackingMaps in that transaction scope are stored in a special transaction cache until the transaction is committed. When a transaction is committed, the pending changes are applied to the BackingMaps and Loaders and become visible to any other clients of that ObjectGrid. ObjectGrid also supports the ability to automatically commit transactions, also known as auto-commit. If any ObjectMap operations are performed outside of the

46

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

context of an active transaction, an implicit transaction is started before the operation and the transaction is automatically committed before returning control to the application.
Session session = objectGrid.getSession(); ObjectMap objectMap = session.getMap("someMap"); session.begin(); objectMap.insert("key1", "value1"); objectMap.insert("key2", "value2"); session.commit(); objectMap.insert("key3", "value3"); // autocommit

Session.flush method
The Session.flush method only makes sense when a Loader is associated with a BackingMap. The flush method invokes the Loader with the current set of changes in the transaction cache. The Loader applies the changes to the backend. These changes are not committed when the flush is invoked. If a Session transaction is committed after a flush invocation, only updates that happen after the flush invocation are applied to the Loader. If a Session transaction is rolled back after a flush invocation, the flushed changes are discarded with all other pending changes in the transaction. Use the Flush method sparingly because it limits the opportunity for batch operations against a Loader. Following is an example of the usage of the Session.flush method:
Session session = objectGrid.getSession(); session.begin(); // make some changes ... session.flush(); // push these changes to the Loader, but dont commit yet // make some more changes ... session.commit();

No write through method


Some ObjectGrid maps are backed by a Loader, which provides persistent storage for the data in the map. Sometimes it is useful to commit data just to the ObjectGrid map and not push data out to the Loader. The Session interface provides the beginNoWriteThough method for this purpose. The beginNoWriteThrough method starts a transaction like the begin method. With the beginWriteThrough method, when the transaction is committed, the data is only committed to the ObjectGrid in-memory map and is not committed to the persistent storage that is provided by the Loader. This method is very useful when performing data preload on the map. The Session interface also provides the isWriteThroughEnabled method to determine what type of transaction is currently active.
Session session = objectGrid.getSession(); session.beginNoWriteThrough(); // make some changes ... session.commit(); // these changes will not get pushed to the Loader

Obtain the TxID object


The TxID object is an opaque object that identifies the active transaction. Use the TxID object for the following purposes: v For comparison when you are looking for a particular transaction. v To store shared data between the TransactionCallback and Loader objects.
Chapter 5. ObjectGrid application programming interface overview

47

See TransactionCallback plug-in on page 108 and Loaders on page 92 for additional information about the Object slot feature.

Set the transaction type for performance monitoring


If you are using ObjectGrid within a WebSphere Application Server application server, it might be necessary to reset the transaction type for performance monitoring. You can set the transaction type with the setTransactionType method. See Monitoring ObjectGrid performance with WebSphere Application Server performance monitoring infrastructure (PMI) on page 136 for more information about the setTransactionType method.

Process a complete LogSequence


ObjectGrid can propagate sets of map changes to other ObjectGrid listeners as a means of distributing maps from one Java Virtual Machine (JVM) to another. See ObjectGrid distributed transaction propagation on page 143 for more information about this ability. To make it easier for the listener to process the received LogSequences, the Session interface provides the processLogSequence method. This method examines each LogElement within the LogSequence and performs the appropriate operation, for example, insert, update, invalidate, and so on, against the BackingMap that is identified by the LogSequence MapName. An ObjectGrid Session must be active before the processLogSequence method is invoked. The application is also responsible for issuing the appropriate commit or rollback calls to complete the Session. Autocommit processing is not available for this method invocation. Normal processing by the receiving ObjectGridEventListener at the remote JVM would be to start a Session using the beginNoWriteThrough method, which prevents endless propagation of changes, followed by a call to this processLogSequence method, and then committing or rolling back the transaction.
// Use the Session object that was passed in during //ObjectGridEventListener.initialization... session.beginNoWriteThrough(); // process the received LogSequence try { session.processLogSequence(receivedLogSequence); } catch (Exception e) { session.rollback(); throw e; } // commit the changes session.commit();

ObjectMap and JavaMap interfaces


This topic describes how applications interact with ObjectGrid using the ObjectMap and JavaMap interfaces. These two interfaces are used for transactional interaction between applications and BackingMaps.

ObjectMap interface
An ObjectMap instance is obtained from a Session object that corresponds to the current thread. The ObjectMap interface is the main vehicle that applications use to make changes to entries in a BackingMap. Obtain an ObjectMap instance

48

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

An application gets an ObjectMap instance from a Session object using the Session.getMap(String) method. The following code snippet demonstrates how to obtain an ObjectMap instance:
ObjectGrid objectGrid = ...; BackingMap backingMap = objectGrid.defineMap("mapA"); Session sess = objectGrid.getSession(); ObjectMap objectMap = sess.getMap("mapA");

Each ObjectMap instance corresponds to a particular Session object. Calling the getMap method multiple times on a particular Session object with the same BackingMap name always returns the same ObjectMap instance. Autocommit Transactions As previously stated, operations against BackingMaps that use ObjectMaps and JavaMaps are most efficiently performed within a Session transaction. ObjectGrid provides autocommit support when methods on the ObjectMap and JavaMap interfaces are called outside of a Session transaction. The methods start an implicit transaction, perform the requested operation, and commit the implicit transaction. Method Semantics Following is an explanation of the semantics behind each method on the ObjectMap and JavaMap interfaces. The setDefaultKeyword method, the invalidateUsingKeyword method, and the methods that have a Serializable argument are discussed in the Keywords Keywords on page 52 topic.The setTimeToLive method is discussed in theEvictors on page 84 topic. See the API documentation for more information on these methods. containsKey method Determines if a key has a value in the BackingMap or Loader. If null values are supported by an application, this method can be used to determine if a null reference that is returned from a get operation refers to a null value or indicates that the BackingMap and Loader do not contain the key. flush method The semantics of this method are similar to the flush method on the Session interface. The notable difference is that the Session flush applies the current pending changes for all of the maps that have been modified in the current session. With this method, only the changes in this ObjectMap are flushed to the loader. get method Fetches the entry from the BackingMap. If the entry is not found in the BackingMap but a Loader is associated with the BackingMap, it attempts to fetch the entry from the Loader. The getAll method is provided to allow batch fetch processing. getForUpdate method Same as the get method, but using the getForUpdate method tells the BackingMap and Loader that the intention is to update the entry. A Loader can use this hint to issue a SELECT for UPDATE query to a database backend. If a Pessimistic LockingStrategy is defined for the BackingMap, the lock manager locks the entry. The getAllForUpdate method is provided to allow batch fetch processing. insert method Inserts an entry into the BackingMap and the Loader. Using this method
Chapter 5. ObjectGrid application programming interface overview

49

tells the BackingMap and Loader that you want to insert a previously nonexistent entry. When you invoke this method on an existing entry, an exception occurs when the method is invoked or when the current transaction is committed. invalidate method The semantics of the invalidate method depend on the value of the isGlobal parameter that is passed to the method. The invalidateAll method is provided to allow batch invalidate processing. Local invalidation is specified when the value false is passed as the isGlobal parameter of the invalidate method. Local invalidation discards any changes to the entry in the transaction cache. If the application issues a get method, the entry is fetched from the last committed value in the BackingMap. If no entry is present in the BackingMap, the entry is fetched from the last flushed or committed value in the Loader. When a transaction is committed, any entries that are marked as being locally invalidated have no impact on the BackingMap. Any changes that were flushed to the Loader are still committed even if the entry was invalidated. Global invalidation is specified when true is passed as the isGlobal parameter of the invalidate method. Global invalidation discards any pending changes to the entry in the transaction cache and bypasses the BackingMap value on subsequent operations that are performed on the entry. When a transaction is committed, any entries that are marked as globally invalidated are evicted from the BackingMap. Consider the following use case for invalidation as an example: The BackingMap is backed by a database table that has an auto increment column. Increment columns are useful for assigning unique numbers to records. The application inserts an entry. After the insert, the application needs to know the sequence number for the inserted row. It knows that its copy of the object is old, so it uses global invalidation to get the value from the Loader. The following code demonstrates this use case:
Session sess = objectGrid.getSession(); ObjectMap map = sess.getMap("mymap"); sess.begin(); map.insert("Billy", new Person("Joe", "Bloggs", "Manhattan")); sess.flush(); map.invalidate("Billy", true); Person p = map.get("Billy"); System.out.println("Version column is: " + p.getVersion()); map.commit();

This code sample adds an entry for Billy. The version attribute of Person is set using an auto-increment column in the database. The application does an insert command first. It then issues a flush, which causes the insert to be sent to the Loader and database. The database sets the version column to the next number in the sequence, which makes the Person object in the transaction outdated. To update the object, the application performs a global invalidate. The next get method that is issued gets the entry from the Loader ignoring the transactions value. The entry is fetched from the database with the updated version value. put method The semantics of the put method are dependent on whether a previous get method was invoked in the transaction for the key. If the application issues a get operation that returns an existent entry in the BackingMap or Loader, the put method invocation is interpreted as an update and returns the previous value in the transaction. A put without a previous get or previous

50

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

get that did not find an entry is interpreted as an insert. The semantics of the insert and update methods apply when the put operation is committed. The putAll method is provided to enable batch insert and update processing. remove method Removes the entry from the BackingMap and the Loader, if one is plugged in. If the entry was previously retrieved in the transaction, this method returns the value. The removeAll method is provided to enable batch deletion processing without the return values. setCopyMode method Specifies a CopyMode for this ObjectMap. With this method, an application can override the CopyMode that is specified on the BackingMap. The specified CopyMode is in effect until clearCopyMode method is invoked. Both methods are invoked outside of transactional bounds. A CopyMode cannot be changed in the middle of a transaction. touch method Updates the last access time for an entry. This method does not retrieve the value from the BackingMap. Use this method in its own transaction. If the provided key does not exist in the BackingMap due to invalidation or removal, an exception occurs during commit processing. update method Explicitly updates an entry in the BackingMap and the Loader. Using this method indicates to the BackingMap and Loader that you want to update an existing entry. An exception occurs if you invoke this method on an entry that does not exist when the method is invoked or during commit processing.

JavaMap interface
A JavaMap instance is obtained from an ObjectMap object. The JavaMap interface has the same method signatures as ObjectMap, but with different exception handling. JavaMap extends the java.util.Map interface, so all exceptions are instances of the java.lang.RuntimeException class. Because JavaMap extends the java.util.Map interface, it is easy to quickly use ObjectGrid with an existing application that uses a java.util.Map interface for object caching. Obtain JavaMap An application gets a JavaMap instance from an ObjectMap object using the ObjectMap.getJavaMap method. The following code snippet demonstrates how to obtain a JavaMap instance.
ObjectGrid objectGrid = ...; BackingMap backingMap = objectGrid.defineMap("mapA"); Session sess = objectGrid.getSession(); ObjectMap objectMap = sess.getMap("mapA"); java.util.Map map = objectMap.getJavaMap(); JavaMap javaMap = (JavaMap) javaMap;

A JavaMap is backed by the ObjectMap from which it was obtained. Calling getJavaMap multiple times using a particular ObjectMap always returns the same JavaMap instance. Supported methods

Chapter 5. ObjectGrid application programming interface overview

51

The JavaMap interface only supports a subset of the methods on the java.util.Map interface. The the java.util.Map interface supports the following methods: v containsKey(java.lang.Object) v get(java.lang.Object) v put(java.lang.Object, java.lang.Object) v putAll(java.util.Map) v remove(java.lang.Object) All other methods inherited from the java.util.Map interface result in the java.lang.UnsupportedOperationException exception.

Keywords
ObjectGrid provides a flexible invalidation mechanism based around keywords. A keyword is a non-null instance of any serializable object. You can associate keywords with BackingMap entries in any way you choose.

Associate keywords with entries


A set of entries can be associated with zero or more keywords. The methods on ObjectMap and JavaMap that manipulate entries, including the get, update, put, insert, and touch methods, all have versions that allow a single keyword to be associated with all of the entries that the method alters. New keyword associations are only visible in the current transaction until the transaction is committed. After a commit, the new association is applied to the BackingMap and is visible to other transactions. If an error occurs during commit processing resulting in a rollback or if a user rolls back an active transaction, the new keyword associations are rolled back. The following code demonstrates how a new entry is associated with a keyword:
Session sess = objectGrid.getSession(); ObjectMap map = sess.getMap("MapA"); sess.begin(); map.insert("Billy", new Person("Joe", "Bloggs", "Manhattan"), "New York"); sess.commit();

The previous example code inserts a new entry into the BackingMap and associates it with the keyword New York. An application that inserts entries must also associate keywords when the entries are retrieved. The application must associate keywords with entries every time it gets them. Consider the following code sample:
sess.begin(); Person p = (Person)map.get("Billy", "New York"); sess.commit();

The previous example code ensures that the retrieved entry is associated with the New York keyword. An application can associate multiple keywords with an entry, but only one keyword per method invocation. To associate more keywords issue another method invocation, like the following sample:
sess.begin(); Person p = (Person)map.get("Billy", "New York"); map.touch("Billy", "Another keyword"); map.get("Billy", "Yet another keyword"); sess.commit();

52

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Default keywords
The setDefaultKeyword method on the ObjectMap and JavaMap interfaces provides a way to associate entries with a particular keyword without using the keyword version of the get, insert, put, update, or touch methods. If a keyword version of the methods is used, the default keyword is ignored, and the supplied keyword object is used.
sess.begin(); map.setDefaultKeyword("New York"); Person p = (Person)map.get("Billy"); p = (Person)map.get("Bob", "Los Angeles"); map.setDefaultKeyword(null); p = (Person)map.get("Jimmy"); sess.commit();

In the preceding example Billy is associated with the default keyword, New York. Bob is not associated with the default keyword because an explicit keyword was passed to the get invocation to retrieve the Bob entry. No keywords are associated with Jimmy because the default keyword was reset and no explicit keyword argument was passed to the get method invocation.

Invalidate entries with keywords


Using the invalidateUsingKeyword method on the ObjectMap and JavaMap interfaces invalidates all entries that are associated with a keyword in the corresponding BackingMap. With this approach you can efficiently invalidate related entries in a single operation.
sess.begin(); map.insert("Billy", new Person("Joe", "Bloggs", "Manhattan"), "New York"); map.invalidateUsingKeyword("New York", false); map.insert("Bob", new Person("Paul", "Henry", "Albany"), "New York"); sess.commit();

In the preceding example, the entry for Billy is invalidated and is not inserted into the BackingMap. The entry for Bob is not invalidated because it was inserted after the invalidateUsingKeyword method invocation. The invalidateUsingKeyword method invalidates entries based on the keyword associations when the method is invoked.

Keyword Grouping
Keywords can also be grouped together in a parent-child relationship. A parent keyword can have multiple children, and a child keyword can have multiple parents. For example, if an application uses the keywords Dublin, Paris, New York, and Los Angeles, it can add the following keyword groupings: v USA groups New York and Los Angeles v Europe groups Dublin and Paris v World groups USA and Europe Invalidating the keyword USA invalidates all entries that are associated with the New York and Los Angeles keywords. Invalidating the World keyword invalidates all entries that are associated with the USA and Europe groupings. Keyword associations are defined using the associateKeyword method on the ObjectGrid interface. Adding child keywords to a parent keyword after an invalidateUsingKeyword method invocation does not cause the entries associated with the child keyword to be invalidated. The following example code defines the set of keyword associations that are described:
Chapter 5. ObjectGrid application programming interface overview

53

ObjectGrid objectGrid = ...; objectGrid.associateKeyword("USA", "New York"); objectGrid.associateKeyword("USA", "Los Angeles"); objectGrid.associateKeyword("Europe", "Dublin"); objectGrid.associateKeyword("Europe", "Paris"); objectGrid.associateKeyword("World", "USA"); objectGrid.associateKeyword("World", "Europe");

LogElement and LogSequence objects


When an application is making changes to a Map during a transaction, a LogSequence object tracks those changes. If the application changes an entry in the map, a corresponding LogElement exists to provide the details of the change. Loaders are given a LogSequence object for a particular map whenever an application calls for a flush or commit to the transaction. The Loader iterates over the LogElements within the LogSequence and applies each LogElement to the backend. ObjectGridEventListeners registered with an ObjectGrid also make use of LogSequences. These listeners are given a LogSequence for each map in a committed transaction. Applications can use these listeners to wait for certain entries to change, like a trigger in a conventional database. This topic describes four log-related interfaces or classes that are provided by the ObjectGrid framework: v com.ibm.websphere.objectgrid.plugins.LogElement v com.ibm.websphere.objectgrid.plugins.LogSequence v com.ibm.websphere.objectgrid.plugins.LogSequenceFilter v com.ibm.websphere.objectgrid.plugins.LogSequenceTransformer

LogElement
A LogElement represents an operation on an entry during a transaction. A LogElement object has the following attributes, the most commonly used ones are the first two on the following list, the type and the current value: type A log element type indicates the kind of operation that this log element represents. The type can be one of the following constants that are defined in the LogElement interface: INSERT, UPDATE, DELETE, EVICT, FETCH, or TOUCH.

current value The current value represents the new value for the operation INSERT, UPDATE or FETCH. If the operation is TOUCH, DELETE, or EVICT, the current value is null. This value can be cast to ValueProxyInfo when a ValueInterface is in use. CacheEntry You can get a reference to the CacheEntry object from the LogElement and use the methods defined on the CacheEntry object to retrieve needed information. pending state If the pending state is true, the change represented by this log element has not been applied to the loader yet. If it is false, the change has been applied to the loader, most likely by the flush operation.

54

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

versioned value Versioned value is a value that can be used for versioning. new keywords The new keyword collection contains any new keywords that have been associated with this entry. last access time Represents the last access time for the entry.

LogSequence
In most transactions, operations to more than one entry in a map occur, so multiple LogElement objects are created. It makes sense to have an object that acts as a composite of multiple LogElement objects. The LogSequence interface serves this purpose by containing a list of LogElement objects. The LogSequence interface has the following methods: size method Returns the number of LogElement objects in the specified sequence. getAllChanges method Returns an iterator of all the changes in the specified log sequence. getPendingChanges method Returns an iterator of all the pending changes. This is most likely to be used by a loader to only apply pending changes to the persistent store. getChangesByKeys method Returns an iterator of the LogElement objects that have the target key, based on the input parameter. getChangesByTypes method Returns an iterator of the LogElement objects that are of the specified LogElement type. getMapName method Returns the name of the backing map to which the changes apply. The caller can use this name as input to the Session.getMap(string) method. isDirty method Returns whether this LogSequence has any LogElements that would dirty a Map. That is, if the LogSequence contains any LogElement objects that are of any type other than Fetch or Get, then the LogSequence is considered dirty. LogElement and LogSequence are widely used in ObjectGrid and by ObjectGrid plug-ins that are written by users when operations are propagated from one component or server to another component or server. For example, a LogSequence object can be used by the distributed ObjectGrid transaction propagation function to propagate the changes to other servers, or it can be applied to the persistence store by the loader. LogSequence is mainly used by the following interfaces. v com.ibm.websphere.objectgrid.plugins.ObjectGridEventListener v com.ibm.websphere.objectgrid.plugins.Loader v com.ibm.websphere.objectgrid.plugins.Evictor v com.ibm.websphere.objectgrid.Session For more details about these interfaces, please refer to the API documentation.

Chapter 5. ObjectGrid application programming interface overview

55

Loader example
This section demonstrates how the LogSequence and LogElement objects are used in a Loader. A Loader is used to load data from and persist data into a persistent store. The batchUpdate method of the Loader interface uses LogSequence:
void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException;

The batchUpdate method is called whenever an ObjectGrid needs to apply all current changes to the Loader. The Loader is given a list of LogElement objects for the map, encapsulated in a LogSequence object. The implementation of the batchUpdate method must iterate over the changes and apply them to the backend. The following code snippet shows how a Loader uses a LogSequence object. The snippet iterates over the set of changes and builds up three batch Java database connectivity (JDBC) statements: one that has inserts, one that has updates and, a third statement that has deletes:
public void batchUpdate(TxID tx, LogSequence sequence) throws LoaderException { // Get a SQL connection to use. Connection conn = getConnection(tx); try { // Process the list of changes and build a set of prepared // statements for executing a batch update, insert, or delete // SQL operations. The statements are cached in stmtCache. Iterator iter = sequence.getPendingChanges(); while ( iter.hasNext() ) { LogElement logElement = (LogElement)iter.next(); Object key = logElement.getCacheEntry().getKey(); Object value = logElement.getCurrentValue(); switch ( logElement.getType().getCode() ) { case LogElement.CODE_INSERT: buildBatchSQLInsert( key, value, conn ); break; case LogElement.CODE_UPDATE: buildBatchSQLUpdate( key, value, conn ); break; case LogElement.CODE_DELETE: buildBatchSQLDelete( key, conn ); break; } } // Run the batch statements that were built by above loop. Collection statements = getPreparedStatementCollection( tx, conn ); iter = statements.iterator(); while ( iter.hasNext() ) { PreparedStatement pstmt = (PreparedStatement) iter.next(); pstmt.executeBatch(); } } catch (SQLException e) { LoaderException ex = new LoaderException(e); throw ex; } }

The previous sample illustrates the high level logic of processing the LogSequence argument and the details of how an SQL insert, update, or delete statement is

56

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

built are not illustrated. This example illustrates that the getPendingChanges method is called on LogSequence argument to obtain an iterator of LogElement objects that a Loader needs to process, and the LogElement.getType().getCode() method is used to determine whether a LogElement is for a SQL insert, update, or delete operation.

Evictor sample
This example explores how LogSequence and LogElement are used in an Evictor. An Evictor is used to evict the map entries from the backing map based on certain criteria. The apply method of the Evictor interface uses LogSequence:
/** * This is called during cache commit to allow the evictor to track object usage * in a backing map. This will also report any entries that have been successfully * evicted. * * @param sequence LogSequence of changes to the map */ void apply(LogSequence sequence);

For information on how the apply method uses LogSequence, refer to the code sample in the Evictors on page 84 topic.

LogSequenceFilter and LogSequenceTransformer


Sometimes, it is necessary to filter the LogElement objects so that only LogElement objects with certain criteria are accepted, and reject other objects. For example, you might want to serialize a certain LogElement based on some criterion. LogSequenceFilter solves this problem with the following method:
public boolean accept (LogElement logElement);

This method returns true if the given LogElement should be used in the operation, and returns false if the given LogElement should not be used. LogSequenceTransformer is a class which utilizes the LogSequenceFilter function described above. It uses the LogSequenceFilter to filter out some LogElement objects and then serialize the accepted LogElement objects. This class has two methods. The first method follows:
public static void serialize(Collection logSequences, ObjectOutputStream stream, LogSequenceFilter filter, DistributionMode mode) throws IOException

This method allows the caller to provide a filter for determining which LogElements to include in the serialization process. The DistributionMode parameter allows the caller to control the serialization process. For example, if the distribution mode is invalidation only, then there is no need to serialize the value. The second method of this class follows:
public static Collection inflate(ObjectInputStream stream, ObjectGrid objectGrid) throws IOException, ClassNotFoundException.

This method reads the log sequence serialized form, which was created by the serialize method, from the provided object input stream.

Chapter 5. ObjectGrid application programming interface overview

57

Locking
This topic describes the locking strategy that is supported by an ObjectGrid BackingMap. Each BackingMap can be configured to use one of the following locking strategies: v Pessimistic locking v Optimistic locking v None Following is an example of how the lock strategy can be set on the map1, map2, and map3 BackingMaps, where each map is using a different locking strategy:
import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.LockStrategy; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; ... ObjectGrid og = ObjectGridManagerFactory.getObjectGridManager().createObjectGrid("test"); BackingMap bm = og.defineMap("map1"); bm.setLockStrategy( LockStrategy.PESSIMISTIC ); bm = og.defineMap("map2"); bm.setLockStrategy( LockStrategy.OPTIMISTIC ); bm = og.defineMap("map3"); bm.setLockStrategy( LockStrategy.NONE );

To avoid a java.lang.IllegalStateException exception, the setLockStrategy method must be called before calling the initialize or getSession methods on the ObjectGrid instance. When either PESSIMISTIC or OPTIMISTIC lock strategy is used, a lock manager is created for the BackingMap. The lock manager uses a hash map to keep track of entries that are locked by one or more transactions. If many map entries exist in the hash map, more lock buckets means better performance. The risk of Java synchronization collisions is lower as the number of buckets grows. More lock buckets also lead to more concurrency. The following example shows how an application can set the number of lock buckets to use for a given BackingMap:
bm.setNumberOfLockBuckets( 503 );

Again, to avoid a java.lang.IllegalStateException exception, the setNumberOfLockBuckets method must be called before calling the initialize or getSession methods on the ObjectGrid instance. The setNumberOfLockBuckets method parameter is a Java primitive integer that specifies the number of lock buckets to use. Using a prime number ensures a uniform distribution of map entries over the lock buckets. A good starting point for best performance is set the number of lock buckets to about ten percent of the expected number of BackingMap entries.

Pessimistic locking
Use the pessimistic locking strategy for read and write maps when other locking strategies are not viable. When an ObjectGrid Map is configured to use the PESSIMISTIC locking strategy, a pessimistic transaction lock for a map entry is obtained when a transaction first

58

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

gets the entry from the BackingMap. The pessimistic lock is held until the application completes the transaction. Typically, the pessimistic locking strategy is used in the following situations: v The BackingMap is configured with or without a loader and versioning information is not available v The BackingMap is used directly by an application that needs help from the ObjectGrid for concurrency control v Versioning information is available, but update transactions frequently collide on the backing entries, resulting in optimistic update failures Because the pessimistic locking strategy has the greatest impact on performance and scalability, this strategy should only be used for read and write maps when other locking strategies are not viable. For example, optimistic update failures occur frequently, or recovery from optimistic failure is difficult for an application to handle.

ObjectMap methods and lock modes


When an application uses the methods of the ObjectMap interface, ObjectGrid automatically attempts to acquire a pessimistic lock for the map entry being accessed. ObjectGrid uses the following lock modes based on which method the application calls in the ObjectMap interface: v The get and getAll methods acquire an S lock, or a shared lock mode for the key of a map entry. The S lock is held until the transaction completes. An S lock mode allows concurrency between transactions that attempt to acquire an S or an upgradeable lock (U lock) mode for the same key, but blocks other transactions that attempt to get an exclusive lock (X lock) mode for the same key. v The getForUpdate and getAllForUpdate methods acquire a U lock, or an upgradeable lock mode for the key of a map entry. The U lock is held until the transaction completes. A U lock mode allows concurrency between transactions that acquire an S lock mode for the same key, but blocks other transactions that attempt to acquire a U lock or X lock mode for the same key. v The put, putAll, remove, removeAll, insert, update, and touch acquire an X lock, or exclusive lock mode for the key of a map entry. The X lock is held until the transaction completes. An X lock mode ensures that only 1 transaction is inserting, updating, or removing a map entry of a given key value. An X lock blocks all other transactions that attempt to acquire a S, U, or X lock mode for the same key. v The global invalidate and global invalidateAll methods acquire an X lock for each map entry that is invalidated. The X lock is held until the transaction completes. No locks are acquired for the local invalidate and local invalidateAll methods because none of the BackingMap entries are invalidated by local invalidate method calls. From the preceding definitions, it is obvious that an S lock mode is weaker than a U lock mode because it allows more transactions to run concurrently when accessing the same map entry. The U lock mode is slightly stronger than the S lock mode because it blocks other transactions that are requesting either a U or X lock mode. The S lock mode only blocks other transactions that are requesting an X lock mode. This small difference is important in preventing some deadlocks from occurring. The X lock mode is the strongest lock mode because it blocks all other transactions attempting to get an S, U, or X lock mode for the same map entry. The net affect of an X lock mode is to ensure that only one transaction can insert,
Chapter 5. ObjectGrid application programming interface overview

59

update, or remove a map entry and to prevent updates from being lost when more than one transaction is attempting to update the same map entry. The following table is a lock mode compatibility matrix that summarizes the described lock modes and is used to determine which lock modes are compatible with each other. To read this matrix, the row in matrix indicates a lock mode that is already granted. The column indicates the lock mode that is requested by another transaction. If Yes is displayed in the column, the lock mode requested by the other transaction is granted because it is compatible with the lock mode that is already granted. No indicates that the lock mode is not compatible and the other transaction must wait for the first transaction to release the lock that it owns.
Table 5. Locking mode compatibility and strength lock S (Shared) S (Shared) Y compatible locks U (Upgradeable) X (exclusive) Y N N N N N weakest normal strongest strength

U (Upgradeable) Y X (Exclusive) N

Lock wait timeout


Each ObjectGrid BackingMap has a default lock wait timeout value. The timeout value is used to ensure that an application does not wait forever for a lock mode to be granted because of a deadlock condition that occurs due to an application error. The application can use the BackingMap interface to override the default lock wait timeout value. The following example illustrates how to set the lock wait timeout value for map1 to 60 seconds:
import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.LockStrategy; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; ... ObjectGrid og = ObjectGridManagerFactory.getObjectGridManager().createObjectGrid("test"); BackingMap bm = og.defineMap("map1"); bm.setLockStrategy( LockStrategy.PESSIMISTIC ); bm.setLockTimeout( 60 );

To avoid a java.lang.IllegalStateException exception, call both the setLockStrategy method and the setLockTimeout method before calling either the initialize or getSession methods on the ObjectGrid instance. The setLockTimeout method parameter is a Java primitive integer that specifies the number of seconds ObjectGrid should wait for a lock mode to be granted. If a transaction waits longer than the lock wait timeout value configured for the BackingMap, ObjectGrid throws a com.ibm.websphere.objectgrid.LockTimeoutException exception. When a LockTimeoutException does occur, the application must determine whether the timeout is occurring because the application is running slower than expected or if the timeout occurred because of a deadlock condition. If an actual deadlock condition occurred, then increasing the lock wait timeout value does not eliminate the exception. Increasing the timeout results in the exception taking longer to occur. However, if increasing the lock wait timeout value does eliminate the exception, then the problem occurred because the application was running slower than expected. The application in this case must determine why performance is slow.

60

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Deadlocks
Consider the following sequence of lock mode requests:
X lock is granted to transaction 1 for key1. X lock is granted to transaction 2 for key2. X lock requested by transaction 1 for key2. (Transaction 1 blocks waiting for lock owned by transaction 2.) X lock requested by transaction 2 for key1. (Transaction 2 blocks waiting for lock owned by transaction 1.)

The preceding sequence is the classic deadlock example of two transactions that attempt to acquire more than a single lock and each transaction acquires the locks in a different order. To prevent this deadlock, each transaction must obtain the multiple locks in the same order. If the OPTIMISTIC lock strategy is used and the flush method on the ObjectMap interface is never used by the application, then lock modes are requested by the transaction only during the commit cycle. During the commit cycle, the ObjectGrid determines the keys for the map entries that need to be locked and requests the lock modes in key sequence. With this method, ObjectGrid prevents the large majority of the classic deadlocks. However, ObjectGrid does not and cannot prevent all possible deadlock scenarios. A couple of scenarios exist that the application needs to consider. Following are the scenarios that the application must be aware of and take preventative action against. One scenario exists where ObjectGrid is able to detect a deadlock without having to wait for a lock wait timeout to occur. If this scenario does occur, ObjectGrid throws a com.ibm.websphere.objectgrid.LockDeadlockException exception. Consider the following code snippet:
Session sess = ...; ObjectMap person = sess.getMap("PERSON"); sess.begin(); Person p = (IPerson)person.get("Billy"); // Billy had a birthday, so we make him 1 year older. p.setAge( p.getAge() + 1 ); person.put( Billy, p ); sess.commit();

It is unlikely that more than one transaction might attempt to increase Billys age, consider a situation where his wife wants to make him older than he is, and both Billy and his wife run the transaction concurrently. In this situation, both transactions own an S lock mode on the Billy entry of the PERSON map as a result of the person.get(Billy) method invocation. As a result of the person.put (Billy, p) method call, both transactions attempt to upgrade the S lock mode to an X lock mode. Both transactions block waiting for the other transaction to release the S lock mode it owns. As a result, a deadlock occurs because a circular wait condition exists between the two transactions. A circular wait condition results when more than one transaction attempts to promote a lock from a weaker to a stronger mode for the same map entry. In this scenario, the ObjectGrid throws a LockDeadlockException exception rather than a LockTimeoutException exception. The application can prevent the LockDeadlockException exception for the preceding example by using the OPTIMISTIC lock strategy rather than the PESSIMISTIC lock strategy. Using the OPTIMISTIC lock strategy is the preferred solution when the map is mostly read and updates to the map are infrequent. See Optimistic locking on page 64 for more details on the optimistic strategy. If the PESSIMISTIC lock strategy must be used, the getForUpdate method can be used instead of the get method in the above example. By doing so, the first transaction to call getForUpdate method acquires a U lock mode rather than a S lock mode. This lock mode causes the second transaction to block when it calls the
Chapter 5. ObjectGrid application programming interface overview

61

getForUpdate method because only one transaction is granted a U lock mode. Because the second transaction is blocked, it does not own any lock mode on the Billy map entry . The first transaction does not block when it attempts to upgrade the U lock mode to an X lock mode as a result of the put method call from the first transaction. This feature demonstrates why U lock mode is called the upgradeable lock mode. When the first transaction is completed, the second transaction unblocks and is granted the U lock mode. An application can prevent the lock promotion deadlock scenario by using the getForUpdate method instead of the get method when PESSIMISTIC lock strategy is being used. Important: This solution does not prevent read only transactions from being able to read a map entry. Read only transactions call the get method, but never call the put, insert, update, or remove methods. Concurrency is just as high as when the regular get method is used. The only reduction in concurrency occurs when the getForUpdate method is called by more than one transaction for the same map entry. You must take care when a transaction calls the getForUpdate method on more than one map entry to ensure that the U locks are acquired in the same order by each transaction. For example, suppose that the first transaction calls the getForUpdate method for the key 1 and the getForUpdate method for key 2. Another concurrent transaction calls the getForUpdate method for the same keys, but in reverse order. This sequence causes the classic deadlock because to multiple locks are obtained in different orders by different transactions. The application still needs to ensure that every transaction that accesses multiple map entries does so in key sequence to ensure that deadlock does not occur. Because the U lock is obtained at the time that the getForUpdate method is called rather than at commit time, the ObjectGrid cannot order the lock requests like it does during the commit cycle. The application must control the lock ordering in this case. Using the flush method on the ObjectMap interface before a commit can introduce additional lock ordering considerations. The flush method is typically used to force changes made to the map out to the backend through the Loader plug-in. In this situation, the backend uses its own lock manager to control concurrency, so the lock wait condition and deadlock can occur in backend rather than in the ObjectGrid lock manager. Consider the following transaction:
Session sess = ...; ObjectMap person = sess.getMap("PERSON"); boolean activeTran = false; try { sess.begin(); activeTran = true; Person p = (IPerson)person.get("Billy"); p.setAge( p.getAge() + 1 ); person.put( Billy, p ); person.flush(); ... p = (IPerson)person.get("Tom"); p.setAge( p.getAge() + 1 ); sess.commit(); activeTran = false; } finally { if ( activeTran ) sess.rollback(); }

62

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Suppose that some other transaction also updated the Tom person, called the flush method, and then updated the Billy person. If this situation occurred, the following interleaving of the two transactions results in a database deadlock condition:
X lock is granted to transaction 1 for "Billy" when flush is executed. X lock is granted to transaction 2 for "Tom" when flush is executed.. X lock requested by transaction 1 for "Tom" during commit processing. (Transaction 1 blocks waiting for lock owned by transaction 2.) X lock requested by transaction 2 for "Billy" during commit processing. (Transaction 2 blocks waiting for lock owned by transaction 1.)

This example demonstrates that the use of the flush method can cause a deadlock to occur in the database rather than in ObjectGrid. This deadlock example can occur regardless of what lock strategy is used. The application must take care to prevent this kind of deadlock from occurring when using the flush method and when a Loader is plugged into the BackingMap. The preceding example also illustrates another reason why ObjectGrid has a lock wait timeout mechanism. A transaction that is waiting for a database lock might be waiting while it owns an ObjectGrid map entry lock. Consequently, problems at database level can cause excessive wait times for an ObjectGrid lock mode and result in ObjectGrid throwing a LockTimeoutException exception.

Exception handling
The examples in this topic do not have any exception handling. To prevent locks from being held for excessive amounts of time when a LockTimeoutException exception or a LockDeadlockException occurs, an application needs to ensure that it catches unexpected exceptions and calls the rollback method when something unexpected occurs. Change the preceding code snippet as demonstrated in the following example:
Session sess = ...; ObjectMap person = sess.getMap("PERSON"); boolean activeTran = false; try { sess.begin(); activeTran = true; Person p = (IPerson)person.get("Billy"); // Billy had a birthday, so we make him 1 year older. p.setAge( p.getAge() + 1 ); person.put( Billy, p ); sess.commit(); activeTran = false; } finally { if ( activeTran ) sess.rollback(); }

The finally block in the snippet of code ensures that a transaction is rolled back when an unexpected exception occurs. It not only handles a LockDeadlockException exception, but any other unexpected exception that might occur. The finally block handles the case where an exception occurs during a commit method invocation. This example is not the only way to deal with unexpected exceptions, and there might be cases where an application wants to catch some of the unexpected exceptions that can occur and throw one of its application exceptions. You can add catch blocks as appropriate, but the application must ensure that the snippet of code does not exit without completing the transaction.

Chapter 5. ObjectGrid application programming interface overview

63

Optimistic locking
The optimistic locking strategy believes that no two transactions might attempt to update the same map entry while running concurrently. Because of this belief, it not necessary to hold a lock mode for the life of the transaction because it is unlikely that more than one transaction might update the map entry concurrently. The optimistic locking strategy is typically used when: v A BackingMap is configured with or without a loader and versioning information is available. v A BackingMap is mostly read. That is, transactions frequently read map entries, and only occasionally insert, update, or remove a map entry. v A BackingMap is inserted, updated, or removed more frequently than it is read, but transactions rarely collide on the same map entry. Like the pessimistic locking strategy, the methods on the ObjectMap interface determine how ObjectGrid automatically attempts to acquire a lock mode for the map entry being accessed. However, here are some very important differences between the pessimistic and optimistic strategies: v Like the pessimistic locking strategy, a S lock mode is acquired by the get and getAll methods when the method is invoked. However, with optimistic locking, the S lock mode is not held until the transaction is completed. Instead, the S lock mode is released before the method returns to the application. The purpose of acquiring the lock mode is so that the ObjectGrid can ensure only committed data from other transactions is visible to the current transaction. After ObjectGrid has verified the data is committed, the S lock mode is released. At commit time an optimistic versioning check is performed to ensure that no other transaction changed the map entry after the current transaction released its S lock mode. v Unlike pessimistic locking strategy, the getForUpdate and getAllForUpdate methods are handled exactly like the get and getAll methods when the optimistic locking strategy is used. That is, a S lock mode is acquired at the start of the method and the S lock mode is released before returning to the application. v All other ObjectMap methods are handled exactly like they are handled for the pessimistic locking strategy. That is, when the commit method is invoked, an X lock mode is obtained for any map entry that is inserted, updated, removed, touched, or invalidated and the X lock mode is held until the transaction completes commit processing. This locking strategy is called optimistic because an optimistic outlook exists. The optimistic locking strategy is that no two transactions might attempt to update the same map entry while running concurrently. Because of this belief, it is not necessary to hold a lock mode for the life of the transaction because it is unlikely that more than one transaction might update the map entry concurrently. However, since a lock mode was not held, another concurrent transaction could potentially update the map entry after the current transaction has released its S lock mode. To handle this possibility, ObjectGrid gets an X lock at commit time and performs an optimistic versioning check to verify that no other transaction has changed the map entry since the current transaction read the map entry from the BackingMap. If another transaction changes the map entry, the version check fails and an OptimisticCollistionException exception occurs. This exception forces the current transaction to be rolled back and the entire transaction must be retried by the application. The optimistic locking strategy is very useful when a map is mostly read and it is unlikely that updates for the same map entry might occur.

64

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

None
When a BackingMap is configured to use a locking strategy of NONE, no transaction locks for a map entry are obtained. A scenario where this strategy is useful is when an application is a persistence manager such as a Java 2 Platform, Enterprise Edition (J2EE) Enterprise JavaBeans (EJB) container or uses Hibernate to obtain persistent data. In this scenario, the BackingMap is configured without a loader and is being used as a data cache by the persistence manager. The persistence manager in this scenario provides concurrency control between transactions that are accessing the same ObjectGrid Map entries. The ObjectGrid does not need to obtain any transaction locks for the purpose of concurrency control. This is assuming that the persistence manager does not release its transaction locks prior to updating the ObjectGrid map with committed changes. If that is not the case, then a PESSIMISTIC or OPTIMISTIC lock strategy must be used. For example, suppose the persistence manager of an EJB container is updating ObjectGrid map with data that was committed in the EJB container-managed transaction. If the update of ObjectGrid map occurs before the persistence manager transaction locks are released, then NONE lock strategy can be used. If theObjectGrid map update occurs after the persistence manager transaction locks are released, then either the OPTIMISTIC or PESSIMISTIC lock strategy is required. Another scenario where the NONE locking strategy can be used is when the application uses a BackingMap directly and a Loader is configured for the map. In this scenario, the loader uses the concurrency control support provided by a relational database management system (RDBMS) by using either Java database connectivity (JDBC) or Hibernate to access data in a relational database. The loader implementation can use either an optimistic or pessimistic approach. A loader that uses an optimistic locking or versioning approach helps to achieve the greatest amount of concurrency and performance. A Loader uses one of the following techniques to implement an optimistic locking approach: v The loader uses versioning information provided by the backend to which the loader connects. This versioning information might be in the form of a hidden field that is made available by the backend, for example, through a special SQL statement syntax. v The loader uses JDBC to perform an SQL operation and the first approach is not available. In this case, the LogElement.Type.getCode method is used to indicate whether an SQL operation is update, insert, or delete. When the return value from the getCode method is LogElement.CODE_UPDATE, the loader uses the getVersionedValue method on the LogElement interface to obtain the version object to use in the where clause of an overqualified SQL update statement. The overqualified where clause is used to ensure that the row was not changed by any other transaction. The loader uses the updateVersionedObjectForValue method on the OptimisticCallback interface to generate a new version object. The new version is used for updating the versioning column of the updated row. The loader then uses the setVersionedValue method on the LogElement interface to update the LogElement with the new version value that the row was updated with. See Loaders on page 92 for an example of how a loader uses the LogElement and OptimisticCallback interfaces to implement this optimistic approach. v If a value interface is configured for a BackingMap, the loader can use the getCurrentValue method on the LogElement interface and the ValueProxyInfo interface to obtain the attributes that were changed by the transaction. This approach allows the loader to use an overqualified where clause that includes
Chapter 5. ObjectGrid application programming interface overview

65

each of the changed attributes. See Loaders on page 92 for an example of how a loader uses the LogElement and ValueProxyInfo interfaces to implement an optimistic approach. A loader that makes use of the pessimistic locking support of the underlying backend may want to make use of the forUpdate parameter that is passed on the get method on the Loader interface. This parameter is set to true if the getForUpdate method of the ObjectMap interface was used by the application to get the data. The loader can use this parameter to determine whether to request an upgradeable lock on the row being read. For example, DB2 obtains an upgradeable lock when an SQL select statement contains a for update clause. This approach offers the same deadlock prevention that is described in the Pessimistic locking topic.

ObjectGrid security
Use ObjectGrid security mechanisms to secure map data through either a configuration or programming method.

Overview
ObjectGrid provides security mechanisms to secure map data. ObjectGrid security is built on the Java Authentication and Authorization Services (JAAS) mechanism, an integral part of Java 2 Security. This section describes mechanisms that ObjectGrid uses to authorize the access to map data. In addition, integration with WebSphere Application Server security is described and demonstrated. The ObjectGridSample enterprise application sample, in the ObjectGridSample.ear file demonstrates how security can be configured and used. You can enable ObjectGrid security through the two following methods: v Configuration. You can use an XML file to define an ObjectGrid and enable the security for that ObjectGrid. Following is the secure-objectgrid-definition.xml file that is used in the ObjectGridSample enterprise application sample. In this XML file, security is enabled with the following setting: securityEnabled=true.
<objectGrids> <objectGrid name="secureClusterObjectGrid" securityEnabled="true" authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS"> <bean id="TransactionCallback" classname="com.ibm.websphere.samples.objecgrid.HeapTransactionCallback"/> .... </objectGrids>

v Programming. If you want to create an ObjectGrid by using the API, call the following method on the ObjectGrid interface to enable security:
/** * Enable the ObjectGrid security */ void setSecurityEnabled();

See the following sections for more information about ObjectGrid security: v Authentication v ObjectGrid authorization v Security with WebSphere Extended Deployment

66

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Authentication
ObjectGrid relies on the environment, either the application servers or applications, for authentication. ObjectGrid does not provide an authentication mechanism. When an ObjectGrid is used in a WebSphere Application Server or WebSphere Extended Deployment environment, the applications can use the WebSphere Application Server security authentication mechanism. When an ObjectGrid is running in a Java 2 Platform, Standard Edition (J2SE) environment, the application has to manage authentications by itself using JAAS authentication or other authentication mechanisms. Refer to the JAAS reference guide for more information on how to use JAAS authentication. The contract between an application and an ObjectGrid instance is with the javax.security.auth.Subject object. After the client is authenticated by the application server or the application, the application can retrieve the authenticated javax.security.auth.Subject object and use this Subject object to get a session from the ObjectGrid instance by calling the ObjectGrid.getSession(Subject) method. This Subject object is used to authorize access to the map data. This contract is called a subject passing mechanism. The ObjectGrid.getSession(Subject) API follows:
/** * This allows the cache to use a specific subject rather than the one configured * on the ObjectGrid to get a session. * @param subject * @return An instance of Session * @throws ObjectGridException * @throws TransactionCallbackException * @throws InvalidSubjectException the subject passed in is invalid based * on the SubjectValidation mechanism. */ public Session getSession(Subject subject) throws ObjectGridException, TransactionCallbackException, InvalidSubjectException;

The getSession method in the ObjectGrid interface can also be used to get a Session object:
/** * This returns a Session object that can be used by a single thread at a time. * Its not allowed to share this Session object between threads without placing a * critical section around it. While the core framework allows the object to move * between threads, the TransactionCallback and Loader may prevent this usage, * especially in J2EE environments. When security is enabled, this will use the * SubjectSource to get a Subject object. * * If the initialize() method has not been invoked prior to the first * getSession invocation, then an implicit initialization will occur. This ensures * that all of the configuration is complete before any runtime usage is required. * * @see #initialize() * @return An instance of Session * @throws ObjectGridException * @throws TransactionCallbackException * @throws IllegalStateException if this method is called after the * destroy() method is called. */ public Session getSession() throws ObjectGridException, TransactionCallbackException;

Chapter 5. ObjectGrid application programming interface overview

67

As the API documentation specifies, when security is enabled, this method uses the SubjectSource plug-in to get a Subject object. The SubjectSource plug-in is one of the security plug-ins that is defined in ObjectGrid to support the authentication.

Security related plug-ins


ObjectGrid provides two security plug-ins that are related to the subject passing mechanism: the SubjectSource plug-in and the SubjectValidation plug-in. SubjectSource plug-in The SubjectSource plug-in is represented by the com.ibm.websphere.objectgrid.security.plugins.SubjectSource interface and is used to get a Subject object from an ObjectGrid running environment. This ObjectGrid environment might be an application that uses the ObjectGrid or an application server that hosts the application. Consider the SubjectSource plug-in an alternative to the subject passing mechanism. Using the subject passing mechanism, the application retrieves the Subject object and uses it to get the ObjectGrid session object. With the SubjectSource plug-in, the ObjectGrid runtime retrieves the Subject object and uses it to get the session object. The subject passing mechanism gives the control of Subject objects to applications, while the SubjectSource plug-in mechanism frees the applications from retrieving the Subject object. This SubjectSource plug-in can be used to get a Subject object that represents an ObjectGrid client, and is then used for ObjectGrid authorization. When the ObjectGrid.getSession() method is called, the Subject getSubject() throws ObjectGridSecurityException () method is called by the ObjectGrid runtime if security is enabled. ObjectGrid provides a default implementation of the SubjectSource plug-in: com.ibm.websphere.objectgrid.security.plugins.builtins. WSSubjectSourceImpl.This implementation can be used to retrieve a caller subject or a RunAs subject from the thread when an application is running in WebSphere Application Server. You can configure this class as the SubjectSource implementation class when you are using ObjectGrid in WebSphere Application Server. Following is a code snippet that shows the main flow of the WSSubjectSourceImpl.getSubject() method:
Subject s = null; try { if (finalType == RUN_AS_SUBJECT) { // get the RunAs subject s = com.ibm.websphere.security.auth.WSSubject.getRunAsSubject(); } else if (finalType == CALLER_SUBJECT) { // get the callersubject s = com.ibm.websphere.security.auth.WSSubject.getCallerSubject(); } } catch (WSSecurityException wse) { throw new ObjectGridSecurityException(wse); } return s;

For other details, refer to API documentation of the SubjectSource plug-in and WSSubjectSourceImpl. SubjectValidation plug-in The SubjectValidation plug-in, represented by the com.ibm.websphere.objectgrid.security.plugins.SubjectValidation interface, is another security plug-in. The SubjectValidation plug-in can be

68

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

used to validate that a javax.security.auth.Subject object, either passed to the ObjectGrid or retrieved by the SubjectSource plug-in, is a valid subject that has not been modified. The Subject validateSubject(Subject subject) throws InvalidSubjectException; method in the SubjectValidation interface takes a Subject object and returns a Subject object. Whether a Subject object is considered valid and whatSubject object is returned are all up to your implementations. If the Subject object is not valid, an InvalidSubjectException exception should be result. You can use this plug-in if you do not trust the Subject object that is passed to this method. However, usually the application developers who develop the code to retrieve the Subject object are considered trustworthy. An implementation of this plug-in needs support from the Subject object creator because only the creator knows if the Subject object has been tampered with. However, the subject creator might not know whether the Subject has been tampered with. In this case, this plug-in is not useful. ObjectGrid provides a default implementation of the SubjectValidation plug-in: the com.ibm.websphere.objectgrid.security.plugins.builtins. WSSubjectValidationImpl class. This class can be used to validate the WebSphere Application Server authenticated subject. You can configure this class as the SubjectValidation implementation class when you are using ObjectGrid in WebSphere Application Server. The WSSubjectValidationImpl implementation considers a Subject object valid if the credential token that is associated with this Subject has not been modified. In other words, users could change other parts of the Subject object. The WSSubjectValidationImpl implementation asks WebSphere Application Server for the original Subject that corresponds to the credential token and returns the original Subject object as the validated Subject object. Therefore, the changes made to the Subject contents other than the credential token have no effects. The following code snippet shows the basic flow of the WSSubjectValidationImpl.validateSubject(Subject) :
// Create a LoginContext with scheme WSLogin and // pass a Callback handler. LoginContext lc = new LoginContext("WSLogin", new WSCredTokenCallbackHandlerImpl(subject)); // When this method is called, the callback handler methods // will be called to log the user in. lc.login(); // Get the subject from the LoginContext return lc.getSubject();

In the code snippet, a credential token callback handler object, the WSCredTOkenCallbackHandlerImpl object, is created with the Subject object to be validated. Then a LoginContext object is created with login scheme WSLogin. When the lc.login() method is called, WebSphere Application Server security retrieves the credential token from the Subject object and then returns the correspondent Subject as the validated Subject object. For other details, refer to the API documentation for SubjectValidation and WSSubjectValidationImpl. plug-in configuration You can configure the SubjectValidation plug-in and SubjectSource plug-in in two ways: v XML Configuration. You can use an XML file to define an ObjectGrid and set these two plug-ins. Following is an example, in which the
Chapter 5. ObjectGrid application programming interface overview

69

WSSubjectSourceImpl class is configured as the SubjectSource plug-in and the WSSubjectValidation class is configured as the SubjectValidation plug-in:
<objectGrids> <objectGrid name="secureClusterObjectGrid" securityEnabled="true" authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS" > <bean id="SubjectSource" className="com.ibm.websphere.objectgrid.security.plugins.builtins. WSSubjectSourceImpl" /> <bean id="SubjectValidation" className= "com.ibm.websphere.objectgrid.security.plugins.builtins. WSSubjectValidationImpl" /> <bean id="TransactionCallback" className= "com.ibm.websphere.samples.objectgrid.HeapTransactionCallback" /> ... </objectGrids>

v Programatically. If you want to create an ObjectGrid through APIs, you can call the following methods to set the SubjectSource or SubjectValidation plug-ins:
/** * Set the SubjectValidation plugin for this ObjectGrid instance. A * SubjectValidation plug-in can be used to validate the Subject object * passed in is a valid Subject. Please refer to {@link SubjectValidation} * for more details. * @param subjectValidation the SubjectValidation plugin */ void setSubjectValidation(SubjectValidation subjectValidation); /** * Set the SubjectSource plug-in. A SubjectSource plug-in can be used * to get a Subject object from the environment to represent the * ObjectGrid client. * * @param source the SubjectSource plug-in */ void setSubjectSource(SubjectSource source);

Write your own JAAS authentication code


You can write your own JAAS authentication code to handle the authentication. You need to write your own login modules and then configure the login modules for your authentication module. The login module receives information about a user and authenticates the user. This information can be anything that can identify the user. For example, the information can include a user ID and password, client certificate, and so on. After receiving the information, the login module verifies that it represents a valid subject and then creates a Subject object. Currently, several implementations of login modules are available. After a login module is written, you must configure this login module so it can be used by runtime. A JAAS login module configuration file must be configured that contains the login module and the its authentication scheme. For example:
FileLogin { com.acme.auth.FileLoginModule required debug=true };

The authentication scheme is FileLogin and the login module is com.acme.auth.FileLoginModule. The required token indicates that the

70

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

FileLoginModule module must validate this login or the scheme fails. Setting the JAAS login module configuration file can be done by one of the following ways: v By setting the file name in the login.config.url setting in the java.security file, for example,
login.config.url.1=file:${java.home}/lib/security/file.login

v By setting the file name from the command line by using the Djava.security.auth.login.config JVM arguments, for example :
Djava.security.auth.login.config ==$JAVA_HOME/jre/lib/security/file.login

. For more details about writing and configuring login modules, see the JAAS authentication tutorial.

ObjectGrid authorization
ObjectGrid authorization is based on the Subject object. ObjectGrid supports two kinds of authorization mechanisms: the Java Authentication and Authorization Service (JAAS) authorization and the custom authorization.

MapPermission permission class


In ObjectGrid, a public com.ibm.websphere.objectgrid.security.MapPermission class is provided to represent permissions to the ObjectGrid resources, specifically, the methods of ObjectMap or JavaMap interfaces. ObjectGrid defines the following permission strings to access the methods of ObjectMap and JavaMap: v read: Grants permission to read the data from the map. The integer constant is defined as MapPermission.READ. v write: Grants permission to update the data in the map. The integer constant is defined as MapPermission.WRITE. v insert: Grants permission to insert the data into the map. The integer constant is defined as MapPermission.INSERT. v remove: Grants permission to remove the data from the map. The integer constant is defined as MapPermission.REMOVE. v invalidate: Grants permission to invalidate the data from the map. The integer constant is defined as MapPermission.INVALIDATE. v all: Grants all permissions: read, write, insert, remove, and invalidate. The integer constant is defined as MapPermission.ALL. You can construct a MapPermission object by passing the fully-qualified ObjectGrid map name in the format [ObjectGrid_name].[ObjectMap_name], and the permission string or integer value. A permission string can be a comma-delimited string of the permission strings such as read, insert, or it can be all which means all permissions are granted. A permission integer value can be any above permission integer constants or a mathematical or value of several integer permission constants, such as MapPermission.GET|MapPermission.PUT. The authorization occurs when a client calls a method of ObjectMap or JavaMap. The ObjectGrid runtime will check different permissions for different methods. If the required permissions are not granted to the client, an AccessControlException exception occurs. The following table displays a list of permissions and the corresponding methods that require each permission.

Chapter 5. ObjectGrid application programming interface overview

71

Table 6. Method permissions Permission com.ibm.websphere.objectgrid.ObjectMap or com.ibm.websphere.objectgrid.JavaMap method boolean containsKey(Object) boolean equals(Object) Object get(Object) Object get(Object, Serializable) read List getAll(List) List getAll(List keyList, Serializable) List getAllForUpdate(List) List getAllForUpdate(List, Serializable) Object getForUpdate(Object) Object getForUpdate (Object, Serializable) Object put(Object key, Object value) void put (Object, Object, Serializable) write void putAll(Map) void putAll(Map, Serializable) void update (Object, Object) void update (Object, Object, Serializable) public void insert (Object, Object) insert void insert (Object, Object, Serializable) remove Object remove (Object) void removeAll(Collection) public void invalidate (Object, boolean) invalidate void invalidateAll(Collection, boolean) void invalidateUsingKeyword(Serializable, boolean) int setTimeToLive(int)

Authorization mechanisms
ObjectGrid supports two kinds of authorization mechanisms: the JAAS authorization and the custom authorization. JAAS authorization augments the Java security policies with user-centric access controls. Permissions can be granted based on what code is running and on who is running the code. JAAS authorization is part of the IBM Software Development Kit (SDK) Version 1.4. ObjectGrid also supports the custom authorization by the com.ibm.websphere.objectgrid.security.plugins.MapAuthorization plug-in. You can implement your own authorization mechanism if you do not want to use JAAS authorization. By using custom authorization mechanism, you can use policy databases, policy servers, or Tivoli Access Manager to manage the ObjectGrid authorizations. You can configure ObjectGrid authorization mechanism in two ways:

72

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v XML Configuration. You can use an XML file to define an ObjectGrid and set the authorization mechanism to either AUTHORIZATION_MECHANISM_JAAS or AUTHORIZATION_MECHANISM_CUSTOM. Following is the secure-objectgriddefinition.xml file that is used in the ObjectGridSample enterprise application sample:
<objectGrids> <objectGrid name="secureClusterObjectGrid securityEnabled="true" authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS" > <bean id="TransactionCallback" className= "com.ibm.websphere.samples.objectgrid.HeapTransactionCallback"/> .... </objectGrids>

v Programatically. If you want to create an ObjectGrid using APIs, you can call the following method to set the authorization mechanism:
/** * Set the authorization Mechanism. The default is * com.ibm.websphere.objectgrid.security.SecurityConstants. * AUTHORIZATION_MECHANISM_JAAS. * @param authMechanism the map authorization mechanism */ void setAuthorizationMechanism(int authMechanism);

JAAS authorization A javax.security.auth.Subject object represents an authenticated user. A Subject is comprised of a set of principals, and each Principal represents an identity for that user. For example, a Subject could have a name Principal (Joe Smith) and a group principal (manager). Using the JAAS authorization policy, permissions can be granted to specific Principals. ObjectGrid associates the Subject with the current access control context. For each method calls to the ObjectMap or JavaMap, the Java runtime determines whether the policy grants the required permission only to a specific Principal. The operation is allowed only if the Subject associated with the access control context contains the designated Principal. ObjectGrid has a special code base that is used for checking the JAAS authorization to the ObjectMap and JavaMap method calls. This special code base is located at: http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction. Use this code base when granting ObjectMap or JavaMap permissions to principals. The template of the policy to grant MapPermission is:
grant codeBase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction" <Principal field(s)> { permission com.ibm.websphere.objectgrid.security.MapPermission "map_name", "action"; .... permission com.ibm.websphere.objectgrid.security.MapPermission "map_name", "action"; };

A Principal field looks like the following code snippet:


Principal Principal_class "principal_name"

The word Principal is followed by the fully-qualified name of a Principal class and a principal name. The map_name is the fully-qualified map name in the format of [ObjectGrid Name].[Map Name], for example,

Chapter 5. ObjectGrid application programming interface overview

73

secureClusterObjectGrid.employees. The action is a comma-delimited string of the permissions strings defined in MapPermission class, such as read, insert, or all. Limited wildcard function is supported. You can replace the ObjectGrid name or the map name with an asterisk symbol (*) to indicate any map. However, ObjectGrid does not support replacing part of the ObjectGrid name or map name with an asterisk. Therefore, the *.employees string, the clusterObjectGrid.* string , and the *.* string are all valid names, but the cluster*.employeesstring is not valid. For example, in the sample application in the ObjectGridSample.ear file, two authorization policy files are defined: the fullAccessAuth.policy file and the readInsertAccessAuth.policy file. The content of the readInsertAccessAuth.policy policy follows:
grant codebase "http://www.ibm.com/com/ibm/ws/objectgrid/ security/PrivilegedAction" /*Lines broken for publication */ Principal com.ibm.ws.security.common.auth.WSPrincipalImpl "principal_name" { permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.employees", "read,insert"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.offices", "read,insert"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.sites", "read,insert"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.counters", "read,insert"; };

In the preceding policy, only insert and read permissions are granted to the four maps to a certain principal. The fullAccessAuth.policy file , grants all permissions to these maps to a principal. Before running the application, you must change the principal_name to an appropriate value and the principal class. The value of the principal_name depends on the user registry. For example, if a local operating system is used as user registry , the machine name is MACH1, and the user ID is user1, the principal_name is MACH1/user1. A JAAS authorization policy can be directly put in the Java policy file, or it can be put in a separate JAAS authorization file and then set it by using the Djava.security.auth.policy=file:[JAAS_AUTH_POLICY_FILE] JVM argument or by using the auth.poliyc.url.x= file:[JAAS_AUTH_POLICY_FILE] setting in the java.security file. Custom authorization with the MapAuthorization plug-in ObjectGrid also supports the custom authorization by the MapAuthorization plug-in. This plug-in can be used to authorize ObjectMap or JavaMap accesses to the principals contained in the Subject object. The boolean checkPermission(Subject subject, MapPermission permission) method in the MapAuthorization interface, is called by the ObjectGrid runtime to check whether the passed-in subject object has the passed-in permission. The implementation of the MapAuthorization interface should return true if the subject object has permission, and false if the subject object does not have permission. A typical implementation of this plug-in is to retrieve the principals from the Subject object and check whether the specified permissions are granted

74

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

to the principals by consulting specific policies. These policies are defined by users. For example, the policies can be defined in a database, a plain file, or a Tivoli Access Manager policy server. ObjectGrid provides two default implementations for this plug-in. The com.ibm.websphere.objectgrid.security.plugins.builtins. JAASMapAuthorizationImpl class is an implementation of the MapAuthorization plug-in, which uses a JAAS mechanism for authorization. The com.ibm.websphere.objectgrid.security.plugins.builtins. TAMMapAuthorizationImpl implementation class shows how Tivoli Access Manager can be used to manage the ObjectGrid authorizations. Following is a code snippet that shows the basic flow of the JAASMapAuthorizationImpl.checkPermission(Subject, MapPermission) method:
// Creates a PrivilegedExceptionAction action to check the permissions. PrivilegedExceptionAction action = MapPermissionCheckAction.getInstance(permission); Subject.doAsPrivileged(subject, action, null);

Do not use this TAMMapAuthorizationImpl plug-in in an out of box scenario because it requires certain restrictive preconditions: v The Subject object contains a com.tivoli.mts.PDPrincipal principal. v The TAM policy server has defined the following permissions for the ObjectMap or JavaMap name object. The object defined in the policy server should have the same name as the ObjectMap or JavaMap name in the format of [OBJECTGRID_NAME].[MAP_NAME]. The permission is the first character of the permission strings defined in the MapPermission. For example, the permission r defined in the policy server represents the read permission to the ObjectMap. Permission checking period ObjectGrid supports caching the permission checking results for performance reason. Without this mechanism, when a method listed on the table in this topic is called, the ObjectGrid runtime calls the configured authorization mechanism to authorize the access. With this permission checking period being set, the authorization mechanism is called periodically based on the permission checking period. The permission checking period can be configured in two ways: v XML configuration. You can use XML file to define an ObjectGrid and set the permission check period. Following is an example to set the permission check period to 45 seconds:
<objectGrids> <objectGrid name="SecureClusterObjectGrid" securityEnabled="true" authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS" permissionCheckPeriod="45"> <bean id="TransactionCallback" className="com.ibm.websphere.samples.objectgrid. HeapTransactionCallback"/> ... </objectGrids>

v Programatically. If you want to create an ObjectGrid with APIs, you can call the following method to set the permission checking period:
/** * This method takes a single parameter indicating how often * the customer wants to check the permission used to allow a * client access. If the parameter is 0 then every single get/
Chapter 5. ObjectGrid application programming interface overview

75

* put/update/remove/evict call will ask the authorization * mechanism, either JAAS authorization or custom authorization * to check if the current subject has permission. This may be * prohibitively expensive from a performance point of view * depending on the authorization implementation but if this is * required then you can do it. * Alternatively, if the parameter is > 0 then it indicates the * number of seconds to cache a set of permissions before * returning to the authorization mechanism to refresh them. * This provides much better performance but you run the risk * that if the backend permissions are changed during this time * then the ObjectGrid will possibly allow or prevent access even * though the backend security provider has been modified. * * @param period the permission check period in seconds. */ void setPermissionCheckPeriod(int period);

Security integration with WebSphere Extended Deployment servers


One of the supported environments for ObjectGrid is WebSphere Extended Deployment server. This section uses the sample application, the ObjectGridSample.ear file, to describe how WebSphere Application Server security can be integrated with ObjectGrid security to realize the subject passing mechanism. The ObjectGridSample.ear sample illustrates ObjectGrid usage within a Java 2 Platform, Enterprise Edition (J2EE) programming environment. The same sample objects: Employee, Office, Sites and Counters, are represented as the same ObjectGrid maps as illustrated in the Eclipse-based sample. However, the ObjectGridSample.ear file demonstrates how ObjectGrid can be accessed in a secure way. This section shows how ObjectGrid can be securely accessed when running in a WebSphere Extended Deployment server.

Security configuration for the application


The security configuration for this application is illustrated in the following diagram:
All authenticated servletUser SecureObjectGridSessionBeanServlet

All authenticated beanUser ObjectGridSampleSession Subject

ObjectGrid

Figure 2. Security Configuration for the sample ObjectGridSample.ear file

The SecureObjectGridSessionBeanServlet servlet is launched on a browser. The servlet calls the ObjectGridSampleSession bean, which in turn calls the ObjectGrid. The securedObjectGridSessoinBeanServlet servlet can only be accessed by the servletUser security role. The ObjectGridSampleSession.executeActionWithSecurity

76

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

can only be called by the beanUser security tole. Both servletUser and beanUser are mapped to All authenticated users. Any authenticated user can access the ObjectGrid.

Retrieve the Subject from WebSphere Extended Deployment


ObjectGrid relies on application servers or applications to do the authentication. See the internalExecuteAction method in the com.ibm.websphere.samples.objectgrid.basic.ejb.ObjectGridSampleSessionBean class, for the following code snippet in the method:
private String internalExecuteAction(String action, boolean isSecurityEnabled, String methodName) { ... ... Subject subject = null; if (isSecurityEnabled) { try { subject = WSSubject.getCallerSubject(); } catch (Exception e) { throw new RuntimeException(e); } }

In this snippet, a Subject object is retrieved from thread using the WSSubject.getCallerSubject method. This Subject represents the caller subject.

Get a session by passing the subject object


Following is the init method in the com.ibm.websphere.samples.ObjectGridSampleWrapper class:
public void init(boolean isSecurityEnabled, Subject subject) { ... ... if(ivObjectGrid!=null){ if (!isSecurityEnabled) { ivSession = ivObjectGrid.getSession(); } else { ivSession = ivObjectGrid.getSession(subject); }

This snippet demonstrates how to use the Subject object that is retrieved from WebSphere Application Server to get the ivSession session object.

Enable WebSphere Application Server global security


To rely on WebSphere Application Server to handle the authentication, WebSphere Application Server global security must be enabled. Perform the following steps to enable the WebSphere Application Server global security: 1. In the administrative console, click Security > Global Security. 2. Verify that the Enable global security check box is selected to enable global security. The Enforce Java 2 security check box is automatically selected. 3. For the Active protocol, select CSI and SAS. 4. For the Active authentication mechanism, select Lightweight Third Party Authentication (LTPA). 5. For the Active user registry, select Local OS. You can choose other user registries. 6. Click OK.
Chapter 5. ObjectGrid application programming interface overview

77

7. If you have not configured the user registry, you are asked to configure the user registry. If you choose Local OS, you are asked to provide server user ID and server user password. 8. Save all the changes. 9. Restart the Application Server to activate the changes made within the global security. After the server has been restarted, you can use the server user ID and password defined in the user registry to gain access to the administrative console.

Add the JAAS authorization policy file to the JVM argument


Two authorization policy files are defined in the ObjectGridSample.ear file: the fullAccessAuth.policy file and the readInsertAccessAuth.policy file. The content of the fullAccessAuth.policy file follows:
grant codebase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction" Principal com.ibm.ws.security.common.auth.WSPrincipalImpl "principal_name" { permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.employees", "all"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.offices", "all"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.sites", "all"; permission com.ibm.websphere.objectgrid.security.MapPermission "secureClusterObjectGrid.counters", "all"; };

You can extract these two policy files and store them in some directory, for example, the install_root/profiles/profile_name/properties directory. WebSphere Application Server uses a special principal class name, com.ibm.ws.security.common.auth.WSPrincipalImpl. Use this principal class. You need to change the principal_name. The principal_name depends on the user registry. Choose one user ID defined in your local OS registry. For example, if local OS is used as user registry, the machine name is MACH1, and the user ID is user1, the principal_name is MACH1/user1. WebSphere Application Server does not support the JAAS authorization policy in the Java policy file, so the authorization policies cannot be placed in the Java policy file. You must set the JAAS authorization policy by using the Djava.security.auth.policy JVM argument. Following are the steps to set the JVM arguments in the WebSphere Extended Deployment administrative console: 1. Navigate to the server target. 2. Click Java and Process Management > Process Definition > Java Virtual Machine. 3. Add the following text to the Generic JVM arguments:
Djava.security.auth.policy=file:[JAAS_AUTH_POLICY_FILE]

where the [JAAS_AUTH_POLICY_FILE] is the JAAS authorization policy file. For example,
Djava.security.auth.policy=file:c:/WASXD/AppServer/profiles/AppSrv01/ properties/fullAccessAuth.policy

78

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Install the application


Install the application on the application server. You can take all the default values during the installation process.

Run the secured servlet


1. After the application is started, launch the server using the URL: http://host:port/ObjectGridSample, where host is the machine name which has the application server running, and port is the HTTP port number, for example, 9080. 2. Click SecureObjectGridSessionBeanServlet Type in the user ID that you configured in the JAAS policy file and the password. 3. A line on the page displays:
The caller of the session is user_ID.

The user_ID variable is the user ID that is used for authentication. 4. You can click all the action buttons because the policy file grants all map permissions to the user. 5. You can change the Djava.security.auth.policy JVM argument value to the readInsertAccessAuth.policy file and then restart the server. With this policy, you cannot do the five actions. For example, if you click the 2. Update maps action, the following exception is displayed on the page:
Caught Throwable: java.security.AccessControlException: access denied (com.ibm.websphere.objectgrid.

This exception occurs because the MapPermission with write action is not granted to the map in the readInsertAccessAuth.policy policy.

Listeners
ObjectGrid provides two listener-type interfaces that you can extend. The extensions can advise you through the extension interface and describe operations that are run on an ObjectGrid instance or a map instance.

ObjectGridEventListener interface
Use the ObjectGridEventListener interface to receive notifications when significant events occur on an ObjectGrid. These events include ObjectGrid initialization, beginning of a transaction, ending a transaction, and destroying an ObjectGrid. To listen for these events, create a class that implements the ObjectGridEventListener interface and add it to the ObjectGrid. Watch for changes in a Map The ObjectGridEventListener#transactionEnd method is very useful for applications that are interested in watching entries in the local Maps. An application can add one of these listeners and then use the transactionEnd method to see when the entries are changed. For example, if the ObjectGrid is working in distributed mode, application can watch for incoming changes. Suppose that the replicated entries were for latest stock prices. This listener can watch for these changes arriving and update a second Map that keeps the value of a position in a portfolio. The listener must make all changes using the Session provided to the listener in the ObjectGridEventListener#initialize method. The listener can distinguish between local changes and incoming remote changes usually by checking if the transaction is write through. The incoming changes from peer ObjectGrids are always write through.
Chapter 5. ObjectGrid application programming interface overview

79

The ObjectGridEventListener interface The ObjectGridEventListener interface has the following methods. These methods are called when certain significant events occur on the ObjectGrid.
/** * This method will be invoked when the ObjectGrid itself is initialized. * A usable Session instance will be passed into this Listener to allow the * for the optional replaying of a received LogSequence into a Map. * * @param session The Session instance that this Listener is associated with. */ void initialize(Session session); /** * This event will signal the beginning of a transaction (session). * A stringified version of the TxID is provided for * correlating with the end of the transaction * (session), if so desired. The type of transaction (session) is * also provided via the isWriteThroughEnabled boolean parameter. * * @param txid Stringified version of the TxID * @param isWriteThroughEnabled Boolean flag indicating whether the * Session was started via beginNoWriteThrough */ void transactionBegin(String txid, boolean isWriteThroughEnabled); /** * This signals the ending of a transaction (session). A stringified * version of the TxID is provided for correlating with the * begin of the transaction * (session), if so desired. Changes are also reported. Typical uses of * this event are for customers doing custom peer invalidation * or peer commit push. This event * listener gives them the changes. Calls to this method are made * after commit and are sequenced so that they are delivered one by one, * not in parallel. The event order is the commit order. * * If the transaction did not commit successfully * (committed == false), the Collection * of changes will be null. * * @param txid Stringified version of the TxID * @param isWriteThroughEnabled a boolean flag indicating * whether the Sesison was * started via beginNoWriteThrough * @param committed a boolean flag indicating whether the Session * was committed * (true) or rolled back (false) * @param changes A Collection of LogSequences that have been * processed for the current Session. If the Session * was rolled back, this parameter will be null. */ void transactionEnd(String txid, boolean isWriteThroughEnabled, boolean committed, Collection /**/ changes); /** * This method will be invoked when the ObjectGrid is destroyed. Its the * opposite of initialize. When this method is called, the * ObjectGridEventListener can free up any resource it uses. */ void destroy();

Add and remove ObjectGridEventListeners objects An ObjectGrid can have multiple ObjectGridEventListeners. Two methods exist on the ObjectGrid that allow ObjectGridEventListeners to be added. ObjectGridEventListeners that have been added can also be removed from an ObjectGrid. The addEventListener method can be used to add an ObjectGridEventListener to an ObjectGrid.

80

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

/** * Add an EventListener to the Session. Significant events * will be communicated to interested listeners via this callback. * Multiple event listeners are allowed to be registered, with no * implied ordering of event notifications. * * Note, this method is allowed to be invoked before and after the * {@link ObjectGrid#initialize()} method. * * @param cb An instance of ObjectGridEventListener */ void addEventListener(ObjectGridEventListener cb);

To add a list of ObjectGridEventListeners, use the setEventListeners method:


/** * This overwrites the current list of callbacks and replaces it with the * supplied list of callbacks. * * Note, this method is allowed to be invoked before and after the * {@link ObjectGrid#initialize()} method. * @param callbacks */ void setEventListeners(List callbacks);

To remove an ObjectGridEventListener from an ObjectGrid use the removeEventListener method:


/** * Removes an EventListener from the Session. If the desired EventListener *is not found on the Session, no error will be returned. * * Note, this method is allowed to be invoked before and after the * {@link ObjectGrid#initialize()} method. * @param cb An instance of ObjectGridEventListener */ void removeEventListener(ObjectGridEventListener cb);

Create a custom ObjectGrid event listener To use a custom ObjectGrid event listener, first create a class that implements the com.ibm.websphere.objectgrid.plugins.ObjectGridEventListener interface. Add the custom listener to an ObjectGrid to receive notification of significant events. An ObjectGridEventListener can be configured programmatically or with XML: v Programatically. Assume that the class name of the ObjectGrid event listener is the com.company.org.MyObjectGridEventListener class. This class implements the ObjectGridEventListener interface. The following code snippet creates the custom ObjectGridEventListener and adds it to an ObjectGrid:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid myGrid = objectGridManager.createObjectGrid("myGrid", false); MyObjectGridEventListener myListener = new MyObjectGridEventListener(); myGrid.addEventListener(myListener);

v With XML. An ObjectGridEventListner can also be configured using XML. The following XML creates a configuration that is equivalent to the described program-created ObjectGrid event listener. The following text must be in the myGrid.xml file:
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=
Chapter 5. ObjectGrid application programming interface overview

81

"http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="myGrid"> <bean id="ObjectGridEventListener" className="com.company.org.MyObjectGridEventListener" /> </objectGrid> </objectGrids> </objectGridConfig>

Provide this file to the ObjectGridManager to facilitate the creation of this configuration. The following code snippet demonstrates how to create an ObjectGrid using this XML file. The ObjectGrid that is created has an ObjectGridEventListener set on the myGrid ObjectGrid.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid myGrid = objectGridManager.createObjectGrid("myGrid", new URL( "file:etc/test/myGrid.xml"), true, false);

MapEventListener interface
Use the MapEventListener interface to receive significant events about a map. Events are sent to the MapEventListener when an entry is evicted from the map and when the preload of a map completes. MapEventListener interface The MapEventListener interface has the following methods. Implement the com.ibm.websphere.objectgrid.plugins.MapEventListener interface to create a custom MapEventListener.
/** * This method is invoked when the specified entry is evicted from * the map. The eviction could have occurred either by Evictor * processing or by invoking one of the invalidate methods on the * ObjectMap. * * @param key The key for the map entry that was evicted. * @param value The value that was in the map entry evicted. The value * object should not be modified. * */ void entryEvicted(Object key, Object value); /** * This method is invoked when preload of this map has completed. * * @param t A Throwable object that indicates if preload completed without * any Throwable occurring during the preload of the map. A null reference * indicates preload completed without any Throwable objects occurring * during the preload of the map. */ void preloadCompleted( Throwable t );

Add and remove MapEventListeners The following BackingMap methods allow MapEventListeners to be added to and removed from a map:
/** * Adds a MapEventListener to this BackingMap. * * Note, this method is allowed to be invoked before and after the * ObjectGrid.initialize() method. * @param eventListener A nonnull reference to a MapEventListener to add * to the list. *

82

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

* @throws IllegalArgumentException if eventListener is null. * * @see MapEventListener */ public void addMapEventListener(MapEventListener eventListener ); /** * Sets the list of MapEventListener objects. * * If this BackingMap already has a List of * MapEventListeners, that list is replaced by the * List passed as an argument to the current invocation * of this method. This method can be called before and * after the ObjectGrid.initialize() method. * * @param eventListenerList A nonnull reference to a List of * MapEventListener objects. * * @throws IllegalArgumentException is thrown if * eventListenerList is null * or the eventListenerList contains either a null * reference or an object that is not an instance of * MapEventListener. * * @see MapEventListener */ public void setMapEventListeners( List /*MapEventListener*/ eventListenerList ); /** * Removes a MapEventListener from this BackingMap. * * Note, this method is allowed to be invoked before and after the * ObjectGrid.initialize() method. * * @param eventListener A nonnull reference to an event listener * that was previously added by invoking either the * addMapEventListener(MapEventListener) or * setMapEventListeners(List) method of this interface. * * @throws IllegalArgumentException if eventListener is null. * * @see MapEventListener */ public void removeMapEventListener(MapEventListener eventListener );

Create a MapEventListener To create a custom MapEventListener, implement the com.ibm.websphere.objectgrid.plugins.MapEventListener interface. To use the MapEventListener, add it to a BackingMap. A MapEventListener can be created and configured programmatically or with XML: v Programmatically. The class name for the custom MapEventListener is the com.company.org.MyMapEventListener class. This class implements the MapEventListener interface. The following code snippet creates the custom MapEventListener and adds it to a BackingMap:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid myGrid = objectGridManager.createObjectGrid("myGrid", false); BackingMap myMap = myGrid.defineMap("myMap"); MyMapEventListener myListener = new MyMapEventListener(); myMap.addMapEventListener(myListener);

v XML creation. A MapEventListner can also be configured using XML. The following XML achieves a configuration that is equivalent to the preceding programmatic creation. The following XML must be in the myGrid.xml file:
Chapter 5. ObjectGrid application programming interface overview

83

<?xml version="1.0" encoding="UTF-8" ?> <objectGridconfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="myGrid"> <backingMap name="myMap" pluginCollectionRef="myPlugins" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="myPlugins"> <bean id="MapEventListener" classname="com.company.org.MyMapEventListener" /> </backingMapPluginCollection> </backingMapPluginCollection> </objectGridConfig>

Providing this file to the ObjectGridManager facilitates the creation of this configuration. The following code snippet shows how to create an ObjectGrid using this XML file. The newly created ObjectGrid has a MapEventListener set on the myMap BackingMap.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid myGrid = objectGridManager.createObjectGrid("myGrid", new URL( "file:etc/test/myGrid.xml"), true, false);

Evictors
ObjectGrid provides a default evictor mechanism. You can also provide a pluggable evictor mechanism. An evictor controls the membership of entries in each BackingMap. The default evictor uses a time to live eviction policy for each BackingMap. If you provide a pluggable evictor mechanism, it typically uses an eviction policy that is based on the number of entries instead of on time. This topic describes both types of evictors.

Default time to live evictor


ObjectGrid provides a time to live (TTL) evictor for every BackingMap. The TTL evictor maintains an expiration time for each entry that is created. When the expiration time for an entry comes, the evictor purges the entry from the BackingMap. To minimize performance impact, the TTL evictor might wait to evict an entry after the expiration time, but never before the entry expires. The BackingMap has attributes that are used to control how the time to live evictor computes the expiration time for each entry. Applications set the ttlType attribute to specify how the TTL evictor should calculate the expiration time. The ttlType attribute can be set to one of the following values: v None indicates that an entry in the BackingMap never expires. The TTL evictor does not evict these entries. v Creation time indicates that the time an entry is created is used in the expiration time calculation. v Last access time indicates that the time that an entry has been last accessed is used in the expiration time calculation.

84

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

If the ttlType attribute is not set on a BackingMap, the default type of None is used so that the TTL evictor does not evict any entries. If the ttlType attribute is set to either creation time or last access time, the value of the time to live attribute on the BackingMap is added to either the creation time or last access time to compute the expiration time. The time precision of the time to live map attribute is in seconds. A value of 0 for the time to live attribute is a special value that is used to indicate that the map entry can live forever, that is, the entry stays in the map until the application explicitly removes or invalidates the map entry. Specify attributes for TTL evictors TTL evictors are associated with BackingMap instances. The following snippet of code illustrates how the BackingMap interface can be used to set the needed attributes so that when each entry is created, it has an expiration time set to ten minutes after it was created.
import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.TTLType; ObjectGridManager ogManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid og = ogManager.createObjectGrid( "grid" ); BackingMap bm = og.defineMap( "myMap" ); bm.setTtlEvictorType( TTLType.CREATION_TIME ); bm.setTimeToLive( 600 );

The setTimeToLive method argument is 600 because it indicates the time to live value is in seconds. The preceding code must run before the initialize method is invoked on the ObjectGrid instance. These BackingMap attributes cannot be changed after the ObjectGrid instance is initialized. After the code runs, any entry that is inserted into the myMap BackingMap has an expiration time. After the expiration time is reached, the TTL evictor purges the entry. If an application requires that the expiration time be set to the last access time plus ten minutes, one line of the preceding code must be changed. The argument that is passed to the setTtlEvictorType method is changed from TTLType.CREATION_TIME to TTLType.LAST_ACCESS_TIME. With this value, the expiration time is computed as the last access time plus 10 minutes. When an entry is first created, the last access time is the creation time. When TTLType.LAST_ACCESS_TIME is used, the ObjectMap and JavaMap interfaces can be used to override the BackingMap time to live value. This mechanism allows an application to use a different time to live value for each entry that is created. Assume the preceding snippet of code was used to set the ttlType attribute to LAST_ACCESS_TIME and the time to live value was set to ten minutes on the BackingMap. An application can then override the time to live value for each entry by running the following code prior to creating or modifying an entry:
import com.ibm.websphere.objectgrid.Session; import com.ibm.websphere.objectgrid.ObjectMap; Session session = og.getSession(); ObjectMap om = session.getMap( "myMap" ); int oldTimeToLive1 = om.setTimeToLive( 1800 ); om.insert("key1", "value1" ); int oldTimeToLive2 = om.setTimeToLive( 1200 ); om.insert("key2", "value2" );

In the above snippet of code, the entry with the key1 key has an expiration time of the insert time plus 30 minutes as a result of the setTimeToLive( 1800 ) method
Chapter 5. ObjectGrid application programming interface overview

85

invocation on the ObjectMap. The oldTimeToLive1 variable is set to 600 because the time to live value from the BackingMap is used as a default value if the setTimeToLive method was not previously called on the ObjectMap. The entry with the key2 key has an expiration time of insert time plus 20 minutes as a result of the setTimeToLive( 1200 ) method call on the ObjectMap. The oldTimeToLive2 variable is set to 1800 because the time to live value from the previous ObjectMap.setTimeToLive method invocation set the time to live to 1800. Reference the API documentation for the setTimeToLive method on the ObjectMap and JavaMap interfaces. It warns you that an IllegalStateException exception results if the BackingMap.getTtlEvictorType() method returns anything other than the TTLType.LAST_ACCESS_TIME value. ObjectMap and JavaMap can only be used to override the time to live value when you are using the LAST_ACCESS_TIME TTL evictor type. This method cannot be used to override the time to live value when you are using the CREATION_TIME TTL evictor type or the NONE TTL evictor type. Use an XML file to specify attributes for the TTL evictor Instead of using the BackingMap interface to programmatically set the BackingMap attributes to be used by the TTL evictor, an XML file can be used to configure each BackingMap. The following code illustrates how to set these attributes for three different BackingMaps:
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="grid1"> <backingMap name="map1" ttlEvictorType="NONE" /> <backingMap name="map2" ttlEvictorType="LAST_ACCESS_TIME" timeToLive="1800" /> <backingMap name="map3" ttlEvictorType="CREATION_TIME" timeToLive="1200" /> </objectgrid> </objectGrids>

The preceding example shows that the map1 BackingMap uses a NONE TTL evictor type. The map2 BackingMap uses a LAST_ACCESS_TIME TTL evictor type and has a time to live value of 1800 seconds, or 30 minutes. The map3 BackingMap is defined to use a CREATION_TIME TTL evictor type and has a time to live value of 1200 seconds, or 20 minutes.

Optional pluggable evictors


The default TTL evictor uses an eviction policy that is based on time, and the number of entries in the BackingMap has no affect on the expiration time of an entry. An optional pluggable evictor can be used to evict entries based on the number of entries that exist instead of based on time. The following optional pluggable evictors provide some commonly used algorithms for deciding which entries to evict when a BackingMap grows beyond some size limit. v LRUEvictor is an evictor that uses a least recently used algorithm to decide which entries to evict when the BackingMap exceeds a maximum number of entries. v LFUEvictor is an evictor that uses a least frequently used algorithm to decide which entries to evict when the BackingMap exceeds a maximum number of entries.

86

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

The BackingMap informs an evictor as entries are created, modified, or removed in a transaction. The BackingMap keeps track of these entries and chooses when to evict one or more entries from the BackingMap. A BackingMap has no configuration information for a maximum size. Instead, evictor properties are set to control the evictor behavior. Both the LRUEvictor and the LFUEvictor have a maximum size property that is used to cause the evictor to begin to evict entries after the maximum size is exceeded. Like the TTL evictor, the LRU and LFU evictors might not immediately evict an entry when the maximum number of entries is reached to minimize impact on performance. If the LRU or LFU eviction algorithm is not adequate for a particular application, you can write your own evictors to achieve the eviction strategy that you want. Specify a pluggable evictor Because evictors are associated with BackingMaps, the BackingMap interface is used to specify the pluggable evictor to use. The following code snippet is an example of specifying a LRUEvictor evictor for the map1 BackingMap and a LFUEvictor evictor for the map2 BackingMap:
import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor; import com.ibm.websphere.objectgrid.plugins.builtins.LFUEvictor; ObjectGridManager ogManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid og = ogManager.createObjectGrid( "grid" ); BackingMap bm = og.defineMap( "map1" ); LRUEvictor evictor = new LRUEvictor(); evictor.setMaxSize(1000); evictor.setSleepTime( 15 ); evictor.setNumberOfLRUQueues( 53 ); bm.setEvictor(evictor); bm = og.defineMap( "map2" ); LFUEvictor evictor2 = new LFUEvictor(); evictor2.setMaxSize(2000); evictor2.setSleepTime( 15 ); evictor2.setNumberOfHeaps( 211 ); bm.setEvictor(evictor2);

The preceding snippet shows an LRUEvictor evictor being used for map1 BackingMap with a maximum number of entries of 1000. The LFUEvictor evictor is used for the map2 BackingMap with a maximum number of entries of 2000. Both the LRU and LFU evictors have a sleep time property that indicates how long the evictor sleeps before waking up and checking to see if any entries need to be evicted. The sleep time is specified in seconds. A value of 15 seconds is a good compromise between performance impact and preventing BackingMap from growing too large. The goal is to use the largest sleep time possible without causing the BackingMap to grow to an excessive size. The setNumberOfLRUQueues method sets the LRUEvictor property that indicates how many LRU queues the evictor uses to manage LRU information. A collection of queues is used so that every entry does not keep LRU information in the same queue. This approach can improve performance by minimizing the number of map entries that need to synchronize on the same queue object. Increasing the number of queues is a good way to minimize the impact that the LRU evictor can cause on

Chapter 5. ObjectGrid application programming interface overview

87

performance. A good starting point is to use ten percent of the maximum number of entries as the number of queues. Using a prime number is typically better than using a number that is not prime. The setNumberOfHeaps method sets the LFUEvictor property to set how many binary heap objects the LFUEvictor uses to manage LFU information. Again, a collection is used to improve performance. Using ten percent of the maximum number of entries is a good starting point and a prime number is typically better than using a number that is not prime. Use XML to specify a pluggable evictor Instead of using various APIs to programmatically plug in an evictor and set its properties, an XML file can be used to configure each BackingMap as illustrated in the following sample:
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="grid"> <backingMap name="map1" ttlEvictorType="NONE" pluginCollectionRef="LRU" /> <backingMap name="map2" ttlEvictorType="NONE" pluginCollectionRef="LFU" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPlugincollection id="LRU"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor"> <property name="maxSize" type="int" value="1000" description="set max size for LRU evictor"> <property name="sleepTime" type="int" value="15" description="evictor thread sleep time" /> <property name="numberOfLRUQueues" type="int" value="53" description="set number of LRU queues" /> </bean> </backingMapPluginCollection> <backingMapPluginCollection id="LFU"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LFUEvictor"> <property name="maxSize" type="int" value="2000" description="set max size for LFU evictor"> <property name="sleepTime" type="int" value="15" description="evictor thread sleep time" /> <property name="numberOfHeaps" type="int" value="211" description="set number of LFU heaps" /> </bean> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

Write a custom evictor


ObjectGrid can be extended to use any eviction algorithm. You must create a custom evictor that implements the com.ibm.websphere.objectgrid.plugins.Evictor interface. The interface follows:
public interface Evictor { void initialize(BackingMap map, EvictionEventCallback callback); void destroy(); void apply(LogSequence sequence); }

88

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v The initialize method is invoked during initialization of the BackingMap object. This method initializes an Evictor plug-in with a reference to the BackingMap and a reference to an object that implements the com.ibm.websphere.objectgrid.plugins.EvictionEventCallback interface. v The apply method is invoked when transactions that access one or more entries of the BackingMap are committed. The apply method is passed a reference to an object that implements the com.ibm.websphere.objectgrid.plugins.LogSequence interface. The LogSequence interface allows an Evictor plug-in to determine which BackingMap entries were created, modified, or removed by the transaction. An Evictor uses this information in deciding when and which entries to evict. v The destroy method is invoked when the BackingMap is being destroyed. This method allows an Evictor to terminate any threads that it might have created. The EvictionEventCallback interface has the following methods:
public interface EvictionEventCallback { void evictEntries(List keysToEvictList); void setEvictorData(Object key, Object data); Object getEvictorData(Object key); }

The EvictionEventCallback methods are used by an Evictor plug-in to call back to the ObjectGrid framework as follows: v The setEvictorData method is used by an evictor to request the framework that is used to store and associate some evictor object it creates with the entry indicated by the key argument. The data is evictor specific and is determined by the information the evictor needs to implement the algorithm it is using. For example, in a least frequently used algorithm, the evictor maintains a count in the evictor data object for tracking how many times the apply method is invoked with a LogElement that refers to an entry for a given key. v The getEvictorData method is used by an evictor to retrieve the data it passed to the setEvictorData method during a prior apply method invocation. If evictor data for the specified key argument is not found, a special KEY_NOT_FOUND object that is defined on the EvictorCallback interface is returned. v The evictEntries method is used by an evictor to request eviction of one or more map entries. Remember: Take care when setting the locking strategy to OPTIMISTIC or PESSIMISTIC for the BackingMap. The transaction that calls the apply method on the Evictor interface owns an X lock on each map entry that is inserted, updated, removed, touched, or invalidated. The implementation of the evictEntries method on the EvictionEventCallback interface uses its own ObjectGrid Session to begin another transaction. Each key in the list of keys passed to the evictEntries method results in the evictEntries transaction attempting to acquire an X lock on the map entry being evicted. Because the apply transaction already owns an X lock on map entry, the evictEntries transaction blocks waiting for the apply transaction to complete. To avoid a deadlock from occurring between the apply transaction and the evictEntries transaction, do not use the same thread that calls the apply method to call the evictEntries method. If the same thread is used, a LockTimeoutException exception is sent to the evictEntries transaction to break the deadlock condition. To ensure that the evictEntries is called from a different thread than the thread
Chapter 5. ObjectGrid application programming interface overview

89

that calls the apply method, implement the initialize method on the Evictor interface so that it spawns a thread as shown by the examples in this section. The spawned thread is the only thread that calls the evictEntries method and the apply method is only called by this thread with a LogSequence that contains LogElement of type EVICT. The EVICT LogElement causes the evictor to take no action because nothing needs to be done. The Evictor and EvictorEventCallback interfaces allow an application to plug in an evictor that implements a user-defined algorithm for eviction. The following snippet of code illustrates how you can implement the initialize method of Evictor interface:
import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.plugins.EvictionEventCallback; import com.ibm.websphere.objectgrid.plugins.Evictor; import com.ibm.websphere.objectgrid.plugins.LogElement; import com.ibm.websphere.objectgrid.plugins.LogSequence; import java.util.LinkedList; // Instance variables private BackingMap bm; private EvictionEventCallback evictorCallback; private LinkedList queue; private Thread evictorThread; public void initialize(BackingMap map, EvictionEventCallback callback) { bm = map; evictorCallback = callback; queue = new LinkedList; // spawn evictor thread evictorThread = new Thread( this ); String threadName = "MyEvictorForMap" + bm.getName(); evictorThread.setName( threadName ); evictorThread.start(); }

The preceding code saves the references to the map and callback objects in instance variables so that they are available to the apply and destroy methods. In this example, a linked list is created that is used as a first in, first out queue for implementing a least recently used (LRU) algorithm. A thread is spawned off and a reference to the thread is kept as an instance variable. By keeping this reference, the destroy method can interrupt and terminate the spawned thread. Ignoring synchronization requirements to make code thread safe, the following snippet of code illustrates how the apply method of the Evictor interface can be implemented:
import com.ibm.websphere.objectgrid.BackingMap; import com.ibm.websphere.objectgrid.plugins.EvictionEventCallback; import com.ibm.websphere.objectgrid.plugins.Evictor; import com.ibm.websphere.objectgrid.plugins.LogElement; import com.ibm.websphere.objectgrid.plugins.LogSequence; public void apply(LogSequence sequence) { Iterator iter = sequence.getAllChanges(); while ( iter.hasNext() ) { LogElement elem = (LogElement)iter.next(); Object key = elem.getCacheEntry().getKey(); LogElement.Type type = elem.getType(); if ( type == LogElement.INSERT ) { // do insert processing here by adding to front of LRU queue. EvictorData data = new EvictorData(key);

90

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

evictorCallback.setEvictorData(key, data); queue.addFirst( data ); } else if ( type == LogElement.UPDATE || type == LogElement.FETCH || type == LogElement.TOUCH ) { // do update processing here by moving EvictorData object to // front of queue. EvictorData data = evictorCallback.getEvictorData(key); queue.remove(data); queue.addFirst(data); } else if ( type == LogElement.DELETE || type == LogElement.EVICT ) { // do remove processing here by removing EvictorData object // from queue. EvictorData data = evictorCallback.getEvictorData(key); if ( data == EvictionEventCallback.KEY_NOT_FOUND ) { // Assumption here is your asynchronous evictor thread // evicted the map entry before this thread had a chance // to process the LogElement request. So you probably // need to do nothing when this occurs. } else { // Key was found. So process the evictor data. if ( data != null ) { // Ignore null returned by remove method since spawned // evictor thread may have already removed it from queue. // But we need this code in case it was not the evictor // thread that caused this LogElement to occur. queue.remove( data ); } else { // Depending on how you write you Evictor, this possibility // may not exist or it may indicate a defect in your evictor // due to improper thread synchronization logic. } } } } }

Insert processing in the apply method typically handles the creation of an evictor data object that is passed to the setEvictorData method of the EvictionEventCallback interface. Because this evictor illustrates a LRU implementation, the EvictorData is also added to the front of the queue that was created by the initialize method. Update processing in the apply method typically updates the evictor data object that was created by some prior invocation of the apply method (for example, by the insert processing of the apply method). Because this evictor is an LRU implementation, it needs to move the EvictorData object from its current queue position to the front of the queue. The spawned evictor thread removes the last EvictorData object in the queue because the last queue element represents the least recently used entry. The assumption is that the EvictorData object has a getKey method on it so that the evictor thread knows the keys of the entries that need to be evicted. Keep in mind that this example is ignoring synchronization requirements to make code thread safe. A real custom evictor is more complicated because it deals with synchronization and performance bottlenecks that occur as a result of the synchronization points.

Chapter 5. ObjectGrid application programming interface overview

91

The following snippets of code illustrate the destroy method and the run method of the runnable thread that the initialize method spawned:
// Destroy method simply interrupts the thread spawned by the initialize method. public void destroy() { evictorThread.interrupt(); } // Here is the run method of the thread that was spawned by the initialize method. public void run() { // Loop until destroy method interrupts this thread. boolean continueToRun = true; while ( continueToRun ) { try { // Sleep for a while before sweeping over queue. // The sleepTime is a good candidate for a evictor // property to be set. Thread.sleep( sleepTime ); int queueSize = queue.size(); // Evict entries if queue size has grown beyond the // maximum size. Obviously, maximum size would // be another evictor property. int numToEvict = queueSize maxSize; if ( numToEvict > 0 ) { // Remove from tail of queue since the tail is the // least recently used entry. List evictList = new ArrayList( numToEvict ); while( queueSize > ivMaxSize ) { EvictorData data = null; try { EvictorData data = (EvictorData) queue.removeLast(); evictList.add( data.getKey() ); queueSize = queue.size(); } catch ( NoSuchElementException nse ) { // The queue is empty. queueSize = 0; } } // Request eviction if key list is not empty. if ( ! evictList.isEmpty() ) { evictorCallback.evictEntries( evictList ); } } } catch ( InterruptedException e ) { continueToRun = false; } } // end while loop } // end run method.

Loaders
An ObjectGrid loader is a pluggable component that allows an ObjectGrid map to behave as a memory cache for data that is typically kept in a persistent store on either the same system or some other system.

92

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Typically, a database or file system is used as the persistent store. A remote Java virtual machine (JVM) can also be used as the source of data allowing hub based caches to be built using ObjectGrid. A loader has the logic for reading and writing data from and to a persistent store. A Loader is a plug-in for an ObjectGrid backing map. Only one Loader can ever be associated with a given backing map. Each backing map has its own Loader instance. The backing map requests any data that it does not contain from its loader. Any changes to the map are pushed out to the loader. The loader plug-in allows the backing map to move data between the map and its persistent store.

Plug in a loader
The following snippet of code illustrates how an application-provided Loader is plugged into the backing map for map1 using the ObjectGrid API:
import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.BackingMap; ObjectGridManager ogManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid og = ogManager.createObjectGrid( "grid" ); BackingMap bm = og.defineMap( "map1" ); MyLoader loader = new MyLoader(); loader.setDataBaseName("testdb"); loader.setIsolationLevel("read committed"); bm.setLoader( loader );

The assumption is that MyLoader is the application-provided class that implements the com.ibm.websphere.objectgrid.plugins.Loader interface. Because the association of a Loader with a backing map cannot be changed after ObjectGrid is initialized, the code must be run before invoking the initialize method of the ObjectGrid interface that is being called. An IllegalStateException exception occurs on a setLoader method call if it is called after initialization has occurred. The application-provided Loader can have set properties. In the example, the MyLoader loader is used to read and write data from a table in a relational database. The loader must have the name of the database and the SQL isolation level to use. The MyLoader loader has the setDataBaseName and setIsolationLevel methods that allow the application to set these two Loader properties. An application provided Loader could also be plugged in by using an XML file. The following example illustrates how the MyLoader loader is plugged into the map1 backing map with the same database name and isolation level Loader properties being set:
<?xml version="1.0" encoding="UTF-8" ?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="grid"> <backingMap name="map1" pluginCollectionRef="map1" lockStrategy="OPTIMISTIC" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="map1"> <bean id="Loader" className="com.myapplication.MyLoader"> <property name="dataBaseName" type="java.lang.String" value="testdb" description="database name" /> <property name="isolationLevel" type="java.lang.String" value="read committed" description="iso level" />
Chapter 5. ObjectGrid application programming interface overview

93

</bean> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

Implement the Loader interface


An application provided Loader must implement the com.ibm.websphere.objectgrid.plugins.Loader interface. The Loader interface has the following definition:
public interface Loader { static final SpecialValue KEY_NOT_FOUND; List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException; void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException; void preloadMap(Session session, BackingMap backingMap) throws LoaderException; }

Each of the following sections gives an explanation and considerations when implementing each of the methods on the Loader interface. get method The backing map calls the Loader get method to get the values associated with a key list that is passed as the keyList argument. The get method is required to return a java.lang.util.List list of values, one for each key that is in the key list. The first value returned in the value list corresponds to the first key in the key list, the second value returned in the value list corresponds to the second key in the key list, and so on. If the loader does not find the value for a key in the key list, the Loader is required to return the special KEY_NOT_FOUND value object that is defined in the Loader interface. Because a backing map can be configured to allow null as a valid value, it is very important for the Loader to return the special KEY_NOT_FOUND object when the Loader is unable to find the key. This value allows the backing map to distinguish between a null value and a value that does not exist because the key was not found. If a backing map does not support null values, a Loader that returns null instead of the KEY_NOT_FOUND object for a key that does not exist results in an exception. The forUpdate argument tells the Loader if the application called a get method on the map or a getForUpdate method on the map. See the com.ibm.websphere.objectgrid.ObjectMap interface for more information. The Loader is responsible for implementing a concurrency control policy that controls concurrent access to the persistent store. For example, many relational database management systems support the for update syntax on the SQL select statement that is used to read data from a relational table. The Loader can choose to use the for update syntax on the SQL select statement based on whether boolean true is passed as the argument value for the forUpdate parameter of this method. Typically, the Loader uses the for update syntax only when using a pessimistic concurrency control policy. For an optimistic concurrency control, the Loader never uses for update syntax on the SQL select statement. The Loader is responsible to decide to use the forUpdate argument based on the concurrency control policy that is being used by the Loader. For an explanation of the txid parameter, see the TransactionCallback plug-in on page 108 topic.

94

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

batchUpdate method The batchUpdate method is critical on the Loader interface. This method is called whenever the ObjectGrid needs to apply all current changes to the Loader. The Loader is given a list of changes for this Map. The changes are iterated and applied to the backend. The method receives the current TxID value and the changes to apply. The following sample iterates over the set of changes and batches three Java database connectivity (JDBC) statements, one with insert, another with update, and one with delete.
import java.util.Collection; import java.util.Map; import java.sql.PreparedStatement; import java.sql.SQLException; import com.ibm.websphere.objectgrid.TxID; import com.ibm.websphere.objectgrid.plugins.Loader; import com.ibm.websphere.objectgrid.plugins.LoaderException; import com.ibm.websphere.objectgrid.plugins.LogElement; import com.ibm.websphere.objectgrid.plugins.LogSequence; public void batchUpdate(TxID tx, LogSequence sequence) throws LoaderException { // Get a SQL connection to use. Connection conn = getConnection(tx); try { // Process the list of changes and build a set of prepared // statements for executing a batch update, insert, or delete // SQL operation. Iterator iter = sequence.getPendingChanges(); while ( iter.hasNext() ) { LogElement logElement = (LogElement)iter.next(); Object key = logElement.getCacheEntry().getKey(); Object value = logElement.getCurrentValue(); switch ( logElement.getType().getCode() ) { case LogElement.CODE_INSERT: buildBatchSQLInsert( tx, key, value, conn ); break; case LogElement.CODE_UPDATE: buildBatchSQLUpdate( tx, key, value, conn ); break; case LogElement.CODE_DELETE: buildBatchSQLDelete( tx, key, conn ); break; } } // Execute the batch statements that were built by above loop. Collection statements = getPreparedStatementCollection( tx, conn ); iter = statements.iterator(); while ( iter.hasNext() ) { PreparedStatement pstmt = (PreparedStatement) iter.next(); pstmt.executeBatch(); } } catch (SQLException e) { LoaderException ex = new LoaderException(e); throw ex; } }

Chapter 5. ObjectGrid application programming interface overview

95

The preceding sample illustrates the high level logic of processing the LogSequence argument, but the details of how a SQL insert, update, or delete statement is built are not illustrated. Some of the key points that are illustrated include: v The getPendingChanges method is called on the LogSequence argument to obtain an iterator over the list of LogElements that the Loader needs to process. v The LogElement.getType().getCode() method is used to determine if the LogElement is for a SQL insert, update, or delete operation. v An SQLException exception is caught and is chained to a LoaderException exception that prints to report that an exception occurred during the batch update. v JDBC batch update support is used to minimize the number of queries to the backend that must be made. preloadMap method During the ObjectGrid initialization, each backing map that is defined is initialized. If a Loader is plugged into a backing map, the backing map invokes the preloadMap method on the Loader interface to allow the loader to pre-fetch data from its backend and load the data into the map. The following sample assumes the first 100 rows of an Employee table is read from the database and is loaded into the map. The EmployeeRecord class is an application provided class that holds the employee data read from the employee table.
import java.util.Map; import java.sql.PreparedStatement; import java.sql.SQLException; import com.ibm.websphere.objectgrid.Session; import com.ibm.websphere.objectgrid.TxID; import com.ibm.websphere.objectgrid.plugins.Loader; import com.ibm.websphere.objectgrid.plugins.LoaderException; public void preloadMap(Session session, BackingMap backingMap) throws LoaderException { boolean tranActive = false; ResultSet results = null; Statement stmt = null; Connection conn = null; try { session.beginNoWriteThrough(); tranActive = true; ObjectMap map = session.getMap( backingMap.getName() ); TxID tx = session.getTxID(); // Get a autocommit connection to use that is set to // a read committed isolation level. conn = getAutoCommitConnection(tx); // Preload the Employee Map with EmployeeRecord // objects. Read all Employees from table, but // limit preload to first 100 rows. stmt = conn.createStatement(); results = stmt.executeQuery( SELECT_ALL ); int rows = 0; while ( results.next() && rows < 100 ) { int key = results.getInt(EMPNO_INDEX); EmployeeRecord emp = new EmployeeRecord( key ); emp.setLastName( results.getString(LASTNAME_INDEX) ); emp.setFirstName( results.getString(FIRSTNAME_INDEX) ); emp.setDepartmentName( results.getString(DEPTNAME_INDEX) ); emp.updateSequenceNumber( results.getLong(SEQNO_INDEX) );

96

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

emp.setManagerNumber( results.getInt(MGRNO_INDEX) ); map.put( new Integer(key), emp ); ++rows; } // Commit the transaction. session.commit(); tranActive = false; } catch (Throwable t) { throw new LoaderException("preload failure: " + t, t); } finally { if ( tranActive ) { try { session.rollback(); } catch ( Throwable t2 ) { // Tolerate any rollback failures and // allow original Throwable to be thrown. } } // Be sure to clean up other databases resources here // as well such a closing statements, result sets, etc. } }

This sample illustrates the following key points: v The preloadMap backing map uses the Session object that is passed to it as the session argument. v The Session.beginNoWriteThrough() method is used to begin the transaction rather than the begin method. The Loader cannot be called for each put operation that occurs in this method for loading the map. v The Loader can map columns of employee table to a field in the EmployeeRecord java object. v The Loader catches all throwable exceptions that occur and throws a LoaderException exception with the caught throwable exception chained to it. v The finally block ensures that any throwable exception that occurs between the time the beginNoWriteThrough method is called and the commit method is called cause the finally block to roll back the active transaction. This action is critical to ensure that any transaction that has been started by the preloadMap method is completed before returning to the caller. The finally block is a good place to perform other clean up actions that might be needed, like closing the JDBC connection and other JDBC objects. The preloadMap sample is using a SQL select statement that selects all rows of the table. In your application provided Loader, you might need to set one or more Loader properties to control how much of the table needs to be preloaded into the map. Because the preloadMap method is only called one time during the BackingMap initialization, it is also a good place to run the one time Loader initialization code. Even if a Loader chooses not to pre-fetch data from the backend and load the data into the map, it probably needs to
Chapter 5. ObjectGrid application programming interface overview

97

perform some other one time initialization to make other methods of the Loader more efficient. The following is an example of caching the TransactionCallback object and OptimisticCallback object as instance variables of the Loader so that the other methods of the Loader do not have to make method calls to get access to these objects. This caching of the ObjectGrid plug-in values can be done because after the BackingMap is initialized, the TransactionCallback and the OptimisticCallback objects cannot be changed or replaced. It is acceptable to cache these object references as instance variables of the Loader.
import import import import com.ibm.websphere.objectgrid.Session; com.ibm.websphere.objectgrid.BackingMap; com.ibm.websphere.objectgrid.plugins.OptimisticCallback; com.ibm.websphere.objectgrid.plugins.TransactionCallback;

// Loader instance variables. MyTransactionCallback ivTcb; // MyTransactionCallback // extends TransactionCallback MyOptimisticCallback ivOcb; // MyOptimisticCallback // implements OptimisticCallback ... public void preloadMap(Session session, BackingMap backingMap) throws LoaderException { // Cache TransactionCallback and OptimisticCallback objects // in instance variables of this Loader. ivTcb = (MyTransactionCallback) session.getObjectGrid().getTransactionCallback(); ivOcb = (MyOptimisticCallback) backingMap.getOptimisticCallback(); // The remainder of preloadMap code (such as shown in prior example). }

Related reference Loader considerations Use the following considerations when implementing a loader.

Loader considerations
Use the following considerations when implementing a loader.

Preload considerations
Each backing map has a boolean preloadMode attribute that can be set to indicate if preload of a map completes asynchronously. By default, the preloadMode attribute is set to false, which indicates that the backing map initialization does not complete until the preload of the map is complete. For example, backing map initialization is not complete until the preloadMap method returns. If the preloadMap method is going to read a large amount of data from its back end and load it into the map, it might take a relatively long time to complete. In this case, you can configure a backing map to use asynchronous preload of the map by setting the preloadMode attribute to true. This setting causes the backing map initialization code to spawn a thread that invokes the preloadMap method, allowing initialization of a backing map to complete while the preload of the map is still in progress. The following snippet of code illustrates how the preloadMode attribute is set to enable asynchronous preload:
BackingMap bm = og.defineMap( "map1" ); bm.setPreloadMode( true );

98

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

The preloadMode attribute can also be set by using a XML file as illustrated in the following example:
<backingMap name="map1" preloadMode="true" pluginCollectionRef="map1" lockStrategy="OPTIMISTIC" />

If the asynchronous preload mode is used, the following issues must be considered in the implementation of the preloadMap method: v An application might get, put and commit a map entry that has yet to be preloaded by the preloadMap transaction. If this situation occurs, the preloadMap transaction might fail, displaying an exception when the transaction attempts to commit. For example, when you use the optimistic lock strategy for this map, the preloadMap transaction might fail as a result of the OptimisticCollisionException exception that results when the commit discovers that the backing map already has a map entry for the key, and the versioning object for the map entry indicates that the preloadMap transaction is working with a stale copy of the data. The application and Loader must be prepared to handle this possibility. To address this problem, the application can provide both a MapEventListener and a Loader plug-in. With the MapEventListener and Loader plug-in, the application can use the MapEventListener to wait for preload to complete before starting any transactions that update map entries. For a read-only transaction, the application can proceed without waiting for the preload to complete. Because the application implements both the MapEventListener and Loader plug-in interfaces, the application can make a trade-off between the complexity of solution and the performance. With additional complexity, it is possible for a Loader that is preloading a partitioned map to track the status of the partition preloads. With this additional information, you can design a MapEventListener and Loader plug-ins that only block update transactions for the partitions that are still being loaded. See Chapter 4, ObjectGrid samples, on page 29 for an example of how an application can provide a MapEventListener plug-in that extends the MapEventListener interface to allow threads to be blocked until preload completes. v Consider using multiple threads for loading large amount of data. Use the session.getObjectGrid() method to get a reference to the ObjectGrid instance. After that is done, the getSession method on the ObjectGrid instance can be used to get additional sessions. Each thread might need to have its own Session because only one transaction can ever be associated with a Session at a time. Each thread reads n rows from a relational table, assuming that the backend is a relational database. The Loader must have a way to partition the work among the threads to reduce the time it takes to preload a map.

TxID and use of the TransactionCallback interface


Both the get method and batchUpdate methods on the Loader interface are passed a TxID object that represents the Session transaction that requires the get or batchUpdate operation to be performed. It is possible that the get and batchUpdate methods are called more than once per transaction. Therefore, transaction-scoped objects that are needed by the Loader are typically kept in a slot of the TxID object. A Java database connectivity (JDBC) Loader is used to illustrate how a Loader uses the TxID and TransactionCallback interfaces. It is also possible that several ObjectGrid maps are stored in the same database. Each map has its own Loader and each Loader might need to connect to the same database. When connecting to the same database, each Loader wants to use the
Chapter 5. ObjectGrid application programming interface overview

99

same JDBC connection so that the changes to each table are committed as part of the same database transaction. Typically, the same person who writes the Loader implementation also writes the TransactionCallback implementation. The best method is if the TransactionCallback interface is extended to add methods that the Loader needs for getting a database connection and for caching prepared statements. The reason for this methodology becomes apparent as you look at how the TransactionCallback and TxID interfaces are used by the Loader. As an example, the Loader might need the TransactionCallback interface to be extended as follows:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.ibm.websphere.objectgrid.TxID; public interface MyTransactionCallback extends TransactionCallback { Connection getAutoCommitConnection(TxID tx, String databaseName) throws SQLException; Connection getConnection(TxID tx, String databaseName, int isolationLevel ) throws SQLException; PreparedStatement getPreparedStatement(TxID tx, Connection conn, String tableName, String sql) throws SQLException; Collection getPreparedStatementCollection( TxID tx, Connection conn, String tableName ); }

Using these new methods, the Loader get and batchUpdate methods can get a connection as follows:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.ibm.websphere.objectgrid.TxID; private Connection getConnection(TxID tx, int isolationLevel) { Connection conn = ivTcb.getConnection(tx, databaseName, isolationLevel ); return conn; }

In the previous example and the examples that follow, ivTcb and ivOcb are Loader instance variables that were initialized as described in the Preload considerations on page 98 section. The ivTcb variable is a reference to the MyTransactionCallback instance and the ivOcb is a reference to the MyOptimisticCallback instance. The databaseName variable is an instance variable of the Loader that was set as a Loader property during the initialization of the backing map. The isolationLevel argument is one of the JDBC Connection constants that are defined for the various isolation levels that JDBC supports. If the Loader is using an optimistic implementation, the get method typically uses a JDBC autocommit connection to fetch the data from the database. In that case, the Loader might have a getAutoCommitConnection method that is implemented as follows:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.ibm.websphere.objectgrid.TxID; private Connection getAutoCommitConnection(TxID tx) { Connection conn = ivTcb.getAutoCommitConnection(tx, databaseName); return conn; }

Recall that the batchUpdate method has the following switch statement:

100

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

switch ( logElement.getType().getCode() ) { case LogElement.CODE_INSERT: buildBatchSQLInsert( tx, key, value, conn ); break; case LogElement.CODE_UPDATE: buildBatchSQLUpdate( tx, key, value, conn ); break; case LogElement.CODE_DELETE: buildBatchSQLDelete( tx, key, conn ); break; }

Each of the buildBatchSQL methods uses the MyTransactionCallback interface to get a prepared statement. Following is a snippet of code that shows the buildBatchSQLUpdate method building an SQL update statement for updating an EmployeeRecord entry and adding it for the batch update:
private void buildBatchSQLUpdate( TxID tx, Object key, Object value, Connection conn ) throws SQLException, LoaderException { String sql = "update EMPLOYEE set LASTNAME = ?, FIRSTNAME = ?, DEPTNO = ?, SEQNO = ?, MGRNO = ? where EMPNO = ?"; PreparedStatement sqlUpdate = ivTcb.getPreparedStatement( tx, conn, "employee", sql ); EmployeeRecord emp = (EmployeeRecord) value; sqlUpdate.setString(1, emp.getLastName()); sqlUpdate.setString(2, emp.getFirstName()); sqlUpdate.setString(3, emp.getDepartmentName()); sqlUpdate.setLong(4, emp.getSequenceNumber()); sqlUpdate.setInt(5, emp.getManagerNumber()); sqlUpdate.setInt(6, key); sqlUpdate.addBatch(); }

After the batchUpdate loop has built all of the prepared statements, it calls the getPreparedStatementCollection method. This method can be implemented as follows:
private Collection getPreparedStatementCollection( TxID tx, Connection conn ) { return ( ivTcb.getPreparedStatementCollection( tx, conn, "employee" ) ); }

When the application invokes the commit method on the Session, the Session code calls the commit method on the TransactionCallback method after it has pushed all the changes made by the transaction out to the Loader for each map that was changed by the transaction. Because all of the Loaders used the MyTransactionCallback method to get any connection and prepared statements they needed, the TransactionCallback method knows which connection to use to request that the back end commits the changes. So, extending the TransactionCallback interface with methods that are needed by each of the Loaders has the following advantages: v The TransactionCallback object encapsulates the use of TxID slots for transaction-scoped data, and the Loader does not require information about the TxID slots. The Loader only needs to know about the methods that are added to TransactionCallback using the MyTransactionCallback interface for the supporting functions needed by the Loader. v The TransactionCallback object can ensure that connection sharing occurs between each Loader that connects to the same backend so that a two phase commit protocol can be avoided.
Chapter 5. ObjectGrid application programming interface overview

101

v The TransactionCallback object can ensure that connecting to the backend is driven to completion through a commit or rollback invoked on the connection when appropriate. v TransactionCallback can ensure that the cleanup of database resources occurs when a transaction completes. v TransactionCallback can hide if it is obtaining a managed connection from a managed environment such as WebSphere Application Server or some other Java 2 Platform, Enterprise Edition (J2EE) compliant application server. This advantage allows the same Loader code to be used in both a managed and unmanaged environments. Only the TransactionCallback plug-in must be changed. For detailed information about how the TransactionCallback implementation uses the TxID slots for transaction-scoped data, see TransactionCallback plug-in on page 108.

OptimisticCallback
As mentioned earlier, the Loader might decide to use an optimistic approach for concurrency control. If that is the case, the buildBatchSQLUpdate method example needs to be modified slightly for implementing an optimistic approach. Several possible ways exist for using an optimistic approach. A typical way is to have either a timestamp column or sequence number counter column for versioning each update of the row. Assume that the employee table has a sequence number column that increments each time the row is updated. You then modify the signature of the buildBatchSQLUpdate method so that it is passed the LogElement object instead of the key and value pair. It also needs to use the OptimisticCallback object that is plugged into the backing map for getting both the initial version object and for updating the version object. The following is an example of a modified buildBatchSQLUpdate method that uses the ivOcb instance variable that was initialized as described in the preloadMap section:
private void buildBatchSQLUpdate( TxID tx, LogElement le, Connection conn )throws SQLException, LoaderException { // Get the initial version object when this map entry was last read // or updated in the database. Employee emp = (Employee) le.getCurrentValue(); long initialVersion = ((Long) le.getVersionedValue()).longValue(); // Have OptimisticCallback update the Employee object with a new version value. ivOcb.updateVersionedObjectForValue( emp ); // Get the version object from the updated Employee for the SQL update //operation. Long currentVersion = (Long)ivOcb.getVersionedObjectForValue( emp ); long nextVersion = currentVersion.longValue(); // Update the LogElement with the updated version object. le.setVersionedValue( currentVersion ); // Now build SQL update that includes the version object in where clause // for optimistic checking. String sql = "update EMPLOYEE set LASTNAME = ?, FIRSTNAME = ?, DEPTNO = ?,SEQNO = ?, MGRNO = ? where EMPNO = ? and SEQNO = ?"; PreparedStatement sqlUpdate = ivTcb.getPreparedStatement( tx, conn, "employee", sql ); sqlUpdate.setString(1, emp.getLastName()); sqlUpdate.setString(2, emp.getFirstName()); sqlUpdate.setString(3, emp.getDepartmentName()); sqlUpdate.setLong(4, nextVersion ); sqlUpdate.setInt(5, emp.getManagerNumber());

102

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

sqlUpdate.setInt(6, key); sqlUpdate.setLong(7, initialVersion); sqlUpdate.addBatch(); }

The example shows that the LogElement is used to obtain the initial version value. When the transaction first accesses the map entry, a LogElement is created with the initial employee object obtained from the map. The employee object is also passed to the getVersionedObjectForValue method on the OptimisticCallback interface and the result in saved in the LogElement. This processing happens before an application is given a reference to the Employee object and has a chance to call some method on it that changes the state of the Employee object. When the batchUpdate method is called on the Loader interface and the LogElement type indicates that the map entry was updated by the application, the code calls the updateVersionedObjectForValue method on the OptimisticCallback to cause a new version object to be generated. The LogElement is updated with this new version object by the Loader calling the setVersionedObject method on the LogElement. This step is necessary because the application might have called the flush method on the map instead of the commit method on the Session. It is possible for the Loader to be called multiple times by a single transaction for the same key. The LogElement must be updated with the new version object each time the row is updated in the employee table. Now that the Loader has both the initial version object and the next version object, it can run an SQL update statement that sets the SEQNO column to the next version object value and uses the initial version object value in the where clause. This approach is sometimes referred to as being an overqualified update statement. The use of the overqualified update statement allows the relational database to verify that the row was not changed by some other transaction in between the time that this transaction read the data from the database and the time that this transaction updates the database. If another transaction modified the row, then the count array that is returned by the batch update indicates that zero rows were updated for this key. The Loader is responsible for verifying that the SQL update operation did in fact update the row. If it does not, the Loader displays a com.ibm.websphere.objectgrid.plugins.OptimisticCollisionException exception to inform the Session that the batchUpdate method failed due to more than one concurrent transaction trying to update the same row in the database table. This exception causes the Session to roll back and the application must retry the entire transaction. The rationale is that the retry will be successful, which is why this approach is called optimistic. The optimistic approach does in fact perform better if data is infrequently changed or concurrent transactions rarely try to update the same row. Other ways for a Loader to implement an optimistic approach include: v No timestamp or sequence number column exists. In this case, the getVersionObjectForValue method on the OptimisticCallback interface simply returns the value object itself as the version. With this approach, the Loader needs to build a where clause that includes each of the fields of the initial version object. This approach is not very efficient, and not all column types are eligible to be used in the where clause of an overqualified SQL update statement. This approach is typically not used. v No timestamp or sequence number column exists. However, unlike the prior approach, the where clause only contains the value fields that were modified by the transaction. One way to detect which fields are modified is to set the copy mode on the backing map to be CopyMode.COPY_ON_WRITE mode. This copy mode
Chapter 5. ObjectGrid application programming interface overview

103

requires that a value interface to be passed to the setCopyMode method on the BackingMap interface. The BackingMap creates dynamic proxy objects that implement the provided value interface. With this copy mode, the Loader can cast each value to a com.ibm.websphere.objectgrid.plugins.ValueProxyInfo object. The ValueProxyInfo interface has a method that allows the Loader to obtain the List of attribute names that were changed by the transaction. This method enables the Loader to call the get methods on the value interface for the attribute names to obtain the changed data and to build an SQL update statement that only sets the changed attributes. The where clause can now be built to have the primary key column plus each of the changed attribute columns. This approach is more efficient than the prior approach, but it requires more code to be written in the Loader and leads to the possibility that the prepared statement cache needs to be larger to handle the different permutations. However, if transactions typically only modify a few of the attributes, this limitation might not be a problem. v Some relational databases might have an API to assist in automatically maintaining column data that is useful for optimistic versioning. Consult your database documentation to determine if this possibility exists.

ObjectTransformer plug-in
Use the ObjectTransformer plug-in when you require high performance. If you see performance issues with CPU usage, add an ObjectTransformer plug-in to each map. If you do not provide an ObjectTransformer plug-in, up to 60-70% of the total CPU time is spent serializing and copying entries.

Purpose
The purpose of the ObjectTransformer plug-in is to allow applications to provide custom methods for the following operations: v Serialize or deserialize the key for an entry v Serialize or deserialize the value for an entry v Copy a key or value for an entry If no ObjectTransformer plug-in is provided, you must be able to serialize the keys and values because the ObjectGrid uses a serialize and deserialize sequence to copy the objects. This method is expensive, so use an ObjectTransformer plug-in when performance is critical. The copying occurs when an application looks up an object in a transaction for the first time. You can avoid this copying by setting the copy mode of the Map to NO_COPY or reduce the copying by setting the copy mode to COPY_ON_READ. Optimize the copy operation when needed by the application by providing a custom copy method on this plug-in. Such a plug-in can reduce the copy overhead from 6570% to 2/3% of total CPU time. Object serialization is also used directly when the ObjectGrid is running in distributed mode. The LogSequence uses the ObjectTransformer plug-in to help it serialize keys and values before transmitting the changes to peers in the ObjectGrid. You must take care when providing a custom serialization method instead of using the built-in JDK serialization. Object versioning is a complex issue and you might encounter problems with version compatibility if you do not ensure that your custom methods are designed for versioning. The following list details how the ObjectGrid tries to serialize both keys and values:

104

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v If a custom ObjectTransformer plug-in is written and plugged in, ObjectGrid calls methods in the ObjectTransformer methods to serialize keys and values and get copies of object keys and values.. v If a custom ObjectTransformer plug-in is not used, ObjectGrid serializes and deserializes according to the default. If the default is used, each object is implemented as externalizable or is implemented as serializable. If the object supports the Externalizable interface, the writeExternal method is called. Objects that are implemented as externalizable lead to better performance. If the object does not support the Externalizable interface and does implement Serializable, the object is saved using the ObjectOutputStream method.

ObjectTransformer interface
See the API documentation for more information about the ObjectTransformer interface. The ObjectTransformer interface contains the following methods that serialize and deserialize keys or values and copy keys or values:
public interface ObjectTransformer { void serializeKey(Object key, ObjectOutputStream stream) throws IOException; void serializeValue(Object value, ObjectOutputStream stream) throws IOException; Object inflateKey(ObjectInputStream stream) throws IOException, ClassNotFoundException; Object inflateValue(ObjectInputStream stream) throws IOException, ClassNotFoundException; Object copyKey(Object value); Object copyValue(Object value); }

ObjectTransformer interface usage


You can use the ObjectTransformer interface in the following situations: v non-serializable object v serializable object but improve serialization performance v key or value copy In the following example, ObjectGrid is used to store the Stock class:
/** * Stock object for ObjectGrid demo * * */ public class Stock implements Cloneable { String ticket; double price; String company; String description; int serialNumber; long lastTransactionTime; /** * @return Returns the description. */ public String getDescription() { return description; } /** * @param description The description to set.
Chapter 5. ObjectGrid application programming interface overview

105

*/ public void setDescription(String description) { this.description = description; } /** * @return Returns the lastTransactionTime. */ public long getLastTransactionTime() { return lastTransactionTime; } /** * @param lastTransactionTime The lastTransactionTime to set. */ public void setLastTransactionTime(long lastTransactionTime) { this.lastTransactionTime = lastTransactionTime; } /** * @return Returns the price. */ public double getPrice() { return price; } /** * @param price The price to set. */ public void setPrice(double price) { this.price = price; } /** * @return Returns the serialNumber. */ public int getSerialNumber() { return serialNumber; } /** * @param serialNumber The serialNumber to set. */ public void setSerialNumber(int serialNumber) { this.serialNumber = serialNumber; } /** * @return Returns the ticket. */ public String getTicket() { return ticket; } /** * @param ticket The ticket to set. */ public void setTicket(String ticket) { this.ticket = ticket; } /** * @return Returns the company. */ public String getCompany() { return company; } /** * @param company The company to set. */ public void setCompany(String company) { this.company = company; } //clone public Object clone() throws CloneNotSupportedException

106

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

{ return super.clone(); } }

You can write a custom object transformer class for the Stock class:
/** * Custom implementation of ObjectGrid ObjectTransformer for stock object * */ public class MyStockObjectTransformer implements ObjectTransformer { /* (nonJavadoc) * @see * com.ibm.websphere.objectgrid.plugins.ObjectTransformer#serializeKey * (java.lang.Object, * java.io.ObjectOutputStream) */ public void serializeKey(Object key, ObjectOutputStream stream) throws IOException { String ticket= (String) key; stream.writeUTF(ticket); } /* (nonJavadoc) * @see com.ibm.websphere.objectgrid.plugins. ObjectTransformer#serializeValue(java.lang.Object, java.io.ObjectOutputStream) */ public void serializeValue(Object value, ObjectOutputStream stream) throws IOException { Stock stock= (Stock) value; stream.writeUTF(stock.getTicket()); stream.writeUTF(stock.getCompany()); stream.writeUTF(stock.getDescription()); stream.writeDouble(stock.getPrice()); stream.writeLong(stock.getLastTransactionTime()); stream.writeInt(stock.getSerialNumber()); } /* (nonJavadoc) * @see com.ibm.websphere.objectgrid.plugins. ObjectTransformer#inflateKey(java.io.ObjectInputStream) */ public Object inflateKey(ObjectInputStream stream) throws IOException, ClassNotFoundException { String ticket=stream.readUTF(); return ticket; } /* (nonJavadoc) * @see com.ibm.websphere.objectgrid.plugins. ObjectTransformer#inflateValue(java.io.ObjectInputStream) */ public Object inflateValue(ObjectInputStream stream) throws IOException, ClassNotFoundException { Stock stock=new Stock(); stock.setTicket(stream.readUTF()); stock.setCompany(stream.readUTF()); stock.setDescription(stream.readUTF()); stock.setPrice(stream.readDouble()); stock.setLastTransactionTime(stream.readLong()); stock.setSerialNumber(stream.readInt()); return stock; }

Chapter 5. ObjectGrid application programming interface overview

107

/* (nonJavadoc) * @see com.ibm.websphere.objectgrid.plugins. ObjectTransformer#copyValue(java.lang.Object) */ public Object copyValue(Object value) { Stock stock = (Stock) value; try{ return stock.clone(); } catch (CloneNotSupportedException e) { //streamize one } } /* (nonJavadoc) * @see com.ibm.websphere.objectgrid.plugins. ObjectTransformer#copyKey(java.lang.Object) */ public Object copyKey(Object key) { String ticket=(String) key; String ticketCopy= new String (ticket); return ticketCopy; } }

Then, plug in this custom MyStockObjectTransformer class into the BackingMap:


ObjectGridManager ogf=ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid og = ogf.getObjectGrid("NYSE"); BackingMap bm = og.defineMap("NYSEStocks"); MyStockObjectTransformer ot = new MyStockObjectTransformer(); bm.setObjectTransformer(ot);

TransactionCallback plug-in
An application usually plugs in both a TransactionCallback plug-in and a Loader as a pair. The Loader is responsible for fetching data from the back end as well as applying changes to the back end. This fetching and flushing usually take place within the context of an ObjectGrid transaction. The TransactionCallback plug-in has the following responsibilities: v Reserves slots for a transaction-specific state that is needed for the transaction and the Loader v Translates or maps an ObjectGrid transaction to a platform transaction v Sets up the per transaction state when the ObjectGrid begins a transaction v Commits the transaction when the ObjectGrid transaction commits v Rolls back the transaction when the ObjectGrid transaction rolls back The ObjectGrid is not a XA transaction coordinator. The ObjectGrid relies on the platform to provide that capability. The ObjectGrid begin, commit, and rollback methods that are presented on a Session are lifecycle calls. The TransactionCallback plug-in must receive these events and make the platform provide the transactional capability for the resources used by the Loaders. This topic examines various scenarios and discusses how the TransactionCallback plug-in can be written to work for these scenarios.

108

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

TransactionCallback plug-in overview


The TransactionCallback plug-in is a POJO that implements the TransactionCallback interface. The TransactionCallback interface looks like the following sample:
public interface TransactionCallback { void initialize(ObjectGrid objectGrid) throws TransactionCallbackException; void begin(TxID id) throws TransactionCallbackException; void commit(TxID id) throws TransactionCallbackException; void rollback(TxID id) throws TransactionCallbackException; boolean isExternalTransactionActive(Session session); }

initialize method The initialize method is called when the ObjectGrid is initialized. The callback reserves slots for the TxID object that it needs. Usually, it reserves a slot for each piece of the state or Object that it wants to create in the begin method when a transaction starts. For example, you want to use a Persistence Manager with the ObjectGrid as a Loader. Assuming that this persistence manager has session and transaction state objects, the TransactionCallback would obtain a session and transaction and keep references to those two objects in slots on the TxID. In this case, the initialize method looks like the following sample:
/** * This is called when the grid first initializes. Well just * reserve our slots in the TxID. */ public void initialize(ObjectGrid objectGrid) throws TransactionCallbackException { // reserve a slot for the persistence manager transaction TXslot = objectGrid.reserveSlot(TxID.SLOT_NAME); // reserve a slot for the persistence manager session SessionSlot = objectGrid.reserveSlot(TxID.SLOT_NAME); }

A TxID has slots. The slots are entries on an ArrayList array. Plug-ins can reserve an entry in the ArrayList array by calling the ObjectGrid.reserveSlot method and indicating that it wants a slot on the TxID object. The method then returns the next entry index to the application. The application can then store information in this slot. The next methods demonstrate this technique. begin method The ObjectGrid calls this method when it starts a new transaction. The plug-in maps this event to a real transaction that the Loaders can then use for the get and update method calls that arrive before the commit method is called. Following is an example begin method that maps an ObjectGrid begin to a persistence manager transaction begin:
/** * This is called when the grid starts a new transaction. We just create a * persistence manager transaction and call begin on it. We then store * the transaction in the TxID slot so we can get it again later * without needing ThreadLocal etc. */ public void begin(TxID id) throws TransactionCallbackException { Session PMsession = getPMcurrentSession();

Chapter 5. ObjectGrid application programming interface overview

109

Transaction tx = PMsession.beginTransaction(); id.putSlot(TXslot, tx); id.putSlot(SessionSlot, PMsession); }

This sample relies on the fact that the initialize method has reserved two slots on the TxID object. One slot is for the persistence manager session and the other slot is for the persistence manager Transaction. The begin method calls the persistence manager to get a session, stores it in the indexed SessionSlot slot, and creates a Transaction on the session and stores a reference to this transaction using the indexed TXSlot slot. commit method The commit method is called when an ObjectGrid transaction is committing. All Loaders have already been flushed. The plug-in responsibility is to communicate this commit event to the platform.
/** * This is called when the grid wants to commit a transaction. * We just pass it on to persistence manager. */ public void commit(TxID id) throws TransactionCallbackException { Transaction tx = (Transaction)id.getSlot(TXslot); tx.commit(); }

The method looks up the persistence manager transaction stored in the slot and then calls the commit method. rollback method This method is called when an ObjectGrid transaction wants to roll back a transaction. The plug-in forwards this to the platform transaction manager. Following is the code snippet:
/** * This is called when the grid wants to rollback a transaction. * We just pass it on to persistence manager. */ public void rollback(TxID id) throws TransactionCallbackException { Transaction tx = (Transaction)id.getSlot(TXslot); tx.rollback(); }

This method is very similar to the commit method. It gets a reference to the persistence manager transaction from a slot and then invokes the rollback method. isExternalTransactionActive method An ObjectGrid session normally works in autocommit mode or in transaction mode. Autocommit mode means an implicit transaction is created around every method call to the ObjectMap instances for the session. If no transaction is active and an application makes a call on an ObjectMap method, the framework calls this method on the TransactionCallback plug-in to check if there is a proper transaction active. If this method returns true then the framework does an automatic begin otherwise, it does autocommit. This method allows the ObjectGrid to be integrated in environments where the application invokes begin, commit, or rollback methods on the platform APIs instead of the ObjectGrid APIs.

110

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Scenario: Simple Java database connectivity (JDBC)-based Java 2 Platform, Standard Edition (J2SE) environment
This example uses a J2SE environment where the application has a JDBC-based Loader. Two Maps exist, each with a Loader that backs each Map by a different table in the database. The TransactionCallback plug-in gets a JDBC connection and then invokes the begin, commit, and rollback methods on the connection. Following is the sample TransactionCallback implementation:
public class JDBCTCB implements TransactionCallback { DataSource datasource; int connectionSlot; public JDBCTCB(DataSource ds) { datasource = ds; } public void initialize(ObjectGrid objectGrid) throws TransactionCallbackException { connectionSlot = objectGrid.reserveSlot(TxID.SLOT_NAME); } public void begin(TxID id) throws TransactionCallbackException { try { Connection conn = datasource.getConnection(); conn.setAutoCommit(false); id.putSlot(connectionSlot, conn); } catch(SQLException e) { throw new TransactionCallbackException("Cannot start transaction", e); } } public void commit(TxID id) throws TransactionCallbackException { Connection conn = null; try { conn = (Connection)id.getSlot(connectionSlot); conn.commit(); conn.close(); } catch(SQLException e) { throw new TransactionCallbackException("Cannot commit transaction", e); } finally { if (conn!=null) { try { conn.close(); } catch (SQLException closeE) { } } } } public void rollback(TxID id) throws TransactionCallbackException { Connection conn = null; try { conn = (Connection)id.getSlot(connectionSlot); conn.rollback(); conn.close();
Chapter 5. ObjectGrid application programming interface overview

111

} catch(SQLException e) { throw new TransactionCallbackException("Cannot rollback transaction", e); } finally { if (conn!=null) { try { conn.close(); } catch (SQLException closeE) { } } } } public boolean isExternalTransactionActive(Session session) { return false; } public int getConnectionSlot() { return connectionSlot; } }

This example shows a TransactionCallback plug-in that converts the ObjectGrid transaction events to a JDBC connection. When the plug-in is initialized, it reserves a single slot to keep a JDBC connection reference. The begin method then obtains a JDBC connection for the new transaction, turns auto commit off, and then stores a reference to the connection in the TxID slot. The commit and rollback methods retrieve the connection from the TxID slot and call the appropriate method on the connection. The isExternalTransaction method always returns false, indicating that the application must use the ObjectGrid transaction APIs explicitly to control transactions. A Loader that is paired with this plug-in obtains the JDBC connection from the TxID. A Loader looks like the following example:
public class JDBCLoader implements Loader { JDBCTCB tcb; public void preloadMap(Session session, BackingMap backingMap) throws LoaderException { tcb = (JDBCTCB)session.getObjectGrid().getTransactionCallback(); } public List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException { Connection conn = (Connection)txid.getSlot(tcb.getConnectionSlot()); // implement get here return null; } public void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException { Connection conn = (Connection)txid.getSlot(tcb.getConnectionSlot()); // TODO implement batch update here } }

The Loader obtains a reference to the JDBCTCB instance when the initialize method is called. It then obtains the Connection obtained by the JDBCTCB when it is required in the get and batchUpdate methods. TransactionCallback implementations and Loaders are typically written in pairs that cooperate with each other. The TransactionCallback implementation handles the Transaction and

112

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

stores objects needed by the Loaders in slots in the TxID. The Loaders then implement get and batchUpdate methods in the context of a transaction managed by the TransactionCallback using resources obtained by the TCB usually.

Scenario: Servlet engine environment


In this scenario, the ObjectGrid is using a JDBC-based Loader but in a managed servlet engine. The container expects us to use the UserTransaction method to begin and commit transactions. This is slightly different from the J2SE case because storing a reference to the JDBC connection in a TxID slot is not necessary. The container manages the JDBC connection. When a container transaction is active, a connection that is looked up using a data source results in the same connection each time because the container remembers which connections are used by this transaction and returns the same connection each time the DataSource.getConnection method is called. Assume that the data source reference is configured as Shareable in the following example:
public class ManagedJDBCTCB implements TransactionCallback { UserTransaction tx; public void initialize(ObjectGrid objectGrid) throws TransactionCallbackException { try { InitialContext ic = new InitialContext(); tx = (UserTransaction)ic.lookup("java:comp/UserTransaction"); } catch(NamingException e) { throw new TransactionCallbackException("Cannot find UserTransaction", e); } } public void begin(TxID id) throws TransactionCallbackException { try { tx.begin(); } catch(SystemException e) { throw new TransactionCallbackException("Cannot begin tx", e); } catch(NotSupportedException e) { throw new TransactionCallbackException("Cannot begin tx", e); } } public void commit(TxID id) throws TransactionCallbackException { try { tx.commit(); } catch(SystemException e) { throw new TransactionCallbackException("Cannot commit tx", e); } catch(HeuristicMixedException e) { throw new TransactionCallbackException("Cannot commit tx", e); } catch(RollbackException e) { throw new TransactionCallbackException("Cannot commit tx", e); }
Chapter 5. ObjectGrid application programming interface overview

113

catch(HeuristicRollbackException e) { throw new TransactionCallbackException("Cannot commit tx", e); } } public void rollback(TxID id) throws TransactionCallbackException { try { tx.rollback(); } catch(SystemException e) { throw new TransactionCallbackException("Cannot commit tx", e); } } public boolean isExternalTransactionActive(Session session) { return false; } }

This example obtains a reference to the UserTransaction method in the initialize method and then maps begin, commit, and rollback on to the appropriate UserTransaction methods. Slots are not needed because the container verifies that the correct connection information is retrieved for this transaction. Following is the JDBC Loader that works with this TransactionCallback implementation:
public class ManagedJDBCLoader implements Loader { DataSource myDataSource; ManagedJDBCLoader(DataSource ds) { myDataSource = ds; } public void preloadMap(Session session, BackingMap backingMap) throws LoaderException { } public List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException { try { Connection conn = myDataSource.getConnection(); // TODO implement get here with this connection return null; } catch(SQLException e) { throw new LoaderException("Cannot get objects", e); } } public void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException { try { Connection conn = myDataSource.getConnection(); // TODO implement update here using this connection } catch(SQLException e) { throw new LoaderException("Cannot update objects", e); } } }

114

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

This example can be simpler than the basic JDBC version because the container manages the connections and verifies that within the same transaction, the DataSource.getConnection method always returns the same connection when it is called with the same transaction active each time. Do not try to cache the connection in a slot as a result, although the application can cache the connection if it chooses to.

OptimisticCallback interface
You can provide a pluggable optimistic callback object that implements the com.ibm.websphere.objectgrid.plugins.OptimisticCallback interface.

Purpose
The OptimisticCallback interface is used to provide optimistic comparison operations for the values of a map. An OptimisticCallback is required when the optimistic lock strategy is being used as described in Optimistic locking on page 64. ObjectGrid provides a default OptimisticCallback implementation. However, usually the application must plug in its own implementation of the OptimisticCallback interface.

Plug in an application-provided OptimisticCallback object


The following example demonstrates how an application can plug in an OptimisticCallback object for the employee backing map in the grid1 ObjectGrid instance:
import com.ibm.websphere.objectgrid.ObjectGridManagerFactory; import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGrid; import com.ibm.websphere.objectgrid.BackingMap; ObjectGridManager ogManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid og = ogManager.createObjectGrid( "grid1" ); BackingMap bm = dg.defineMap("employees"); EmployeeOptimisticCallbackImpl cb = new EmployeeOptimisticCallbackImpl(); bm.setOptimisticCallback( cb );

The EmployeeOptimisticCallbackImpl object in the preceding example must implement the OptimisticCallback interface. The application can also use an XML file to plug in its OptimisticCallback object as shown in the following example:
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="grid1"> <backingMap name="employees" pluginCollectionRef="employees" lockStrategy="OPTIMISTIC" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="employees"> <bean id="OptimisticCallback" className="com.xyz.EmployeeOptimisticCallbackImpl" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

Chapter 5. ObjectGrid application programming interface overview

115

Default implementation
The ObjectGrid framework provides a default implementation of the OptimisticCallback interface that is used if the application does not plug in an application-provided OptimisticCallback object, as demonstrated in the previous section. The default implementation always returns the special value of NULL_OPTIMISTIC_VERSION as the version object for the value and never updates the version object. This action makes optimistic comparison a no operation function. In most cases, you do not want the no operation function to occur when you are using the optimistic locking strategy. Your applications must implement the OptimisticCallback interface and plug in their own OptimisticCallback implementations so that the default implementation is not used. However, at least one scenario exists where the default provided OptimisticCallback implementation is useful. Consider the following situation: v A loader is plugged in for the backing map. v The loader knows how to perform the optimistic comparison without assistance from an OptimisticCallback plug-in. How can the Loader to know how to deal with optimistic versioning without assistance from an OptimisticCallback object? The Loader has knowledge of the value class object and knows which field of the value object is used as an optimistic versioning value. For example, suppose the following interface is used for the value object for the employees map:
public interface Employee { // Sequential sequence number used for optimistic versioning. public long getSequenceNumber(); public void setSequenceNumber(long newSequenceNumber); // Other get/set methods for other fields of Employee object. }

In this case, the Loader knows that it can use the getSequenceNumber method to get the current version information for an Employee value object. It increments the returned value to generate a new version number before to updating the persistent storage with the new Employee value. For a Java database connectivity (JDBC) Loader, the current sequence number in the where clause of an overqualified SQL update statement is used, and it uses the new generated sequence number to set the sequence number column to the new sequence number value. Another possibility is the Loader makes use of some backend-provided function that automatically updates a hidden column that can be used for optimistic versioning. In some cases, a stored procedure or trigger can possibly be used to help maintain a column that holds versioning information. If the Loader is using one of these techniques for maintaining optimistic versioning information, then the application does not need to provide an OptimisticCallback implementation. The default OptimisticCallback is usable in this case because the Loader is able to handle optimistic versioning without any assistance from an OptimisticCallback object.

Implement the OptimisticCallback interface


The OptimisticCallback interface contains the following methods and special values:
public interface OptimisticCallback { final static Byte NULL_OPTIMISTIC_VERSION; Object getVersionedObjectForValue(Object value); void updateVersionedObjectForValue(Object value); void serializeVersionedValue(Object versionedValue,

116

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

ObjectOutputStream stream) throws IOException; Object inflateVersionedValue(ObjectInputStream stream) throws IOException, ClassNotFoundException; }

The following list provides a description or consideration for each of the methods in the OptimisticCallback interface: NULL_OPTIMISTIC_VERSION This special value is returned by getVersionedObjectForValue method if the default OptimisticCallback implementation is used instead of an application-provided OptimisticCallback implementation. getVersionedObjectForValue method This method might return a copy of the value or it might return an attribute of the value that can be used for versioning purposes. This method is called whenever an object is associated with a transaction. When no Loader is plugged into a backing map, the backing map uses this value at commit time to perform an optimistic version comparison. The optimistic version comparison is used by the backing map to ensure that the version has not changed since this transaction first accessed the map entry that was modified by this transaction. If another transaction had already modified the version for this map entry, the version comparison fails and the backing map displays an OptimisticCollisionException exception to force rollback of the transaction. If a Loader is plugged in, the backing map does not use the optimistic versioning information. Instead, the Loader is responsible for performing the optimistic versioning comparison and updating the versioning information when necessary. The Loader typically gets the initial versioning object from the LogElement passed to the Loaders batchUpdate method, which is called when a flush operation occurs or a transaction is committed. The following code shows the implementation used by the EmployeeOptimisticCallbackImpl object:
public Object getVersionedObjectForValue(Object value) { if (value == null) { return null; } else { Employee emp = (Employee) value; return new Long( emp.getSequenceNumber() ); } }

As demonstrated in the previous example, the sequenceNumber attribute is returned in a java.lang.Long object as expected by the Loader, which implies that the same person that wrote the Loader either wrote the EmployeeOptimisticCallbackImpl implementation or worked closely with the person that implemented the EmployeeOptimisticCallbackImpl - for example, agreed on the value returned by the getVersionedObjectForValue method. As previously described, the default OptimisticCallback returns the special value NULL_OPTIMISTIC_VERSION as the version object. updateVersionedObjectForValue method This method is called whenever a transaction has updated a value and a
Chapter 5. ObjectGrid application programming interface overview

117

new versioned object is needed. If the getVersionedObjectForValue returns an attribute of the value, this method typically updates the attribute value with a new version object. If getVersionedObjectForValue returns a copy of the value, this method typically would do nothing. The default OptimisticCallback does nothing since the default implementation of getVersionedObjectForValue always returns the special value NULL_OPTIMISTIC_VERSION as the version object. The following shows the implementation used by the EmployeeOptimisticCallbackImpl object that is used in the OptimisticCallback section:
public void updateVersionedObjectForValue(Object value) { if ( value != null ) { Employee emp = (Employee) value; long next = emp.getSequenceNumber() + 1; emp.updateSequenceNumber( next ); } }

As demonstrated in the previous example, the sequenceNumber attribute is incremented by one so that the next time the getVersionedObjectForValue method is called, the java.lang.Long value that is returned has a long value that is the original sequence number value plus one, for example, is the next version value for this employee instance. Again, this example implies that the same person that wrote the Loader either wrote EmployeeOptimisticCallbackImpl or worked closely with the person that implemented the EmployeeOptimisticCallbackImpl. serializeVersionedValue method This method writes the versioned value to the specified stream. Depending on the implementation, the versioned value can be used to identify optimistic update collisions. In some implementations, the versioned value is a copy of the original value. Other implementations might have a sequence number or some other object to indicate the version of the value. Since the actual implementation is unknown, this method is provided to perform the proper serialization. The default implementation does a writeObject call. inflateVersionedValue method This method takes the serialized version of the versioned value and returns the actual versioned value object. Depending on the implementation, the versioned value can be used to identify optimistic update collisions. In some implementations, the versioned value is a copy of the original value. Other implementations might have a sequence number or some other object to indicate the version of the value. Because the actual implementation is unknown, this method is provided to perform the proper deserialization. The default implementation does a readObject.

ObjectGrid configuration
You can configure ObjectGrid programmatically or with XML. The ObjectGridManager interface is the entry point for both means of configuration. Several methods exist on the ObjectGridManager interface that can be used to create an ObjectGrid. For a complete description of each method, see the ObjectGridManager interface topic. The following topics describe the methods for configuring the ObjectGrid:

118

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v Basic ObjectGrid configuration discusses how to create a very simple XML file with one ObjectGrid and one BackingMap defined. v Complete ObjectGrid configuration defines each element and attribute of the XML file and discusses how to achieve the same result as the XML file programmatically. v Mixed mode ObjectGrid configuration describes how to use a combination of XML and programmatic configuration methods.

Basic ObjectGrid configuration


This topic demonstrates how to create a very simple XML file, the bookstore.xml file, with one ObjectGrid and one BackingMap defined. The first few lines of the file are the required header for each ObjectGrid XML file. The following XML defines the bookstore ObjectGrid with the book BackingMap: bookstore.xml
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" /> </objectGrid> </objectGrids> </objectGridConfig>

The XML file is fed to the ObjectGridManager interface to create an ObjectGrid instance based on the file. The following code snippet validates the bookstore.xml file against the XML schema, and creates an ObjectGrid named bookstore. The newly created ObjectGrid instance is not cached.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", new URL("file:etc/test/bookstore.xml"), true, false);

The following code accomplishes the same task without XML. Use this code to programmatically define a BackingMap on an ObjectGrid. This code creates the book BackingMap on the bookstoreGrid ObjectGrid:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid ("bookstore", false); BackingMap bookMap = bookstoreGrid.defineMap("book");

Complete ObjectGrid configuration


This topic is a complete guide to configuring an ObjectGrid. Each element and attribute of the XML file is defined. Sample XML files are given, along with code that accomplishes the same task programmatically. The following XML file, sample1.xml, is referred to throughout this topic. The elements and attributes of this file are described in detail following this example. sample1.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd"
Chapter 5. ObjectGrid application programming interface overview

119

xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <bean id="ObjectGridEventListener" classname="com.company.organization.MyObjectGridEventListener" /> <backingMap name="books" pluginCollectionRef="collection1" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="collection1"> <bean id="Evictor" classname="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor"> <property name="maxSize" type="int" value="321" /> </bean> </backingMapPluginCollection> </objectGridConfig>

objectGridConfig element
Number of occurrences: one Child elements: objectGrids element and backingMapPluginCollections element The objectGridConfig element is the top level element of the XML file. It must be written in the XML document as shown in the preceding example. This element sets up the namespace of the file and the schema location. The schema is defined in the objectGrid.xsd file. ObjectGrid looks for this file in the root directory of the ObjectGrid jar file.

objectGrids element
Number of occurrences: one Child element: objectGrid element The objectGrids element is a container for all the objectGrid elements. In the sample1.xml file, the objectGrids element contains one objectGrid that has the name bookstore.

objectGrid element
Number of occurrences: one or many Child elements: bean element and backingMap element Use the objectGrid element to define an ObjectGrid in an XML file. Each of the attributes on the objectGrid element corresponds to a method on the ObjectGrid interface.
<objectGrid (1) name="objectGridName" (2) securityEnabled="true|false" (3) authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS| AUTHORIZATION_MECHANISM_CUSTOM" (4) permissionCheckPeriod="permission check period" />

Attributes: 1. name attribute (required): Specifies the name that is assigned to the ObjectGrid. If this attribute is missing, the XML validation fails.

120

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

2. securityEnabled attribute (optional, default is false): Setting this attribute to true enables security for the ObjectGrid. By default, security is disabled. 3. authroizationMechanism attribute (optional, defaults to AUTHORIZATION_MECAHNISM_JAAS): Sets the authorization mechanism for this ObjectGrid. This attribute can be set to one of two values: AUTHORIZATION_MECHANISM_JAAS or AUTHORIZATION_MECHANISM_CUSTOM. Set to AUTHORIZATION_MECHANISM_CUSTOM when using a custom MapAuthorization plug-in. This setting takes effect if the securityEnabled attribute is set to true. 4. permissionCheckPeriod (optional, defaults to 0): Specifies an integer value in seconds that indicates how often to check the permission that is used to allow a client access. If the attribute value is 0, then every get, put, update, remove, or evict method call asksthe authorization mechanism, either JAAS authorization or custom authorization, to check if the current subject has permission. A value greater than 0 indicates the number of seconds to cache a set of permissions before returning to the authorization mechanism to refresh. This setting take seffect if the securityEnabled attribute is set to true. The following example XML file, the objectGridAttr.xml file, demonstrates one way to configure the attributes of an objectGrid. In this example, security is enabled, authorization mechanism is set to JAAS, and the permission check period is set to 45 seconds. objectGridAttr.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore" securityEnabled="true" authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS" permissionCheckPeriod="45"> </objectGrid> </objectGrids> </objectGridConfig>

The following code demonstrates the programmatic approach to achieve the same configuration as the objectGridAttr.xml file in the previous example.
ObjectGridManager objectGridManager = ObjectGridManagerFactory .getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", false); bookstoreGrid.setSecurityEnabled(); bookstoreGrid.setAuthorizationMechanism( SecurityConstants.AUTHORIZATION_MECHANISM_JAAS); bookstoreGrid.setPermissionCheckPeriod(45);

backingMap element
Number of occurrences: zero to many Child elements: none The backingMap element is used to define a BackingMap on an ObjectGrid. Each of the attributes on the backingMap element corresponds to a method on the BackingMap interface.

Chapter 5. ObjectGrid application programming interface overview

121

<backingMap (1) name="backingMapName" (2) readOnly="true|false" (3) pluginCollectionRef="reference to backingMapPluginCollection" (4) numberOfBuckets="number of buckets" (5) preloadMode="true|false" (6) lockStrategy="OPTIMISTIC|PESSIMISTIC|NONE" (7) numberOfLockBuckets="number of lock buckets" (8) lockTimeout="lock timeout" (9) copyMode="COPY_ON_READ_AND_COMMIT|COPY_ON_READ| COPY_ON_WRITE|NO_COPY" (10) valueInterfaceClassName="value interface class name" (11) copyKey="true|false" (12) nullValuesSupported="true|false" (13) ttlEvictorType="CREATION_TIME|LAST_ACCESS_TIME|NONE" (14) timeToLive="time to live" />

Attributes: 1. name attribute (required): Specifies the name that is assigned to the BackingMap. If this attribute is missing, XML validation fails. 2. readOnly attribute (optional, defaults to false): Setting this attribute to true makes a read-only BackingMap. Setting the attribute to false makes a read-write BackingMap. If a value is not specified, the default of read-write results. 3. pluginCollectionRef attribute (optional): Specifies a reference to a backingMapPluginCollection plug-in. The value of this attribute must match the id attribute of a backingMapCollection plug-in. Validation fails if no matching id exists. This reference is designed to be an easy way to reuse BackingMap plug-ins. 4. numberOfBuckets attribute (optional, defaults to 503): The number of buckets to be used by the BackingMap. The BackingMap implementation uses a hash map for its implementation. If a lot of entries exist in the BackingMap more buckets lead to better performance because the risk of collisions is lower as the number of buckets grows. More buckets also leads to more concurrency. 5. preloadMode attribute (optional, defaults to false): Sets the preload mode if a Loader plug-in is set for this BackingMap. If the attribute is set to true, the Loader.preloadMap(Session, BackingMap) method is invoked asynchronously. Otherwise it blocks running the method when loading data so that the cache is unavailable until preload completes. Preloading occurs during ObjectGrid initialization. 6. lockStrategy attribute (optional, defaults to OPTIMISTIC): Sets the LockStrategy that is used for the BackingMap. The locking strategy determines if the internal ObjectGrid lock manager is used whenever a map entry is accessed by a transaction. This attribute can be set to one of three values: OPTIMISTIC, PESSIMISTIC, or NONE. OPTIMISTIC is typically used for a map that does not have a Loader plug-in, the map is mostly read, and the locking is not provided by persistence manager using the objectGrid as a side cache or by the application. For the optimistic locking strategy, an exclusive lock is obtained on a map entry being inserted, updated, or removed at commit time. The lock ensures version information cannot be changed by another transaction while the transaction being committed is performing an optimistic versioning check. PESSIMISTIC is typically used for a map that does not have a Loader plug-in and locking is not provided by a persistence manager using the objectGrid as a side cache, by a Loader plug-in, or by the application. The pessimistic locking strategy is used when the optimistic approach fails too often because

122

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

update transactions frequently collide on the same map entry. The optimistic approach can fail when the map is not mostly read, or a large number of clients access a small map. NONE indicates that internal LockManager use is not needed because concurrency control is provided outside of the ObjectGrid, either by the persistence manager using ObjectGrid as a side cache, application, or by Loader plug-in that uses database locks to control concurrency. 7. numberOfLockBuckets attribute (optional, defaults to 383): Sets number of lock buckets that are used by the lock manager for this BackingMap. When the lockStrategy attribute is set to OPTIMISTIC or PESSIMISTIC, a lock manager is created for the BackingMap. The lock manager uses a hash map to keep track of entries that are locked by one or more transactions. If a lot of entries exist in the hash map, then more lock buckets lead to better performance because the risk of collisions is lower as the number of buckets grows. More lock buckets also lead to more concurrency. When the lockStrategy attribute is set to NONE, no lock manager is used by this BackingMap. In this case, setting numberOfLockBuckets attribute is not needed. 8. lockTimeout attribute (optional, defaults to 15): Sets the lock timeout that is used by the lock manager for this BackingMap. When the lockStrategy attribute is set to OPTIMISTIC or PESSIMISTIC, a lock manager is created for the BackingMap. To prevent deadlocks from occurring, the lock manager has a default timeout value for waiting for a lock to be granted. If this timeout limit is exceeded, a LockTimeoutException exception occurs. The default value of 15 seconds is sufficient for most applications, but on a heavily loaded system, a timeout might occur when no deadlock exists. In that case, this method can be used to increase the lock timeout value from the default to prevent false timeout exceptions from occurring. When the lock strategy is NONE, no lock manager is used by this BackingMap. In this case, setting the lockTimeout attribute is not needed. 9. copyMode attribute (optional, defaults to COPY_ON_READ_AND_COMMIT): The copyMode attribute determines if a get operation of an entry in the BackingMap returns the actual value, a copy of the value, or a proxy for the value. The copyMode attribute can be set to one of four values: COPY_ON_READ_AND_COMMIT, COPY_ON_READ, COPY_ON_WRITE, or NO_COPY. The COPY_ON_READ_AND_COMMIT mode ensures that an application never has a reference to the value object that is in the BackingMap, and instead the application is always working with a copy of the value that is in the BackingMap. The COPY_ON_READ mode improves performance over the COPY_ON_READ_AND_COMMIT mode by eliminating the copy that occurs when a transaction is committed. To preserve integrity of BackingMap data, the application promises to destroy every reference that it has to an entry after the transaction is committed. This mode results in a ObjectMap.get method returning a copy of the value instead of a reference to the value to ensure that changes that are made by the application to the value does not affect the BackingMap value until the transaction is committed. The COPY_ON_WRITE mode improves performance over the COPY_ON_READ_AND_COMMIT mode by eliminating the copy that occurs when ObjectMap.get method is called for the first time by a transaction for a given key. Instead, the ObjectMap.get method returns a proxy to the value instead of a direct reference to the value object. The proxy ensures that a copy of the value is not made unless the application calls a set method on the value interface.

Chapter 5. ObjectGrid application programming interface overview

123

The NO_COPY mode allows an application to promise that it never modifies a value object that is obtained using an ObjectMap.get method in exchange for performance improvements. If this mode is used, the value is not copied. 10. valueInterfaceClassName attribute (optional): When the copyMode attribute is set to COPY_ON_WRITE, a valueInterfaceClassName attribute is required. It is ignored for all other modes. Copy on write uses a proxy when ObjectMap.get method calls are made. The proxy ensures that a copy of the value is not made unless the application calls a set method on the class that is specified as the valueInterfaceClassName attribute. 11. copyKey attribute (optional, defaults to false): This attribute determines if the key needs to be copied when a map entry is created. Copying the key object allows the application to use the same key object for each ObjectMap operation. Setting to true copies the key object when a map entry is created. 12. nullValuesSupported attribute (optional, defaults to true): Supporting null values means that a null value can be put in a map. If set to true, null values are supported in the ObjectMap, otherwise null values are not supported. If null values are supported, a get operation that returns null could mean that the value is null or that the map does not contain the passed-in key. 13. ttlEvictorType attribute (optional, defaults to NONE): The ttlEvictorType attribute determines how the expiration time of a BackingMap entry is computed. This attribute can be set to one of three values: CREATION_TIME, LAST_ACCESS_TIME, or NONE. NONE indicates that an entry has no expiration time and is allowed to live in the BackingMap until the application explicitly removes or invalidates the entry. CREATION_TIME indicates that an entry expiration time is the sum of the creation time of the entry plus the timeToLive attribute value. LAST_ACCESS_TIME indicates that an entry expiration time is the sum of the last access time of the entry plus the timeToLive attribute value. 14. timeToLive attribute (optional, defaults to 0): The time to live of each map entry, in seconds. The default value of 0 means that the map entry lives forever, or until the application explicitly removes or invalidates the entry. If the attribute is not 0, the TTL evictor is used to evict the map entry based on this value. The following XML file, the backingMapAttr.xml file, demonstrates a sample backingMap configuration. This example makes use of all the optional attributes except the pluginCollectionRef attribute. For an example that shows how to use the pluginCollectionRef, see backingMapPluginCollection element on page 128. backingMapAttr.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" readOnly="true" numberOfBuckets="641" preloadMode="false" lockStrategy="OPTIMISTIC" numberOfLockBuckets="409" lockTimeout="30" copyMode="COPY_ON_WRITE" valueInterfaceClassName= "com.ibm.websphere.samples.objectgrid.CounterValueInterface" copyKey="true" nullValuesSupported="false"

124

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

ttlEvictorType="LAST_ACCESS_TIME" timeToLive="3000" /> </objectGrid> </objectGrids> </objectGridConfig>

The following sample code demonstrates the programmatic approach to achieve the same configuration as the backingMapAttr.xml file in the preceding example:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", false); BackingMap bookMap = bookstoreGrid.defineMap("book"); bookMap.setReadOnly(true); bookMap.setNumberOfBuckets(641); bookMap.setPreloadMode(false); bookMap.setLockStrategy(LockStrategy.OPTIMISTIC); bookMap.setNumberOfLockBuckets(409); bookMap.setLockTimeout(30); // when setting copy mode to COPY_ON_WRITE, a valueInterface class is required bookMap.setCopyMode(CopyMode.COPY_ON_WRITE, com.ibm.websphere.samples.objectgrid.CounterValueInterface.class); bookMap.setCopyKey(true); bookMap.setNullValuesSupported(false); bookMap.setTtlEvictorType(TTLType.LAST_ACCESS_TIME); bookMap.setTimeToLive(3000); // set time to live to 50 minutes

bean element
Number of occurrences (within the objectGrid element): zero to many Number of occurrences (within the the backingMapPluginCollection element): zero to many Child element: property element Use the bean element to define plug-ins. Plug-ins can be attached to ObjectGrids and BackingMaps. The ObjectGrid plug-ins: v TransactionCallback plug-in v ObjectGridEventListener plug-in v SubjectSource plug-in v MapAuthorization plug-in v SubjectValidation plug-in The BackingMap plug-ins: v Loader plug-in v ObjectTransformer plug-in v OptimisticCallback plug-in v Evictor plug-in v MapEventListener plug-in bean element attributes

Chapter 5. ObjectGrid application programming interface overview

125

<bean (1) id="TransactionCallback|ObjectGridEventListener|SubjectSource| MapAuthorization|SubjectValidation|Loader|ObjectTransformer| OptimisticCallback|Evictor|MapEventListener" (2) className="class name" />

1. id attribute (required): Specifies the type of plug-in to create. For a bean that is a child element of the objectGrid element, the valid values are TransactionCallback, ObjectGridEventListener, SubjectSource, MapAuthorization, and SubjectValidation. For a bean that is a child element of the backingMapPluginCollection element , the valid values are Loader, ObjectTransformer, OptimisticCallback, Evictor, and MapEventListener. Each of the valid values for the id attribute represent an interface. 2. className attribute (required): Specifies the name of the class to instantiate to create the plug-in. The class must implement of the plug-in type interface. The following bean.xml file sample demonstrates how to use the bean element to configure plug-ins. In this XML file, an ObjectGridEventListener plug-in is added to the bookstore ObjectGrid. The className for this bean is the com.ibm.websphere.objectgrid.plugins.builtins.TranPropListener class. This class implements the com.ibm.websphere.objectgrid.plugins.ObjectGridEventListener interface as required. A BackingMap plug-in is also defined in the following bean.xml file sample. An Evictor plug-in is added to the book BackingMap. Because the bean id is Evictor, the className attribute must specify a class that implements the com.ibm.websphere.objectgrid.plugins.Evictor interface. The com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor class implements this interface. The backingMap references its plug-ins using the pluginCollectionRef attribute. See BackingMap interface on page 42 for more information on how to add plug-ins to a BackingMap. bean.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <bean id="ObjectGridEventListener" className="com.ibm.websphere.objectgrid.plugins.builtins.TranPropListener" /> <backingMap name="book" pluginCollectionRef="bookPlugins" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="bookPlugins"> <bean id="Evictor" classname="com.ibm.websphere.objectGrid.plugins.builtins.LRUEvictor" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

The following code demonstrates the programmatic approach to achieve the same configuration as the previous bean.xml file.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid ("bookstore", false); TranPropListener tranPropListener = new TranPropListener();

126

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

bookstoreGrid.addEventListener(tranPropListener); BackingMap bookMap = bookstoreGrid.defineMap("book"); Evictor lruEvictor = new com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor(); bookMap.setEvictor(lruEvictor);

property element
Number of occurrences: zero to many Child element: none The property element is used to add properties to plug-ins. The name of the property corresponds to a set method on the className attribute of the bean that contains the property. property element attributes
<property (1) name="name" (2) type="java.lang.String|boolean|java.lang.Boolean|int| java.lang.Integer|double|java.lang.Double|byte| java.lang.Byte|short|java.lang.Short|long| java.lang.Long|float|java.lang.Float|char| java.lang.Character" (3) value="value" (4) description="description" />

1. name attribute (required): Specifies the name of the property. The value that is assigned to this attribute must correspond to a set method on the class that is provided as the className attribute on the bean element. For example, if the className attribute of the bean is set to com.ibm.MyPlugin and the name of the property provided is size, then the com.ibm.MyPlugin class must have a setSize method. 2. type attribute (required): Specifies the type of the property. It is the type of the parameter that is passed to the set method that is identified by the name attribute. The valid values are the Java primitives, their java.lang counterparts, and java.lang.String. The name and type attributes must correspond to a method signature on the className attribute of the bean. For example, if name is size and type is int, then a setSize(int) method must exist on the class that is specified as the className attribute for the bean. 3. value attribute (required): Specifies the value of the property. This value is converted to the type that is specified by the type attribute, and is then used as a parameter in the call to the set method that is identified by the name and type attributes. The value of this attribute is not validated in any way. The plug-in implementer must verify that the value passed in is valid. The implementor can display an IllegalArgumentException exception in the set method if the parameter is not valid. The implementor must display this exception, ObjectGrid does not implement the exception. 4. description attribute (optional): Use this attribute to write a description of the property. The following property.xml file demonstrates how to add a property element to a bean. In this example, a property with the name maxSize and type int is added to an Evictor. The com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor Evictor has a method signature that matches the setMaxSize(int) method. An

Chapter 5. ObjectGrid application programming interface overview

127

integer value of 499 is passed to the setMaxSize(int) method on the com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor class. property.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" pluginCollectionRef="bookPlugins" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="bookPlugins"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor"> <property name="MaxSize" type="int" value="449" description="The maximum size of the LRU Evictor" /> </bean> </backingMapPluginCOllection> </backingMapPluginCollections> </objectGridConfig>

The following code achieves the same configuration as the property.xml file:
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid ("bookstore", false); BackingMap bookMap = bookstoreGrid.defineMap("book"); LRUEvictor lruEvictor = new com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor(); // if the XML file were used instead, // the property that was added would cause the following call to be made lruEvictor.setMaxSize(449); bookMap.setEvictor(lruEvictor);

backingMapPluginCollections element
Number of occurrences: zero to one Child element: backingMapPluginCollection element The backingMapPluginCollections element is a container for all the backingMapPluginCollection elements. In the sample1.xml file, the backingMapPluginCollections element contains one backingMapPluginCollection with the id collection1.

backingMapPluginCollection element
Number of occurrences: zero to many Child element: bean element The backingMapPluginCollection element is where BackingMap plug-ins are defined. Each backingMapPluginCollection element is identified by its id attribute. Each backingMap element must reference its plug-ins using the pluginCollectionRef attribute on the backingMap element. If several BackingMaps

128

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

exist that must have their plug-ins configured similarly, each of them can reference the same backingMapPluginCollection element. backingMapPluginCollection element attributes
<backingMapPluginCollection (1) id="id" />

1. id attribute (required): The identifier for the backingMapPluginCollection. Each id must be unique. The id is referenced by the pluginCollectionRef attribute of the backingMap element. If the value of a pluginCollectionRef attribute does not match the id of one backingMapPluginCollection element, XML validation fails. Any number of backingMap elements can reference a single backingMapPluginCollection element. The following collection.xml file demonstrates how to use the backingMapPluginCollection element. In this file, three backingMap elements have been defined. The book and customer BackingMaps both use the collection1 backingMapPluginCollection. Each of these twhoBackingMaps have their own LRUEvictor evictor. The employee BackingMap references the collection2 backingMapPluginCollection. This BackingMap has an LFUEvictor evictor set as an Evictor plug-in and the EmployeeOptimisticCallbackImpl class set as an OptimisticCallback plug-in. collection.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" pluginCollectionRef="collection1" /> <backingMap name="customer" pluginCollectionRef="collection1" /> <backingMap name="employee" pluginCollectionRef="collection2" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="collection1"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor" /> </backingMapPluginCollection> <backingMapPluginCollection id="collection2"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LFUEvictor" /> <bean id="OptimisticCallback" className="com.ibm.websphere.samples.objectgrid. EmployeeOptimisticCallBackImpl" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

The following code demonstrates how to programmatically achieve the same configuration as the collection.xml file.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid ("bookstore", false); BackingMap bookMap = bookstoreGrid.defineMap("book"); LRUEvictor bookEvictor = new LRUEvictor(); bookMap.setEvictor(bookEvictor);

Chapter 5. ObjectGrid application programming interface overview

129

BackingMap customerMap = bookstoreGrid.defineMap("customer"); LRUEvictor customerEvictor = new LRUEvictor(); customerMap.setEvictor(customerEvictor); BackingMap employeeMap = bookstoreGrid.defineMap("employee"); LFUEvictor employeeEvictor = new LFUEvictor(); employeeMap.setEvictor(employeeEvictor); OptimisticCallback employeeOptCallback = new EmployeeOptimisticCallbackImpl(); employeeMap.setOptimisticCallback(employeeOptCallback);

Mixed mode ObjectGrid configuration


ObjectGrid can be configured using a combination of XML configuration and programmatic configuration. To accomplish a mixed configuration, first create an XML file to pass to the ObjectGridManager interface. After an ObjectGrid has been created based on the XML file, the ObjectGrid can be manipulated programmatically, as long as the ObjectGrid.initialize() method has not been called. The ObjectGrid.getSession() method implicitly calls theObjectGrid.initialize() method if it has not been called by the application.

Example
Following is a demonstration of how to achieve a mixed mode configuration. The following mixedBookstore.xml, file is used. mixedbookstore.xml file
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config/ ..objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" readOnly="true" numberOfBuckets="641" pluginCollectionRef="bookPlugins" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="bookPlugins"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LFUEvictor" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

The following code snippet that shows the XML being passed to the ObjectGridManager, and the newly created ObjectGrid is further manipulated.
ObjectGridManager objectGridManager = ObjectGridManagerFactory.getObjectGridManager(); ObjectGrid bookstoreGrid = objectGridManager.createObjectGrid("bookstore", new URL("file:etc/test/document/mixedBookstore.xml"), true, false); // at this point, we have the ObjectGrid that was defined in the XML // now lets modify the BackingMap that was created and configured BackingMap bookMap = bookstoreGrid.getMap("book"); // the XML set readOnly to true // here it is changed to false bookMap.setReadOnly(false); // the XML did not set nullValuesSupported, so // it would default to true. Here we set the

130

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

// value to false bookMap.setNullValuesSupported(false); // get the Evictor that was set in the XML, // and set its maxSize LFUEvictor lfuEvictor = (LFUEvictor) bookMap.getEvictor(); lfuEvictor.setMaxSize(443); bookstoreGrid.initialize(); // further configuration is not allowed // to this ObjectGrid after the initialize call

Chapter 5. ObjectGrid application programming interface overview

131

132

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 6. Integrating ObjectGrid with WebSphere Application Server


Use ObjectGrid with the features that are provided with WebSphere Application Server to enhance your applications with ObjectGrid capability. Install WebSphere Application Server and WebSphere Extended Deployment. After WebSphere Extended Deployment is installed, you can add ObjectGrid functions to your Java 2 Platform, Enterprise Edition (J2EE) applications. The ObjectGrid API can be used in a WebSphere Application Server-targeted J2EE application. The ObjectGrid.jar file is in the \base\lib directory after WebSphere Extended Deployment is installed. In addition to integrating the ObjectGrid API with the J2EE application programming model, you can leverage the distributed transaction propagation support. With this support, you can configure ObjectGrid instances to coordinate transaction commit results across a WebSphere Application Server cluster. 1. Perform the basic programming steps for enabling a J2EE application with ObjectGrid. See Integrating ObjectGrid in a Java 2 Platform, Enterprise Edition environment for more information. 2. Monitor performance data for your ObjectGrid applications. See Monitoring ObjectGrid performance with WebSphere Application Server performance monitoring infrastructure (PMI) on page 136 for more information. 3. Coordinate cache entry updates during ObjectGrid session transaction commit completion between two or more ObjectGrid instances that have the same instance name in one or more clusters. See ObjectGrid distributed transaction propagation on page 143 for more information. 4. When ObjectGrid is embedded, the transactions might be started and ended by an external transaction coordinator. See ObjectGrid and external transaction interaction on page 148 for more information. 5. Use the partitioning facility with ObjectGrid. The ObjectGrid feature provides the capacity of caching key and value pairs in the transactional fashion, and the Partition Facility feature provides the capacity of context-based routing according to object characteristics. See Integrating ObjectGrid and the partitioning facility on page 151 for more information. You can also use ObjectGrid with JMS to distribute changes between different tiers or in environments with mixed platforms. See Java Message Service for distributing transaction changes on page 186 for more information.

Integrating ObjectGrid in a Java 2 Platform, Enterprise Edition environment


ObjectGrid supports both servlet and Enterprise JavaBeans (EJB) programming models in the Java 2 Platform, Enterprise Edition (J2EE) environment. This topic explores the common programming steps for enabling a J2EE application with ObjectGrid.

Copyright IBM Corp. 2004, 2005

133

1. Define an ObjectGrid configuration. Define an ObjectGrid configuration either with XML files, through programmatic interface or with a mixed usage of XML files and programmatic configuration. For more information, see ObjectGrid configuration. 2. Create a URL object. If the ObjectGrid configuration is in an XML file, create a URL object that points to that XML file. You can use this URL object to create ObjectGrid instances by using the ObjectGridManager API. If the ObjectGrid configuration XML file is included in a Web archive (WAR) or Enterprise JavaBeans (EJB) Java archive (JAR) file, it is accessible as a resource to the class loader for both the Web and EJB module. For example, if the ObjectGrid configuration XML file is in the WEBINF folder of the Web module WAR file, servlets that are in that WAR file can create a URL object with the following pattern:
URL url =className.class.getClassLoader(). getResource("METAINF/objectgriddefinition.xml"); URL objectgridUrl = ObjectGridCreationServlet.class.getClassLoader(). getResource("WEBINF/objectgriddefinition.xml");

3. Create or get ObjectGrid instances. Use the ObjectGridManager API to get and create ObjectGrid instances. With the ObjectGridManager API, you can create ObjectGrid instances with XML and use utility methods to quickly create a simple ObjectGrid instance. Applications must use the ObjectGridManagerFactory API to get a reference to the ObjectGridManager API. See the following coding example:
import com.ibm.websphere.objectgrid.ObjectGridManager; import com.ibm.websphere.objectgrid.ObjectGridManagerFactory ; ... ObjectGridManager objectGridManager = ObjectGridManagerFactory. getObjectGridManager(); ObjectGrid ivObjectGrid = objectGridManager. createObjectGrid(objectGridName, objectgridUrl, true, true);

For more information about the ObjectGridManager API, see the ObjectGridManager interface topic. 4. Initialize the ObjectGrid instances. Use the initialize method in the ObjectGrid interface to begin bootstrapping the ObjectGrid and Session instances. This initialize method is considered optional because the first call to the getSession method performs an implicit initialization. After this method is invoked, the ObjectGrid configuration is considered complete and is ready for runtime usage. Any additional configuration method invocations, such as calling the defineMap(String mapName) method, result in an exception. 5. Get a Session and ObjectMap instance. A session is a container for the ObjectGrid maps, that is represented by an ObjectMap instance. A thread must get its own Session object to interact with the ObjectGrid core. You can think of this technique as a session that can only be used by a single thread at a time. The session is shareable across threads if it uses only one thread at a time. However, if a J2EE connection or transaction infrastructure is used, the session object is not shareable across threads. A good analogy for this object is a Java Database Connectivity (JDBC) connection to a database. An ObjectMap map is a handle to a named map. Maps must have homogenous keys and values. An ObjectMap instance can be used only by the thread that is currently associated with the session that was used to get this ObjectMap instance. Multiple threads cannot share Session and ObjectMap objects concurrently. Keywords are applied within a transaction. A transaction rollback rolls back any keyword association that is applied during this transaction.The coding example follows:

134

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Session ivSession = ivObjectGrid.getSession(); ObjectMap ivEmpMap = ivSession.getMap("employees"); ObjectMap ivOfficeMap = ivSession.getMap("offices"); ObjectMap ivSiteMap = ivSession.getMap("sites"); ObjectMap ivCounterMap = ivSession.getMap("counters");

6. Begin a session, read or write objects, and commit or roll back the session. Map operations must be within a transactional context. The begin method of the Session object is used to begin an explicit transactional context. After the session begins, applications can start performing map operations. The most common operations include get, update, insert, and remove method calls for objects against maps. At the end of map operations, the commit or rollback method of the Session object is called to either commit an explicit transactional context or roll back an explicit transactional context. A programming example follows:
ivSession.begin(); Integer key = new Integer(1); if (ivCounterMap.containsKey(key) == false) { ivCounterMap.insert(key, new Counter(10)); } ivSession.commit();

You performed the basic programming steps for enabling a J2EE application with ObjectGrid. See Building ObjectGrid-enabled Java 2 Platform, Enterprise Edition (J2EE) applications and Considerations for the integration of Java 2 Platform, Enterprise Edition (J2EE) applications and ObjectGrid for more information.

Building ObjectGrid-enabled Java 2 Platform, Enterprise Edition applications


Use this task to configure the build path, or class path, of ObjectGrid-enabled Java 2 Platform, Enterprise Edition (J2EE) applications. The class path must include the objectgrid.jar file that is located in the $install_root/lib directory. Develop an ObjectGrid enabled J2EE application. See Integrating ObjectGrid in a J2EE environment for more information. This task demonstrates how to set the build path to include the objectgrid.jar file in IBM Rational Software Development Platform Version 6.0. 1. In the Project Explorer view of the J2EE perspective, right-click the WEB or Enterprise JavaBeans (EJB) project, and select Properties. The Properties window is displayed. 2. Select the Java build path in the left panel, click the Libraries tab in the right panel, and click Add Variable. The New Variable Classpath Entry window is displayed. 3. Click Configure Variables to open the Preference window. 4. Add a new variable entry. a. Click New. b. Type OBJECTGRID_JAR in the name field. Click File to open the JAR Selection window. c. Browse to the /lib directory, click the objectgrid.jar file, and click Open to close the JAR Selection window. d. Click OK to close the New Variable Entry window. The OBJECTGRID_JAR variable is displayed in the Classpath variables list.
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

135

5. Click OK to close the Preference window. 6. Select the OBJECTGRID_JAR variable from the variables list and click OK to close the New Variable Classpath Entry window. The OBJECTGRID_JAR variable is displayed in the Libraries panel. 7. Click OK to close the Properties window. You set the build path to include the objectgrid.jar file in IBM Rational Software Development Platform Version 6.0.

Considerations for the integration of Java 2 Platform, Enterprise Edition applications and ObjectGrid
Use these considerations when integrating a Java 2 Platform, Enterprise Edition (J2EE) application with ObjectGrid.

Startup beans and ObjectGrid


You can use startup beans for an application to bootstrap an ObjectGrid instance when an application starts and destroy the ObjectGrid instance when the application stops. A startup bean is a stateless session bean with a com.ibm.websphere.startupservice.AppStartUpHome remote home and a com.ibm.websphere.startupservice.AppStartUp remote interface. When WebSphere Application Server sees an Enterprise JavaBean (EJB), it recognizes the startup bean. The remote interface has two methods, the start method and the stop method. Use the start method to bootstrap the grid, and call the grid destroy method with the stop method. The application can keep a reference to the grid by using the ObjectGridManager.getObjectGrid method to get a reference when needed. For more information, see the ObjectGridManager interface topic.

Class loaders and ObjectGrid instances


You must take care when sharing a single ObjectGrid instance between application modules that use different class loaders. Application modules that use different class loaders do not work and result in class cast exceptions in the application. An ObjectGrid must be shared only by application modules that use the same class loader or when the application objects, for example the plug-ins, keys, and values are on a common class loader.

Manage the life cycle of ObjectGrid instances in a servlet


You can manage the life cycle of ObjectGrid instances with the init method and destroy method of a servlet. Use the init method to create and initialize ObjectGrid instances that are needed by the application. After the ObjectGrid instances are created and cached, you can obtain the instances by their names with the ObjectGridManager API. Use the destroy method to destroy these ObjectGrid instances and to release system resources. For more information, see the ObjectGridManager interface topic.

Monitoring ObjectGrid performance with WebSphere Application Server performance monitoring infrastructure (PMI)
ObjectGrid supports performance monitoring infrastructure (PMI) when running in a WebSphere Application Server or WebSphere Extended Deployment application server. PMI collects performance data on runtime applications and provides interfaces that support external applications to monitor performance data.

136

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

For more information about the statistics that ObjectGrid provides, see ObjectGrid statistics. ObjectGrid uses the custom PMI feature of WebSphere Application Server to add its own PMI instrumentation. With this approach, you can enable and disable ObjectGrid PMI with the administrative console or with Java Management Extensions (JMX) interfaces. In addition, you can access ObjectGrid statistics with the standard PMI and JMX interfaces that are used by monitoring tools, including the Tivoli Performance Viewer. 1. Enable ObjectGrid PMI. You must enable PMI to view the PMI statistics. See Enabling ObjectGrid PMI on page 140 for more information. 2. Retrieve ObjectGrid PMI statistics. View the performance of your ObjectGrid applications with the Tivoli Performance Viewer. See Retrieving ObjectGrid PMI statistics on page 142 for more information.

ObjectGrid statistics
ObjectGrid provides two performance monitoring infrastructure (PMI) modules: the objectGridModule module and the mapModule module.

objectGridModule module
The objectGridModule module contains one time statistic: transaction response time. An ObjectGrid transaction is defined as the duration between the Session.begin method call and the Session.commit method call. This duration is tracked as the transaction response time. The root element of the objectGridModule module, the ObjectGrids element, serves as the entry point to the ObjectGrid statistics. This root element has ObjectGrid instances as its children that have transaction types as their children. The response time statistic is associated with each transaction type. The objectGridModule module structure is shown in the following diagram:

ObjectGrids

ObjectGrid instance

transaction type

response time

Figure 3. ObjectGridModule module structure

The following diagram shows an example of the ObjectGrid PMI module structure. In this example, two ObjectGrid instances exist in the system: the objectGrid1 ObjectGrid and the objectGrid2 ObjectGrid. The objectGrid1 instance has two types of transactions: update and read, and the objectGrid2 instance has only type of transaction: update.

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

137

ObjectGrids

ObjectGrid 1

ObjectGrid 2

update

read

update

avg: 750ms

avg: 300ms

avg: 980ms

Figure 4. ObjectGrid PMI module structure

Transaction types are defined by application developers because they know what types of transactions their applications use. The transaction type is set using the following Session.setTransactionType(String) method:
/** * Sets the transaction type for future transactions. * * After this method is called, all of the future transactions have the * same type until another transaction type is set. If no transaction * type is set, the default TRANSACTION_TYPE_DEFAULT transaction type * is used. * * Transaction types are used mainly for statistical data tracking purpose. * Users can predefine types of transactions that run in an * application. The idea is to categorize transactions with the same characteristics * to one category (type), so one transaction response time statistic can be * used to track each transaction type. * * This tracking is useful when your application has different types of * transactions. * Among them, some types of transactions, such as update transactions, process * longer than other transactions, such as readonly transactions. By using the * transaction type, different transactions are tracked by different statistics, * so the statistics can be more useful. * * @param tranType the transaction type for future transactions. */ void setTransactionType(String tranType);

The following example sets transaction type to updatePrice:


// Set the transaction type to updatePrice // The time between session.begin() and session.commit() will be // tracked in the time statistic for updatePrice. session.setTransactionType("updatePrice"); session.begin(); map.update(stockId, new Integer(100)); session.commit();

The first line indicates that the subsequent transaction type is updatePrice. An updatePrice statistic exists under the ObjectGrid instance that corresponds to the session in the example. Using Java Management Extensions (JMX) interfaces, you can get the transaction response time for updatePrice transactions. You can also get the aggregated statistic for all types of transactions on the specified ObjectGrid.

138

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

mapModule module
The mapModule PMI module contains three statistics that are related to ObjectGrid maps: v Map hit rate: This BoundedRangeStatistic statistic tracks the hit rate of a map. Hit rate is a float value between 0 and 100 inclusively, which represents the percentage of map hits in relation to map get operations. v Number of entries: This CountStatistic statistic tracks the number of entries in the map. v Loader batch update response time: This TimeStatistic statistic tracks the response time that is used for the loader batch update operation. The root element of the mapModule module, the ObjectGrid Maps element, serves as the entry point to the ObjectGrid Map statistics. This root element has ObjectGrid instances as its children, which have map instances as their children. Every map instance has the three listed statistics. The mapModule structure is shown in the following diagram:

ObjectGrid Maps

ObjectGrid instances

Map instances

hit rate number of entries loader batch update time

Figure 5. mapModule module structure

The following diagram shows an example of the mapModule structure:

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

139

ObjectGrid Maps

objectGrid1

objectGrid2

map1

map2

map3

hit rate 55% number of entries: 480 batch update time: Avg 870ms

hit rate 95% number of entries: 4300 batch update time: Avg 300ms

hit rate 30% number of entries: 700 batch update time: Avg 1300ms

Figure 6. mapModule module structure example

Enabling ObjectGrid PMI


You can use WebSphere Application Server Performance Monitoring Infrastructure (PMI) to enable or disable statistics at any level. For example, you can choose to just enable the map hit rate statistic for a particular map, but not the number of entry statistic or the loader batch update time statistic. This topic shows how to use the administrative console and wsadmin scripts to enable ObjectGrid PMI. Use WebSphere Application Server PMI to provide a granular mechanism with which you can enable or disable statistics at any level. For example, you can choose to enable map hit rate statistic for a particular map, but not the number of entry statistic or the loader batch update time statistic. This section shows how to use the administrative console and wsadmin scripts to enable ObjectGrid PMI. 1. Open the administrative console, for example, http://localhost:9060/ibm/console. 2. Click Monitoring and Tuning > Performance Monitoring Infrastructure > server_name. 3. Verify that Enable Performance Monitoring Infrastructure (PMI) is selected. This setting is enabled by default. If the setting is not enabled, select the check box and then restart the server. 4. Click Custom. In the configuration tree, select the ObjectGrid and ObjectGrid Maps module. Enable the statistics for each module. The transaction type category for ObjectGrid statistics is created at run time. You can see only the subcategories of the ObjectGrid and Map statistics on the Runtime panel. For example, you can perform the following steps to enable PMI statistics for the sample application: 1. Launch the application using the http://host:port/ObjectGridSample Web address, where host and port are the host name and HTTP port number of the server where the sample is installed. 2. In the sample application, click ObjectGridCreationServlet, and then click action buttons 1, 2, 3, 4, and 5 to generate some actions to the ObjectGrid and maps. Do not close this servlet page at this time.

140

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

3. Go back to the administrative console, click Monitoring and Tuning > Performance Monitoring Infrastructure > server_name. Click the Runtime tab. 4. Click the Custom radio button. 5. Expand the ObjectGrid Maps module in the runtime tree, and then click the clusterObjectGrid link. Under ObjectGrid Maps group, one ObjectGrid instance exists that is called clusterObjectGrid, and under this clusterObjectGrid group, four maps exist: counters, employees, offices, and sites. In the ObjectGrids instance, one clusterObjectGrid instance exists, and under that instance is a transaction type called DEFAULT. 6. You can enable the statistics that you are interested in. For demonstration purpose, you can enable number of map entries for employees map, and transaction response time for the DEFAULT transaction type. You can automate the task of enabling PMI with scripting. See Enabling ObjectGrid PMI with scripting for more information.

Enabling ObjectGrid PMI with scripting


Automate the task of enabling ObjectGrid PMI with the wsadmin tool. Your application server must be started and have an ObjectGrid enabled application installed. You also must be able to log in and use the wsadmin tool. For more information about the wsadmin tool, see Using scripting (wsadmin) in the WebSphere Extended Deployment Version 6.0.x information center. Use this task to automate enabling PMI. To enable PMI with the administrative console, see Enabling ObjectGrid PMI. 1. Open a command line prompt. Navigate to the install_root/bin directory. Type type wsadmin to start the wsadmin command line tool. 2. Modify the ObjectGrid PMI runtime configuration. Check to see if PMI is enabled for the server with the following commands:
wsadmin>set s1 [$AdminConfig getid /Cell:CELL_NAME/Node:NODE_NAME/Server: APPLICATION_SERVER_NAME/] wsadmin>set pmi [$AdminConfig list PMIService $s1] wsadmin>$AdminConfig show $pmi.

If PMI is not enabled, run the following commands to enable PMI:


wsadmin>$AdminConfig modify $pmi {{enable true}} wsadmin>$AdminConfig save

If you need to enable PMI, restart the server. 3. Set variables for changing the statistic set to a custom set. Run the following commands:
wsadmin>set perfName [$AdminControl completeObjectName type= Perf,process=APPLICATION_SERVER_NAME,*] wsadmin>set perfOName [$AdminControl makeObjectName $perfName] wsadmin>set params [java::new {java.lang.Object[]} 1] wsadmin>$params set 0 [java::new java.lang.String custom] wsadmin>set sigs [java::new {java.lang.String[]} 1] wsadmin>$sigs set 0 java.lang.String

4. Set statistic set to custom: Run the following command:


wsadmin>$AdminControl invoke_jmx $perfOName setStatisticSet $params $sigs

5. Set variables to enable the objectGridModule PMI statistic. Run the following commands:

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

141

wsadmin>set params [java::new {java.lang.Object[]} 2] wsadmin>$params set 0 [java::new java.lang.String objectGridModule=1] wsadmin>$params set 1 [java::new java.lang.Boolean false] wsadmin>set sigs [java::new {java.lang.String[]} 2] wsadmin>$sigs set 0 java.lang.String wsadmin>$sigs set 1 java.lang.Boolean

6. Set the statistics string. Run the following command:


wsadmin>$AdminControl invoke_jmx $perfOName setCustomSetString $params $sigs

7. Set variables to enable the mapModule PMI statistic. Run the following commands:
wsadmin>set params2 [java::new {java.lang.Object[]} 2] wsadmin>$params2 set 0 [java::new java.lang.String mapModule=*] wsadmin>$params2 set 1 [java::new java.lang.Boolean false] wsadmin>set sigs2 [java::new {java.lang.String[]} 2] wsadmin>$sigs2 set 0 java.lang.String wsadmin>$sigs2 set 1 java.lang.Boolean

8. Set the statistics string. Run the following command:


wsadmin>$AdminControl invoke_jmx $perfOName setCustomSetString $params2 $sigs2

These steps enable ObjectGrid runtime PMI, but do not modify the PMI configuration. If you restart the application server, the PMI settings are lost, other than the main PMI enablement. After PMI is enabled, you can view PMI statistics with the administrative console or through scripting. See Retrieving ObjectGrid PMI statistics and Retrieving ObjectGrid PMI statistics with scripts for more information.

Retrieving ObjectGrid PMI statistics


See the performance statistics of your ObjectGrid applications. After the ObjectGrid statistics are enabled, you can retrieve them. To enable ObjectGrid PMI, see Enabling ObjectGrid PMI. Use this task to see the performance statistics of your ObjectGrid applications. 1. Open the administrative console. For example, http://localhost:9060/ibm/console. 2. Click Monitoring and Tuning > Performance Viewer > Current Activity. 3. Click the server that you want to monitor using Tivoli Performance Viewer and enable the monitoring. 4. Click the server to view the Performance viewer page. 5. Expand the configuration tree. Click ObjectGrid Maps > clusterObjectGrid, and select employees. Expand ObjectGrids > clusterObjectGrid, and select DEFAULT. 6. In the ObjectGrid sample application, go back to the ObjectGridCreationServlet servlet , click button 1, populate maps. You can view the statistics in the viewer. You can view ObjectGrid Statistics in the Tivoli Performance Viewer. You can automate the task of retrieving statistics by using Java Management Extensions (JMX) or with the wsadmin tool. See Retrieving ObjectGrid PMI statistics with scripts

Retrieving ObjectGrid PMI statistics with scripts


Use this task to retrieve performance statistics for your ObjectGrid applications.

142

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Enable Performance Monitoring Infrastructure (PMI) in your application server environment. See Enabling ObjectGrid PMI or Enabling ObjectGrid PMI with scripts for more information. You also must be able to log in and use the wsadmin tool. For more information about the wsadmin tool, see Using scripting (wsadmin) in the WebSphere Extended Deployment Version 6.0.x Information Center. Use this task to get performance statistics for your application server environment. For more information about the ObjectGrid statistics that can be retrieved, see ObjectGrid statistics on page 137. 1. Open a command-line prompt. Navigate to the install_root/bin directory. Type wsadmin to start the wsadmin command-line tool. 2. Set variables for the environment. Run the following commands:
wsadmin>set perfName [$AdminControl completeObjectName type=Perf,*] wsadmin>set perfOName [$AdminControl makeObjectName $perfName] wsadmin>set mySrvName [$AdminControl completeObjectName type=Server, name=APPLICATION_SERVER_NAME,*]

3. Set variables to get mapModule statistics. Run the following commands:


wsadmin>set params [java::new {java.lang.Object[]} 3] wsadmin>$params set 0 [$AdminControl makeObjectName $mySrvName] wsadmin>$params set 1 [java::new java.lang.String mapModule] wsadmin>$params set 2 [java::new java.lang.Boolean true] wsadmin>set sigs [java::new {java.lang.String[]} 3] wsadmin>$sigs set 0 javax.management.ObjectName wsadmin>$sigs set 1 java.lang.String wsadmin>$sigs set 2 java.lang.Boolean

4. Get mapModule statistics. Run the following command:


wsadmin>$AdminControl invoke_jmx $perfOName getStatsString $params $sigs

5. Set variables to get objectGridModule statistics. Run the following commands:


wsadmin>set params2 [java::new {java.lang.Object[]} 3] wsadmin>$params2 set 0 [$AdminControl makeObjectName $mySrvName] wsadmin>$params2 set 1 [java::new java.lang.String objectGridModule] wsadmin>$params2 set 2 [java::new java.lang.Boolean true] wsadmin>set sigs2 [java::new {java.lang.String[]} 3] wsadmin>$sigs2 set 0 javax.management.ObjectName wsadmin>$sigs2 set 1 java.lang.String wsadmin>$sigs2 set 2 java.lang.Boolean

6. Get objectGridModule statistics. Run the following command:


wsadmin>$AdminControl invoke_jmx $perfOName getStatsString $params2 $sigs2

See ObjectGrid statistics on page 137 for more information about the statistics that are returned.

ObjectGrid distributed transaction propagation


Distributed transaction propagation is used to coordinate cache entry updates during ObjectGrid session transaction commit completion between two or more ObjectGrid instances that have the same instance name in one or more clusters. With distributed transaction propagation, you can either propagate invalidation requests or key and value updates to peers by instantiating the same ObjectGrid instance. For example, you have cache instances that are mostly read. At some point, you update a specific key and value pair. To ensure that the other cluster members do not serve stale information for that key, you must be able to coordinate the various

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

143

WebSphere Application Server cluster members that are providing that data to users. You can use one of the following approaches: v Invalidate the data in the other servers You might have data in a data store such as a database, and have an ObjectGrid loader implemented to retrieve information from that database instance. If a user accesses information on the servers that have had the entry invalidated, the query causes the ObjectGrid loader to retrieve the fresh data from a data source instead of giving the user a stale version of the data. v Update the data in the remote servers with the most current information. If a loader is not implemented for a specific ObjectGrid instance, you can populate the cache with the ObjectGrid Map or JavaMap APIs. In this case, it can be useful if the actual value for the particular entry that was updated on one cluster member is propagated and applied to the remote ObjectGrid instances. As a result, users that retrieve data from the cache have the most recent version. The update and invalidate actions are asynchronous and are not guaranteed to commit on the remote cluster member. This situation is similar to the update method situation, where multiple members might have different versions of the information. If multiple members might update the data, the appropriate distribution mode constraints are not consistent.

Requirements
To enable the distributed transaction propagation behavior, your configuration must meet the following requirements: v A configured ObjectGridEventListener listener for the specific ObjectGrid. You can configure this listener with XML or programmatically. See Listeners on page 79 for more information. v The ObjectGrid instance in each application server must have the same name attribute in each application server instance, and the instance must be initialized. v The ObjectGridEventListener TranPropListener class that provides this function must be used only within a WebSphere Application Server application server instance. If not, the class fails to initialize. v Each application server that is hosting an ObjectGrid instance must be registered in the same HA manager core group. If you want distributed transaction propagation behavior outside of a WebSphere Application Server environment, see Chapter 8, Distributing changes between peer Java virtual machines, on page 183 for more information.

Overview
With this listener class, you can propagate successful ObjectGrid transaction commits to other WebSphere Application Servers that host the same ObjectGrid instance, based upon the ObjectGrid name. Each ObjectGrid is distinguished by the name attribute. If the name is the same, and both are configured with the same listener on separate WebSphere Application Server core group members, changes that are not read-only are propagated depending on the options that the user selects. Four options for distribution are provided: v push v push with versioning (the default) v invalidate

144

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

v invalidate with versioning See the DistributionMode class in the API documentation for more information. In addition to the distribution mode, you can configure if the listener propagates transactions to other cluster members, only receives transaction commits from other similarly configured ObjectGrid instances, or propagates and receives for each application server instance, the default option. This support provides for optimistic, asynchronous distributed transaction commit support only. You can configure if compression is enabled. Generally for typical transaction commit streams, the compression support expends some local Java virtual machine (JVM) CPU and memory footprint. This footprint is used to create a compressed stream for transmission to listener support enabled instances of the map that are located in other core group members. However, a common compression concern exists. For the scenarios when very short messages exist that have little transaction commit content, the overhead might be more than the computing resources that are saved by doing the compression. You can disable compression in that case. This class only initializes correctly within a WebSphere Application Server runtime environment and requires the high availability manager to operate successfully. You must configure each ObjectGrid instance that you want to send and receive distributed transactions in the appropriate WebSphere Application Server cluster member instances with this listener. This support has worker thread support to ensure that all transaction content is processed in the background, which is also known as asynchronous support. The thread that processes this work is normally started during the initial ObjectGrid instance initialization. The ObjectGridManager interface implementation caches this thread for a single JVM. Use the ObjectGridManager interface to consistently manage a single ObjectGrid instance within a single JVM. Server shutdown causes the worker thread to exit. If you want to control the worker thread support, you can use the provided methods, but do so carefully. The anticipated use of this support is the following sequence: 1. Start the server. 2. Use the ObjectGridManager interface implementation to create the initial instance. 3. Have all of the JVM users look up the existing instance of the ObjectGrid from the ObjectGridManager. 4. Clean up the threads that handle distribution transaction support finally at server shutdown in an orderly manner, without programmer or administrator intervention. Each application that is using the ObjectGrid in a WebSphere Application Server and is sharing commit information must be in the same high availability manager core group. Related reference Distributed transaction propagation distribution mode options on page 146 You can set the distribution mode to one of four propagation policies.

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

145

Attributes govern the type of operation that is transmitted to the other cluster members ObjectGrid instance, and dictate what action is completed during a remote transaction commit.

Distributed transaction propagation configuration


You can configure the distributed transaction propagation support with XML or by using the programmatic interfaces. Use distributed transaction propagation to coordinate cache entry updates during ObjectGrid session transaction commit completion.

XML configuration
The following example illustrates how to configure the TranPropListener class for a specific ObjectGrid:
<bean id="ObjectGridEventListener" className="com.ibm.websphere.objectgrid.plugins.builtins.TranPropListener"> <property name="propagateService" type="java.lang.String" value="all" description="Option all includes propagating and receiving transaction commits.(propagate) and (receive) other options. "/> <property name="propagateMode" type="java.lang.String" value="update" description="Propagate value (update), or just convert all updates to invalidates in remote cache (invalidate)." /> <property name="propagateVersionOption" type="java.lang.String" value="enable" description="Enable the use of versioning (enable) or disable (disable)."/> <property name="compressionMode" type="java.lang.String" value="enable" description="Enable the use of stream compression for transmission enable (enable) or disable (disable)." /> </bean>

Programmatic configuration
In addition to configuring this support with XML, you can use the following approach to configure programmatically:
... String ogn, boolean enableDist, String pMode, String pVersion, String compressMode; .... ObjectGridManager manager= ObjectGridManagerFactory.getObjectGridManager(); og=manager.createObjectGrid(ogName); if (enableDist){ TranPropListener tpl=new TranPropListener(); if (pMode!=null){ tpl.setPropagateMode(pMode); } if (pVersion!=null){ tpl.setPropagateVersionOption(pVersion); } if (compressMode!=null) { tpl.setCompressionMode(compressMode); } og.addEventListener(tpl); }

Distributed transaction propagation distribution mode options


You can set the distribution mode to one of four propagation policies. Attributes govern the type of operation that is transmitted to the other cluster members ObjectGrid instance, and dictate what action is completed during a remote transaction commit.

146

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Purpose
You must set the propagateMode and propagateVersionOption attributes to implement a specific policy. The following table describes the particular policy, and the attribute combinations that must be set when you configure the distributed transaction propagation with XML or through a program to achieve the particular transaction propagation approach.
Table 7. Attribute values that must be set to enforce a particular propagation policy Propagation policy Invalidate Invalidate conditional Update Update conditional (default) propagateMode attribute value invalidate invalidate update update propagateVersionOption attribute value disable enable disable enable

Each distribution mode policy is described in the following list . The default mode is Update conditional.

Invalidate mode
The Invalidate , or INVALIDATE mode in the API documentation, distribution mode is used to communicate invalidate operations to the receivers of the LogSequence. Any updates, such as update, delete, and evict operations in the current LogSequence are communicated as invalidates on the receiving side. The receivers of the LogSequence object can invalidate entries that are now out-of-date due to the LogSequence that was just processed. The INVALIDATE mode sends only the keys that are in the distributed LogSequence because that is all that is required for an unqualified invalidate.

Invalidate conditional mode


The Invalidate conditional, or INVALIDATE_CONDITIONAL in the API documentation. distribution mode is very much like the INVALIDATE distribution mode except that it also considers the versioned value data that is associated with each entry. Before the invalidates are performed on the receiving side, the versioned value and the value currently in the receiving side ObjectMap are compared. If the versioned value is older than the current value, then the invalidate is not performed.

Update
The Update, or PUSH in the API documentation, distribution mode is used to communicate changes to the receivers of the LogSequence. Any LogElements elements in the current LogSequence object are communicated as corresponding LogElements on the receiving side. As a result, the receivers of the LogSequence can properly update their instance of the ObjectMap map. The PUSH mode processes the LogSequence unconditionally, without any concern of optimistic collisions. An Update action done with PUSH mode can overwrite the value in the receiving ObjectMap that was performed through another transaction.

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

147

Update conditional
The Update conditional, or PUSH_CONDITIONAL in the API documentation, distribution mode is very much like the PUSH distribution mode, except that it also considers the versioned value data that is associated with each entry. Before the updates are applied on the receiving side, the versioned value and the value that is currently in the receiving side ObjectMap are compared. If the versioned value is older than the current value, then the operation is not performed.

ObjectGrid and external transaction interaction


Usually, ObjectGrid transactions begin with the session.begin method and end with the session.commit method. However, when ObjectGrid is embedded, the transactions might be started and ended by an external transaction coordinator. In this case, you do not need to call the session.begin method and end with the session.commit method.

External transaction coordination


The ObjectGrid TransactionCallback plug-in is extended with the isExternalTransactionActive(Session session) method that associates the ObjectGrid session with an external transaction. The method header follows:
public synchronized boolean isExternalTransactionActive(Session session)

For example, ObjectGrid can be set up to integrate with WebSphere Application Server and WebSphere Extended Deployment. The key to this seamless integration is the exploitation of the ExtendedJTATransaction API in WebSphere Application Server Version 5.x and Version 6.x. However, if you are using WebSphere Application Server Version 6.0.2, you must apply APAR PK07848 to support this method. Use the following sample code to associate an ObjectGrid session with a WebSphere Application Server transaction ID:
/** * This methodis required to associate an objectGrid session with a WebSphere * transaction ID. */ Map/**/ localIdToSession; public synchronized boolean isExternalTransactionActive(Session session) { // remember that this localid means this session is saved for later. localIdToSession.put(new Integer(jta.getLocalId()), session); return true; }

Retrieve an external transaction


Sometimes you might need to retrieve an external transaction service object for the ObjectGrid TransactionCallback plug-in to use. In the WebSphere Application Server server, you look up the ExtendedJTATransaction object from its namespace as shown in the following example:
public J2EETransactionCallback() { super(); localIdToSession = new HashMap(); String lookupName="java:comp/websphere/ExtendedJTATransaction"; try { InitialContext ic = new InitialContext(); jta = (ExtendedJTATransaction)ic.lookup(lookupName); jta.registerSynchronizationCallback(this); }

148

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

catch(NotSupportedException e) { throw new RuntimeException("Cannot register jta callback", e); } catch(NamingException e){ throw new RuntimeException("Cannot get transaction object"); } }

For other products, you can use a similar approach to retrieve the transaction service object.

Control commit by external callback


The TransactionCallback plug-in needs to receive an external signal to commit or roll back the ObjectGrid session. To receive this external signal, use the callback from the external transaction service. You need to implement the external callback interface and register it with the external transaction service. For example, in the WebSphere Application Server case, you need to implement the SynchronizationCallback interface, as shown in the following example:
public class J2EETransactionCallback implements com.ibm.websphere.objectgrid.plugins.TransactionCallback, SynchronizationCallback { public J2EETransactionCallback() { super(); String lookupName="java:comp/websphere/ExtendedJTATransaction"; localIdToSession = new HashMap(); try { InitialContext ic = new InitialContext(); jta = (ExtendedJTATransaction)ic.lookup(lookupName); jta.registerSynchronizationCallback(this); } catch(NotSupportedException e) { throw new RuntimeException("Cannot register jta callback", e); } catch(NamingException e) { throw new RuntimeException("Cannot get transaction object"); } } public synchronized void afterCompletion(int localId, byte[] arg1, boolean didCommit) { Integer lid = new Integer(localId); // find the Session for the localId Session session = (Session)localIdToSession.get(lid); if(session != null) { try { // if WebSphere Application Server is committed when // hardening the transaction to backingMap. // We already did a flush in beforeCompletion if(didCommit) { session.commit(); } else { // otherwise rollback session.rollback();
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

149

} } catch(NoActiveTransactionException e) { // impossible in theory } catch(TransactionException e) { // given that we already did a flush, this should not fail } finally { // always clear the session from the mapping map. localIdToSession.remove(lid); } } } public synchronized void beforeCompletion(int localId, byte[] arg1) { Session session = (Session)localIdToSession.get(new Integer(localId)); if(session != null) { try { session.flush(); } catch(TransactionException e) { // WebSphere Application Server does not formally define // a way to signal the // transaction has failed so do this throw new RuntimeException("Cache flush failed", e); } } } }

Use ObjectGrid APIs with the TransactionCallback plug-in


This plug-in, when used as the TransactionCallback plug-in for an ObjectGrid, disables autocommit. The normal usage pattern for an ObjectGrid follows:
Session ogSession = ...; ObjectMap myMap = ogSession.getMap("MyMap"); ogSession.begin(); MyObject v = myMap.get("key"); v.setAttribute("newValue"); myMap.update("key", v); ogSession.commit();

When this TransactionCallback plug-in is in use, ObjectGrid assumes that the application uses the ObjectGrid when a container-managed transaction is present. The previous code snippet changes to the following code in this environment:
public void myMethod() { UserTransaction tx = ...; tx.begin(); Session ogSession = ...; ObjectMap myMap = ogSession.getMap("MyMap"); MyObject v = myMap.get("key"); v.setAttribute("newValue"); myMap.update("key", v); tx.commit(); }

150

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

The myMethod method is similar to a web application case. The application uses the normal UserTransaction interface to begin, commit, and roll back transactions. The ObjectGrid automatically begins and commits around the container transaction. If the method is an Enterprise JavaBeans (EJB) method that uses the TX_REQUIRES attribute, then remove the UserTransaction reference and the calls to begin and commit transactions and the method works the same way. In this case, the container is responsible for starting and ending the transaction.

Integrating ObjectGrid and the partitioning facility


Use the ObjectGridPartitionCluster sample application to learn about the combined functions of ObjectGrid and the partitioning facility (WPF). See ObjectGrid and the partitioning facility for a summary of how the ObjectGrid and partitioning facility work together. To use ObjectGrid with the partitioning facility, you must have WebSphere Extended Deployment installed in your environment. The ObjectGridPartitionCluster sample demonstrates the combined functions of ObjectGrid and Partitioning Facility (WPF). The ObjectGrid feature provides the capacity of caching key and value pairs by transaction, and the partitioning facility feature provides the capacity of context-based routing according to object characteristics. v Install and run the ObjectGridPartitionCluster sample application. See Installing and running the ObjectGridPartitionCluster sample application on page 153 for more information. v If you want to view or modify the source code of the sample application, you can load the enterprise archive (EAR) file into your development tool. See Building an integrated ObjectGrid and partitioning facility application on page 156 for more information. v Learn about the sample application. See Example: ObjectGrid and partitioning facility programming on page 160 for explanation about the code that is in the sample application. See Chapter 6, Integrating ObjectGrid with WebSphere Application Server, on page 133 for more information about how to integrate ObjectGrid with other WebSphere Application Server features. For more information about the ObjectGrid programming model, see Chapter 5, ObjectGrid application programming interface overview, on page 33.

ObjectGrid and the partitioning facility


ObjectGrid and the partitioning facility (WPF) features can work together to provide the caching of key and value pairs and context-based routing based on object characteristics. The ObjectGridPartitionCluster sample demonstrates the combined functions of ObjectGrid and the partitioning facility (WPF). ObjectGrid and partitioning facility are two features in the WebSphere Extended Deployment product. The ObjectGrid feature provides the capacity of caching key and value pairs in the transactional fashion, and the partitioning facility feature provides the capacity of context-based routing according to object characteristics. In addition to demonstrating loader and TransactionCallback plug-in features, this sample also demonstrates how to use the ObjectGridEventListener,
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

151

ObjectTransformer, and OptimisticCallback plug-ins. In particular, the sample demonstrates how to propagate local ObjectGrid transactions and how to invalidate the changed objects from one server to other servers with and without the optimistic version checker. You must use the partitioning facility context-based routing feature to ensure that object update, insert, and remove requests for the same key are routed to the same Java virtual machine (JVM) and that the object retrieval requests can be distributed across all of the ObjectGrid JVMs with workload management. Using the partitioning facility maintains data integrity across the different cluster member ObjectGrid instances. To maintain ObjectGrid consistency and integrity, you can use the partitioning facility to spread a large ObjectGrid out into many partitioned ObjectGrids, and the partitioning facility context-based routing directs requests according to ObjectGrid keys. For example, you need ObjectGrid to handle a large number of objects that cannot fit into a JVM ObjectGrid. You can use the partitioning facility feature to load data into different servers with the partitionLoadEvent method as preload and the partitioning facility context-based routing finds the right ObjectGrid for you. The sample creates a set of hash-based partitions and partition cluster routing contexts: v You can partition and map ObjectGrid keys to the WPF partitions with a many-to-many strategy. v The WPF partitions can be hosted in the WebSphere Application Server cluster in a many-to-many strategy. The following diagram shows the typical settings and configuration for the ObjectGridPartitionCluster sample :
Machine M1

WebSphere Application Server application server S1 ObjectGrid instance OGI1 Partition P1 OGK1 OGK2 OGK3

Machine M2

WebSphere Application Server application server S3 ObjectGrid instance OGI3 Partition P1200

Partition P2 OGK90

OGK120

OGK6

OGK1999

Partition P777 WebSphere Application Server application server S2 ObjectGrid instance OGI2 Partition P800 OGK999 OGK92 OGK96 OGK98 OGK99999

152

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

In the preceding diagram, the M1 machine and the M2 machine are used to deploy the ObjectGridPartitionCluster sample. Each physical machine can host one or more WebSphere Application Servers. For instance, the M1 machine hosts two application servers: the S1 application server and the S2 application server. The M2 machine hosts one server, which is the S3 application server. Each server has an ObjectGrid instance: OGI1ObjectGrid instance for the S1 application server, the OGI2 ObjectGrid instance for the S2 application server, and the OGI3 ObjectGrid instance for the S3 application server. Each application server can host many partitions. For example, the S1 server hosts the P1 partition and the P2 partition and the S3 server hosts the P1200 partition and the P777 partition. Each partition can host many ObjectGrid keys. For example, the P1 partition hosts the OGK1 and OGK2 ObjectGrid keys and the OGK3 P800 hosts the OGK92, OGK96, OGK98, and OGK9999 partitions. All of the ObjectGrid update, insert, and remove requests are routed according to ObjectGrid keys. You have two options for object retrievals: from any server in a workload managed strategy or from the particular server partition for this key.

Installing and running the ObjectGridPartitionCluster sample application


Use this task to install and run the ObjectGridPartitionCluster sample application to test the functionality between ObjectGrid and the partitioning facility. Install WebSphere Extended Deployment. See the WebSphere Extended Deployment Library page for instructions. A good environment for running the ObjectGridPartitionCluster sample includes installing the WebSphere Extended Deployment into two physical machines, or creating two nodes and federating them together with the deployment manager. 1. To adequately demonstrate the features of this sample, configure a cluster that has three or more cluster members. 2. Install the D_ObjectGridPartitionClusterSample.ear file. The partitioning facility (WPF) deployed D_ObjectGridPartitionClusterSample.ear file is ready to install and run. If you modify the sample source code, follow the build and wpf-deploy instructions to build and deploy your enterprise archive (EAR) file. The common way to install application EAR files is to use the administrative console. Follow the enterprise application install procedure to install the D_ObjectGridPartitionClusterSample.ear file. To access this part of the administrative console, click Applications > Install a new application. Do not deploy the EAR file during the installation. Use the default settings except in the step where you are asked to select an installation location. On this step, select the cluster that you defined, instead of the default server1 server. 3. Run the ObjectGridPartitionClusterSample client. a. Start the cluster. In the administrative console, click Servers > Clusters. Select the cluster and click Start. b. Run the WAS_INSTALL_ROOT\bin\wpfadmin balance script command. Verify that the partition has an active status using the WAS_INSTALL_ROOT\bin\wpfadmin list command. For information on the wpfadmin script and its commands, see the Partitioning Facility Guide in the WebSphere Extended Deployment Information Center.
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

153

c. To run the ObjectGridPartitionClusterSample client, run the following command:


WAS_INSTALL_ROOT/bin/launchClient.bat|sh \ WAS_INSTALL_ROOT/installableApps/D_ObjectGridPartitionClusterSample.ear \ CCBootstrapPort=PORT

Where PORT is the server RMI port that you can find in the server SystemOut.log file after you start the server. Usually this port value is one of the following values: 9810, 9811, 9812. For example, you might run the following command:
WAS_INSTALL_ROOT/bin/launchClient.bat|sh WAS_INSTALL_ROOT/installableApps/D_ObjectGridPartitionClusterSample.ear CCBootstrapPort=9811

For more advanced usage of this script, see ObjectGridPartitionClusterSample application client options on page 155. 4. Change the number of partitions. Change the number of partitions that the ObjectGridPartitionCluster session enterprise bean creates: The number of partitions that is created by the PFClusterObjectGridEJB session bean is decided by the NumberOfPartitions environment entry variable that is in the METAINF\ejbjar.xml file. The default value is 10. You can change the value of this environment variable and reinstall the application to create different numbers of partitions. Set the number of partitions to less than 999999. 5. Change the distributed listener options. You can change the following ObjectGrid distributed listener options:
Table 8. Distributed listener options Variable name enableDistribution, Description You can enable an ObjectGrid distributed listener with the enableDistribution environment entry variable that is in the EJB deployment descriptor. The default is true , which is enabled. Set the value to false to turn off the distributed listener. You can change the propagation mode with the propagationMode environment entry variable that is in the EJB deployment descriptor. The default is update. You can change the value to invalidate if you do not want the default value. You can change the propagation version option with the propagationVersionOption environment entry variable that is located in the EJB deployment descriptor. The default is enable. You can set the value to disable. You can change the compression mode with the compressionMode environment entry variable that is located in the EJB deployment descriptor. The default is enable. You can set the value to disable.

propagationMode

propagationVersionOption,

compressionMode

The default is to propagate the updates with the version check. You might want to set the value to the invalidate mode without the version check. You installed and ran the ObjectGridPartitionCluster sample application.

154

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

ObjectGridPartitionClusterSample application client options


Use these options for advanced use in running the D_ObjectGridPartitionClusterSample.ear file.

Advanced sample usage


See Installing and running the ObjectGridPartitionCluster sample application on page 153 for more information about installing and running the D_ObjectGridPartitionClusterSample.ear file. For advanced use of the sample, refer to the following full usage guide:
WAS_INSTALL_ROOT/bin/launchClient.bat|sh WAS_INSTALL_ROOT/installableApps/D_ObjectGridPartitionClusterSample.ear CCproviderURL=corbaloc::HOSTNAME:SERVER_RMI_PORT [loop LOOP] [threads NUMBER_OF_THREADS] [add NUMBER_OF_STOCKS_PER_PARTITION] [waitForPropagation SECONDS_TO_WAIT_FOR_PROPAGATION] [getIteration NUMBER_OF_ITERATION_PER_OGKEY]

Fill in the following variables: v HOSTNAME : Specifies the host name of the application server that is running. v SERVER_RMI_PORT: Specifies the Bootstrap port of the application server. v LOOP: Specifies how many loops the client runs. This parameter is optional. The default value is 1. v NUMBER_OF_THREADS: Specifies how many threads the client runs. This parameter is optional. The default value is 1. v NUMBER_OF_STOCKS_PER_PARTITION: Specifies the number of stocks for each partition to add. This parameter is optional. The default is 3. v SECONDS_TO_WAIT_FOR_PROPAGATION : Specifies the seconds to wait for newly added or updated ObjectGrid objects to be propagated to other servers. The default is 2seconds. v NUMBER_OF_ITERATION_PER_OGKEY : Specifies the number of iterations of retrieving objects in ObjectGrid in a workload managed fashion. The default is 6. With more iterations specified, a clear pattern is seen for objects of the same key in different WebSphere Application Server servers.

Sample output
The output of this command looks like the following example:
C:\dev\xd6\bin>launchClient D_ObjectGridPartitionClusterSample.ear CCBootstrapPort=9812 IBM WebSphere Application Server, Release 6.0 J2EE Application Client Tool Copyright IBM Corp., 19972004 WSCL0012I: Processing command line arguments. WSCL0013I: Initializing the J2EE Application Client Environment. WSCL0035I: Initialization of the J2EE Application Client Environment has completed. WSCL0014I: Invoking the Application Client class com.ibm.websphere.samples.objectgrid.partitionclust er.client.PartitionObjectGrid ObjectGrid Partition Sample has 10 partitions PARTITION: ObjectGridHashPartition000007>clusterdevNode01/s2 PARTITION: ObjectGridHashPartition000003>clusterdevNode02/s3 PARTITION: ObjectGridHashPartition000005>clusterdevNode01/s2 PARTITION: ObjectGridHashPartition000010>clusterdevNode02/s3 PARTITION: ObjectGridHashPartition000006>clusterdevNode02/s3 PARTITION: ObjectGridHashPartition000009>clusterdevNode01/s2 PARTITION: ObjectGridHashPartition000008>clusterdevNode01/s1 PARTITION: ObjectGridHashPartition000002>clusterdevNode02/s3
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

155

PARTITION: ObjectGridHashPartition000001>clusterdevNode02/s3 PARTITION: ObjectGridHashPartition000004>clusterdevNode01/s2 ************** Partition=ObjectGridHashPartition000004**************** ObjectGrid Operations: Stock Ticket=Stock000104 get on partition for ticket: Stock000104>clusterdevNode02/s2 update: Stock000104>clusterdevNode02/s2 sleep 2 seconds..... Iteration 1 : Stock000104>clusterdevNode01/s2 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 Iteration 2 : Stock000104>clusterdevNode01/s1 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 Iteration 3 : Stock000104>clusterdevNode02/s3 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 Iteration 4 : Stock000104>clusterdevNode01/s2 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 Iteration 5 : Stock000104>clusterdevNode02/s3 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 Iteration 6 : Stock000104>clusterdevNode02/s3 > ObjectGrid Stock000104 price=43.35478459674703 lastTransaction=1121137456584 ObjectGrid Operations: Stock Ticket=Stock000114 get on partition for ticket: Stock000114>clusterdevNode01/s2 update: Stock000114>clusterdevNode02/s2 sleep 2 seconds..... Iteration 1 : Stock000114>clusterdevNode02/s3 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 Iteration 2 : Stock000114>clusterdevNode01/s2 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 Iteration 3 : Stock000114>clusterdevNode01/s1 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 Iteration 4 : Stock000114>clusterdevNode02/s3 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 Iteration 5 : Stock000114>clusterdevNode01/s2 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 Iteration 6 : Stock000114>clusterdevNode02/s3 > ObjectGrid Stock000114 price=39.70991373766818 lastTransaction=1121137458737 ObjectGrid Operations: Stock Ticket=Stock000124 get on partition for ticket: Stock000124>clusterdevNode02/s2 update: Stock000124>clusterdevNode01/s2 sleep 2 seconds..... Iteration 1 : Stock000124>clusterdevNode02/s3 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 Iteration 2 : Stock000124>clusterdevNode02/s3 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 Iteration 3 : Stock000124>clusterdevNode01/s2 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 Iteration 4 : Stock000124>clusterdevNode01/s1 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 Iteration 5 : Stock000124>clusterdevNode02/s3 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 Iteration 6 : Stock000124>clusterdevNode01/s2 > ObjectGrid Stock000124 price=35.37356414423455 lastTransaction=1121137460940 C:\dev\xd6\bin>

Building an integrated ObjectGrid and partitioning facility application


Open, modify, and install the ObjectGrid partitioning sample application. Use these steps to modify, export, and install the ObjectGridPartitionSample.ear file in a WebSphere Extended Deployment environment. If you do not want to make changes to the sample file, you can use the deployed and partitioning facility (WPF)-enabled D_ObjectGridPartitionClusterSample.ear file. If you use the D_ObjectGridPartitionClusterSample.ear file, you can install and run the file without performing the following steps. Both enterprise archive (EAR) files are in the WAS_INSTALL_ROOT/installableApps directory.

156

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

1. Set up theObjectGridPartitionSample.ear file in your build environment, such as IBM Rational Application Developer Version 6.0.x or the Application Server Toolkit Version 6.0.x. See Getting started with building an ObjectGrid and partitioning facility application for more information. 2. Modify any of source code in the sample. 3. Export the ObjectGridPartitionClusterSample application from your build environment as an EAR file. Exporting the ObjectGridPartitionClusterSample.ear file in IBM Rational Application Developer on page 158 for more information. 4. Deploy the application so that it can work with the partitioning facility. See Deploying the ObjectGridPartitionClusterSample.ear file to work with the partitioning facility on page 159 for more information. 5. Install the ObjectGridPartitionClusterSample.ear file into WebSphere Extended Deployment. The common way to install application EAR files is to use the WebSphere Application Server administrative console. Follow the enterprise application installation procedure of the administrative console to install the D_ObjectGridPartitionClusterSample.ear file. Do not deploy the file during the installation; use the default instead. Use the default settings for each step except when you are asked to select where to install. In this step, select the cluster that you defined instead, of the default server1 server. You installed the ObjectGridPartitionClusterSample.ear file into a WebSphere Extended Deployment environment. For more information about programming with ObjectGrid, partitioning facility, and the sample applications, see Example: ObjectGrid and partitioning facility programming on page 160.

Getting started with building an ObjectGrid and partitioning facility application


Use the Application Server Toolkit Version 6.0.x or IBM Rational Application Developer Version 6.0.x to rebuild the sample application. The ObjectGridPartitionClusterSample.ear file in the WAS_INSTALL_ROOT/installableApps directory contains all the source code. You can use the Application Server Toolkit Version 6.0.x or IBM Rational Application Developer Version 6.0.x to rebuild this sample application. This task uses Rational Application Developer as an example to establish the build environment for the ObjectGridPartitionClusterSample.ear file. You can also use the Application Server Toolkit; a free assembly tool that is shipped with WebSphere Application Server on a separate CD. The deployed and WPF-enabled enterprise archive (EAR) file, the D_ObjectGridPartitionClusterSample.ear file, also in the WAS_INSTALL_ROOT/installableApps directory, is ready to install and run. 1. Import the ObjectGridPartitionClusterSample.ear file into Rational Application Developer. a. Start Rational Application Developer. b. Optional: Open the Java 2 Platform, Enterprise Edition (J2EE) perspective to work with J2EE projects. Click Window > Open Perspective > Other > J2EE. c. Optional: Open the Project Explorer view. Click Window > Show View > Project Explorer. Another helpful view is the Navigator view: Window > Show View > Navigator.
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

157

d. Import the ObjectGridPartitionClusterSample.ear file. Click File > Import > EAR file, then click Next. e. Select the ObjectGridPartitionClusterSample.ear file from the WAS_INSTALL_ROOT/installableApps directory. f. Optional: Click New to open the New Server Runtime wizard and follow the instructions. g. In the Target server field, select the WebSphere Application Server V6.0 type of Server Runtime. h. Click Finish. The ObjectGridPartitionClusterSample, ObjectGridPartitionClusterSampleEJB, and ObjectGridPartitionClusterSampleClient projects must be created and visible in the Project Explorer view. 2. Set up the ObjectGridPartitionClusterSampleEJB project. a. In the Project Explorer view of the J2EE perspective, right-click the ObjectGridPartitionClusterSampleEJB project in the EJB projects, and select Properties. The Properties window is displayed. b. Click the Java Build Path in the left panel, click the Libraries tab in the right panel, and select Add Variable. The New Variable Classpath Entry window is displayed. c. Click Configure Variables to open the Preference window. d. Click New to open the New Variable Entry window. e. Type ObjectGridPartitionCluster_JAR for the Name and click File to open the JAR Selection window. f. Browse to the WAS_INSTALL_ROOT/lib directory, and select ObjectGrid.jar. Click Open to close the JAR Selection window. g. Click OK to close the New Variable Entry window. The ObjectGridPartitionCluster_JAR variable is displayed in the Class path variables list. h. Click OK to close the Preference window. i. Select the ObjectGridPartitionCluster_JAR variable from the variables list and click OK to close the New Variable Classpath Entry window. The ObjectGridPartitionCluster_JAR variable is displayed in the Libraries panel. j. Repeat this procedure to add the wpf.jar file to your environment. k. Verify that the wpf.jar file and the objectgrid.jar file are in your build class path. After your build environment is set up, you can modify source code and apply other changes. See Building an integrated ObjectGrid and partitioning facility application on page 156 for more information.

Exporting the ObjectGridPartitionClusterSample.ear file in IBM Rational Application Developer


After you make changes to the sample file, you can export the ObjectGridPartitionClusterSample application to create an enterprise archive (EAR) file that you can install on WebSphere Extended Deployment servers. You must have the ObjectGridPartitionSample.ear file imported into your development tools so that you can make changes to the source. See Getting started with building an ObjectGrid and partitioning facility application on page 157 for more information. Before exporting, make your changes to the sample application.

158

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

You can export the ObjectGridPartitionClusterSample.ear file from the ObjectGridPartitionClusterSample project in enterprise applications in the IBM Rational Application Developer. You can install the exported ObjectGridPartitionClusterSample.ear file on any WebSphere Extended Deployment Version 6.0 server after deploying the partitioning facility. 1. In the Project Explorer view of the Java 2 Platform, Enterprise Edition (J2EE) perspective, right-click the ObjectGridPartitionClusterSample application that is located under Enterprise Applications. Click Export > EAR file. The Export window is displayed. 2. Click Browse to open the Save As window. Locate the target output directory, specify the file name as ObjectGridPartitionClusterSample, and click Save. 3. Click Browse to open the Save As window. Locate the target output directory and specify the file name as ObjectGridPartitionClusterSample. Click Save. The ObjectGridPartitionClusterSample.ear file is created in the specified target output directory. After you deploy the ObjectGridPartitionClusterSample.ear file for the partitioning facility (WPF), you can run the file in WebSphere Extended Deployment. See Deploying the ObjectGridPartitionClusterSample.ear file to work with the partitioning facility for more information.

Deploying the ObjectGridPartitionClusterSample.ear file to work with the partitioning facility


If you plan on installing the ObjectGridPartitionClusterSample.ear file on WebSphere Extended Deployment, you must perform a wpf-deploy operationon the file. You must have an existing ObjectGridPartitionClusterSample.ear file. To modify the existing file, see Building an integrated ObjectGrid and partitioning facility application on page 156. Perform the wpf-deploy operation to prepare the enterprise archive (EAR) file in a WebSphere Extended Deployment environment. 1. Create a DEST_DIR directory. 2. Copy the ObjectGridPartitionClusterSample.ear file nto the DEST_DIR directory. Rename the ObjectGridPartitionClusterSample.ear file to the old_ObjectGridPartitionClusterSample.ear file. 3. Run the following command, where WORKING_DIR is the working directory for the ejbdeloy tool, for example, the c:\temp directory.
WAS_HOME\bin\ejbdeploy.bat|ejbdeploy.sh DEST_DIR\old_ObjectGridPartitionClusterSample.ear WORKING_DIR DEST_DIR\ObjectGridPartitionClusterSample.ear

4. Run the following command, where TEMP_DIR is a temporary directory for the tool. If the -keep argument is specified, the temporary directories that are created by the wpfStubUtil utility are not deleted.
WAS_HOME\bin\wpfStubUtil.cmd|wpfStubUtil.sh DEST_DIR\ObjectGridPartitionClusterSample.ear ObjectGridPartitionClusterSampleEJB.jar com/ibm/websphere/samples/ objectgrid/partitioncluster/ejb/PFClusterObjectGridEJB.class TEMP_DIR [stubDebug|keep]

The ObjectGridPartitionClusterSample.ear file is ready to run in a WebSphere Extended Deployment environment. The D_ObjectGridPartitionClusterSample.ear

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

159

sample file that ships with WebSphere Extended Deployment is already deployed. You do not need to deploy this file before you install the application if you did not change the source code. Install the ObjectGridPartitionClusterSample.ear file into the WebSphere Extended Deployment environment with the administrative console. See Building an integrated ObjectGrid and partitioning facility application on page 156 for more information.

Example: ObjectGrid and partitioning facility programming


This example demonstrates how to use the combined functions of ObjectGrid and the partitioning facility in a Java 2 Platform, Enterprise Edition environment in WebSphere Extended Deployment.

Purpose
In addition to demonstrating the combined ObjectGrid and partitioning facility (WPF) functions, this example also demonstrates ObjectGrid distributed listener propagation and invalidation. Object update, insert, and remove requests are routed to specific servers where partitions are hosted for the corresponding ObjectGrid keys. ObjectGrid get method requests are workload managed among all the servers. This example also illustrates how to partition a big ObjectGrid into many smaller ObjectGrids and use the partitionLoadEvent method to preload data so that the partitioned ObjectGrid can host an unlimited number of objects.

Overview
The ObjectGridPartitionClusterSampler.ear file creates a stock object that illustrates how ObjectGrid and the partitioning facility work together. The stock object contains the following properties: v ticket v company v serialNumber v description v lastTransaction v price Where the lastTransaction property is the time that the stock has been changed. Use the lastTransaction property to indicate the freshness of objects in the ObjectGrid of different Java virtual machines (JVM). In the sample, the ObjectGrid instance is created in the Enterprise JavaBeans (EJB) setContext method with the ObjectGridFactory class. Define a set of hash-based partitions. The default value is 10 partitions, but you can change the number of partitions. Hash the stock tickets into these partitions by using the SampleUtility.java file. Each partition can host many ObjectGrid key and value pairs. The sample demonstrates how the ObjectGrid insert, update, and remove requests are routed to a specific partitioned server, and how the ObjectGrid get method

160

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

requests route to either a particular server for its key or any server in a cluster. The sample compares object values for a key from different servers after value changes due to an update, insert, or remove operation for this key occurs in a particular server.

Location
Use this sample in a cluster environment where each server can host many partitions and where each partition can host many objects with different keys. Two ObjectGrid partition cluster sample files exist in the <install_root>\installableApps\ directory: v The ObjectGridPartitionClusterSampler.ear file contains the source code. To see the source, expand the EAR file into the file system or import the source into a development environment. See Building an integrated ObjectGrid and partitioning facility application on page 156 for more information. v The D_ObjectGridPartitionClusterSample.ear file is already deployed for the partitioning facility. Follow the readme file and instructions to get this file running quickly.

Explanation
The following sections include explanation about the ObjectGrid partition cluster sample application: v ObjectGrid operation EJB Interface v PartitionKey class on page 163 v SampleUtility class and partition mapping on page 165 v ObjectGrid creation in the enterprise bean setContext method on page 167 v Singleton ObjectGridFactory class on page 168 v ObjectGrid partition preload on page 170

ObjectGrid operation EJB Interface


This article demonstrates the ObjectGrid operation Enterprise JavaBeans (EJB) interface that performs get, get from partitioned server, insert, update, and remove operations.

Purpose
The ObjectGrid operation EJB interface performs get, get from partitioned server, insert, update, and remove operations. The get from partitioned server method is routed to a partition that corresponds to the key it requests. The get method is routed in a worked load managed strategy to any server.

The PFClusterObjectGridEJB interface


The PFClusterObjectGridEJB interface content follows:
/** * Remote interface for Enterprise Bean: PFClusterObjectGridEJB */ public interface PFClusterObjectGridEJB extends javax.ejb.EJBObject { public String PARTITION_PREFIX = "ObjectGridHashPartition"; /** * Get all Partitions * * @return Array of Strings
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

161

* @throws java.rmi.RemoteException */ public String [] getAllPartitions() throws java.rmi.RemoteException; /** * Get where partition is hosted * * @param partition * @return String * @throws java.rmi.RemoteException */ public String getServer(String partition) throws java.rmi.RemoteException; /** * Get Stock object and its server information * (ServerIDResult) for a stock ticket * from any server in a cluster (that has had workload management) * * @param ticket * @return * @throws java.rmi.RemoteException */ public ServerIDResult getStock(String ticket) throws java.rmi.RemoteException; /** * Get Stock object and its partitioned server * information for a stock ticket * from the partition this ticket key is hashed to * * @param ticket * @return ServerIDResult * @throws java.rmi.RemoteException */ public ServerIDResult getStockOnPartitionedServer(String ticket) throws java.rmi.RemoteException; /** * Update stock in a particular server where the partition is * active for this stock ticket key. * * @param stock * @return ServerIDResult * @throws java.rmi.RemoteException */ public ServerIDResult updateStock(Stock stock) throws java.rmi.RemoteException; /** * Remove stock in a particular server where the partition is * active for this stock ticket key. * * @param ticket * @return ServerIDResult * @throws java.rmi.RemoteException */ public ServerIDResult removeStock(String ticket) throws java.rmi.RemoteException; /** * Insert stock in a particular server where the partition is * active for this stock ticket key. * * @param stock * @return ServerIDResult * @throws java.rmi.RemoteException

162

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

*/ public ServerIDResult insertStock(Stock stock) throws java.rmi.RemoteException; /** * Retrieve data from all servers and compare values * * @param server * @return ServerObjectGridVerification * @throws java.rmi.RemoteException */ public ServerObjectGridVerification verifyObjectGrid(String server) throws java.rmi.RemoteException; }

PartitionKey class
The PartitionKey class controls the behavior of the partitioning facility context-based routing. The following code illustrates the sample partition key class. When the method returns not null, it is routed with the partitioning facility (WPF) router. When the method returns null, it is forwarded to the workload management (WLM) router.
/** * PartitionKey for Partitioned Stateless Session Bean WPFKeyBasedPartition * */ public class PFClusterObjectGridEJB_PartitionKey { /** * Number of Partitions * * Default is 10. * */ static int numOfPartitions=10; /** * Only once to getPartitionNumbers */ static boolean getNumOfPartitions=true; /** * Get the number of partitions * */ static void getPartitionNumbers(){ //get only once if (getNumOfPartitions){ try { InitialContext ic = new InitialContext(); PFClusterObjectGridEJBHome home = (PFClusterObjectGridEJBHome) PortableRemoteObject.narrow( ic.lookup("java:comp/env/ejb/PFClusterObjectGridEJB"), PFClusterObjectGridEJBHome.class); final PFClusterObjectGridEJB session = home.create(); String[] PARTITIONS = session.getAllPartitions(); numOfPartitions=PARTITIONS.length; getNumOfPartitions=false; } catch (ClassCastException e) { e.printStackTrace(); numOfPartitions=10; } catch (RemoteException e) { e.printStackTrace(); numOfPartitions=10; }
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

163

catch (NamingException e) { e.printStackTrace(); numOfPartitions=10; } catch (CreateException e) { e.printStackTrace(); numOfPartitions=10; } } } /** * Return partition key * * @param partition * @return String */ public static String getStock(String key) { return null; } /** * Return partition key * * @param key * @return String */ public static String getServer(String key) { return key; } /** * Retrieve ObjectGrid data from a partitioned server where * datas changes happen (the highest quality and integrity). * * @param ticket * @return hashcode of stock ticket */ public static String getStockOnPartitionedServer(String ticket) { if (ticket==null){ return null; } getPartitionNumbers(); return SampleUtility.hashStockKeyToPartition(ticket, numOfPartitions); } /** * Return partition key * * @param stock * @return hashcode of stock ticket */ public static String updateStock(Stock stock) { getPartitionNumbers(); String ticket=null; if (stock!=null){ ticket=stock.getTicket(); } return SampleUtility.hashStockKeyToPartition(ticket, numOfPartitions); } /** * Return partition key * * @param stock * @return hashcode of stock ticket */ public static String insertStock(Stock stock) { getPartitionNumbers(); String ticket=null; if (stock!=null){ ticket=stock.getTicket();

164

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

} return SampleUtility.hashStockKeyToPartition(ticket, numOfPartitions); } /** * Return partition key * * @param server * @return String */ public static String verifyObjectGrid(String server) { return server; } /** * Return partition key * * @param stock * @return hashcode of stock ticket */ public static String removeStock(String ticket) { if (ticket==null){ return null; } getPartitionNumbers(); return SampleUtility.hashStockKeyToPartition(ticket, numOfPartitions); } /** * Return partition key * * @param partition * @return */ public static String getAllPartitions() { return null; } }

Each remote method must a corresponding method that returns a valid string or null.

SampleUtility class and partition mapping


Use the SampleUtility.java file to manipulate keys, stock tickets, hash, and partitions. You can also use this file to map ObjectGrid keys to partitions. You can develop similar a utility class to map ObjectGrid keys to partitions that can meet your business needs. To use the partitioning facility with ObjectGrid, you must map different keys into different partitions.

SampleUtility class
The utility class for the ObjectGridPartitionCluster sample follows:
/** * Utility class for ObjectGridPartitionCluster sample * * */ public class SampleUtility { /** * Container for recording partitions. */ static Map serverPartitions= new HashMap(); /** * Partition name prefix */ public static String PARTITION_PREFIX = "ObjectGridHashPartition";
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

165

/** * Stock name prefix */ public static String STOCK_PREFIX="Stock"; /** * Retrieve the number part of partition name * * @param partition * @return int */ public static int getIntFromPartition(String partition){ int result=1; int pre=PARTITION_PREFIX.length(); int p=partition.length(); String num=partition.substring(pre, p); result=Integer.parseInt(num); return result; } /** * Retrieve the number part of stock ticket * * @param ticket * @return */ public static int getIntFromStockTicket(String ticket){ int result=1; int pre=STOCK_PREFIX.length(); int p=ticket.length(); String num=ticket.substring(pre, p); result=Integer.parseInt(num); return result; } /** * Hash stock ticket to a given hash base. * * @param ticket * @param base * @return int */ public static int hashTicket(String ticket, int base){ if (base<1){ return 0; } int hash=0; int num=getIntFromStockTicket(ticket); hash= num % base; return hash; } /** * Hash stock key to a partition * * @param ticket * @param base * @return String partition name */ public static String hashStockKeyToPartition(String ticket, int base){ String p=null; int hashcode=hashTicket(ticket, base)+1; p=PARTITION_PREFIX+ padZeroToString(hashcode+"", 6); return p; } /** * Record server/partition *

166

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

* @param server * @param partition */ public static void addServer(String server, String partition){ serverPartitions.put(server, partition); } /** * Remove server/partition * * @param server */ public static void removeServer(String server){ serverPartitions.remove(server); } /** * Get all servers where partitions are active. * * @return Iterator String */ public static Iterator getAllServer(){ return serverPartitions.values().iterator(); } }

You must use the same global hash base and parse variable to hash into the hash base. Consider the following example:
myKey.hashCode % hashBase

You need to parse out myKey as hash variable and you need to keep the same hash base among different servers. In the preceding example, the same variable from the Java environment is looked up. You cannot use key1 % 100, but you can use key2 % 90.

ObjectGrid creation in the enterprise bean setContext method


Create the ObjectGrid instance in the enterprise bean setContext method as in the PFClusterObjectGridEJBBean.java file and retrieve the preload data.
/** * setSessionContext * * with ObjectGrid instance */ public void setSessionContext(javax.ejb.SessionContext ctx) { mySessionCtx = ctx; try { InitialContext ic = new InitialContext(); //get PartitionManager ivManager = (PartitionManager) ic.lookup("java:comp/websphere/wpf/PartitionManager"); // get enableDistribution configuration boolean enableDistribution = ((Boolean) ic.lookup("java:comp/env/enableDistribution")).booleanValue(); System.out.println("***** enableDistribution="+ enableDistribution); // get propagationMode configuration String propagationMode = (String) ic.lookup("java:comp/env/propagationMode"); System.out.println("***** pMode="+ propagationMode); String pMode=null; if (propagationMode.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_MODE_DEFAULT_KEY)|| propagationMode.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_MODE_INVALID_KEY) ){ pMode=propagationMode; } // get propagationVersionOption configuration String propagationVersionOption = (String)
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

167

ic.lookup("java:comp/env/propagationVersionOption"); System.out.println("***** pVersionOption="+ propagationVersionOption); String pVersion=null; if (propagationVersionOption.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_MODE_VERS_KEY)|| propagationMode.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_MODE_NOVERS_KEY) ){ pVersion=propagationVersionOption; } // get compressionMode configuration String compressionMode = (String) ic.lookup("java:comp/env/compressionMode"); System.out.println("***** compressMode="+ compressionMode); String compressMode=null; if (compressionMode.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_COMPRESS_DISABLED)|| propagationMode.equals(com.ibm.ws.objectgrid.Constants. OBJECTGRID_TRAN_PROPAGATION_COMPRESS_ENABLED) ){ compressMode=compressionMode; } // whethere preload is enabled bPreload = ((Boolean) ic.lookup("java:comp/env/preload")).booleanValue(); System.out.println("***** enablePreload="+ bPreload); //whethere remove is enabled bRemove = ((Boolean) ic.lookup("java:comp/env/remove")).booleanValue(); System.out.println("***** enableRemove="+ bRemove); // whethere Loader is enabled boolean bLoader = ((Boolean) ic.lookup("java:comp/env/loader")).booleanValue(); System.out.println("***** enableLoader="+ bLoader); // get file path and name String filePathandName = (String) ic.lookup("java:comp/env/filePathandName"); System.out.println("***** fileName="+ filePathandName); //get ObjectGrid instance og=ObjectGridFactory.getObjectGrid(ogName, enableDistribution, pMode, pVersion, compressMode, bLoader, filePathandName); if (og==null){ throw new RuntimeException ("ObjectGrid insance is null in ObjectGridPartitionClusterSample"); } System.out.println("Bean Context, getObjectGrid=" + og + " for name="+ ogName); if (bPreload && !lock){ System.out.println("Preload data"); PersistentStore store=PersistentStore.getStore(filePathandName); store.preload(10); store.verify(10); lock=true; preloadData=store.getAllRecords(); } } catch (Exception e) { logger.logp(Level.SEVERE, CLASS_NAME, "setSessionContext", "Exception: " + e); throw new EJBException(e); } }

Singleton ObjectGridFactory class


An ObjectGrid instance is created with a custom factory that caches the ObjectGrid instance with custom settings.

168

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

An example of how to create an ObjectGrid instance programatically, set the ObjectGridTransformer object, configure the propagation event listener, and set this listener to the ObjectGrid instance follows. You also can use an XML file to perform this configuration.
/** * * Create ObjectGrid instance and configure it. * * */ public class ObjectGridFactory { /** * ObjectGrid name */ static String ogName="WPFObjectGridSample"; /** * ObjectGrid instance */ static ObjectGrid og=null; /** * ObjectGrid session */ static Session ogSession=null; /** * Map name */ static String mapName="SampleStocks"; /** * ObjectGrid cache */ static Map ogCache= new HashMap(); /** * Get ObjectGrid instance * * @param ogn * @param enableDist * @param pMode * @param pVersion * @param compressMode * @return */ public static synchronized ObjectGrid getObjectGrid(String ogn, boolean enableDist, String pMode, String pVersion, String compressMode, boolean loader, String fileName){ if (ogn!=null){ ogName=ogn; } else { throw new IllegalArgumentException ("ObjectGrid name given is null"); } if (ogCache.containsKey(ogName)){ return (ObjectGrid) ogCache.get(ogName); } try { ObjectGridManager manager= ObjectGridManagerFactory. getObjectGridManager(); og=manager.createObjectGrid(ogName); if (enableDist){ TranPropListener tpl=new TranPropListener(); if (pMode!=null){ tpl.setPropagateMode(pMode);
Chapter 6. Integrating ObjectGrid with WebSphere Application Server

169

} if (pVersion!=null){ tpl.setPropagateVersionOption(pVersion); } if (compressMode!=null) { tpl.setCompressionMode(compressMode); } og.addEventListener(tpl); } // Define BackingMap and set the Loader BackingMap bm = og.defineMap(mapName); ObjectTransformer myTransformer= new MyStockObjectTransformer(); bm.setObjectTransformer(myTransformer); OptimisticCallback myOptimisticCallback= new MyStockOptimisticCallback(); if (loader){ TransactionCallback tcb=new MyTransactionCallback(); Loader myLoader= new MyCacheLoader(fileName, mapName); og.setTransactionCallback(tcb); bm.setLoader(myLoader); } og.initialize(); ogCache.put(ogName, og); } catch (Exception e) { } return og; } }

ObjectGrid partition preload


This topic discusses how to preload an ObjectGrid instance. Use the partitionLoadEvent method to load objects that are related to this partition only when the partition is activated. By loading objects when the partitioning is activated, you partition ObjectGrid so that ObjectGrid can handle large numbers of objects.
/** * This is called when a specific partition is assigned to this server process. * @param partitionName * @return */ public boolean partitionLoadEvent(String partitionName) { //preload data preloadDataForPartition(partitionName); logger.logp( Level.FINER, CLASS_NAME, "partitionLoadEvent", "Loading "+ partitionName ); return true; } /** * * preload data * * @param partition */ private synchronized void preloadDataForPartition(String partition){ if (bPreload && (preloadData!=null)){ Iterator itr=preloadData.keySet().iterator(); while (itr.hasNext()){ String ticket= (String) itr.next();

170

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

String p=SampleUtility. hashStockKeyToPartition(ticket, numOfPartitions); if (partition.equals(p)){ Stock stock= (Stock) preloadData.get(ticket); System.out.println("preload in partition=" + partition + " with data ticket="+ ticket); insertStock(stock); } } } }

You might need to disable the distributed updates if you use the partitioned preload of your big ObjectGrid to partition your big ObjectGrid. The current version of distributed updates cannot be partitioned. The partitioning facility (WPF) context-based routing finds the correct data at the correct partition.

Chapter 6. Integrating ObjectGrid with WebSphere Application Server

171

172

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 7. ObjectGrid performance best practices


You can improve the performance of a ObjectGrid Map with the following best practices. These best practices are implemented only in the context of the application and its architecture. Every application and environment uses a different solution for performance. ObjectGrid provides built-in customizations to improve performance, but you can also improve performance within the application architecture. The following areas offer performance improvements: v Locking performance best practices Choose between the different locking strategies that can affect the performance of your applications. v copyMode method best practices on page 174 Choose between the different copy modes that can be used to change how ObjectGrid maintains and copies entries. v ObjectTransformer interface best practices on page 178 Use the ObjectTransformer interface to allow callbacks to the application to provide custom implementations of common and expensive operations such as object serialization and a deep copy on an object. v Plug-in evictor performance best practices on page 179 Choose between least frequently used (LFU) and least recently used (LRU) eviction strategies. v Default evictor best practices on page 181 Properties for the default time to live (TTL) evictor, the default evictor that is created with every backingMap.

Locking performance best practices


Locking strategies can affect the performance of your applications. For more details about the following locking strategies, see the Locking on page 58 topic.

Pessimistic locking strategy


You can use the pessimistic locking strategy for read and write map operations where keys often collide. The pessimistic locking strategy has the greatest impact on performance.

Optimistic locking strategy


Optimistic locking is the default configuration. This strategy improves both on performance and scalability over the pessimistic strategy. Use this strategy when your applications can tolerate some optimistic update failures, while still performing better than pessimistic strategy. This strategy works great for read mostly, infrequent update applications.

Copyright IBM Corp. 2004, 2005

173

None locking strategy


Use the none locking strategy is good for applications that are read only. The none locking strategy does not obtain any locks. Therefore, it offers the most concurrency, performance and scalability.

copyMode method best practices


ObjectGrid makes a copy of the value based on the CopyMode setting. You can use the BackingMap API setCopyMode(CopyMode, valueInterfaceClass) method to set the copy mode to one of the following final static fields that are defined in the com.ibm.websphere.objectgrid.CopyMode class. When an application uses the ObjectMap interface to obtain a reference to a map value, it is recommended to use that reference only within the ObjectGrid transaction that obtained the reference. Using the reference in a different ObjectGrid transaction can lead to errors. For example, if you use the pessimistic locking strategy for the BackingMap, a get or getForUpdate method call acquires an S (shared) or U (update) lock respectively. The get method returns the reference to the value and the lock that is obtained is released when the transaction completes. The transaction must call the get or getForUpdate method to lock the map entry in a different transaction. Each transaction must obtain its own reference to the value by calling the get or getForUpdate method instead of reusing the same value reference in multiple transactions. Use the following information to choose between the copy modes with the following information:

COPY_ON_READ_AND_COMMIT mode
The COPY_ON_READ_AND_COMMIT mode is the default mode. The valueInterfaceClass argument is ignored when this mode is used. This mode ensures that an application does not contain a reference to the value object that is in the BackingMap, and instead the application is always working with a copy of the value that is in the BackingMap. The copy_on_read mode ensures the application can never inadvertently corrupt the data that is cached in the BackingMap. When an application transaction calls an ObjectMap.get method for a given key, and it is the first access of the ObjectMap entry for that key, a copy of the value is returned. When the transaction is committed, any changes committed by the application are copied to the BackingMap to ensure that the application does not have a reference to the committed value in the BackingMap.

COPY_ON_READ mode
The COPY_ON_READ mode improves performance over the COPY_ON_READ_AND_COMMIT mode by eliminating the copy that occurs when a transaction is committed. The valueInterfaceClass argument is ignored when this mode is used. To preserve the integrity of the BackingMap data, the application ensures that every reference that it has for an entry is destroyed after the transaction is committed. With this mode, the ObjectMap.get method returns a copy of the value instead of a reference to the value to ensure that changes made by the application to the value does not affect the BackingMap value until the transaction is committed. However, when the transaction does commit, a copy of changes is not made. Instead, the reference to the copy that was returned by the ObjectMap.get method is stored in the BackingMap. The application destroys all map entry references after the transaction is committed. If application fails to do

174

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

this, the application might cause the data cached in BackingMap to become corrupted. If an application is using this mode and is having problems, switch to COPY_ON_READ_AND_COMMIT mode to see if the problem still exists. If the problem goes away, then the application is failing to destroy all of its references after the transaction has committed.

COPY_ON_WRITE mode
The COPY_ON_WRITE mode improves performance over the COPY_ON_READ_AND_COMMIT mode by eliminating the copy that occurs when the ObjectMap.get method is called for the first time by a transaction for a given key. The ObjectMap.get method returns a proxy to the value instead of a direct reference to the value object. The proxy ensures that a copy of the value is not made unless the application calls a set method on the value interface that is specified by the valueInterfaceClass argument. The proxy provides a copy on write implementation. When a transaction commits, the BackingMap examines the proxy to determine if any copy was made as a result of a set method being called. If a copy was made, then the reference to that copy is stored in the BackingMap. The big advantage of this mode is that a value is never copied on a read or at a commit when the transaction never calls a set method to change the value. The COPY_ON_READ_AND_COMMIT and COPY_ON_READ modes both make a deep copy when a value is retrieved from the ObjectMap. If an application only updates some of the values that are retrieved in a transaction then this mode is not optimal. The COPY_ON_WRITE mode supports this behavior in an efficient manner but requires that the application uses a simple pattern. The value objects are required to support an interface. The application must use the methods on this interface when interacting with the value within a ObjectGrid Session. If this is the case, then the ObjectGrid creates proxies for the values that are returned to the application. The proxy has a reference to be real value. If the application just does reads, they always run against the real copy. If the application modifies an attribute on the object, the proxy makes a copy of the real object and then makes the modification on the copy. The proxy then uses the copy from that point on. This allows the copy operation to be avoided completely for objects that are only read by the application. All modify operations must start with the set prefix. Enterprise JavaBeans normally are coded to use this style of method naming for methods that modify the objects attributes. This convention must be followed. Any objects that are modified are copied at the time they are modified by the application. This is the most efficient read and write scenario supported by the ObjectGrid. You can configure a map to use COPY_ON_WRITE as follows. In this example, the application wants to store Person objects keyed using the name in the Map. The person object looks like the following code snippet:
class Person { String name; int age; public Person() { } public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a)
Chapter 7. ObjectGrid performance best practices

175

{ age = a; } public int getAge() { return age; } }

The application uses IPerson interface only when interacts with values that are retrieved from a ObjectMap. Modify the object to use an interface as in the following example.
interface IPerson { void setName(String n); String getName(); void setAge(int a); int getAge(); } // Modify Person to implement IPerson interface class Person implements IPerson { ... }

The application then needs to configure the BackingMap to use COPY_ON_WRITE mode, like in the following example:
ObjectGrid dg = ...; BackingMap bm = dg.defineMap("PERSON"); // use COPY_ON_WRITE for this Map with // IPerson as the valueProxyInfo Class bm.setCopyMode(CopyMode.COPY_ON_WRITE,IPerson.class); // The application should then use the following // pattern when using the PERSON Map. Session sess = ...; ObjectMap person = sess.getMap("PERSON"); ... sess.begin(); // the application casts the returned value to IPerson and not Person IPerson p = (IPerson)person.get("Billy"); p.setAge(p.getAge()+1); ... // make a new Person and add to Map Person p1 = new Person(); p1.setName("Bobby"); p1.setAge(12); person.insert(p1.getName(), p1); sess.commit(); // the following snippet WONT WORK. Will result in ClassCastException sess.begin(); // the mistake here is that Person is used rather than // IPerson Person a = (Person)person.get("Bobby"); sess.commit();

The first section shows the application retrieving a value that was named Billy in the map. The application casts the returned value to IPerson object, and not the Person object because the proxy that is returned implements two interfaces: v The interface specified in the BackingMap.setCopyMode method call. v The com.ibm.websphere.objectgrid.ValueProxyInfo interface You can cast the proxy to two types. The last part of the preceding code snippet demonstrates what is not allowed in COPY_ON_WRITE mode. The application

176

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

retrieves the Bobby record and tries to cast it to a Person object. This action fails with a class cast exception because the proxy that is returned is not a Person object. The returned proxy implements the IPerson object and ValueProxyInfo. ValueProxyInfo interface and partial update support This interface allows an application to retrieve either the committed read-only value referenced by the proxy or the set of attributes that have been modified during this transaction.
public interface ValueProxyInfo { List /**/ ibmGetDirtyAttributes(); Object ibmGetRealValue(); }

The ibmGetRealValue method returns a read only copy of the object. The application must not modify this value. The ibmGetDirtyAttributes method returns a list of strings representing the attributes that have been modified by the application during this transaction. The main use case for ibmGetDirtyAttributes is in a Java database connectivity (JDBC) or CMP based loader. Only the attributes that are named in the list need be updated on either the SQL statement or object mapped to the table, which leads to more efficient SQL generated by the Loader. When a copy on write transaction is committed and if a loader is plugged in, the the loader can cast the values of the modified objects to the ValueProxyInfo interface to obtain this information. Handling the equals method when using COPY_ON_WRITE or proxies. For example, the following code constructs a Person object and then inserts it to a an ObjectMap. Next, it retrieves the same object using ObjectMap.get method. The value is cast to the interface. If the value is cast to the Person interface, a ClassCastException exception results because the returned value is a proxy that implements the IPerson interface and is not a Person object. The equality check fails when using the == operation because they are not the same object.
session.begin(); // new the Person object Person p = new Person(...); personMap.insert(p.getName, p); // retrieve it again, remember to use the interface for the cast IPerson p2 = personMap.get(p.getName()); if(p2 == p) { // they are the same } else { // they are not }

Another consideration is when you must override the equals method. As illustrated in the following snippet of code, the equals method must verify that the argument is an object that implements IPerson interface and cast the argument to be a IPerson. Because the argument might be a proxy that implements the IPerson interface, you must use the getAge and getName methods when comparing instance variables for equality.
public boolean equals(Object obj) { if ( obj == null ) return false; if ( obj instanceof IPerson )
Chapter 7. ObjectGrid performance best practices

177

{ IPerson x = (IPerson) obj; return ( age.equals( x.getAge() ) && name.equals( x.getName() ) ) } return false; }

NO_COPY mode
The NO_COPY mode allows an application to ensure that it never modifies a value object that is obtained using an ObjectMap.get method in exchange for performance improvements. The valueInterfaceClass argument is ignored when this mode is used. If this mode is used, no copy of the value is ever made. If the application does modify values, then data in the BackingMap is corrupted. The NO_COPY mode is primarily useful for read-only maps where data is never modified by the application. If the application is using this mode and it is having problems, then switch to the COPY_ON_READ_AND_COMMIT mode to see if the problem still exists. If the problem goes away, then the application is modifying the value returned by ObjectMap.get method, either during transaction or after transaction has committed.

ObjectTransformer interface best practices


ObjectTransformer uses callbacks to the application to provide custom implementations of common and expensive operations such as object serialization and a deep copy on an object. For specific details about the ObjectTransformer interface, see the ObjectTransformer plug-in on page 104 topic. From a performance point of view and information from the CopyMode method in the copyMode method best practices on page 174 topic, it is clear that ObjectGrid copies the values for all cases except when in NO_COPY mode. The default copying mechanism that is employed within ObjectGrid is serialization, which is known as an expensive operation. The ObjectTransformer interface can be used in this situation. The ObjectTransformer interface uses callbacks to the application to provide a custom implementation of common and expensive operations such as object serialization and deep copies on objects. An application can provide an implementation of the ObjectTransformer interface to a map. The ObjectGrid then delegates to the methods on this object and relies on the application to provide an optimized version of each method in the interface. The ObjectTransformer interface follows:
public interface ObjectTransformer { void serializeKey(Object key, ObjectOutputStream stream) throws IOException; void serializeValue(Object value, ObjectOutputStream stream) throws IOException; Object inflateKey(ObjectInputStream stream) throws IOException, ClassNotFoundException; Object inflateValue(ObjectInputStream stream) throws IOException, ClassNotFoundException; Object copyValue(Object value); Object copyKey(Object key); }

You can associate an ObjectTransformer interface with a BackingMap by using the following example code:

178

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

ObjectGrid g = ...; BackingMap bm = g.defineMap("PERSON"); MyObjectTransformer ot = new MyObjectTransformer(); bm.setObjectTransformer(ot);

Tune object serialization and inflation


Object serialization is usually the biggest performance issue with ObjectGrid. ObjectGrid uses the default serializable mechanism if an ObjectTransformer plug-in is not supplied by the application. An application can provide implementations of either the Serializable readObject and writeObject or it can have the objects implement the Externalizable interface, which is around 10 times faster. If the objects in the Map cannot be modified, then an application can associate an ObjectTransformer with the ObjectMap. The serialize and inflate methods are provided to allow the application to provide custom code to optimize these operations given their large performance impact on the system. The serialize methods serialize the object and provide a stream. The method serializes the method to the provided stream. The inflate methods provide the input stream and expect the application to create the object, inflate it using data in the stream and then return the object. The implementations of the serialize and inflate methods must mirror each other.

Tune deep copy operations


After an application receives an object from an ObjectMap then the ObjectGrid performs a deep copy on the object value to ensure that the copy in the BaseMap map stays safe. The application can then modify the object value safely. When the transaction commits, the copy of the object value in the BaseMap map is updated to the new modified value and the application stops using the value from that point on. You could have copied the object again at the commit phase to make a private copy, but in this case the performance cost of this action was traded off against telling the application programmer to not use the value after the transaction commits. The default object copy mechanism attempts to use either a clone or a serialize and inflate pair to generate a copy. The serialize and inflate pair is the worst case performance scenario. If profiling reveals that serialize and inflate is a problem for your application, provide an ObjectTransformer plug-in and implement the copyValue and copyKey methods using a more efficient object copy.

Plug-in evictor performance best practices


If you use plug-in evictors, they are not active until you create them and tell the backing map to use them. Use these best practices and performance tips for least frequently used (LFU) and least recently used (LRU) evictors.

Least frequently used (LFU) evictor


The concept of a LFU evictor is to remove entries from the map that are used infrequently. The entries of the map are spread over a set amount of binary heaps. As the usage of a particular cache entry grows, it becomes ordered higher in the heap. When the evictor attempts a set of evictions it removes only the cache entries that are located lower than a specific point on the binary heap. As a result, the least frequently used entries are evicted.

Chapter 7. ObjectGrid performance best practices

179

Least recently used (LRU) evictor


The LRU Evictor follows the same concepts of the LFU Evictor with a few differences. The main difference is that the LRU uses a first in, first out queue (FIFO) instead of a set of binary heaps. Every time a cache entry is accessed, it moves to the head of the queue. Consequently, the front of queue contains the most recently used map entries and the end becomes the least recently used map entries. For example, if the A cache entry is used 50 times, and the B cache entry is used only once right after the B cache entry, the B cache entry is the front of the queue and the A cache entry is at the end of the queue because the B cache entry was used most recently. The LRU evictor evicts the cache entries that are at the tail of the queue, which are the least recently used map entries.

LFU and LRU properties and best practices to improve performance


Number of heaps When using the LFU evictor, all of the cache entries for a particular map are ordered over the number of heaps that you specify, improving performance drastically and preventing all of the evictions from synchronizing on one binary heap that contains all of the ordering for the map. More heaps also speeds up the time that is required for reordering the heaps because each heap has fewer entries. Set the number of heaps to 10% of the number of entries in your BaseMap. Number of queues When using the LRU evictor, all of the cache entries for a particular map are ordered over the number of LRU queues that you specify, improving performance drastically and preventing all of the evictions from synchronizing on one queue that contains all of the ordering for the map. Set the number of queues to 10% of the number of entries in your BaseMap. MaxSize property When an LFU or LRU evictor begins evicting entries, it uses the MaxSize evictor property to determine how many binary heaps or LRU queue elements to evict. For example, assume that you set the number of heaps or queues to have about 10 map entries in each map queue. If your MaxSize property is set to 7, the evictor evicts 3 entries from each heap or queue object to bring the size of each heap or queue back down to 7. The evictor only evicts map entries from a heap or queue when that heap or queue has more than the MaxSize property value of elements in it. Set the MaxSize to 70% of your heap or queue size. For this example, the value is set to 7. You can get an approximate size of each heap or queue by dividing the number of BaseMap entries by the number of heaps or queues that are used. SleepTime property An evictor does not constantly remove entries from your map. Instead it sleeps for a set amount of time, only waking every n number of seconds , where n refers to the SleepTime property. This property also positively affects performance: running an eviction sweep too often lowers performance because of the resources that are needed for processing them. However, not using the evictor enough can leave you with a map of unneeded entries. A map full of unneeded entries can negatively affect both the memory requirements and processing resources that required for your map. Setting the eviction sweeps to fifteen seconds is a good practice for most maps. If the map is written to frequently and is used at a high

180

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

transaction rate, it might be more useful to set the value to a lower time. However, if the map is accessed very infrequently, you can set the time to a higher value.

Example
The following example defines a map, creates a new LFU evictor, sets the evictor properties, and sets the map to use the evictor:
//Use ObjectGridManager to create/get the ObjectGrid. Refer to // the ObjectGridManger section ObjectGrid objGrid = ObjectGridManager.create............ BackingMap bMap = objGrid.defineMap("SomeMap"); //Set properties assuming 50,000 map entries LFUEvictor someEvictor = new LFUEvictor(); someEvictor.setNumberOfHeaps(5000); someEvictor.setMaxSize(7); someEvictor.setSleepTime(15); bMap.setEvictor(someEvictor);

Using the LRU evictor is very similar to using an LFU evictor. Following is an example:
ObjectGrid objGrid = new ObjectGrid; BackingMap bMap = objGrid.defineMap("SomeMap"); //Set properties assuming 50,000 map entries LRUEvictor someEvictor = new LRUEvictor(); someEvictor.setNumberOfLRUQueues(5000); someEvictor.setMaxSize(7); someEvictor.setSleepTime(15); bMap.setEvictor(someEvictor);

Notice that only 2 lines are different from the LFUEvictor example.

Default evictor best practices


Best practices for the default time to live evictor. Besides plug-in evictors that are described in the Plug-in evictor performance best practices on page 179 topic, a default TTL evictor is created with every backing map. The default evictor removes entries based on a time to live concept. This behavior is defined by the ttlType attribute. Three ttlTypes attributes exist: v None : Specifies that entries never expire and therefore are never removed from the map. v Creation time : Specifies that entries are evicted depending on when they were created. v Last accessed time : Specifies that entries are evicted depending upon when they were last accessed.

Default evictor properties and best practices for performance


TimeToLive property This property, along with ttlType property, is the most crucial from a performance perspective. If you are using the CREATION_TIME ttlType, the evictor evicts an entry when it its time from creation equals its TimeToLive attribute value. If you set the TimeToLive attribute value to 10 seconds, everything in the entire map is evicted after ten seconds. It is important to take caution when setting this value for the
Chapter 7. ObjectGrid performance best practices

181

CREATION_TIME ttlType. This evictor is best used when reasonably high amounts of additions to the cache exist that are only used for a set amount of time. With this strategy, anything that is created is removed after the set amount of time. Following is an example of where a TTL type of CREATION_TIME is useful. You are using a Web application that obtains stock quotes, and getting the most recent quotes is not critical. In this case, the stock quotes are cached in an ObjectGrid for 20 minutes. After 20 minutes, the ObjectGrid map entries expire and are evicted. Every twenty minutes or so the ObjectGrid map uses the Loader plug-in to refresh the map data with fresh data from the database. The database is updated every 20 minutes with the most recent stock quotes. So for this application, using a TimeToLive value of 20 minutes is ideal. If you are using the LAST_ACCESSED_TIME ttlType attribute, set the TimeToLive to a lower number than if you are using the CREATION_TIME ttlType, because the entries TimeToLive attribute is reset every time it is accessed. In other words, if the TimeToLive attribute is equal to 15 and an entry has existed for 14 seconds but then gets accessed, it does not expire again for another 15 seconds. If you set the TimeToLive to a relatively high number, many entries might never be evicted. However, if you set the value to something like 15 seconds, entries might be removed when they are not often accessed. Following is an example of where a TTL type of LAST_ACCESSED_TIME is useful. An ObjectGrid map is used to hold session data from a client. Session data must be destroyed if the client does not use the session data for some period of time. For example, the session data times out after 30 minutes of no activity by the client. In this case, using a TTL type of LAST_ACCESSED_TIME with the TimeToLive attribute set to 30 minutes is exactly what is needed for this application. Example The following example creates a backing map, set its default evictor ttlType attribute, and sets its TimeToLive property.
ObjectGrid objGrid = new ObjectGrid; BackingMap bMap = objGrid.defineMap("SomeMap"); bMap.setTtlEvictorType(TTLType.LAST_ACCESSED_TIME); bMap.setTimeToLive(15);

Most evictor settings should be set prior to ObjectGrid initialization. For a more in-depth understanding of the evictors, see Evictors on page 84.

182

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 8. Distributing changes between peer Java virtual machines


The LogSequence and LogElement objects communicate the changes that have occurred in an ObjectGrid transaction with a plug-in. For more information about how Java Message Service (JMS) can be used to distribute transactional changes, see Java Message Service for distributing transaction changes on page 186. The objects provide a means for an application to easily publish changes that have occurred in an object grid using a message transport to peer ObjectGrids in remote Java virtual machines (JVM) and then apply those changes on that JVM. The LogSequenceTransformer class is critical to enabling this support. This article examines how to write a listener using a Java Message Service (JMS) messaging system for propagating the messages. ObjectGrid supports transmitting LogSequences that result from an ObjectGrid transaction commit across WebSphere Application Server cluster members with an IBM-provided plug-in. This function is not enabled by default, but can be configured to be operational. See Distributed transaction propagation configuration on page 146 for more information. However, when either the consumer or producer is not a WebSphere Application Server, using an external JMS messaging system might be required. 1. Initialize the plug-in. The ObjectGrid calls the initialize method of the plug-in, part of the ObjectGridEventListener interface contract, when the ObjectGrid starts. The initialize method must obtain its JMS resources, including connections, sessions, and publishers, and start the thread that is the JMS listener. The initialize method looks like the following example:
public void initialize(Session session) { mySession = session; myGrid = session.getObjectGrid(); try { if(mode == null) { throw new ObjectGridRuntimeException("No mode specified"); } if(userid != null) { connection = topicConnectionFactory.createTopicConnection( userid, password); } else connection = topicConnectionFactory.createTopicConnection(); // need to start the connection to receive messages. connection.start(); // the jms session is not transactional (false). jmsSession = connection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); if(topic == null) if(topicName == null) { throw new ObjectGridRuntimeException("Topic not specified");
Copyright IBM Corp. 2004, 2005

183

} else { topic = jmsSession.createTopic(topicName); } publisher = jmsSession.createPublisher(topic); // start the listener thread. listenerRunning = true; listenerThread = new Thread(this); listenerThread.start(); } catch(Throwable e) { throw new ObjectGridRuntimeException("Cannot initialize", e); } }

The code to start the thread uses a Java 2 Platform, Standard Edition (J2SE) thread. If you are running a WebSphere Application Server Version 6.x or a WebSphere Application Server Version 5.x Enterprise server, use the asynchronous bean application programming interface (API) to start this daemon thread. You can also use the common APIs. Following is an example replacement snippet showing the same action using a work manager:
// start the listener thread. listenerRunning = true; workManager.startWork(this, true);

The plug-in must also implement the Work interface instead of the Runnable interface. You also need to add a release method to set the listenerRunning variable to false. The plug-in must be provided with a WorkManager instance in its constructor or by injection if using an Inversion of Control (IoC) container. 2. Transmit the changes. Following is a sample transactionEnd method for publishing the local changes that are made to an ObjectGrid. This uses JMS although clearly, you can use any message transport that is capable of reliable publish and subscribe messaging.
// This method is synchronized to make sure the // messages are published in the order the transaction // were committed. If we started publishing the messages // in parallel then the receivers could corrupt the Map // as deletes may arrive before inserts etc. public synchronized void transactionEnd(String txid, boolean isWriteThroughEnabled, boolean committed, Collection changes) { try { // must be write through and commited. if(isWriteThroughEnabled && committed) { // write the sequences to a byte [] ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); if (publishMaps.isEmpty()) { // serialize the whole collection LogSequenceTransformer.serialize(changes, oos, this, mode); } else { // filter LogSequences based on publishMaps contents Collection publishChanges = new ArrayList(); Iterator iter = changes.iterator(); while (iter.hasNext()) { LogSequence ls = (LogSequence) iter.next();

184

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

if (publishMaps.contains(ls.getMapName())) { publishChanges.add(ls); } } LogSequenceTransformer.serialize(publishChanges, oos, this, mode); } // make an object message for the changes oos.flush(); ObjectMessage om = jmsSession.createObjectMessage( bos.toByteArray()); // set properties om.setStringProperty(PROP_TX, txid); om.setStringProperty(PROP_GRIDNAME, myGrid.getName()); // transmit it. publisher.publish(om); } } catch(Throwable e) { throw new ObjectGridRuntimeException("Cannot push changes", e); } }

This method uses several instance variables: v jmsSession variable: A JMS session that is used to publish messages. It is created when the plug-in initializes v mode variable: The distribution mode. v publishMaps variable: A set that contains the name of each Map with changes to publish. If the variable is empty, then all the Maps are published. v publisher variable: A TopicPublisher object that is created during the plug-in initialize method. 3. Receive and apply update messages. Following is the run method. This method runs in a loop until the application stops the loop. Each loop iteration attempts to receive a JMS message and apply it to the ObjectGrid.
private synchronized boolean isListenerRunning() { return listenerRunning; } public void run() { try { System.out.println("Listener starting"); // get a jms session for receiving the messages. // Non transactional. TopicSession myTopicSession; myTopicSession = connection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); // get a subscriber for the topic, true indicates dont receive // messages transmitted using publishers // on this connection. Otherwise, wed receive our own updates. TopicSubscriber subscriber = myTopicSession.createSubscriber(topic, null, true); System.out.println("Listener started"); while(isListenerRunning()) { ObjectMessage om = (ObjectMessage)subscriber.receive(2000); if(om != null) { // Use Session that was passed in on the initialize... // very important to use no write through here
Chapter 8. Distributing changes between peer Java virtual machines

185

mySession.beginNoWriteThrough(); byte[] raw = (byte[])om.getObject(); ByteArrayInputStream bis = new ByteArrayInputStream(raw); ObjectInputStream ois = new ObjectInputStream(bis); // inflate the LogSequences Collection collection = LogSequenceTransformer.inflate(ois, myGrid); Iterator iter = collection.iterator(); while (iter.hasNext()) { // process each Maps changes according to the mode when // the LogSequence was serialized LogSequence seq = (LogSequence)iter.next(); mySession.processLogSequence(seq); } mySession.commit(); } // if there was a message } // while loop // stop the connection connection.close(); } catch(IOException e) { System.out.println("IO Exception: " + e); } catch(JMSException e) { System.out.println("JMS Exception: " + e); } catch(ObjectGridException e) { System.out.println("ObjectGrid exception: " + e); System.out.println("Caused by: " + e.getCause()); } catch(Throwable e) { System.out.println("Exception : " + e); } System.out.println("Listener stopped"); }

The LogSequenceTransformer class, and the ObjectGridEventListener, LogSequence and LogElement APIs allow any reliable publish and subscribe to be used to distribute the changes and filter the Maps that you want to distribute. The snippets in this task show how to use these APIs with JMS to build a peer-to-peer ObjectGrid that is shared by applications that are hosted on a diverse set of platforms that share a common message transport.

Java Message Service for distributing transaction changes


Use Java Message Service (JMS) for distributed changes between different tiers or in environments on mixed platforms. JMS is an ideal protocol for distributed changes between different tiers or in environments on mixed platforms. For example, some applications that use the ObjectGrid might be deployed on Gluecode or Tomcat, where as other applications might run on WebSphere Application Server Version 6.0. JMS is ideal for distributed changes between ObjectGrid peers in these different environments. The high availability manager message transport is very fast, but can only distribute changes to JVMs that are in a single core group. JMS is slower, but allows larger and a more diverse set of application clients to share an ObjectGrid. JMS is ideal for example when sharing data in an ObjectGrid between a fat Swing client and an application deployed on WebSphere Extended Deployment.

186

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Overview
JMS is implemented for distributing transaction changes by using a Java object that behaves as an ObjectGridEventListener listener. This object can propagate the state in the following four ways: Invalidate Any entry that is evicted, updated or deleted is removed on all peer Java Virtual Machines (JVMs) when they receive the message. Invalidate conditional The entry is evicted only if the local version is the same or older than the version on the publisher. Push Any entry that was evicted, updated, deleted or inserted is added or overwritten on all peer JVMs when they receive the JMS message.

Push conditional The entry is only updated or added on the receive side if the local entry is less recent than the version that is being published.

Listen for changes for publishing


The plug-in implements the ObjectGridEventListener interface to intercept the transactionEnd event. When the ObjectGrid invokes this method, the plug-in attempts to convert the LogSequence list for each Map that is touched by the transaction to a JMS message and then publish it. The plug-in can be configured to publish changes for all Maps or a subset of Maps. LogSequence objects are processed for the Maps that have publishing enabled. The LogSequenceTransformer ObjectGrid class serializes a filtered LogSequence for each Map to a stream. After all LogSequences are serialized to the stream then a JMS ObjectMessage is created and published to a well known topic.

Listen for JMS messages and apply them to the local ObjectGrid
The same plug-in also starts a thread that spins in a loop, receiving all messages that are published to the well known topic. When a message arrives, it passes the message contents to the LogSequenceTransformer class to convert it to a set of LogSequence objects. Then, a no write through transaction is started. Each LogSequence object is provided to the Session.processLogSequence method, which updates the local Maps with the changes. The processLogSequence method understands the distribution mode. The transaction is committed and the local cache now reflects the changes. For more information about using JMS to distribute transaction changes, see Chapter 8, Distributing changes between peer Java virtual machines, on page 183 .

Chapter 8. Distributing changes between peer Java virtual machines

187

188

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 9. Injection-based container integration


You can use the Inversion of Control (IoC) framework to completely configure the ObjectGrid. As a result, you do not need to use the ObjectGrid XML configuration framework.

Injection-based containers
Injection based containers, also known as Inversion of Control (IoC), is a common pattern that is used by applications on the client side and on the server side. Several open source implementations of such containers exist. The new Enterprise JavaBeans (EJB) Version 3.0 specification also borrows some of these concepts. Most of these frameworks are Enterprise JavaBean containers, and take the responsibility of creating an instance of a particular bean. These frameworks can also initialize a bean with a set of properties and wire other Enterprise JavaBeans that it requires by using getter and setter pairs on Enterprise JavaBean attributes. The ObjectGrid application programming interfaces (API) are designed to work well with such containers. Starting with the ObjectGridManager.createObjectGrid methods, you can configure these containers to bootstrap an ObjectGrid so that the application has a reference to either a working ObjectGrid or can ask for the container to provide an ObjectGrid session when required.

Supported patterns
The following sections discuss what has been done to verify that the ObjectGrid APIs can be used cleanly by an application that employs an IoC framework. Use ObjectGridManager to create ObjectGrids The ObjectGrid APIs are designed to work well with IoC frameworks. The root singleton used by such a framework is the ObjectGridManager interface, which has several createObjectGrid factory methods that return a reference to a named ObjectGrid. You can set this ObjectGrid reference as a singleton in the IoC framework, so that subsequent requests for the bean return the same ObjectGrid instance. ObjectGrid Plug-ins The plug-ins on the ObjectGrid include: v TransactionCallback v ObjectGridEventListener v SubjectSource v MapAuthorization v SubjectValidation Each of the plug-ins are simple JavaBeans that implement an interface. You can use the IoC framework to create these plug-ins and wire them to the appropriate attributes on the ObjectGrid instance. Each of these plug-ins has a corresponding set method on the ObjectGrid interface for clean integration with the IoC framework. Define maps
Copyright IBM Corp. 2004, 2005

189

The defineMap factory method on the ObjectGrid interface can be used to define a newly named Map. Any plug-ins that are required by the BackingMap (the object returned by defineMap) are constructed using the IoC framework and then wired to the BackingMap by using the appropriate attribute name. Because the BackingMap is not referenced by any other object, the IoC frameworks do not automatically construct it. You can wire each BackingMap to a dummy attribute on the main application bean as a work around. Backing map plug-ins The plug-ins on the BackingMap behave in the same manner as those on the ObjectGrid. Each plug-in has a corresponding set method on the BackingMap interface. The BackingMap plug-ins are: v Loader v ObjectTransformer v OptimisticCallback v Evictor v MapEventListener

Usage patterns
After the IoC framework has its configuration file set up to produce an ObjectGrid, create an enterprise bean that is called gridName_Session or something similar. Define it as an Enterprise JavaBean that is obtained by calling the getSession method on the ObjectGrid singleton Enterprise JavaBean. The application then uses the IoC framework to obtain a reference to a gridName_Session object whenever an object requires a new session.

Summary
Using the ObjectGrid in environments that already use an IoC framework for bean instantiation and configuration is straightforward. You can use the IoC framework to completely configure the ObjectGrid, and as a result, you do not need to use the XML configuration framework. The ObjectGrid works seamlessly with your existing IoC framework.

190

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Chapter 10. Troubleshooting


This section describes scenarios for troubleshooting problems that are caused by an application error or an application design issue. If you suspect ObjectGrid has a defect, you might need to enable tracing of ObjectGrid as described in the tracing section of ObjectGridManager.

Intermittent and unexplained errors


An application attempts to improve performance by using the COPY_ON_READ, COPY_ON_WRITE, or NO_COPY copy mode as described in the CopyMode section. The application encounters intermittent problems when the symptom of the problem is changing and the problem is unexplained or unexpected. Intermittent problems do not occur when the copy mode is changed to the COPY_ON_READ_AND_COMMIT mode.

Problem
The problem might be due to corrupted data in the ObjectGrid map, which is a result of the application violating the programming contract of the copy mode that is being used. Data corruption can cause unpredictable errors to occur intermittently or in an unexplained or unexpected fashion.

Solution
The solution is for the application to comply with the programming contract stated for the copy mode being used. For the COPY_ON_READ and COPY_ON_WRITE copy modes, this means the application uses a reference to a value object outside of the transaction scope where the value reference was obtained. To use these modes, the application must agree to destroy the reference to the value object after the transaction is completed and to obtain a new reference to the value object in each transaction that needs to access the value object. For the NO_COPY copy mode, the application must agree to never change the value object. In this case, the application must either be changed so that it does not change the value object or the application must use a different copy mode. See the CopyMode section for additional details regarding the copy mode setting.

General exception handling technique


Knowing the root cause of a Throwable object is helpful in isolating the source of a problem. The following is an example of a utility method that can be used by an exception handler to find the root cause of the Throwable that occurred.

Example
static public Throwable findRootCause( Throwable t ) { // Start with Throwable that occurred as the root. Throwable root = t; // Follow cause chain until last Throwable in chain is found. Throwable cause = root.getCause(); while ( cause != null ) { root = cause;
Copyright IBM Corp. 2004, 2005

191

cause = root.getCause(); } // Return last Throwable in the chain as the root cause. return root; }

Specific exception handling techniques


Duplicate insert
This problem should typically only occurs in a distributed transaction propagation environment. It does not happen often.

Message
[7/11/05 22:02:11:303 CDT] 00000032 SessionImpl < processLogSequence Exit [7/11/05 22:02:11:303 CDT] 00000032 SessionImpl > commit for: TX:08FE0C6701054000E0001540090A5759 Entry [7/11/05 22:02:11:303 CDT] 00000032 SessionImpl > rollbackPMapChanges for: TX:08FE0C6701054000E0001540090A5759 as result of Throwable: com.ibm.websphere.objectgrid.plugins. CacheEntryException: Duplicate key on an insert! Entry com.ibm.websphere.objectgrid.plugins.CacheEntryException: Duplicate key on an insert! at com.ibm.ws.objectgrid.map.BaseMap.applyPMap(BaseMap.java:528) at com.ibm.ws.objectgrid.SessionImpl.commit(SessionImpl.java:405) at com.ibm.ws.objectgrid.plugins.TranPropWorkerThread.commitPropagatedLogSequence (TranPropWorkerThread.java:553) at com.ibm.ws.objectgrid.plugins.TranPropWorkerThread.processCommitRequest (TranPropWorkerThread.java:449) at com.ibm.ws.objectgrid.plugins.TranPropWorkerThread.run (TranPropWorkerThread.java:200) at java.lang.Thread.run(Thread.java:568)

Problem
When the filtered log sequence is propagated from one JVM to another, the foreign log sequence is processed in the second JVM. The entry for this key may exist or the two log sequence operation codes should be different This problem happens occasionally.

Impact and solution


When this problem occurs, the entry is not updated in another JVM which can cause an inconsistency in ObjectGrid. However, a workaround exists to avoid this problem. You can use the partitioning facility (WPF) on object retrieval in addition to the object insert/update/remove. Reference the Integrating WPF and ObjectGrid section for more information on this technique.

Optimistic collision exception


You can receive an OptimisticCollisionException exception directly, or receive it while receiving an ObjectGridException exception. The following code is an example of how to catch the exception and then display its message:
try { ... } catch (ObjectGridException oe) { System.out.println(oe); }

192

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Exception cause
An OptimisticCollisionException exception is created in a situation where two different clients try to update the same map entry at relatively the same time. One clients session is committed and updates the map entry. However, the other client has already read the data before the commit and contains old or incorrect data. The other client will then attempt to commit the incorrect data, which is when the exception is created.

Retrieving the key that triggered the exception


It might be useful, when troubleshooting such an exception, to retrieve the key corresponding to the entry which triggered the exception. The benefit of the OptimisticCollisionException is that it has a built in method getKey that returns the object representing that key. Following is an example on how to retrieve and print the key when catching the OptimisticCollisionException :
try { ... } catch (OptimisticCollisionException oce) { System.out.println(oce.getKey()); }

An OptimisticCollisionException might be the cause of an ObjectGridException. If this is the case, you can use the following code to determine the exception type and print out the key. The code below uses the findRootCause utility method as described in the General Exception Handling Technique section.
try { ... } catch (ObjectGridException oe) { Throwable Root = findRootCause( oe ); if (Root instanceof OptimisticCollisionException) { OptimisticCollisionException oce = (OptimisticCollisionException)Root; System.out.println(oce.getKey()); } }

LockTimeoutException exception
Message
You can catch a LockTimeoutException exception directly, or while catching an ObjectGridException. The following code snippet shows how to catch the exception and display the message.
try { ... } catch (ObjectGridException oe) { System.out.println(oe); }

The result is: com.ibm.websphere.objectgrid.plugins.LockTimeoutException: %Message %Message represents the string that is passed as a parameter when the exception is created and the exception properties and methods are used to display an accurate error message. It most likely describes the type of lock that was requested, and
Chapter 10. Troubleshooting

193

which map entry the transaction acted upon.

Exception cause
When a transaction or client is asking for a lock to be granted for a specific map entry it will often have to wait for the current client to release the lock. If the lock request remains idle for an extended period of time, and a lock never gets granted, a LockTimeoutException is created. This is to prevent a deadlock, which is described in more detail in the following section. You are more likely to see this exception when using a pessimistic locking strategy because the lock is never released until the transaction is committed.

Getting more details about the lock request and exception


The LockTimeoutException has a built in method called getLockRequestQueueDetails which returns a string that contains a more in-depth description of the situation that triggered the exception. The following is an example of some code that catches the exception, and displays an error message.
try { ... } catch (LockTimeoutException lte) { System.out.println(lte.getLockRequestQueueDetails()); }

The output result is:


lock request queue >[TX:163C269E01054000E0D75B3B090A571D, state = Granted 5348 milliseconds ago, mode = U] >[TX:163C273401054000E0245B3B090A571D, state = Waiting for 5348 milliseconds, mode = U] >[TX:163C328C01054000E1145B3B090A571D, state = Waiting for 1402 milliseconds, mode = U]

If you get the exception in an ObjectGridException catch block, the following code determines the exception and print out the queue details. It uses the findRootCause utility method described in General Exception Handling Technique section.
try { ... } catch (ObjectGridException oe) { Throwable Root = findRootCause( oe ); if (Root instanceof LockTimeoutException) { LockTimeoutException lte = (LockTimeoutException)Root; System.out.println(lte.getLockRequestQueueDetails()); } }

Possible solution
The LockTimeoutException is to prevent possible deadlocks in your application. An exception of this type will be thrown when it has waited a set amount of time. The amount of time it waits, however, can be set using the setLockTimeout(int) method which is available for the BackingMap. Using the setLockTimeout method eliminates the LockTimeoutException. If a deadlock does not actually exist in your application, adjusting the lock timeout can help you avoid the LockTimeoutException.

194

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

The following code shows how to create an ObjectGrid, define a map, and set its LockTimeout value to 30 seconds
ObjectGrid objGrid = new ObjectGrid(); BackingMap bMap = objGrid.defineMap("MapName"); //This will set the amount of time that a // lock request will wait before an exception is thrown bMap.setLockTimeout(30);

The previous code can be used for hard-coding ObjectGrid and map properties. If you create ObjectGrid from an XML file, the LockTimeout property can be set within the backingMap tag. Following is an example of a backingMap tag that sets a map LockTimeout value to 30 seconds.
<backingMap name="MapName" lockStrategy="PESSIMISTIC" lockTimeout="30">

LockDeadlockException
Message
You can catch a LockDeadLockException directly, or get it while catching an ObjectGridException. Following is a code example that shows catching the exception, then the displayed message.
try { ... } catch (ObjectGridException oe) { System.out.println(oe); }

The result is:


com.ibm.websphere.objectgrid.plugins.LockDeadlockException: %Message

%Message represents the string that is passed as a parameter when the exception is created and thrown.

Exception cause
The most common type of deadlock happens when using the pessimistic lock strategy, and two separate clients each own a shared lock on a particular object. Then, both attempt to promote to an exclusive lock on that object. Following is a diagram that shows such a situation with transaction blocks that would cause the exception to be thrown.

Chapter 10. Troubleshooting

195

Client 1

Client 2

Session 2 Session.begin() Map.get(x) Map.update(x) Session.commit() ObjectMap

Session 2 Session.begin() Map.get(x) Map.update(x) Session.commit()

Figure 7. Example of a potential deadlock situation

This is an abstract view of what is occurring in your program when the exception occurs. In an application with many threads updating the same ObjectMap, it is possible to encounter this situation. The following is a step-by-step example of when two clients execute the transaction code blocks, described in the previous figure.

196

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

When each client attempts a Map.get(x) method call Shared Lock Client 1 Cache Entry(x) Shared Lock Client 2

When Client 2 attempts a Map.update(x) method call Still Shared Client 1 Cache Entry(x) Still Shared Client 2

Client 1 asks the Lock Manager to promote to an "Exclusive" lock

Lock Manager

Client 2 asks the Lock Manager to promote to an "Exclusive" lock

When Client 1 attempts a Map.update(x) method call Still Shared Client 1 Cache Entry(x) Still Shared Client 2

Client 1 asks the Lock Manager to promote to an "Exclusive" lock

Lock Manager

Figure 8. A deadlock situation

As shown, when both clients are trying to promote to exclusive locks and still own the shared locks, it is impossible for either of them to actually get one. They always wait for the other client to release its shared lock, and thus a LockDeadlockException occurs.

Possible solutions
Receiving this exception is positive on occasion. When there are many threads, all of which execute transactions on a particular map, it is possible that you will encounter the situation described previously (Figure1). This exception is thrown to keep your program from hanging. Catching this exception allows you to notify yourself and, if you want to, add code to the catch block so that you can get more details of the cause. Since you will only see this exception in a pessimistic locking strategy, one simple solution is to simply use an optimistic locking strategy. If pessimistic is required, however, you can use the getForUpdate method instead of the get method. This eliminates getting the exceptions for the situation described previously.

Chapter 10. Troubleshooting

197

XML configuration problem diagnosis


Referencing a nonexistent plug-in collection
When using XML to define BackingMap plug-ins, the pluginCollectionRef attribute of the backingMap element must reference a backingMapPluginCollection. The pluginCollectionRef attribute must match the id of one of the backingMapPluginCollection elements.

Message
If the pluginCollectionRef attribute does not match any id attributes of any of the backingMapPluginConfiguration elements, a message similar to the following message is displayed in the log.
[7/14/05 14:02:01:971 CDT] 686c060e XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 14; URI: null; Message: Key pluginCollectionRef with value bookPlugins not found for identity constraint of element objectGridConfig..

The following message is an excerpt from the log with trace enabled:
[7/14/05 14:02:01:971 CDT] 686c060e XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 14; URI: null; Message: Key pluginCollectionRef with value bookPlugins not found for identity constraint of element objectGridConfig.. [7/14/05 14:02:01:991 CDT] 686c060e SystemErr R com.ibm.websphere.objectgrid. ObjectGridException: Invalid XML file: etc/test/document/bookstore.xml [7/14/05 14:02:01:991 CDT] 686c060e SystemErr R at com.ibm.ws.objectgrid.config.XmlConfigBuilder.<init>(XmlConfigBuilder.java:160) [7/14/05 14:02:01:991 CDT] 686c060e SystemErr R at com.ibm.websphere.objectgrid.ProcessConfigXML$2.run(ProcessConfigXML.java:99) ... [7/14/05 14:02:02:001 CDT] 686c060e SystemErr R Caused by: org.xml.sax. SAXParseException: Key pluginCollectionRef with value bookPlugins not found for identity constraint of element objectGridConfig. [7/14/05 14:02:02:001 CDT] 686c060e SystemErr R at org.apache.xerces.util. ErrorHandlerWrapper.createSAXParseException(Unknown Source) [7/14/05 14:02:02:001 CDT] 686c060e SystemErr R at org.apache.xerces.util. ErrorHandlerWrapper.error(Unknown Source) [7/14/05 14:02:02:001 CDT] 686c060e SystemErr R at org.apache.xerces.impl. XMLErrorReporter.reportError(Unknown Source) [7/14/05 14:02:02:001 CDT] 686c060e SystemErr R at org.apache.xerces.impl. XMLErrorReporter.reportError(Unknown Source) [7/14/05 14:02:02:011 CDT] 686c060e SystemErr R at org.apache.xerces.impl.xs. XMLSchemaValidator$XSIErrorReporter.reportError(Unknown Source) [7/14/05 14:02:02:011 CDT] 686c060e SystemErr R at org.apache.xerces.impl.xs. XMLSchemaValidator.reportSchemaError(Unknown Source) ...

Problem
The XML file that was used to produce this error is shown below. Notice that the book BackingMap has its pluginCollectionRef attribute set to bookPlugins, and the single backingMapPluginCollection has an id of collection1.

198

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" pluginCollectionRef="bookPlugin" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="collection1"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

Solution
To fix the problem, ensure that the value of each pluginCollectionRef matches the id of one of the backingMapPluginCollection elements. In this example, simply changing the name of the pluginCollectionRef to collection1 avoids this error. Other ways to fix the problem include changing the id of the existing backingMapPluginCollection to match the pluginCollectionRef, or adding an additional backingMapPluginCollection with an id that matches the pluginCollectionRef.

Missing a required attribute


Many of the elements in the XML file have several optional attributes. You can include or exclude optional attributes in the file. The XML will pass validation either way. However, there are some required attributes. If these required attributes are not present when their associated element is used, XML validation fails.

Message
When a required attribute is missing, a message similar to the one that follows is found in the log. In this example, the type attribute is missing from the property element.
[7/15/05 13:41:41:267 CDT] 6873dcac XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 12; URI: null; Message: cvccomplextype.4: Attribute type must appear on element property..

The following message is an excerpt from the log with trace enabled.
[7/15/05 14:08:48:506 CDT] 6873dff9 XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 12; URI: null; Message: cvccomplextype.4: Attribute type must appear on element property.. [7/15/05 14:08:48:526 CDT] 6873dff9 SystemErr R com.ibm.websphere.objectgrid. ObjectGridException: Invalid XML file: etc/test/document/bookstore.xml [7/15/05 14:08:48:536 CDT] 6873dff9 SystemErr R at com.ibm.ws.objectgrid.config. XmlConfigBuilder.<init>(XmlConfigBuilder.java:160) [7/15/05 14:08:48:536 CDT] 6873dff9 SystemErr R at com.ibm.websphere.objectgrid. ProcessConfigXML$2.run(ProcessConfigXML.java:99) ... [7/15/05 14:08:48:536 CDT] 6873dff9 SystemErr R Caused by: org.xml.sax. SAXParseException: cvccomplextype.4: Attribute type must appear on element property. [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.util.
Chapter 10. Troubleshooting

199

ErrorHandlerWrapper.createSAXParseException(Unknown Source) [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.util. ErrorHandlerWrapper.error(Unknown Source) [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.impl. XMLErrorReporter.reportError(Unknown Source) [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.impl. XMLErrorReporter.reportError(Unknown Source) [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.impl.xs. XMLSchemaValidator$XSIErrorReporter.reportError(Unknown Source) [7/15/05 14:08:48:546 CDT] 6873dff9 SystemErr R at org.apache.xerces.impl.xs. XMLSchemaValidator.reportSchemaError(Unknown Source) ...

Problem
The following example is the XML file that was used to produce the previous error. Notice that the property on the Evictor has only two of the three required attributes. The name and value attributes are both present, but the type attribute is missing. This missing attribute causes XML validation to fail.
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore"> <backingMap name="book" pluginCollectionRef="collection1" /> </objectGrid> </objectGrids> <backingMapPluginCollections> <backingMapPluginCollection id="collection1"> <bean id="Evictor" className="com.ibm.websphere.objectgrid.plugins.builtins.LRUEvictor" /> <property name="maxSize" value="89" /> </backingMapPluginCollection> </backingMapPluginCollections> </objectGridConfig>

Solution
To solve this problem, add the required attribute to the XML file. In the example XML file shown previously, you need to add the attribute type and assign the integer value.

Missing a required element


A few of the XML elements are required by the schema. If they are not present, the XML fails validation.

Message
When a required element is missing, a message similar to the one that follows is found in the log. In this case, the objectGrid element is missing.
[7/15/05 14:54:23:729 CDT] 6874d511 XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 5; URI: null; Message: cvccomplextype.2.4.b: The content of element objectGrids is not complete. One of {"http://ibm.com/ws/objectgrid/config":objectGrid} is expected..

Enable trace to see more information regarding this error. Section ObjectGridManager covers information on how to turn on the trace.

200

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Problem
The following example is the XML file that was used to produce this problem. Notice that the objectGrids element does not have objectGrid child elements. According to the XML schema, the objectGrid element must occur within the objectGrids tags at least once. This missing element causes XML validation to fail.
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> </objectGrids> </objectGridConfig>

Solution
To fix this problem, make sure that the required elements are in the XML file. In the previous example, at least one objectGrid element must be placed within the objectGrids tag. Once the required elements are present, you can successfully validate the XML file. The following valid XML file contains the required elements present.
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore" /> </objectGrids> </objectGridConfig>

XML value of attribute is not valid


Message
Some of the attributes in the XML file can only be assigned certain values. These attributes have their acceptable values enumerated by the schema. These attributes include: v authorizationMechanism v attribute on the objectGrid element v copyMode attribute on the backingMap element v lockStrategy attribute on the backingMap element v ttlEvictorType attribute on the backingMap element v type attribute on the property element If one of these attributes is assigned an invalid value, XML validation fails. When an attribute is set to a value that isnt one of its enumerated values, the following message shows in the log:
[7/19/05 16:45:40:992 CDT] 6870e51b XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 6; URI: null; Message: cvcenumerationvalid: Value INVALID_COPY_MODE is not facetvalid with respect to enumeration [COPY_ON_READ_AND_COMMIT, COPY_ON_READ, COPY_ON_WRITE, NO_COPY]. It must be a value from the enumeration..

The following excerpt is from the log with trace enabled.

Chapter 10. Troubleshooting

201

[7/19/05 16:45:40:992 CDT] 6870e51b XmlErrorHandl E CWOBJ9002E: This is an English only Error message: Invalid XML file. Line: 6; URI: null; Message: cvcenumerationvalid: Value INVALID_COPY_MODE is not facetvalid with respect to enumeration [COPY_ON_READ_AND_COMMIT, COPY_ON_READ, COPY_ON_WRITE, NO_COPY]. It must be a value from the enumeration.. [7/19/05 16:45:41:022 CDT] 6870e51b SystemErr R com.ibm.websphere.objectgrid .ObjectGridException: Invalid XML file: etc/test/document/backingMapAttrBad.xml [7/19/05 16:45:41:022 CDT] 6870e51b SystemErr R at com.ibm.ws.objectgrid.config .XmlConfigBuilder.<init>(XmlConfigBuilder.java:160) [7/19/05 16:45:41:022 CDT] 6870e51b SystemErr R at com.ibm.websphere.objectgrid .ProcessConfigXML$2.run(ProcessConfigXML.java:99)... [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R Caused by: org.xml.sax.SAXParseException: cvcenumerationvalid: Value INVALID_COPY_MODE is not facetvalid with respect to enumeration [COPY_ON_READ_AND_COMMIT, COPY_ON_READ, COPY_ON_WRITE, NO_COPY]. It must be a value from the enumeration. [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.util .ErrorHandlerWrapper.createSAXParseException(Unknown Source) [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.util .ErrorHandlerWrapper.error(Unknown Source) [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.impl .XMLErrorReporter.reportError(Unknown Source) [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.impl .XMLErrorReporter.reportError(Unknown Source) [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.impl .xs.XMLSchemaValidator$XSIErrorReporter.reportError(Unknown Source) [7/19/05 16:45:41:032 CDT] 6870e51b SystemErr R at org.apache.xerces.impl .xs.XMLSchemaValidator.reportSchemaError(Unknown Source) ...

Problem
An attribute that is assigned a value out of a specific set of values has been set improperly. In this case, the copyMode attribute is not set to one of its enumerated values. It was set to INVALID_COPY_MODE. The following is the XML file that was used to produce this error.
<?xml version="1.0" encoding="UTF-8"?> <objectGridConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ibm.com/ws/objectgrid/config ../objectGrid.xsd" xmlns="http://ibm.com/ws/objectgrid/config"> <objectGrids> <objectGrid name="bookstore" /> <backingMap name="book" copyMode="INVALID_COPY_MODE"/> </objectGrid> </objectGrids> </objectGridConfig>

Solution
In this example, copyMode has an invalid value. Set the attribute to one of these valid values: COPY_ON_READ_AND_COMMIT, COPY_ON_READ, COPY_ON_WRITE, or NO_COPY.

Validating XML without support of an implementation


The IBM Software Development Kit (SDK) version 1.4.2 contains an implementation of some JAXP function to use for XML validation against a schema.

202

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Message
When using a SDK that does not contain this implementation, attempts to validate may fail. If you would like to validate XML using a SDK that does not contain this implementation, download Xerces, and include its Java archive (JAR) files in the classpath. When you attempt to validate XML with a SDK that does not have the necessary implementation, the following error is found in the log.
[7/19/05 10:50:45:066 CDT] 15c7850 XmlConfigBuil d XML validation is enabled [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R com.ibm.websphere .objectgrid[7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at com.ibm.ws.objectgrid .ObjectGridManagerImpl.getObjectGridConfigurations (ObjectGridManagerImpl.java:182) [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at com.ibm.ws.objectgrid .ObjectGridManagerImpl.createObjectGrid(ObjectGridManagerImpl.java:309) [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at com.ibm.ws.objectgrid.test. config.DocTest.main(DocTest.java:128) [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R Caused by: java.lang .IllegalArgumentException: No attributes are implemented [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at org.apache.crimson.jaxp. DocumentBuilderFactoryImpl.setAttribute(DocumentBuilderFactoryImpl.java:93) [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at com.ibm.ws.objectgrid.config .XmlConfigBuilder.<init>(XmlConfigBuilder.java:133) [7/19/05 10:50:45:086 CDT] 15c7850 SystemErr R at com.ibm.websphere.objectgrid .ProcessConfigXML$2.run(ProcessConfigXML.java:99) ...

Problem
The SDK that is used does not contain an implementation of JAXP function that is necessary to validate XML files against a schema.

Solution
After you download Apache Xerces and include the JARs in the classpath, you can validate the XML file successfully.

ObjectGrid messages
This reference information provides additional information about messages you might encounter while using the ObjectGrid. Messages are identified by the message key and have explanation and user response. They could be information, warning, or errors and are indicated by the last letter (I, W, or E) of the message key. The explanation part of the message explains why the message occurs. The user response part of the message describes what action should be taken in the case of warning or error message.
CWOBJ0001E: Method, {0}, was called after initialization was completed. Explanation: After initialization has completed, certain method invocations are no longer accepted. User Response: Restructure your code so that the configuration is completed before usage of the runtime is initiated. CWOBJ0002W: ObjectGrid component is ignoring unexpected exception: {0}. Explanation: CMSG0001 User Response: CMSG0002 CWOBJ0004E: The value {0} is not valid for {1}. Explanation: An invalid value was specified for the variable. User Response: Set the appropriate value. Please refer to ObjectGrid document to know the valid values for the variables or properties.
Chapter 10. Troubleshooting

203

CWOBJ0005W: The thread throws an InterruptedException: {0} Explanation: An InterruptedException is thrown. User Response: Check the exception message to see whether this is an expected interruption or not. CWOBJ0006W: An Exception is caught : {0} Explanation: An Exception is thrown during the runtime. User Response: Check the exception message to see whether this is an expected exception or not. CWOBJ0010E: Message key {0} is missing. Explanation: A message key is missing in the message resource bundle User Response: CMSG0002 CWOBJ0011=CWOBJ0011W: Could not deserialize field {0} in class {1}; default value will be used. Explanation: During deserialization of an object, an expected field was not found. This is most likely due to the fact that the object is being deserialized by a different version of the class than the one that serialized it. User Response: This warning indicates a potential problem, but no user action is necessary unless further errors arise. CWOBJ0012E: The LogElement type code, {0} ({1}), is not recognized for this operation. Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ0013E: Exception caught while attempting to evict entries from the cache: {0} Explanation: A problem occurred while attempting to apply the eviction entries to the cache. User Response: Check the exception message to see whether this is an expected exception or not. CWOBJ0014E: The ObjectGrid runtime detected an attempt to nest transactions. Explanation: Nesting of transactions is not permitted. User Response: Modify the code to avoid the nesting of transactions. CWOBJ0015E: Exception caught while attempting to process a transaction: {0} Explanation: A problem occurred while attempting to process a transaction. User Response: Check the exception message to see whether this is an expected exception or not. CWOBJ0016E: No active transaction was detected for the current operation. Explanation: An active transaction is necessary to perform this operation. User Response: Modify the code to start a transaction before performing this operation. CWOBJ0017E: A duplicate key exception was detected while processing the ObjectMap operation: {0} Explanation: The key for the entry already exists in the cache. User Response: Modify the code to avoid inserting the same key more than once. CWOBJ0018E: The key was not found while processing the ObjectMap operation: {0} Explanation: The key for the entry does not exist in the cache. User Response: Modify the code to ensure the entry exists before attempting the operation. CWOBJ0019W: Did not find data in the cache entry slot reserved for {0} to use for map name {1}. Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ0020E: Cache entry is not in backing map name {0}. Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ0021E: A usable ObjectTransformer instance was not found during the deserialization of the LogSequence for ObjectGrid {0} and ObjectMap {1}. Explanation: The receiving side of a LogSequence does not have the proper configuration to support the

204

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

required ObjectTransformer instance. User Response: Verify the configuration of the ObjectGrid instances for both the serving and receiving sides of the LogSequence. CWOBJ0022E: Caller does not own mutex: {0}. Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ0023E: The CopyMode ({0}) is not recognized for this operation. Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ0024E: Could not deserialize field {0} in class {1}. Deserialization failed. Explanation: During deserialization of an object, a required field was not found. This is likely an ObjectGrid runtime error. User Response: CMSG0002 CWOBJ0025E: The serialization of the LogSequence failed. The number of serialized LogElements ({0}) does not match the number of read LogElements ({1}). Explanation: Internal error in ObjectGrid runtime. User Response: CMSG0002 CWOBJ1207W: The property {0} on plugin {1} is not a supported property. Explanation: Java primitives and their java.lang counterparts are the only supported property types. java.lang.String is also supported. User Response: Please check the property type and change it to one of the supported types. CWOBJ1208W: The {0} is not a supported Plugin. Explanation: The type of Plugin is unsupported. User Response: Add one of the supported Plugin types. CWOBJ1211E: The PMI creation of {0} fails. The exception is {1} Explanation: An attempt to create ObjectGrid PMI has failed for some reasons. User Response: Please examine the exception message and the FFDC log for why it fails. CWOBJ1215I: ObjectGrid Transaction Propagation Event Listener Initializing [ObjectGrid {0}]. Explanation: ObjectGrid Transaction Propagation Event Listener Initializing. User Response: None. Informational entry. CWOBJ1216I: ObjectGrid Transaction Propagation Event Listener Initialized [ObjectGrid {0}]. Explanation: ObjectGrid Transaction Propagation Event Listener Initialized. User Response: None. Informational entry. CWOBJ1217I: ObjectGrid Transaction Propagation Service Point Initialized [ObjectGrid {0}]. Explanation: ObjectGrid Transaction Propagation Service Point Initialized. User Response: None. Informational entry. CWOBJ1218E: ObjectGrid Transaction Propagation Event Listener failure [ObjectGrid {0} Exception message {1}]. Explanation: ObjectGrid Transaction Propagation failure encountered. User Response: Please examine the exception to determine the failure. CWOBJ1219E: ObjectGrid Transaction Propagation Service End Point failure [ObjectGrid {0} Exception message {1}]. Explanation: ObjectGrid Transaction Propagation Service End Point failure encountered. User Response: Please examine the exception to determine the failure. CWOBJ9000I: This is an English only Informational message: {0}. Explanation: Message was added after translation cutoff and so is not translated. User Response: See message for details. CWOBJ9001W: This is an English only Warning message: {0}. Explanation: Message was added after translation cutoff and so is not translated. User Response: See message for details.

Chapter 10. Troubleshooting

205

CWOBJ9002E: This is an English only Error message: {0}. Explanation: Message was added after translation cutoff and so is not translated. CWOBJ9002. User Response: See message for details.

206

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Notices
References in this publication to IBM products, programs, or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM product, program, or service is not intended to state or imply that only IBMs product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any of IBMs intellectual property rights may be used instead of the IBM product, program, or service. Evaluation and verification of operation in conjunction with other products, except those expressly designated by IBM, is the users responsibility. IBM may have patents or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to: IBM Director of Licensing IBM Corporation 500 Columbus Avenue Thornwood, New York 10594 USA

Copyright IBM Corp. 2004, 2005

207

208

IBM WebSphere WebSphere Extended Deployment Version 6.0: ObjectGrid programming model guide

Trademarks and service marks


The following terms are trademarks of IBM Corporation in the United States, other countries, or both: v AIX v CICS v Cloudscape v DB2 v Domino v IBM v Lotus v MQSeries v MVS v OS/390 v RACF v Redbooks v Tivoli v WebSphere v z/OS Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. LINUX is a trademark of Linus Torvalds in the U.S., other countries, or both. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both. UNIX is a registered trademark of The Open Group in the United States and other countries. Other company, product, and service names may be trademarks or service marks of others.

Copyright IBM Corp. 2004, 2005

209

You might also like