Package org.jasig.portal.concurrency.locking

Source Code of org.jasig.portal.concurrency.locking.ReferenceEntityLockService

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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.jasig.portal.concurrency.locking;

import java.util.Date;

import org.jasig.portal.EntityIdentifier;
import org.jasig.portal.concurrency.IEntityLock;
import org.jasig.portal.concurrency.IEntityLockService;
import org.jasig.portal.concurrency.LockingException;
import org.jasig.portal.properties.PropertiesManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Dan Ellentuck
* @version $Revision: 19776 $
*/
public class ReferenceEntityLockService implements IEntityLockService
{
    private static final Log log = LogFactory.getLog(ReferenceEntityLockService.class);
   
    // Singleton instance:
    private static IEntityLockService singleton = null;

    // Store for IEntityLocks:
    private IEntityLockStore lockStore = null;

    // Locking properties, initialized with default values, are settable
    // via portal.properties:

    // Are we running in a multi-server environment?  If so, the lock store
    // will be in persistent storage.
    private boolean multiServer = false;

    // Lifetime of a lock in seconds, defaults to 5 minutes.
    private int defaultLockPeriod = 300;

    /* Fudge factor in milliseconds, extends the apparent expiration times
     * of potentially conflicting locks beyond their actual expirations.
     * We only use it when checking for locking conflicts and then only if
     * inMemory == false.  Defaults to 5000.
     */
    private int lockToleranceMillis = 5000;
/**
* ReferenceEntityLockingService constructor comment.
*/
public ReferenceEntityLockService() throws LockingException
{
    super();
    initialize();
}
/**
* Attempts to change the lock's <code>lockType</code> to <code>newType</code>.
* @param lock IEntityLock
* @param newType int
* @exception org.jasig.portal.concurrency.LockingException
*/
public void convert(IEntityLock lock, int newType) throws LockingException
{
    convert(lock, newType, defaultLockPeriod);
}
/**
* Attempts to change the lock's <code>lockType</code> to <code>newType</code>.
* @param lock IEntityLock
* @param newType int
* @param newDuration int
* @exception org.jasig.portal.concurrency.LockingException
*/
public void convert(IEntityLock lock, int newType, int newDuration) throws LockingException
{
    if ( lock.getLockType() == newType )
       { throw new LockingException("Could not convert " + lock + " : old and new lock TYPEs are the same."); }

    if ( ! isValidLockType(newType)  )
        { throw new LockingException("Could not convert " + lock + " : lock TYPE " + newType + " is invalid."); }

    if ( ! isValid(lock) )
        { throw new LockingException("Could not convert " + lock + " : lock is invalid."); }

    if ( newType == WRITE_LOCK && retrieveLocks(lock.getEntityType(), lock.getEntityKey(), null).length > 1 )
        { throw new LockingException("Could not convert " + lock + " : another lock already exists."); }

    if ( newType == READ_LOCK )
        { /* Can always convert to READ */ }

    Date newExpiration = getNewExpiration(newDuration);
    getLockStore().update(lock, newExpiration, new Integer(newType));
    ((EntityLockImpl)lock).setLockType(newType);
    ((EntityLockImpl)lock).setExpirationTime(newExpiration);
}

/**
* Answer if this <code>IEntityLock</code> exists in the store.
* @param lock
* @return boolean
*/
public boolean existsInStore(IEntityLock lock) throws LockingException
{
    Class entityType = lock.getEntityType();
    String key = lock.getEntityKey();
    Integer lockType = new Integer(lock.getLockType());
    Date expiration = lock.getExpirationTime();
    String owner = lock.getLockOwner();
    IEntityLock[] lockArray = getLockStore().find(entityType, key, lockType, expiration, owner);

    return (lockArray.length > 0);
}

/**
* @return int
*/
private int getDefaultLockPeriod() {
    return defaultLockPeriod;
}
/**
* @return org.jasig.portal.concurrency.locking.IEntityLockStore
*/
private IEntityLockStore getLockStore() {
    return lockStore;
}
/**
* @return int
*/
private int getLockToleranceMillis() {
    return lockToleranceMillis;
}
/**
* @return java.util.Date
*/
private Date getNewExpiration(int durationSecs)
{
    return new Date(System.currentTimeMillis() + (durationSecs*1000));
}
/**
* @exception LockingException
*/
private void initialize() throws LockingException
{
    String eMsg = null;

    try
    {
        multiServer = PropertiesManager.getPropertyAsBoolean("org.jasig.portal.concurrency.multiServer", false);

        lockStore = ( multiServer )
            ? RDBMEntityLockStore.singleton()
            : MemoryEntityLockStore.singleton();
    }
    catch ( Exception e )
    {
        eMsg = "ReferenceEntityLockingService.initialize(): Failed to instantiate entity lock store. " + e;
        log.error( eMsg);
        throw new LockingException(eMsg);
    }

    try
    {
        int lockDuration = PropertiesManager.getPropertyAsInt
            ("org.jasig.portal.concurrency.IEntityLockService.defaultLockDuration");
        setDefaultLockPeriod(lockDuration);
    }
    catch ( Exception ex ) { /* defaults to 5 minutes. */ }

    if ( multiServer ) {
        try
        {
            int lockTolerance = PropertiesManager.getPropertyAsInt
                ("org.jasig.portal.concurrency.clockTolerance");
            setLockToleranceMillis(lockTolerance);
        }
        catch ( Exception ex ) { /* defaults to 0. */ }
    }
}
/**
* Answers if the entity represented by the entityType and entityKey already
* has a lock of some type.
*
* @param entityType
* @param entityKey
* @exception org.jasig.portal.concurrency.LockingException
*/
private boolean isLocked(Class entityType, String entityKey) throws LockingException
{
    return isLocked(entityType, entityKey, null);
}
/**
* Answers if the entity represented by entityType and entityKey has one
* or more locks.  Param <code>lockType</code> can be null.
*
* @param entityType
* @param entityKey
* @param lockType (optional)
* @exception org.jasig.portal.concurrency.LockingException
*/
private boolean isLocked(Class entityType, String entityKey, Integer lockType) throws LockingException
{
    IEntityLock[] locks = retrieveLocks(entityType, entityKey, lockType);
    return locks.length > 0;
}
/**
* @return boolean
*/
private boolean isMultiServer() {
    return multiServer;
}
/**
* @param lock IEntityLock
* @return boolean
*/
private boolean isUnexpired(IEntityLock lock)
{
    return lock.getExpirationTime().getTime() > System.currentTimeMillis();
}
/**
* Answers if this <code>IEntityLock</code> represents a lock that is still
* good.  To be valid, a lock must exist in the underlying store and be
* unexpired.
*
* @param lock IEntityLock
* @exception org.jasig.portal.concurrency.LockingException
*/
public boolean isValid(IEntityLock lock) throws LockingException
{
    return isUnexpired(lock) && existsInStore(lock);
}
/**
*
*/
private boolean isValidLockType(int lockType)
{
    return ( (lockType == READ_LOCK) || (lockType == WRITE_LOCK) );
}

/**
* Returns a lock for the entity, lock type and owner if no conflicting locks exist.
* @param entityType
* @param entityKey
* @param lockType
* @param owner
* @return org.jasig.portal.groups.IEntityLock
* @exception LockingException
*/
public IEntityLock newLock(Class entityType, String entityKey, int lockType, String owner)
throws LockingException
{
    return newLock(entityType, entityKey, lockType, owner, defaultLockPeriod);
}

/**
* Returns a lock for the entity, lock type and owner if no conflicting locks exist.
* @param entityType
* @param entityKey
* @param lockType
* @param owner
* @param durationSecs
* @return org.jasig.portal.groups.IEntityLock
* @exception LockingException
*
* Retrieves potentially conflicting locks and checks them before adding
* the new lock to the store.  The add of a write lock will fail if any
* other lock exists for the entity.  The add of a read lock will fail if
* a write lock exists for the entity.  After we add a write lock we
* check the store a second time and roll back if any other lock has snuck
* in.  I think this is slightly safer than depending on the db isolation
* level for transactional integrity. 
*/
public IEntityLock newLock(Class entityType, String entityKey, int lockType, String owner, int durationSecs)
throws LockingException
{
    int expirationSecs = durationSecs;
    Date expires = getNewExpiration(expirationSecs);
    IEntityLock newLock = new EntityLockImpl(entityType, entityKey, lockType, expires, owner, this);

    // retrieve potentially conflicting locks:
    IEntityLock[] locks = retrieveLocks(entityType, entityKey, null);

    if ( lockType == WRITE_LOCK )
    {
        if ( locks.length > 0 )
            { throw new LockingException("Could not create lock: entity already locked."); }
       
        getLockStore().add(newLock);

        locks = retrieveLocks(entityType, entityKey, null);
        if ( locks.length > 1 // another lock snuck in
        {
            release(newLock);
            throw new LockingException("Could not create lock: entity already locked.");
        }
    }

    else // ( lockType == READ_LOCK )
    {
        for ( int i = 0; i<locks.length; i++ )
        {
            if ( locks[i].getLockType() == WRITE_LOCK )
                { throw new LockingException("Could not create lock: entity already write locked."); }
            else
            {
                if ( locks[i].equals(newLock) )
                {
                    // another read lock from the same owner; bump the expiration time.
                    expirationSecs++;
                    expires = getNewExpiration(expirationSecs);
                    newLock = new EntityLockImpl(entityType, entityKey, lockType, expires, owner, this);
                }
            }
        }
        getLockStore().add(newLock);
    }
    return newLock;
}
/**
* Returns a lock for the entity, lock type and owner if no conflicting locks exist.
* @return org.jasig.portal.groups.IEntityLock
* @param entityID org.jasig.portal.EntityIdentifier
* @param lockType int
* @param owner String
* @exception LockingException
*/
public IEntityLock newLock(EntityIdentifier entityID, int lockType, String owner)
throws LockingException
{
    return newLock(entityID.getType(), entityID.getKey(), lockType, owner, defaultLockPeriod);
}
/**
* Returns a lock for the entity, lock type and owner if no conflicting locks exist.
* @return org.jasig.portal.groups.IEntityLock
* @param entityID org.jasig.portal.EntityIdentifier
* @param lockType int
* @param owner String
* @param durationSecs int
* @exception LockingException
*/
public IEntityLock newLock(EntityIdentifier entityID, int lockType, String owner, int durationSecs)
throws LockingException
{
    return newLock(entityID.getType(), entityID.getKey(), lockType, owner, durationSecs);
}
/**
* Releases the <code>IEntityLock</code>.
* @param lock IEntityLock
* @exception LockingException
*/
public void release(IEntityLock lock) throws LockingException
{
    getLockStore().delete(lock);
    ((EntityLockImpl)lock).setExpirationTime(new Date(0));
}
/**
* Extends the expiration time of the lock by some service-defined increment.
* @param lock IEntityLock
* @exception LockingException
*/
public void renew(IEntityLock lock) throws LockingException
{
    renew(lock, defaultLockPeriod);
}
/**
* Extends the expiration time of the lock by some service-defined increment.
* @param lock IEntityLock
* @exception LockingException
*/
public void renew(IEntityLock lock, int duration) throws LockingException
{
    if ( isValid(lock) )
    {
        Date newExpiration = getNewExpiration(duration);
        getLockStore().update(lock, newExpiration);
        ((EntityLockImpl)lock).setExpirationTime(newExpiration);
    }
    else
        { throw new LockingException("Could not renew " + lock + " : lock is invalid."); }
}
/**
* Returns an IEntityLock[] containing unexpired locks for the entityType, entityKey
* and lockType.  Param <code>lockType</code> can be null.
*
* @param entityType
* @param entityKey
* @param lockType (optional)
* @exception LockingException
*/
private IEntityLock[] retrieveLocks(Class entityType, String entityKey, Integer lockType) throws LockingException
{
    Date expiration = ( multiServer )
        ? new Date(System.currentTimeMillis() - getLockToleranceMillis())
        : new Date();

    return getLockStore().findUnexpired(expiration, entityType, entityKey, lockType, null);
}
/**
* @param newDefaultLockPeriod int
*/
private void setDefaultLockPeriod(int newDefaultLockPeriod) {
    defaultLockPeriod = newDefaultLockPeriod;
}
/**
* @param newLockToleranceMillis int
*/
private void setLockToleranceMillis(int newLockToleranceMillis) {
    lockToleranceMillis = newLockToleranceMillis;
}
/**
* @param newMultiServer boolean
*/
private void setMultiServer(boolean newMultiServer) {
    multiServer = newMultiServer;
}
/**
* @return org.jasig.portal.concurrency.locking.ReferenceEntityLockService
*/
public static synchronized IEntityLockService singleton() throws LockingException
{
    if ( singleton == null )
        { singleton = new ReferenceEntityLockService(); }
    return singleton;
}
}
TOP

Related Classes of org.jasig.portal.concurrency.locking.ReferenceEntityLockService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.