Hi guys,

A few months ago I'm working on customizing the CAS for my organization, I
have had some difficulties related to lack of architectural control in data
integration, due to the use of various frameworks (JPA, Hibernate, JDBC).
One example is the current implementation of the ticket cleaning lock
strategy.

Because of that, i'm trying to replace the JDBC-based implementations to
JPA. The first step was to implement a class that replaces
JdbcLockingStrategy called JpaLockingStrategy.

The class is fully based on the implementation of JdbcLockingStrategy and
has been tested on version 3.4.4 of cas server.

Cheers,

Yuri Feitosa Negócio

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.validation.constraints.NotNull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.ticket.registry.support.LockingStrategy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.transaction.annotation.Transactional;


public class JpaLockingStrategy
    implements LockingStrategy, InitializingBean {

    protected final Log logger =
LogFactory.getLog(JpaLockingStrategy.class);

    @NotNull
    private JpaTemplate jpaTemplate;

    public JpaLockingStrategy(final EntityManagerFactory factory) {
            this.jpaTemplate = new JpaTemplate(factory);
    }

    /** Default lock timeout is 1 hour */
    public static final int DEFAULT_LOCK_TIMEOUT = 3600;

    /** Unique identifier that identifies the client using this lock
instance */
    @NotNull
    private String uniqueId;

    /**
     * Application identifier that identifies rows in the locking table,
     * each one of which may be for a different application or usage within
     * a single application.
     */
    @NotNull
    private String applicationId;

    /** Amount of time in seconds lock may be held */
    private int lockTimeout = DEFAULT_LOCK_TIMEOUT;

    /**
     * @param  id  Identifier used to identify this instance in a row of the
     *             lock table.  Must be unique across all clients vying for
     *             locks for a given application ID.
     */
    public void setUniqueId(final String id) {
        this.uniqueId = id;
    }

    /**
     * @param  id  Application identifier that identifies a row in the lock
     *             table for which multiple clients vie to hold the lock.
     */
    public void setApplicationId(final String id) {
        this.applicationId = id;
    }


    /**
     * @param  seconds  Maximum amount of time in seconds lock may be held.
     */
    public void setLockTimeout(final int seconds) {
        this.lockTimeout = seconds;
    }

    /**
     * @see org.jasig.cas.ticket.registry.support.LockingStrategy#acquire()
     */
    @Transactional
    public boolean acquire() {
        boolean lockAcquired = false;
        try {

            List<Lock> locks = this.jpaTemplate.find("SELECT l FROM Lock l
WHERE l.applicationId = ?1",this.applicationId);

            final Timestamp expDate = getExpirationDate();

            if ((locks==null)||locks.isEmpty()) {
                   logger.info("New lock. {applicationId: " +
this.applicationId + ", uniqueId: " + this.uniqueId + ", timestamp: " +
expDate + " }");
                Lock newLock = new Lock();
                // No row exists for this applicationId so create it.
                // Row is created with uniqueId of this instance
                // which indicates the lock is initially held by this
instance.
                newLock.setApplicationId(this.applicationId);
                newLock.setExpirationDate(expDate);
                this.jpaTemplate.persist(newLock);
                return true;
            } else {
                Lock currentLock = locks.get(0);
                lockAcquired = canAcquire(currentLock);
                if (lockAcquired) {
                       logger.info("Lock acquired. {applicationId: " +
this.applicationId + ", uniqueId: " + this.uniqueId + ", timestamp: " +
expDate + " }");

                    // Update unique ID of row to indicate this instance
holds lock
                    currentLock.setUniqueId(this.uniqueId);

                    this.jpaTemplate.merge(currentLock);
                }
            }
        } finally {

        }
        return lockAcquired;
    }

    /**
     * @see org.jasig.cas.ticket.registry.support.LockingStrategy#release()
     */
    @Transactional
    public void release() {
        logger.info("Lock released. { applicationId: " + this.applicationId
+ " }");
        Lock lock = this.jpaTemplate.find(Lock.class, this.applicationId);
        lock.setUniqueId(null);
        lock.setExpirationDate(null);
        this.jpaTemplate.merge(lock);
    }

    /**
     * Determines whether this instance can acquire the lock.
     *
     * @param  lockRow  Row of lock data for this application ID.
     *
     * @return  True if lock can be acquired, false otherwise.
     */
    private boolean canAcquire(Lock lock) {
        if (lock.getUniqueId() != null) {
            final Calendar expCal = Calendar.getInstance();
            expCal.setTime(lock.getExpirationDate());
            return Calendar.getInstance().after(expCal);
        }
        return true;
    }

    /**
     * @return  The expiration date for a lock acquired at the current
system
     * time
     */
    private Timestamp getExpirationDate() {
        final Calendar cal = Calendar.getInstance();
        cal.add(Calendar.SECOND, this.lockTimeout);
        return new Timestamp(cal.getTimeInMillis());
    }

    public void afterPropertiesSet() throws Exception {
        //compatibility
    }
}



import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="LOCKS")
public class Lock {

    /** Database column name that holds application identifier */
    @Id
    @Column(name="application_id")
    private String applicationId;
    /** Database column name that holds unique identifier */
    @Column(name="unique_id")
    private String uniqueId;

    /** Database column name that holds expiration date */
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="expiration_date")
    private Date expirationDate;

    public String getApplicationId() {
        return applicationId;
    }
    public void setApplicationId(String applicationId) {
        this.applicationId = applicationId;
    }
    public String getUniqueId() {
        return uniqueId;
    }
    public void setUniqueId(String uniqueId) {
        this.uniqueId = uniqueId;
    }
    public Date getExpirationDate() {
        return expirationDate;
    }
    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }

}

-- 
You are currently subscribed to [email protected] as: 
[email protected]
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/cas-user

Reply via email to