org.jasig.schedassist.impl.DefaultAvailableScheduleReflectionServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sched-assist-spi Show documentation
Show all versions of sched-assist-spi Show documentation
Scheduling Assistant Common Service Provider implementations.
/**
* 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.schedassist.impl;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.schedassist.ICalendarDataDao;
import org.jasig.schedassist.impl.owner.AvailableScheduleDao;
import org.jasig.schedassist.impl.owner.OwnerDao;
import org.jasig.schedassist.model.AvailableSchedule;
import org.jasig.schedassist.model.IScheduleOwner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Default implementation of {@link AvailableScheduleReflectionService}.
*
* @author Nicholas Blair, [email protected]
* @version $Id: DefaultAvailableScheduleReflectionServiceImpl.java $
*/
public class DefaultAvailableScheduleReflectionServiceImpl implements AvailableScheduleReflectionService {
protected static final Log LOG = LogFactory.getLog(DefaultAvailableScheduleReflectionServiceImpl.class);
private SimpleJdbcTemplate simpleJdbcTemplate;
private ICalendarDataDao calendarDataDao;
private AvailableScheduleDao availableScheduleDao;
private OwnerDao ownerDao;
private TransactionTemplate transactionTemplate;
private boolean supportsForUpdate = false;
/**
* @param dataSource the dataSource to set
*/
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
/**
* @param platformTransactionManager the platformTransactionManager to set
*/
@Autowired
public void setPlatformTransactionManager(
PlatformTransactionManager platformTransactionManager) {
this.transactionTemplate = new TransactionTemplate(platformTransactionManager);
this.transactionTemplate.setIsolationLevel(Isolation.READ_COMMITTED.value());
}
/**
* @param calendarDataDao the calendarDataDao to set
*/
@Autowired
public void setCalendarDataDao(ICalendarDataDao calendarDataDao) {
this.calendarDataDao = calendarDataDao;
}
/**
* @param availableScheduleDao the availableScheduleDao to set
*/
@Autowired
public void setAvailableScheduleDao(AvailableScheduleDao availableScheduleDao) {
this.availableScheduleDao = availableScheduleDao;
}
/**
* @param ownerDao the ownerDao to set
*/
@Autowired
public void setOwnerDao(OwnerDao ownerDao) {
this.ownerDao = ownerDao;
}
/**
* @param supportsForUpdate the supportsForUpdate to set
*/
public void setSupportsForUpdate(boolean supportsForUpdate) {
this.supportsForUpdate = supportsForUpdate;
}
/*
* (non-Javadoc)
* @see org.jasig.schedassist.impl.AvailableScheduleReflectionService#reflectAvailableSchedule(org.jasig.schedassist.model.IScheduleOwner)
*/
@Override
public void reflectAvailableSchedule(IScheduleOwner owner) {
boolean success = processScheduleOwner(owner);
if(!success) {
LOG.warn("failed to process owner " + owner);
}
}
/**
* First attempts to obtain the semaphore for the specified {@link IScheduleOwner}.
* If successful, then retrieve's the owner's current {@link AvailableSchedule} and
* passes it to {@link CalendarDao#reflectAvailableSchedule(IScheduleOwner, AvailableSchedule)}.
*
* @param owner
* @return true if able to execute the operation, false if failed to obtain the lock
*/
protected boolean processScheduleOwner(final IScheduleOwner owner) {
// add owner to lock table
addOwnerToLockTableIfNotPresent(owner);
boolean result = this.transactionTemplate.execute(new TransactionCallback() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
// obtain "lock" for owner
if(lock(owner)) {
// reflect schedule
AvailableSchedule schedule = availableScheduleDao.retrieve(owner);
calendarDataDao.reflectAvailableSchedule(owner, schedule);
return true;
} else {
return false;
}
}
});
return result;
}
/*
* (non-Javadoc)
* @see org.jasig.schedassist.impl.AvailableScheduleReflectionService#reflectAvailableSchedule(long)
*/
@Override
public void reflectAvailableSchedule(long ownerId) {
IScheduleOwner owner = this.ownerDao.locateOwnerByAvailableId(ownerId);
reflectAvailableSchedule(owner);
}
/*
* (non-Javadoc)
* @see org.jasig.schedassist.impl.AvailableScheduleReflectionService#purgeReflections(org.jasig.schedassist.model.IScheduleOwner, java.util.Date, java.util.Date)
*/
@Override
public void purgeReflections(IScheduleOwner owner, Date start, Date end) {
this.calendarDataDao.purgeAvailableScheduleReflections(owner, start, end);
}
/*
* (non-Javadoc)
* @see org.jasig.schedassist.impl.AvailableScheduleReflectionService#purgeReflections(long, java.util.Date, java.util.Date)
*/
@Override
public void purgeReflections(long ownerId, Date start, Date end) {
IScheduleOwner owner = this.ownerDao.locateOwnerByAvailableId(ownerId);
purgeReflections(owner, start, end);
}
/**
* Store a row in the reflect_locks table for the specified {@link IScheduleOwner}, if
* there isn't a row already.
* This row will be used as a semaphore in {@link #processScheduleOwner(IScheduleOwner)}.
*
* @param owner
*/
void addOwnerToLockTableIfNotPresent(IScheduleOwner owner) {
List locks = this.simpleJdbcTemplate.query("select owner_id from reflect_locks where owner_id = ?",
new SingleColumnRowMapper(Long.class),
owner.getId());
Long lock = DataAccessUtils.singleResult(locks);
if(lock == null) {
int rows = this.simpleJdbcTemplate.update("insert into reflect_locks (owner_id) values (?)", owner.getId());
if(LOG.isDebugEnabled()) {
LOG.debug("inserted " + rows + " row into reflect_locks for owner id " + owner.getId());
}
}
}
/**
* Attempt to acquire the semaphore for the specified {@link IScheduleOwner}.
*
* Only really functional within a transaction with appropriate Isolation.
*
* @param owner
* @return true if the lock is set, false if cannot acquire lock
*/
boolean lock(IScheduleOwner owner) {
StringBuilder sql = new StringBuilder();
sql.append("select owner_id from reflect_locks where owner_id = ?");
if(supportsForUpdate) {
sql.append(" for update nowait");
}
try {
this.simpleJdbcTemplate.query(sql.toString(),
new SingleColumnRowMapper(),
owner.getId());
LOG.debug("lock acquired for owner " + owner);
return true;
} catch (CannotAcquireLockException e) {
LOG.warn("lock attempt failed for owner " + owner);
return false;
}
}
}