FileDocCategorySizeDatePackage
VersionLockingPolicy.javaAPI DocGlassfish v2 API28872Tue May 22 16:54:16 BST 2007oracle.toplink.essentials.descriptors

VersionLockingPolicy

public class VersionLockingPolicy extends Object implements Serializable, oracle.toplink.essentials.internal.descriptors.OptimisticLockingPolicy

Purpose: Used to allow a single version number to be used for optimistic locking.

since
TOPLink/Java 2.0

Fields Summary
protected DatabaseField
writeLockField
protected int
lockValueStored
protected ClassDescriptor
descriptor
protected transient Expression
cachedExpression
public static final int
IN_CACHE
public static final int
IN_OBJECT
Constructors Summary
public VersionLockingPolicy()
PUBLIC: Create a new VersionLockingPolicy. Defaults to storing the lock value in the cache.


                        
      
        super();
        storeInCache();
    
public VersionLockingPolicy(String fieldName)
PUBLIC: Create a new VersionLockingPolicy. Defaults to storing the lock value in the cache.

param
fieldName specifies the field name for the write lock field.

        this(new DatabaseField(fieldName));
    
public VersionLockingPolicy(DatabaseField field)
PUBLIC: Create a new VersionLockingPolicy. Defaults to storing the lock value in the cache.

param
the write lock field.

        this();
        setWriteLockField(field);
    
Methods Summary
public voidaddLockFieldsToUpdateRow(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Add update fields for template row. These are any unmapped fields required to write in an update.

        if (isStoredInCache()) {
            databaseRow.put(getWriteLockField(), null);
        }
    
public voidaddLockValuesToTranslationRow(ObjectLevelModifyQuery query)
INTERNAL: This method adds the lock value to the translation row of the passed in query. depending on the storage flag, the value is either retrieved from the cache of the object.

        Object value;
        if (isStoredInCache()) {
            value = query.getSession().getIdentityMapAccessor().getWriteLockValue(query.getPrimaryKey(), query.getObject().getClass());
        } else {
            value = lockValueFromObject(query.getObject());
        }
        if (value == null) {
            if (query.isDeleteObjectQuery()) {
                throw OptimisticLockException.noVersionNumberWhenDeleting(query.getObject(), query);
            } else {
                throw OptimisticLockException.noVersionNumberWhenUpdating(query.getObject(), query);
            }
        }
        query.getTranslationRow().put(getWriteLockField(), value);
    
public ExpressionbuildDeleteExpression(DatabaseTable table, Expression mainExpression, oracle.toplink.essentials.internal.sessions.AbstractRecord row)
INTERNAL: When given an expression, this method will return a new expression with the optimistic locking values included. The values are taken from the passed in database row. This expression will be used in a delete call.

        //use the same expression as update
        return buildUpdateExpression(table, mainExpression, row, null);
    
protected ExpressionbuildExpression()
INTERNAL: Returns an expression that will be used for both the update and delete where clause

        ExpressionBuilder builder = new ExpressionBuilder();

        return builder.getField(getWriteLockField()).equal(builder.getParameter(getWriteLockField()));
    
public ExpressionbuildUpdateExpression(DatabaseTable table, Expression mainExpression, oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.sessions.AbstractRecord row2)
INTERNAL: When given an expression, this method will return a new expression with the optimistic locking values included. The values are taken from the passed in database row. This expression will be used in an update call.

        if (cachedExpression == null) {
            cachedExpression = buildExpression();
        }
        if (getWriteLockField().getTableName().equals(table.getName())) {
            return mainExpression.and(cachedExpression);
        }
        return mainExpression;
    
public java.lang.Objectclone()
INTERNAL: Clone the policy

        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    
public intcompareWriteLockValues(java.lang.Object value1, java.lang.Object value2)
INTERNAL: This method compares two writeLockValues. The writeLockValues should be non-null and of type Number. Returns: -1 if value1 is less (older) than value2; 0 if value1 equals value2; 1 if value1 is greater (newer) than value2. Throws: NullPointerException if the passed value is null; ClassCastException if the passed value is of a wrong type.

        BigDecimal bigDecimalValue1, bigDecimalValue2;
        if(value1 instanceof BigDecimal) {
            bigDecimalValue1 = (BigDecimal)value1;
        } else {
            bigDecimalValue1 = new BigDecimal(((Number)value1).longValue());
        }
        if(value2 instanceof BigDecimal) {
            bigDecimalValue2 = (BigDecimal)value2;
        } else {
            bigDecimalValue2 = new BigDecimal(((Number)value2).longValue());
        }
        return bigDecimalValue1.compareTo(bigDecimalValue2);
    
public java.lang.ObjectgetBaseValue()
INTERNAL: This is the base value that is older than all other values, it is used in the place of null in some situations.

        return new BigDecimal(0);
    
protected java.lang.ClassgetDefaultLockingFieldType()
INTERNAL: Return the default version locking filed java type, default is BigDecimal

        return ClassConstants.BIGDECIMAL;

    
protected ClassDescriptorgetDescriptor()
INTERNAL:

        return descriptor;
    
protected java.lang.ObjectgetInitialWriteValue(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: returns the initial locking value

        return new BigDecimal(1);
    
protected java.lang.ObjectgetNewLockValue(ModifyQuery query)
INTERNAL: This method gets the write lock value from either the cache or the object stored in the query. It then returns the new incremented value.

        Class objectClass = query.getDescriptor().getJavaClass();
        Number value;
        Number newWriteLockValue = null;
        if (isStoredInCache()) {
            value = (Number)query.getSession().getIdentityMapAccessor().getWriteLockValue(((WriteObjectQuery)query).getPrimaryKey(), objectClass);
        } else {
            value = (Number)lockValueFromObject(((ObjectLevelModifyQuery)query).getObject());
        }
        if (value == null) {
            throw oracle.toplink.essentials.exceptions.OptimisticLockException.noVersionNumberWhenUpdating(((ObjectLevelModifyQuery)query).getObject(), (ObjectLevelModifyQuery)query);
        }

        // Increment the value, this goes to the database
        newWriteLockValue = incrementWriteLockValue(value);
        return newWriteLockValue;
    
protected java.util.VectorgetUnmappedFields()
INTERNAL: This method returns any of the fields that are not mapped in the object. In the case of the value being stored in the cache, a vector with one value is returned. In the case of being stored in the object, an empty vector is returned.

        Vector fields = new Vector(1);
        if (isStoredInCache()) {
            fields.addElement(getWriteLockField());
        }
        return fields;
    
public java.lang.ObjectgetValueToPutInCache(oracle.toplink.essentials.internal.sessions.AbstractRecord row, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Return the value that should be stored in the identity map. If the value is stored in the object, then return a null.

        if (isStoredInCache()) {
            return row.get(getWriteLockField());
        } else {
            return null;
        }
    
public intgetVersionDifference(java.lang.Object currentValue, java.lang.Object domainObject, java.util.Vector primaryKeys, oracle.toplink.essentials.internal.sessions.AbstractSession session)
PUBLIC: Return the number of versions different between these objects.

param
currentValue the new lock value
param
domainObject the object containing the version to be compared to
param
primaryKeys a vector containing the primary keys of the domainObject
param
session the session to be used with the comparison

        Number writeLockFieldValue;
        Number newWriteLockFieldValue = (Number)currentValue;

        // 2.5.1.6 if the write lock value is null, then what ever we have is treated as newer.
        if (newWriteLockFieldValue == null) {
            return 0;//merge it as either the object is new or being forced merged.
        }

        if (isStoredInCache()) {
            writeLockFieldValue = (Number)session.getIdentityMapAccessor().getWriteLockValue(primaryKeys, domainObject.getClass());
        } else {
            writeLockFieldValue = (Number)lockValueFromObject(domainObject);
        }
        if (writeLockFieldValue == null) {
            writeLockFieldValue = new BigDecimal(0);// the object is not in the cache so assume this is a new object
        }
        if (!(writeLockFieldValue instanceof BigDecimal)) {
            writeLockFieldValue = new BigDecimal(writeLockFieldValue.longValue());
        }
        if (!(newWriteLockFieldValue instanceof BigDecimal)) {
            newWriteLockFieldValue = new BigDecimal(newWriteLockFieldValue.longValue());
        }
        return ((BigDecimal)newWriteLockFieldValue).subtract((BigDecimal)writeLockFieldValue).intValue();
    
public DatabaseFieldgetWriteLockField()
INTERNAL: Return the write lock field.

        return writeLockField;
    
public java.lang.StringgetWriteLockFieldName()
PUBLIC: Return the field name of the field that stores the write lock value.

        return getWriteLockField().getQualifiedName();
    
public ExpressiongetWriteLockUpdateExpression(ExpressionBuilder builder)
INTERNAL: Retrun an expression that updates the write lock

        return ExpressionMath.add(builder.getField(writeLockField.getName()), 1);
    
public java.lang.ObjectgetWriteLockValue(java.lang.Object domainObject, java.util.Vector primaryKey, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: This method will return the optimistic lock value for the object

        Number writeLockFieldValue;
        if (isStoredInCache()) {
            writeLockFieldValue = (Number)session.getIdentityMapAccessor().getWriteLockValue(primaryKey, domainObject.getClass());
        } else {
            writeLockFieldValue = (Number)lockValueFromObject(domainObject);
        }
        return writeLockFieldValue;
    
protected java.lang.NumberincrementWriteLockValue(java.lang.Number numberValue)
INTERNAL: adds 1 to the value passed in

        BigDecimal writeLockValue;

        if (numberValue instanceof BigDecimal) {
            writeLockValue = (BigDecimal)numberValue;
        } else {
            writeLockValue = new BigDecimal(numberValue.doubleValue());
        }

        return writeLockValue.add(new BigDecimal(1));
    
public voidinitialize(oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: It is responsible for initializing the policy;

        DatabaseMapping mapping = mappingFromLockField();
        if (mapping == null) {
            if (isStoredInObject()) {
                throw OptimisticLockException.mustHaveMappingWhenStoredInObject(descriptor.getJavaClass());
            } else {
                return;
            }
        }
        if (mapping.isReadOnly()) {
            throw DescriptorException.mappingCanNotBeReadOnly(mapping);
        } else {
            if (isStoredInCache()) {
                session.getIntegrityChecker().handleError(DescriptorException.mustBeReadOnlyMappingWhenStoredInCache(mapping));
            }
        }
    
public voidinitializeProperties()
INTERNAL: It is responsible for initializing the policy properties;

        DatabaseField dbField = getWriteLockField();

        descriptor.buildField(dbField);
        if (dbField.getType() == null) {
            //set the default type
            dbField.setType(getDefaultLockingFieldType());

        }
        Enumeration enumtr = this.getUnmappedFields().elements();
        while (enumtr.hasMoreElements()) {
            DatabaseField lockField;
            lockField = (DatabaseField)enumtr.nextElement();
            descriptor.getFields().addElement(lockField);
        }
    
public booleanisChildWriteLockValueGreater(oracle.toplink.essentials.internal.sessions.AbstractSession session, java.util.Vector primaryKey, java.lang.Class original, oracle.toplink.essentials.internal.sessions.ObjectChangeSet changeSet)
INTERNAL: Update the parent write lock value if the unit of works has been incremented.

        if (isStoredInCache()) {
            // If this uow changed the object the version must be updated,
            // we can check this by ensuring our value is greater than our parent's.
            Number writeLockValue = (Number)changeSet.getWriteLockValue();
            Number parentValue = (Number)session.getIdentityMapAccessor().getWriteLockValue(primaryKey, original);
            if ((parentValue != null) && (!(parentValue instanceof BigDecimal))) {
                parentValue = new BigDecimal(parentValue.longValue());
            }
            if ((writeLockValue != null) && (!(writeLockValue instanceof BigDecimal))) {
                writeLockValue = new BigDecimal(writeLockValue.longValue());
            }
            if (writeLockValue != null) {// This occurs if the object was deleted
                if ((parentValue == null) || (((BigDecimal)parentValue).compareTo((BigDecimal)writeLockValue) == -1)) {
                    return true;
                }
            }
        }
        return false;
    
public booleanisChildWriteLockValueGreater(oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow, java.util.Vector primaryKey, java.lang.Class original)
INTERNAL: Update the parent write lock value if the unit of works has been incremented.

        if (isStoredInCache()) {
            // If this uow changed the object the version must be updated,
            // we can check this by ensuring our value is greater than our parent's.
            Number writeLockValue = (Number)uow.getIdentityMapAccessor().getWriteLockValue(primaryKey, original);
            Number parentValue = (Number)uow.getParent().getIdentityMapAccessor().getWriteLockValue(primaryKey, original);
            if ((parentValue != null) && (!(parentValue instanceof BigDecimal))) {
                parentValue = new BigDecimal(parentValue.longValue());
            }
            if ((writeLockValue != null) && (!(writeLockValue instanceof BigDecimal))) {
                writeLockValue = new BigDecimal(writeLockValue.longValue());
            }
            if (writeLockValue != null) {// This occurs if the object was deleted
                if ((parentValue == null) || (((BigDecimal)parentValue).compareTo((BigDecimal)writeLockValue) == -1)) {
                    return true;
                }
            }
        }
        return false;
    
public booleanisNewerVersion(java.lang.Object currentValue, java.lang.Object domainObject, java.util.Vector primaryKey, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Compares the value with the value from the object (or cache). Will return true if the object is newer.

        Number writeLockFieldValue;
        Number newWriteLockFieldValue = (Number)currentValue;

        // 2.5.1.6 if the write lock value is null, then what ever we have is treated as newer.
        if (newWriteLockFieldValue == null) {
            return true;
        }

        if (isStoredInCache()) {
            writeLockFieldValue = (Number)session.getIdentityMapAccessor().getWriteLockValue(primaryKey, domainObject.getClass());
        } else {
            writeLockFieldValue = (Number)lockValueFromObject(domainObject);
        }
        if (!(writeLockFieldValue instanceof BigDecimal)) {
            writeLockFieldValue = new BigDecimal(writeLockFieldValue.longValue());
        }
        if (!(newWriteLockFieldValue instanceof BigDecimal)) {
            newWriteLockFieldValue = new BigDecimal(newWriteLockFieldValue.longValue());
        }
        if ((writeLockFieldValue != null) && (((BigDecimal)newWriteLockFieldValue).compareTo((BigDecimal)writeLockFieldValue) != 1)) {
            return false;
        }
        return true;
    
public booleanisNewerVersion(oracle.toplink.essentials.internal.sessions.AbstractRecord databaseRow, java.lang.Object domainObject, java.util.Vector primaryKey, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Compares the value from the row and from the object (or cache). Will return true if the object is newer than the row.

        Number writeLockFieldValue;
        Number newWriteLockFieldValue = (Number)databaseRow.get(getWriteLockField());
        if (isStoredInCache()) {
            writeLockFieldValue = (Number)session.getIdentityMapAccessor().getWriteLockValue(primaryKey, domainObject.getClass());
        } else {
            writeLockFieldValue = (Number)lockValueFromObject(domainObject);
        }
        if (!(writeLockFieldValue instanceof BigDecimal)) {
            writeLockFieldValue = new BigDecimal(writeLockFieldValue.longValue());
        }
        if (!(newWriteLockFieldValue instanceof BigDecimal)) {
            newWriteLockFieldValue = new BigDecimal(newWriteLockFieldValue.longValue());
        }
        if ((writeLockFieldValue != null) && (((BigDecimal)newWriteLockFieldValue).compareTo((BigDecimal)writeLockFieldValue) != 1)) {
            return false;
        }
        return true;
    
public booleanisStoredInCache()
PUBLIC: Return true if the lock value is stored in the cache.

        return lockValueStored == IN_CACHE;
    
public booleanisStoredInObject()
PUBLIC: Return true if the lock value is stored in the object.

        return lockValueStored == IN_OBJECT;
    
protected java.lang.ObjectlockValueFromObject(java.lang.Object domainObject)
INTERNAL: retrieves the lock value from the object.

        return getDescriptor().getObjectBuilder().getBaseValueForField(getWriteLockField(), domainObject);
    
protected DatabaseMappingmappingFromLockField()
INTERNAL: returns the mapping from the writelock field. returns null if not found

        return descriptor.getObjectBuilder().getMappingForField(getWriteLockField());
    
public voidmergeIntoParentCache(oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow, java.util.Vector primaryKey, java.lang.Object object)
INTERNAL: Only applicable when the value is stored in the cache. Will merge with the parent unit of work.

        if (isStoredInCache()) {
            Object parentValue = uow.getParent().getIdentityMapAccessor().getWriteLockValue(primaryKey, object.getClass());
            uow.getIdentityMapAccessor().updateWriteLockValue(primaryKey, object.getClass(), parentValue);
        }
    
public voidprepareFetchGroupForReadQuery(FetchGroup fetchGroup, ObjectLevelReadQuery query)
INTERNAL: Prepare fetch group for read query

        //version stored in object, add the version attribute to the fetch group attribute list
        if (isStoredInObject()) {
            String verAttributeName = descriptor.getObjectBuilder().getMappingForField(writeLockField).getAttributeName();
            fetchGroup.addAttribute(verAttributeName);
        } else {//stored in cache, add the version field to the select fields
            if (!query.getAdditionalFields().contains(writeLockField)) {
                query.addAdditionalField(writeLockField);
            }
        }
    
public voidsetDescriptor(ClassDescriptor descriptor)
INTERNAL:

        this.descriptor = descriptor;
    
public voidsetIsStoredInCache(boolean isStoredInCache)
PUBLIC: Set whether to store the lock in the cache or in the object.

param
isStoredInCache set this to true if you would like to store lock in the cache and set it to false if you would like to store it in the object.

        if (isStoredInCache) {
            storeInCache();
        } else {
            storeInObject();
        }
    
public voidsetWriteLockField(DatabaseField writeLockField)
ADVANCED: Set the write lock field. This can be used for advanced field types, such as XML nodes, or to set the field type.

        this.writeLockField = writeLockField;
    
public voidsetWriteLockFieldName(java.lang.String writeLockFieldName)
PUBLIC: Set the write lock field name.

param
writeLockFieldName the name of the field to lock against.

        setWriteLockField(new DatabaseField(writeLockFieldName));
    
public voidsetupWriteFieldsForInsert(ObjectLevelModifyQuery query)
INTERNAL: This method must be included in any locking policy. Put the initial writelock value into the modifyRow.

        Object lockValue = getInitialWriteValue(query.getSession());
        ObjectChangeSet objectChangeSet = query.getObjectChangeSet();
        if (objectChangeSet != null) {
            objectChangeSet.setInitialWriteLockValue(lockValue);
        }
        updateWriteLockValueForWrite(query, lockValue);
    
public voidstoreInCache()
PUBLIC: Configure the version lock value to be stored in the cache. This allows for the object not to require to store its version value as an attribute. Note: if using a stateless model where the object can be passed to a client and then later updated in a different transaction context, then the version lock value should not be stored in the cache, but in the object to ensure it is the correct value for that object. This is the default.

        lockValueStored = IN_CACHE;
    
public voidstoreInObject()
PUBLIC: Configure the version lock value to be stored in the object. The object must define a mapping and an attribute to store the version value. Note: the value will be updated internally by TopLink and should not be updated by the application.

        lockValueStored = IN_OBJECT;
    
public voidupdateRowAndObjectForUpdate(ObjectLevelModifyQuery query, java.lang.Object domainObject)
INTERNAL: This method updates the modify row, and the domain object with the new lock value. #see this method in VersionLockingPolicy

        Object lockValue = getNewLockValue(query);
        if (isStoredInCache()) {
            query.getSession().getIdentityMapAccessor().updateWriteLockValue(query.getPrimaryKey(), domainObject.getClass(), lockValue);
        }
        updateWriteLockValueForWrite(query, lockValue);
    
protected voidupdateWriteLockValueForWrite(ObjectLevelModifyQuery query, java.lang.Object lockValue)
INTERNAL: Update the row, object and change set with the version value. This handles the version being mapped in nested aggregates, writable or read-only.

        query.getModifyRow().put(getWriteLockField(), lockValue);
        ObjectChangeSet objectChangeSet = query.getObjectChangeSet();
        if (objectChangeSet != null) {
            objectChangeSet.setWriteLockValue(lockValue);
        }

        // CR#3173211
        // If the value is stored in the cache or object, there still may
        // be read-only mappings for it, so the object must always be updated for
        // any writable or read-only mappings for the version value.
        // Reuse the method used for returning as has the same requirements.
        AbstractRecord record = getDescriptor().getObjectBuilder().createRecord(1);
        record.put(getWriteLockField(), lockValue);
        getDescriptor().getObjectBuilder().assignReturnRow(query.getObject(), query.getSession(), record);
        if (query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
            if (objectChangeSet == null) {
                // For aggregate collections the change set may be null, as they use the old commit still.
                objectChangeSet = (ObjectChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet().getObjectChangeSetForClone(query.getObject());
            }
            query.getQueryMechanism().updateChangeSet(getDescriptor(), objectChangeSet, record, query.getObject());
        }
    
public voidvalidateDelete(int rowCount, java.lang.Object object, WriteObjectQuery query)
INTERNAL: Check the row count for lock failure.

        if (rowCount <= 0) {
            throw OptimisticLockException.objectChangedSinceLastReadWhenDeleting(object, query);
        }
    
public voidvalidateUpdate(int rowCount, java.lang.Object object, WriteObjectQuery owner)
INTERNAL: Check the row count for lock failure.

        if (rowCount <= 0) {
            throw OptimisticLockException.objectChangedSinceLastReadWhenUpdating(object, owner);
        }
    
public voidwriteLockValueIntoRow(ObjectLevelModifyQuery query, java.lang.Object domainObject)
INTERNAL: This method updates the modify row with the old lock value.

        Object lockValue = getWriteLockValue(domainObject, query.getPrimaryKey(), query.getSession());
        query.getModifyRow().put(getWriteLockField(), lockValue);
        if (isStoredInCache()) {
            query.getSession().getIdentityMapAccessor().updateWriteLockValue(query.getPrimaryKey(), domainObject.getClass(), lockValue);
        }