/** * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2016 * * Licensed 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.glite.security.voms.admin.persistence.dao.hibernate; import java.util.Date; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.Validate; import org.glite.security.voms.admin.configuration.ServiceID; import org.glite.security.voms.admin.persistence.dao.generic.TaskLockDAO; import org.glite.security.voms.admin.persistence.model.task.TaskLock; import org.hibernate.LockMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TaskLockDAOHibernate extends GenericHibernateDAO<TaskLock, String> implements TaskLockDAO { public static final Logger LOG = LoggerFactory .getLogger(TaskLockDAOHibernate.class); private static final String GET_LOCK_QUERY = "from TaskLock t where t.taskName = :taskName"; private static final int QUERY_TIMEOUT_IN_SECS = 1; private void logAcquireTaskLock(String taskName, TaskLock lock) { LOG.debug("Acquiring lock for task {} at {}. TaskLock: {}", new Object[] { taskName, new Date(), lock }); } private void reassignLockToThisService(TaskLock lock) { lock.setServiceId(ServiceID.getServiceID()); lock.setCreatedAt(new Date()); lock.setFinishedAt(null); makePersistent(lock); } private TaskLock createLock(String taskName) { TaskLock lock = new TaskLock(); lock.setTaskName(taskName); lock.setServiceId(ServiceID.getServiceID()); lock.setCreatedAt(new Date()); makePersistent(lock); return lock; } private TaskLock findLock(String taskName) { return (TaskLock) getSession().createQuery(GET_LOCK_QUERY) .setLockMode("t", LockMode.UPGRADE) .setTimeout(QUERY_TIMEOUT_IN_SECS) .setString("taskName", taskName) .uniqueResult(); } private boolean lockHasExpired(TaskLock lock, long taskPeriodInSecs) { if (lock.isTaskDone()) { return false; } final long now = System.currentTimeMillis(); final long lockExpirationTime = lock.getCreatedAt() .getTime() + 3 * TimeUnit.SECONDS.toMillis(taskPeriodInSecs); return (now > lockExpirationTime); } private boolean taskAlreadyRanInPeriod(TaskLock lock, long taskPeriodInSecs) { if (!lock.isTaskDone()) { return false; } final long now = System.currentTimeMillis(); // Actually the next scheduled execution time should be calculated // taking into account the task creation time, but we take the last // completion time here to have some margin. final long nextScheduledExeutionTime = lock.getFinishedAt() .getTime() + TimeUnit.SECONDS.toMillis(taskPeriodInSecs); return (now < nextScheduledExeutionTime && !lock.getServiceId().equals(ServiceID.getServiceID())); } @Override public TaskLock acquireLock(String taskName, long taskPeriod) { Validate.notEmpty(taskName); TaskLock lock = findLock(taskName); if (lock == null) { lock = createLock(taskName); logAcquireTaskLock(taskName, lock); return lock; } // A lock exists for this task LOG.debug("Lock found for task {}: {}", taskName, lock); if (lock.isTaskDone()) { if (taskPeriod > 0 && taskAlreadyRanInPeriod(lock, taskPeriod)) { LOG.info( "Backing off. Task {} was last finished at {} on service {} and period is {} " + "seconds", new Object[] { lock.getTaskName(), lock.getFinishedAt(), lock.getServiceId(), taskPeriod }); return null; } reassignLockToThisService(lock); logAcquireTaskLock(taskName, lock); return lock; } // Task is not done yet. Check if the lock has expired (only if taskPeriod // was set) if (taskPeriod > 0 && lockHasExpired(lock, taskPeriod)) { LOG.warn( "Task {} has a lock created at time {} that's not finished at {}. " + "Acquiring it", new Object[] { lock.getTaskName(), lock.getCreatedAt(), new Date() }); reassignLockToThisService(lock); logAcquireTaskLock(taskName, lock); return lock; } // Task not done and lock not expired, backoff LOG.debug("Backing off since lock {} is still active", lock); return null; } @Override public TaskLock releaseLock(TaskLock lock) { Validate.notNull(lock); final String taskName = lock.getTaskName(); // Refresh lock from the database lock = findLock(taskName); if (lock == null) { LOG.error( "Could not release lock for task {}, as no lock was found for such task", taskName); return null; } if (lock.isTaskDone()) { LOG.error( "Could not release lock for task {}, as lock has already been released on {}", taskName, lock.getFinishedAt()); return null; } if (!lock.getServiceId() .equals(ServiceID.getServiceID())) { LOG.error( "Could not release lock for task {}, as lock is currently owned by service {}", taskName, lock.getServiceId()); return null; } Date now = new Date(); lock.setFinishedAt(now); makePersistent(lock); LOG.debug("Lock {} released succesfully", lock); return lock; } @Override public TaskLock acquireLock(String taskName) { return acquireLock(taskName, -1L); } }