package eu.eudat.logic.managers; import eu.eudat.data.dao.criteria.LockCriteria; import eu.eudat.logic.services.ApiContext; import eu.eudat.models.data.lock.Lock; import eu.eudat.models.data.security.Principal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import javax.persistence.NoResultException; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.UUID; @Component public class LockManager { private final Comparator compareByTouchedAt = Comparator.comparing(o -> o.getTouchedAt().getTime()); private ApiContext apiContext; private Environment environment; @Autowired public LockManager(ApiContext apiContext, Environment environment) { this.apiContext = apiContext; this.environment = environment; } public eu.eudat.data.entities.Lock createOrUpdate(Lock lock, Principal principal) throws Exception { if (lock.getId() != null) { try { eu.eudat.data.entities.Lock entity = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().find(lock.getId()); if (entity != null) { if (!entity.getLockedBy().getId().equals(principal.getId())) { throw new Exception("Is not locked by that user"); } } }catch(NoResultException e) { lock.setId(null); } } eu.eudat.data.entities.Lock newLock = lock.toDataModel(); newLock = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().createOrUpdate(newLock); return newLock; } public boolean isLocked(String targetId, Principal principal) throws Exception { LockCriteria criteria = new LockCriteria(); criteria.setTarget(UUID.fromString(targetId)); Long availableLocks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).count(); if (availableLocks == 1) { eu.eudat.data.entities.Lock lock = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).getSingle(); if (lock.getLockedBy().getId().equals(principal.getId())) { lock.setTouchedAt(new Date()); this.createOrUpdate(new Lock().fromDataModel(lock), principal); return false; } return this.forceUnlock(targetId) > 0; } else if (availableLocks > 1) { this.forceUnlock(targetId); return this.isLocked(targetId, principal); } return false; } private Long forceUnlock(String targetId) { LockCriteria criteria = new LockCriteria(); criteria.setTarget(UUID.fromString(targetId)); Long availableLocks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).count(); long deletedLocks = 0L; if (availableLocks > 0) { List locks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).toList(); for (eu.eudat.data.entities.Lock lock : locks) { if (new Date().getTime() - lock.getTouchedAt().getTime() > environment.getProperty("database.lock-fail-interval", Integer.class)) { this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().delete(lock); deletedLocks++; } } if (deletedLocks == 0) { eu.eudat.data.entities.Lock recentlock = locks.stream().max(compareByTouchedAt).get(); for (eu.eudat.data.entities.Lock lock : locks) { if (lock != recentlock) { this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().delete(lock); deletedLocks++; } } } } return availableLocks - deletedLocks; } public void unlock(String targetId, Principal principal) throws Exception { LockCriteria criteria = new LockCriteria(); criteria.setTarget(UUID.fromString(targetId)); Long availableLocks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).count(); if (availableLocks == 1) { eu.eudat.data.entities.Lock lock = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).getSingle(); if (!lock.getLockedBy().getId().equals(principal.getId())) { throw new Exception("Only the user who created that lock can delete it"); } this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().delete(lock); } else if (availableLocks > 1) { List locks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).toList(); locks.stream().filter(lock -> lock.getLockedBy().getId().equals(principal.getId())).forEach(lock -> this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().delete(lock)); } } public Lock getFromTarget(String targetId, Principal principal) throws Exception { LockCriteria criteria = new LockCriteria(); criteria.setTarget(UUID.fromString(targetId)); Long availableLocks = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).count(); if (availableLocks > 0) { eu.eudat.data.entities.Lock lock = this.apiContext.getOperationsContext().getDatabaseRepository().getLockDao().getWithCriteria(criteria).getSingle(); if (!lock.getLockedBy().getId().equals(principal.getId())) { throw new Exception("Only the user who created that lock can access it"); } return new Lock().fromDataModel(lock); } return null; } }