/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.portal.lock.service.impl; import com.liferay.portal.kernel.dao.jdbc.aop.MasterDataSource; import com.liferay.portal.kernel.dao.orm.ORMException; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.lock.LockListener; import com.liferay.portal.kernel.lock.LockListenerRegistryUtil; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.transaction.Propagation; import com.liferay.portal.kernel.transaction.TransactionConfig; import com.liferay.portal.kernel.transaction.TransactionInvokerUtil; import com.liferay.portal.kernel.util.ReflectionUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.lock.exception.DuplicateLockException; import com.liferay.portal.lock.exception.ExpiredLockException; import com.liferay.portal.lock.exception.NoSuchLockException; import com.liferay.portal.lock.model.Lock; import com.liferay.portal.lock.service.base.LockLocalServiceBaseImpl; import java.util.Date; import java.util.List; import java.util.Objects; import java.util.concurrent.Callable; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.LockAcquisitionException; /** * @author Brian Wing Shun Chan * @author Shuyang Zhou */ public class LockLocalServiceImpl extends LockLocalServiceBaseImpl { @Override public void clear() { lockPersistence.removeByLtExpirationDate(new Date()); } @Override public Lock fetchLock(String className, long key) { return fetchLock(className, String.valueOf(key)); } @Override public Lock fetchLock(String className, String key) { Lock lock = lockPersistence.fetchByC_K(className, key); if (lock != null) { if (lock.isExpired()) { expireLock(lock); lock = null; } } return lock; } @Override public Lock getLock(String className, long key) throws PortalException { return getLock(className, String.valueOf(key)); } @Override public Lock getLock(String className, String key) throws PortalException { Lock lock = lockPersistence.findByC_K(className, key); if (lock.isExpired()) { expireLock(lock); throw new ExpiredLockException(); } return lock; } @Override public Lock getLockByUuidAndCompanyId(String uuid, long companyId) throws PortalException { List<Lock> locks = lockPersistence.findByUuid_C(uuid, companyId); if (locks.isEmpty()) { StringBundler sb = new StringBundler(5); sb.append("{uuid="); sb.append(uuid); sb.append(", companyId="); sb.append(companyId); sb.append("}"); throw new NoSuchLockException(sb.toString()); } return locks.get(0); } @Override public boolean hasLock(long userId, String className, long key) { return hasLock(userId, className, String.valueOf(key)); } @Override public boolean hasLock(long userId, String className, String key) { Lock lock = fetchLock(className, key); if ((lock != null) && (lock.getUserId() == userId)) { return true; } else { return false; } } @Override public boolean isLocked(String className, long key) { return isLocked(className, String.valueOf(key)); } @Override public boolean isLocked(String className, String key) { Lock lock = fetchLock(className, key); if (lock == null) { return false; } else { return true; } } @Override public Lock lock( long userId, String className, long key, String owner, boolean inheritable, long expirationTime) throws PortalException { return lock( userId, className, String.valueOf(key), owner, inheritable, expirationTime); } @Override public Lock lock( long userId, String className, long key, String owner, boolean inheritable, long expirationTime, boolean renew) throws PortalException { return lock( userId, className, String.valueOf(key), owner, inheritable, expirationTime, renew); } @Override public Lock lock( long userId, String className, String key, String owner, boolean inheritable, long expirationTime) throws PortalException { return lock( userId, className, key, owner, inheritable, expirationTime, true); } @Override public Lock lock( long userId, String className, String key, String owner, boolean inheritable, long expirationTime, boolean renew) throws PortalException { Date now = new Date(); Lock lock = lockPersistence.fetchByC_K(className, key); if (lock != null) { if (lock.isExpired()) { expireLock(lock); lock = null; } else if (lock.getUserId() != userId) { throw new DuplicateLockException(lock); } } boolean isNew = false; if (lock == null) { User user = userLocalService.getUser(userId); long lockId = counterLocalService.increment(); lock = lockPersistence.create(lockId); lock.setCompanyId(user.getCompanyId()); lock.setUserId(user.getUserId()); lock.setUserName(user.getFullName()); lock.setClassName(className); lock.setKey(key); lock.setOwner(owner); lock.setInheritable(inheritable); isNew = true; } else if (!renew) { return lock; } lock.setCreateDate(now); if (expirationTime == 0) { lock.setExpirationDate(null); } else { lock.setExpirationDate(new Date(now.getTime() + expirationTime)); } lock = lockPersistence.update(lock); if (isNew) { lock.setNew(true); } return lock; } @MasterDataSource @Override public Lock lock(String className, String key, String owner) { return lock(className, key, null, owner); } @MasterDataSource @Override public Lock lock( final String className, final String key, final String expectedOwner, final String updatedOwner) { while (true) { try { return TransactionInvokerUtil.invoke( _transactionConfig, new Callable<Lock>() { @Override public Lock call() { Lock lock = lockPersistence.fetchByC_K( className, key, false); if (lock == null) { long lockId = counterLocalService.increment(); lock = lockPersistence.create(lockId); lock.setCreateDate(new Date()); lock.setClassName(className); lock.setKey(key); lock.setOwner(updatedOwner); lockPersistence.update(lock); lock.setNew(true); } else if (Objects.equals( lock.getOwner(), expectedOwner)) { lock.setCreateDate(new Date()); lock.setClassName(className); lock.setKey(key); lock.setOwner(updatedOwner); lockPersistence.update(lock); lock.setNew(true); } return lock; } }); } catch (Throwable t) { Throwable cause = t; if (t instanceof ORMException) { cause = t.getCause(); } if (cause instanceof ConstraintViolationException || cause instanceof LockAcquisitionException) { continue; } ReflectionUtil.throwException(t); } } } @Override public Lock refresh(String uuid, long companyId, long expirationTime) throws PortalException { Date now = new Date(); List<Lock> locks = lockPersistence.findByUuid_C(uuid, companyId); if (locks.isEmpty()) { StringBundler sb = new StringBundler(5); sb.append("{uuid="); sb.append(uuid); sb.append(", companyId="); sb.append(companyId); sb.append("}"); throw new NoSuchLockException(sb.toString()); } Lock lock = locks.get(0); LockListener lockListener = LockListenerRegistryUtil.getLockListener( lock.getClassName()); String key = lock.getKey(); if (lockListener != null) { lockListener.onBeforeRefresh(key); } try { lock.setCreateDate(now); if (expirationTime == 0) { lock.setExpirationDate(null); } else { lock.setExpirationDate( new Date(now.getTime() + expirationTime)); } lockPersistence.update(lock); return lock; } finally { if (lockListener != null) { lockListener.onAfterRefresh(key); } } } @Override public void unlock(String className, long key) { unlock(className, String.valueOf(key)); } @Override public void unlock(String className, String key) { try { lockPersistence.removeByC_K(className, key); } catch (NoSuchLockException nsle) { } } @MasterDataSource @Override public void unlock( final String className, final String key, final String owner) { while (true) { try { TransactionInvokerUtil.invoke( _transactionConfig, new Callable<Void>() { @Override public Void call() { Lock lock = lockPersistence.fetchByC_K( className, key, false); if (lock == null) { return null; } if (Objects.equals(lock.getOwner(), owner)) { lockPersistence.remove(lock); } return null; } }); return; } catch (Throwable t) { Throwable cause = t; if (t instanceof ORMException) { cause = t.getCause(); } if (cause instanceof ConstraintViolationException || cause instanceof LockAcquisitionException) { continue; } ReflectionUtil.throwException(t); } } } protected void expireLock(Lock lock) { LockListener lockListener = LockListenerRegistryUtil.getLockListener( lock.getClassName()); String key = lock.getKey(); if (lockListener != null) { lockListener.onBeforeExpire(key); } try { lockPersistence.remove(lock); lockPersistence.flush(); } finally { if (lockListener != null) { lockListener.onAfterExpire(key); } } } private final TransactionConfig _transactionConfig = TransactionConfig.Factory.create( Propagation.REQUIRES_NEW, new Class<?>[] {Exception.class}); }