Plug-ins for managing transaction life cycle events
Use the TransactionCallback plug-in to customize versioning and comparison operations of cache objects when you are using the optimistic locking strategy.
You can provide a pluggable optimistic callback object that implements the com.ibm.websphere.objectgrid.plugins.OptimisticCallback interface. For entity maps, a high performance OptimisticCallback plug-in is automatically configured.
Purpose
Use the OptimisticCallback interface to provide optimistic comparison operations for the values of a map. An OptimisticCallback implementation is required when you use the optimistic locking strategy. WebSphere® eXtreme Scale provides a default OptimisticCallback implementation. However, usually the application must plug in its own implementation of the OptimisticCallback interface. See Locking strategies for more information.
Default implementation
- A loader is plugged for the backing map.
- The loader knows how to perform the optimistic comparison without assistance from an OptimisticCallback plug-in.
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. The loader increments the returned value to generate a new version number before 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 that 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. You can use the default OptimisticCallback implementation because the loader is able to handle optimistic versioning without any assistance from an OptimisticCallback object.
Default implementation for entities
Entities are stored in the ObjectGrid using tuple objects. The default OptimisticCallback implementation behaves similarly to the behavior for non-entity maps. However, the version field in the entity is identified using the @Version annotation or the version attribute in the entity descriptor XML file.
The version attribute can be of the following types: int, Integer, short, Short, long, Long or java.sql.Timestamp. An entity should have only one version attribute defined. The version attribute should be set only during construction. After the entity is persisted, the value of the version attribute should not be modified.
If a version attribute is not configured and the optimistic locking strategy is used, then the entire tuple is implicitly versioned using the entire state of the tuple.
@Entity
public class Employee
{
private long sequence;
// Sequential sequence number used for optimistic versioning.
@Version
public long getSequenceNumber() {
return sequence;
}
public void setSequenceNumber(long newSequenceNumber) {
this.sequence = newSequenceNumber;
}
// Other get/set methods for other fields of Employee object.
}
Writing an OptimisticCallback implementation
An OptimisticCallback implementation must implement the OptimisticCallback interface and follow the common ObjectGrid plug-in conventions
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
The getVersionedObjectForValue 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 set 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 batchUpdate method on the Loader, which is called when a flush operation occurs or a transaction is committed.
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 implementation. For example, these people agreed on the value that is returned by the getVersionedObjectForValue method. As previously described, the default OptimisticCallback implementation returns the special value NULL_OPTIMISTIC_VERSION as the version object.
updateVersionedObjectForValue method
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 increments 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 implementation or worked closely with the person that implemented the EmployeeOptimisticCallbackImpl implementation.
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. Because the actual implementation is unknown, this method is provided to perform the proper serialization. The default implementation calls the writeObject method.
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 calls the readObject method.
Using an application-provided OptimisticCallback implementation
You have two approaches to add an application-provided OptimisticCallback into the BackingMap configuration: programmatic configuration and XML configuration.
Programmatically plug in an OptimisticCallback implementation
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 );
XML configuration approach to plug in an OptimisticCallback implementation
<?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>