blob: 971b0b9d37cdb8d4879a50b49f43542662b5300c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.kernel;
/**
* {@link LockManager} implementation that provides support
* for version checking and version updating when locks are acquired.
* This lock manager may be used standalone or extended for additional locking.
*
* @author Marc Prud'hommeaux
*/
public class VersionLockManager
extends AbstractLockManager {
private boolean _versionCheckOnReadLock = true;
private boolean _versionUpdateOnWriteLock = true;
private boolean _refreshing = false;
/**
* Returns the given instance's lock level, assuming that the state's
* lock object is a number. If the given instance is embedded, traverses
* to its owner. Override if lock is not stored as a number.
*/
@Override
public int getLockLevel(OpenJPAStateManager sm) {
while (sm.getOwner() != null)
sm = sm.getOwner();
Number level = (Number) sm.getLock();
return (level == null) ? LOCK_NONE : level.intValue();
}
/**
* Sets the given instance's lock level to the given number. Override
* to store something else as the lock.
*/
protected void setLockLevel(OpenJPAStateManager sm, int level) {
sm.setLock(level);
}
/**
* Nulls given instance's lock object.
*/
@Override
public void release(OpenJPAStateManager sm) {
sm.setLock(null);
}
/**
* Delegates to {@link #lockInternal} after traversing to owning
* instance (if embedded) and assuring that the instance is persistent,
* is not new, and is not already locked at a higher level. After
* locking, calls {@link #setLockLevel} with the given level.
*/
@Override
public void lock(OpenJPAStateManager sm, int level, int timeout,
Object sdata) {
commonLock(sm, level, timeout, sdata, !_refreshing);
}
@Override
public void refreshLock(OpenJPAStateManager sm, int level, int timeout,
Object sdata) {
try {
_refreshing = true;
commonLock(sm, level, timeout, sdata, false);
} finally {
_refreshing = false;
}
}
private void commonLock(OpenJPAStateManager sm, int level, int timeout,
Object sdata, boolean postLockVersionCheck) {
if (level == LOCK_NONE)
return;
while (sm.getOwner() != null)
sm = sm.getOwner();
int oldLevel = getLockLevel(sm);
if (!sm.isPersistent() || sm.isNew() || level <= oldLevel)
return;
try {
lockInternal(sm, level, timeout, sdata, postLockVersionCheck);
} catch (RuntimeException re) {
// revert lock
setLockLevel(sm, oldLevel);
throw re;
}
}
/**
* Marks the instance's transactional status in accordance with
* the settings of {@link #getVersionCheckOnReadLock}
* and {@link #getVersionUpdateOnWriteLock}. Override to perform
* additional locking.
*
* @see StoreContext#transactional
*/
protected void lockInternal(OpenJPAStateManager sm, int level, int timeout,
Object sdata, boolean postLockVersionCheck) {
optimisticLockInternal(sm, level, timeout, sdata, postLockVersionCheck);
}
protected void optimisticLockInternal(OpenJPAStateManager sm, int level,
int timeout, Object sdata, boolean postLockVersionCheck) {
// Set lock level first to prevent infinite recursion with
// transactional(..) call
setLockLevel(sm, level);
if (level >= LockLevels.LOCK_WRITE && _versionUpdateOnWriteLock)
getContext().transactional(sm.getManagedInstance(), true, null);
else if (level >= LockLevels.LOCK_READ && _versionCheckOnReadLock)
getContext().transactional(sm.getManagedInstance(), false, null);
}
/**
* Whether or not we should force a version check at commit
* time when a read lock is requested in order to verify read
* consistency. Defaults to true.
*/
public void setVersionCheckOnReadLock(boolean versionCheckOnReadLock) {
_versionCheckOnReadLock = versionCheckOnReadLock;
}
/**
* Whether or not we should force a version check at commit
* time when a read lock is requested in order to verify read
* consistency. Defaults to true.
*/
public boolean getVersionCheckOnReadLock() {
return _versionCheckOnReadLock;
}
/**
* Whether or not we should force an update to the version at commit
* time when a write lock is requested. Defaults to true.
*/
public void setVersionUpdateOnWriteLock(boolean versionUpdateOnWriteLock) {
_versionUpdateOnWriteLock = versionUpdateOnWriteLock;
}
/**
* Whether or not we should force an update to the version at commit
* time when a write lock is requested. Defaults to true.
*/
public boolean getVersionUpdateOnWriteLock() {
return _versionUpdateOnWriteLock;
}
}