org.jasig.schedassist.impl.DefaultAvailableScheduleReflectionServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.schedassist.impl.DefaultAvailableScheduleReflectionServiceImpl.java

Source

/**
 * 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, nblair@doit.wisc.edu
 * @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<Boolean>() {
            @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<Long> locks = this.simpleJdbcTemplate.query("select owner_id from reflect_locks where owner_id = ?",
                new SingleColumnRowMapper<Long>(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<Long>(), owner.getId());
            LOG.debug("lock acquired for owner " + owner);
            return true;
        } catch (CannotAcquireLockException e) {
            LOG.warn("lock attempt failed for owner " + owner);
            return false;
        }
    }
}