Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (C) 2015 BonitaSoft S.A.
* BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
* 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
* version 2.1 of the License.
* 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.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/package org.bonitasoft.engine.lock.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException;
import org.bonitasoft.engine.lock.BonitaLock;
import org.bonitasoft.engine.lock.LockService;
import org.bonitasoft.engine.lock.SLockException;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
/**
* This service must be configured as a singleton.
*
* @author Elias Ricken de Medeiros
* @author Baptiste Mesta
*/publicclassMemoryLockServiceimplementsLockService{
protectedstaticfinal String SEPARATOR = "_";
privatefinal Map usedLocks = Collections.synchronizedMap(new HashMap());
protectedfinal TechnicalLoggerService logger;
protectedfinalint lockTimeout;
privatefinal Map mutexs;
protectedfinalboolean debugEnabled;
privatefinalboolean traceEnabled;
privatefinalint lockPoolSize;
/**
* @param lockTimeout timeout to obtain a lock (in seconds)
* @param lockPoolSize the size of the lock pool
*/publicMemoryLockService(final TechnicalLoggerService logger, finalint lockTimeout, finalint lockPoolSize){
this.logger = logger;
this.lockTimeout = lockTimeout;
debugEnabled = logger.isLoggable(getClass(), TechnicalLogSeverity.DEBUG);
traceEnabled = logger.isLoggable(getClass(), TechnicalLogSeverity.TRACE);
this.lockPoolSize = lockPoolSize;
// The goal of this map of mutexs is not to solve completely the competition between keys,// it is only improving the default "one lock" behaviour by partitioning ids among a chosen pool size.// This a sharding approach.final Map tmpMutexs = new HashMap<>();
for (int i = 0; i < lockPoolSize; i++) {
tmpMutexs.put(i, new MemoryLockServiceMutex());
}
mutexs = Collections.unmodifiableMap(tmpMutexs);
}
privatestaticfinalclassMemoryLockServiceMutex{
}
private Object getMutex(finallong objectId){
finalint poolKeyForThisObjectId = Long.valueOf(objectId % lockPoolSize).intValue();
if (!mutexs.containsKey(poolKeyForThisObjectId)) {
thrownew RuntimeException("No mutex defined for objectToLockId '" + objectId + "' with generated key '" + poolKeyForThisObjectId + "'");
}
return mutexs.get(poolKeyForThisObjectId);
}
protected ReentrantLock getLockAndPutItInMap(final String key){
if (!usedLocks.containsKey(key)) {
// use fair mode?
usedLocks.put(key, new ReentrantLock());
}
return getLockFromKey(key);
}
protected ReentrantLock removeLockFromMapIfNotUsed(final String key){
final ReentrantLock reentrantLock = getLockFromKey(key);
// The reentrant lock must not have any waiting thread that tries to lock it, nor a lockservice.lock() that locked it:if (reentrantLock != null && !reentrantLock.hasQueuedThreads()) {
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, "removed from map " + reentrantLock.hashCode() + " id=" + key);
}
usedLocks.remove(key);
}
return reentrantLock;
}
private String buildKey(finallong objectToLockId, final String objectType, finallong tenantId){
return objectType + SEPARATOR + objectToLockId + SEPARATOR + tenantId;
}
@Overridepublicvoidunlock(final BonitaLock bonitaLock, finallong tenantId){
final String key = buildKey(bonitaLock.getObjectToLockId(), bonitaLock.getObjectType(), tenantId);
final Lock lock = bonitaLock.getLock();
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, "Will unlock " + lock.hashCode() + " id=" + key);
}
synchronized (getMutex(bonitaLock.getObjectToLockId())) {
ReentrantLock removedLock = removeLockFromMapIfNotUsed(key);
// Compare the referencesif (removedLock != lock) {
thrownew IllegalStateException("The lock held by the BonitaLock and the one associated to the key do not match.");
}
lock.unlock();
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, "Unlocked " + lock.hashCode() + " id=" + key);
}
}
}
@Overridepublic BonitaLock tryLock(long objectToLockId, String objectType, long timeout, TimeUnit timeUnit, long tenantId){
// Let's try to tryLock() 10 times maximum:for (int i = 0; i < 10; i++) {
try {
return internalTryLock(objectToLockId, objectType, lockTimeout, TimeUnit.SECONDS, tenantId);
} catch (SBonitaRuntimeException ignored) {
if (debugEnabled) {
logger.log(getClass(), TechnicalLogSeverity.DEBUG, "Bad luck concurrency trying to lock. Let's retry to lock...");
}
} finally {
if (i > 0 && debugEnabled) {
logger.log(getClass(), TechnicalLogSeverity.DEBUG,
MessageFormat.format("********* YES! retrying solved the problem after {0} retries *********. You can remove those logs, now", i));
}
}
}
// Could not retrieve the lock for 10 times: log specific exception and return null:
logger.log(getClass(), TechnicalLogSeverity.WARNING, MessageFormat.format("Tried to acquire the lock for 10 times without success for {0}:{1}{3}",
objectType, objectToLockId, getDetailsOnLock(objectToLockId, objectType, tenantId)));
returnnull;
}
protected BonitaLock internalTryLock(finallong objectToLockId, final String objectType, finallong timeout, final TimeUnit timeUnit, finallong tenantId){
final String key = buildKey(objectToLockId, objectType, tenantId);
final ReentrantLock lock;
synchronized (getMutex(objectToLockId)) {
lock = getLockAndPutItInMap(key);
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, MessageFormat.format("TryLock {0} with id={1}", String.valueOf(lock.hashCode()), key));
}
if (lock.isHeldByCurrentThread()) {
// We do not want to support reentrant accessfinal String message = "Trying to acquire the lock another time by the same Thread, this should not happen !";
logger.log(getClass(), TechnicalLogSeverity.WARNING, message);
thrownew IllegalStateException(message);
}
}
try {
if (lock.tryLock(timeout, timeUnit)) {
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, "Locked " + lock.hashCode() + " id=" + key);
}
// Ensure the lock is still in the map : someone may have called unlock// between the synchronized block and the tryLock callsynchronized (getMutex(objectToLockId)) {
ReentrantLock previousLock = getLockFromKey(key);
if (previousLock == null) {
// Someone unlocked the lock while we were tryLocking so it was removed from the Map.// Let's add it againif (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE, "Yes, someone just released the lock, let's take it !");
}
usedLocks.put(key, lock);
} elseif (previousLock != lock) {
if (traceEnabled) {
try {
Method getOwnerMethod = previousLock.getClass().getDeclaredMethod("getOwner");
getOwnerMethod.setAccessible(true);
Thread ownerThread = (Thread) getOwnerMethod.invoke(previousLock);
String previousThread = ownerThread.getName();
logger.log(getClass(), TechnicalLogSeverity.TRACE,
MessageFormat.format("Bad luck, someone (thread {0}) was faster that us to take our lock on {1} {2}", previousThread,
objectType, objectToLockId));
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ignored) {
}
}
// Compare the 2 Locks by reference : if they are the same: fine, just continue as we locked on the correct lock.// Otherwise we have to release the lock just acquired : someone was faster than us to lock on the same key.
lock.unlock();
thrownew SBonitaRuntimeException("Lock acquire concurrency exception. Should trigger a retry lock.");
}
// else everything is fine : this is the same lock. Go on !
}
returnnew BonitaLock(lock, objectType, objectToLockId);
}
} catch (final InterruptedException e) {
logger.log(getClass(), TechnicalLogSeverity.ERROR, "The trylock was interrupted " + lock.hashCode() + " id=" + key);
}
if (traceEnabled) {
logger.log(getClass(), TechnicalLogSeverity.TRACE,
MessageFormat.format("Could not lock after {0} {1} the lock {2} with id={3}", lockTimeout, TimeUnit.SECONDS,
String.valueOf(lock.hashCode()), key));
}
returnnull;
}
@Overridepublic BonitaLock lock(finallong objectToLockId, final String objectType, finallong tenantId)throws SLockException {
BonitaLock lock = tryLock(objectToLockId, objectType, lockTimeout, TimeUnit.SECONDS, tenantId);
if (lock == null) {
thrownew SLockException(MessageFormat.format("Unable to acquire the lock after {0} {1} for {2}:{3}{4}", lockTimeout, TimeUnit.SECONDS,
objectType, objectToLockId, getDetailsOnLock(objectToLockId, objectType, tenantId)));
}
return lock;
}
protected StringBuilder getDetailsOnLock(finallong objectToLockId, final String objectType, finallong tenantId){
String key = buildKey(objectToLockId, objectType, tenantId);
ReentrantLock reentrantLock = getLockFromKey(key);
StringBuilder details = new StringBuilder(", Details: ");
if (reentrantLock == null) {
details.append("The lock was removed from the locks map in the memory lock service");
} elseif (reentrantLock.isLocked()) {
details.append("The lock on ").append(key).append(" is locked");
if (reentrantLock.isHeldByCurrentThread()) {
details.append(", held by current thread.");
} else {
try {
Method getOwnerMethod = reentrantLock.getClass().getDeclaredMethod("getOwner");
getOwnerMethod.setAccessible(true);
Thread ownerThread = (Thread) getOwnerMethod.invoke(reentrantLock);
details.append(", held by thread ");
details.append(ownerThread.getName());
} catch (Exception e) {
logger.log(getClass(), TechnicalLogSeverity.INFO, "Error while fetching exception details on lock.", e);
}
}
} else {
details.append("no additional details could be found (lock exists and is not locked, there should be no problem).");
}
return details;
}
protected ReentrantLock getLockFromKey(final String key){
return usedLocks.get(key);
}
}