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