org.vpac.ndg.storage.dao.TimeSliceLockDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.vpac.ndg.storage.dao.TimeSliceLockDaoImpl.java

Source

/*
 * This file is part of the Raster Storage Archive (RSA).
 *
 * The RSA is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * The RSA is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * the RSA.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright 2013 CRCSI - Cooperative Research Centre for Spatial Information
 * http://www.crcsi.com.au/
 */

package org.vpac.ndg.storage.dao;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.vpac.ndg.common.datamodel.RunningTaskState;
import org.vpac.ndg.storage.model.Process;
import org.vpac.ndg.storage.model.TimeSlice;
import org.vpac.ndg.storage.model.TimeSliceLock;
import org.vpac.ndg.storage.util.CustomHibernateDaoSupport;

public class TimeSliceLockDaoImpl extends CustomHibernateDaoSupport implements TimeSliceLockDao {

    final Logger log = LoggerFactory.getLogger(TimeSliceLockDaoImpl.class);

    @Autowired
    TimeSliceDao timeSliceDao;
    @Autowired
    ProcessDao processDao;

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public TimeSliceLock create(TimeSliceLock tsl) {
        getHibernateTemplate().save(tsl);
        return tsl;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public void update(TimeSliceLock tsl) {
        getHibernateTemplate().update(tsl);
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public void delete(TimeSliceLock tsl) {
        getHibernateTemplate().delete(tsl);
    }

    @Transactional
    public TimeSliceLock retrieve(String id) {
        return getHibernateTemplate().get(TimeSliceLock.class, id);
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public List<TimeSliceLock> tryReadLock(List<String> timeSliceIds, String processId, String operation,
            String user) throws IllegalMonitorStateException {
        // Just iterate over the ids in one transaction. If any can't be
        // acquired, roll the whole transaction back.

        List<TimeSliceLock> locks = new ArrayList<>();
        for (String tsid : timeSliceIds) {
            TimeSliceLock tslock = tryReadLock(tsid, processId, operation, user);
            locks.add(tslock);
        }
        return locks;
    }

    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    TimeSliceLock tryReadLock(String timeSliceId, String processId, String operation, String user)
            throws IllegalMonitorStateException {
        // First lock the timeslice row. Set scope to prevent cascading.
        TimeSlice ts = (TimeSlice) getSession().load(TimeSlice.class, timeSliceId,
                new LockOptions(LockMode.PESSIMISTIC_WRITE));

        // Check status. Can't lock if there is a write lock, but can if there
        // are read locks.
        if (ts.getLockCount() > 0 && ts.getLockMode() == 'w') {
            // Throw unchecked exception to force rollback.
            // http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/transaction.html#transaction-declarative-rolling-back
            log.debug("{} is write-locked", ts);
            throw new IllegalMonitorStateException("Already locked.");
        }

        log.trace("Incrementing read lock for {}; was {}", ts.getLockCount());
        ts.setLockCount(ts.getLockCount() + 1);
        ts.setLockMode('r');
        getSession().saveOrUpdate(ts);

        // Now that the lock has been obtained, create some metadata to allow
        // others to trace the lock. This is very important: without this, it's
        // impossible to distinguish between a valid lock and one that belonged
        // to a machine that has crashed.
        TimeSliceLock tslock = new TimeSliceLock();
        tslock.setTimesliceId(ts.getId());
        tslock.setProcessId(processId);
        tslock.setOperation(operation);
        tslock.setUser(user);
        tslock.setState(RunningTaskState.RUNNING);
        create(tslock);
        return tslock;
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public List<TimeSliceLock> tryWriteLock(List<String> timeSliceIds, String processId, String operation,
            String user) throws IllegalMonitorStateException {
        // Just iterate over the ids in one transaction. If any can't be
        // acquired, roll the whole transaction back.

        List<TimeSliceLock> locks = new ArrayList<>();
        for (String tsid : timeSliceIds) {
            TimeSliceLock tslock = tryWriteLock(tsid, processId, operation, user);
            locks.add(tslock);
        }
        return locks;
    }

    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    TimeSliceLock tryWriteLock(String timeSliceId, String processId, String operation, String user)
            throws IllegalMonitorStateException {
        // First lock the timeslice row. Set scope to prevent cascading.
        TimeSlice ts = (TimeSlice) getSession().load(TimeSlice.class, timeSliceId,
                new LockOptions(LockMode.PESSIMISTIC_WRITE));

        // Check status. We can't lock if there are *any* other locks held -
        // read or write.
        if (ts.getLockCount() > 0) {
            // Throw unchecked exception to force rollback.
            // http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/transaction.html#transaction-declarative-rolling-back
            log.debug("{} is already locked (count: {})", ts, ts.getLockCount());
            throw new IllegalMonitorStateException("Already locked.");
        }

        log.trace("Incrementing write lock for {}; was {}", ts.getLockCount());
        ts.setLockCount(ts.getLockCount() + 1);
        ts.setLockMode('w');
        getSession().saveOrUpdate(ts);

        // Now that the lock has been obtained, create some metadata to allow
        // others to trace the lock. This is very important: without this, it's
        // impossible to distinguish between a valid lock and one that belonged
        // to a machine that has crashed.
        TimeSliceLock tslock = new TimeSliceLock();
        tslock.setTimesliceId(ts.getId());
        tslock.setProcessId(processId);
        tslock.setOperation(operation);
        tslock.setUser(user);
        tslock.setState(RunningTaskState.RUNNING);
        create(tslock);
        return tslock;
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void unlock(List<TimeSliceLock> locks) {
        // Just iterate over the ids in one transaction.
        for (TimeSliceLock lock : locks) {
            unlock(lock);
        }
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void unlock(TimeSliceLock lock) {
        // First lock the timeslice row. Set scope to prevent cascading.
        TimeSlice ts = (TimeSlice) getSession().load(TimeSlice.class, lock.getTimesliceId(),
                new LockOptions(LockMode.PESSIMISTIC_WRITE));

        // Check status.
        if (ts.getLockCount() <= 0) {
            throw new IllegalMonitorStateException(String.format("Time slice %s is not locked.", ts));
        }

        log.trace("Decrementing read lock for {}", ts);
        ts.setLockCount(ts.getLockCount() - 1);
        getSession().saveOrUpdate(ts);

        delete(lock);
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void updateRunningState(List<TimeSliceLock> lockTokens, RunningTaskState state) {
        for (TimeSliceLock lockToken : lockTokens) {
            lockToken.setState(state);
            log.debug("update running state(id: {} )", lockToken.getId());
            update(lockToken);
        }
    }

    @Transactional
    @Override
    public List<TimeSliceLock> findByTimeSlice(String timeSliceId) {
        @SuppressWarnings("unchecked")
        List<TimeSliceLock> list = getHibernateTemplate()
                .find("FROM TimeSliceLock as lock WHERE lock.timesliceId = ?", timeSliceId);

        return list;
    }

    @Transactional
    @Override
    public List<TimeSliceLock> findByProcess(String processId) {
        @SuppressWarnings("unchecked")
        List<TimeSliceLock> list = getHibernateTemplate()
                .find("FROM TimeSliceLock as lock WHERE lock.processId = ?", processId);

        return list;
    }

    @Transactional
    @Override
    public List<TimeSliceLock> listOrphaned() {
        @SuppressWarnings("unchecked")
        List<TimeSliceLock> list = getHibernateTemplate().find(
                "FROM TimeSliceLock as lock WHERE not exists " + "(FROM Process as p WHERE p.id = lock.processId)");

        return list;
    }

    @Override
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public TimeSliceLock adoptOne(String adopterProcessId) throws InterruptedException {
        List<TimeSliceLock> lockList = listOrphaned();
        if (lockList.size() == 0)
            return null;

        // Lock the row of the token and try to update it.
        TimeSliceLock lockToken = lockList.get(0);
        lockToken = (TimeSliceLock) getSession().load(TimeSliceLock.class, lockToken.getId(),
                new LockOptions(LockMode.PESSIMISTIC_WRITE));

        Process proc = processDao.retrieve(lockToken.getProcessId());
        if (proc != null) {
            throw new InterruptedException(
                    String.format("Lock %s was " + "apparently taken by another process.", lockToken.getId()));
        }

        lockToken.setProcessId(adopterProcessId);
        update(lockToken);
        return lockToken;
    }

    public boolean isLocked(TimeSliceLock lockToken) {
        boolean bResult = false;
        if (lockToken.getTimesliceId() == null || lockToken.getTimesliceId().isEmpty()) {
            return bResult;
        }

        TimeSlice ts = timeSliceDao.retrieve(lockToken.getTimesliceId());
        if (ts == null) {
            return bResult;
        }

        if (ts.getLockCount() < 0) {
            // Lock count not initialized correctly
            throw new IllegalMonitorStateException(String.format("Time slice %s lock count not initialised.", ts));
        } else if (ts.getLockCount() == 0) {
            // time slice not locked
            bResult = false;
        } else {
            // time slice locked
            bResult = true;
        }

        return bResult;
    }
}