Semaphore.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2025 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
/**
* Semaphore
*
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class Semaphore {
public static final long DEFAULT_LOCK_ACQUISITION_TIMEOUT_MS = 30000;
private static volatile long defaultLockAcquisitionTimeoutMs = DEFAULT_LOCK_ACQUISITION_TIMEOUT_MS;
public static long getDefaultLockAcquisitionTimeoutMs() {
return defaultLockAcquisitionTimeoutMs;
}
public static void setDefaultLockAcquisitionTimeoutMs(long timeoutMs) {
defaultLockAcquisitionTimeoutMs = timeoutMs;
}
public static final long DEFAULT_LOCK_HOLD_TIMEOUT_MS = 60000;
private static volatile long defaultLockHoldTimeoutMs = DEFAULT_LOCK_HOLD_TIMEOUT_MS;
public static long getDefaultLockHoldTimeoutMs() {
return defaultLockHoldTimeoutMs;
}
public static void setDefaultLockHoldTimeoutMs(long timeoutMs) {
defaultLockHoldTimeoutMs = timeoutMs;
}
private static Logger logDebug = null;
public static Logger getLogDebug() {
return logDebug;
}
public static void setLogDebug(Logger logDebug) {
Semaphore.logDebug = logDebug;
}
private static volatile boolean defaultDebug = false;
public static boolean isDefaultDebug() {
return defaultDebug;
}
public static void setDefaultDebug(boolean d) {
defaultDebug = d;
}
private Long instanceLockAcquisitionTimeoutMs = null;
public long getInstanceLockAcquisitionTimeoutMs() {
return this.instanceLockAcquisitionTimeoutMs != null && this.instanceLockAcquisitionTimeoutMs.longValue()>0 ? this.instanceLockAcquisitionTimeoutMs : defaultLockAcquisitionTimeoutMs;
}
public void setInstanceLockAcquisitionTimeoutMs(Long timeoutMs) {
this.instanceLockAcquisitionTimeoutMs = timeoutMs;
}
public void setInstanceLockAcquisitionTimeoutMs(long timeoutMs) {
this.instanceLockAcquisitionTimeoutMs = timeoutMs;
}
private volatile long instanceLockHoldTimeoutMs = -1;
public long getInstanceLockHoldTimeoutMs() {
return this.instanceLockHoldTimeoutMs>0 ? this.instanceLockHoldTimeoutMs : defaultLockHoldTimeoutMs;
}
public void setInstanceLockHoldTimeoutMs(long timeoutMs) {
this.instanceLockHoldTimeoutMs = timeoutMs;
}
private volatile boolean instanceDebug = false;
public boolean isInstanceDebug() {
return this.instanceDebug || defaultDebug;
}
public void setInstanceDebug(boolean d) {
this.instanceDebug = d;
}
private static SemaphoreType semaphoreType = SemaphoreType.Semaphore;
public static SemaphoreType getSemaphoreType() {
return semaphoreType;
}
public static void setSemaphoreType(SemaphoreType semaphoreType) {
Semaphore.semaphoreType = semaphoreType;
}
private static volatile boolean fair = true;
public static boolean isFair() {
return fair;
}
public static void setFair(boolean fair) {
Semaphore.fair = fair;
}
private final java.util.concurrent.Semaphore concurrentSemaphore;
private final java.util.concurrent.locks.ReentrantLock reentrantLock;
private String semaphoreName = null;
private int permits = -1;
public Semaphore(String name) {
this(name, Semaphore.semaphoreType, Semaphore.fair);
}
public Semaphore(String name, boolean fair) {
this(name, Semaphore.semaphoreType, fair);
}
public Semaphore(String name, SemaphoreType semaphoreType, boolean fair) {
this.semaphoreName = name;
switch (semaphoreType) {
case ReentrantLock:
this.reentrantLock = new ReentrantLock(fair);
this.concurrentSemaphore = null;
break;
case Semaphore:
this.reentrantLock = null;
this.concurrentSemaphore = new java.util.concurrent.Semaphore(1, fair);
break;
default:
this.reentrantLock = null;
this.concurrentSemaphore = null;
}
this.permits = 1;
}
public Semaphore(String name, int permits) {
this(name, permits, Semaphore.fair);
}
public Semaphore(String name, int permits, boolean fair) {
this.semaphoreName = name;
this.concurrentSemaphore = new java.util.concurrent.Semaphore(permits, fair);
this.reentrantLock = null;
this.permits = permits;
}
public boolean hasQueuedThreads() {
if(this.concurrentSemaphore!=null) {
return this.concurrentSemaphore.hasQueuedThreads();
}
else {
return this.reentrantLock.hasQueuedThreads();
}
}
public boolean available() {
if(this.concurrentSemaphore!=null) {
return this.concurrentSemaphore.availablePermits()>0;
}
else {
return !this.reentrantLock.isLocked();
}
}
public int availablePermits() {
if(this.concurrentSemaphore!=null) {
return this.concurrentSemaphore.availablePermits();
}
else {
return -1;
}
}
String getPrefix(String methodName, String idTransazione) {
String idTr = "";
if(idTransazione!=null) {
idTr = " (idTransazione:"+idTransazione+")";
}
return this.semaphoreName+"."+methodName+" [Thread:"+Thread.currentThread().getName()+"]"+idTr+" ";
}
public SemaphoreLock acquire(String methodName) throws UtilsException {
return this.acquire(methodName, null);
}
public SemaphoreLock acquire(String methodName, String idTransazione) throws UtilsException {
try {
if(this.getInstanceLockAcquisitionTimeoutMs()<=0) {
return acquireWithoutTimeout(methodName, idTransazione);
}
else {
return acquireWithTimeout(methodName, idTransazione);
}
}
catch(SemaphoreTimeoutException ste) {
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" acquire("+this.getInstanceLockAcquisitionTimeoutMs()+"ms) failed: "+ste.getMessage());
}
throw new UtilsException(ste.getMessage(),ste);
}
catch(InterruptedException ie) {
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" interrupted: "+ie.getMessage());
}
Thread.currentThread().interrupt();
throw new UtilsException(ie.getMessage(),ie);
}
}
private SemaphoreLock acquireWithoutTimeout(String methodName, String idTransazione) throws InterruptedException {
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" acquire ...");
}
if(this.concurrentSemaphore!=null) {
this.concurrentSemaphore.acquire();
}
else {
this.reentrantLock.lock();
}
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" acquired");
}
return new SemaphoreLock(this, methodName, idTransazione);
}
private SemaphoreLock acquireWithTimeout(String methodName, String idTransazione) throws InterruptedException, SemaphoreTimeoutException {
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" acquire("+this.getInstanceLockAcquisitionTimeoutMs()+"ms) ...");
}
boolean acquire = false;
if(this.concurrentSemaphore!=null) {
acquire = this.concurrentSemaphore.tryAcquire(this.getInstanceLockAcquisitionTimeoutMs(), TimeUnit.MILLISECONDS);
}
else {
acquire = this.reentrantLock.tryLock(this.getInstanceLockAcquisitionTimeoutMs(), TimeUnit.MILLISECONDS);
}
if(!acquire) {
throw new SemaphoreTimeoutException("["+this.semaphoreName+"] Could not acquire semaphore after "+this.getInstanceLockAcquisitionTimeoutMs()+"ms");
}
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" acquired");
}
return new SemaphoreLock(this, methodName, idTransazione);
}
public SemaphoreLock acquireThrowRuntime(String methodName) throws SemaphoreRuntimeException {
return this.acquireThrowRuntime(methodName, null);
}
public SemaphoreLock acquireThrowRuntime(String methodName, String idTransazione) throws SemaphoreRuntimeException {
try {
return this.acquire(methodName, idTransazione);
}
catch(Exception t) {
throw new SemaphoreRuntimeException(t.getMessage(),t);
}
}
public void release(SemaphoreLock semaphoreLock, String methodName) {
this.release(semaphoreLock, methodName, null);
}
public void release(SemaphoreLock semaphoreLock, String methodName, String idTransazione) {
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" release ...");
}
if(this.concurrentSemaphore!=null) {
if(this.concurrentSemaphore.availablePermits()<this.permits) {
this.concurrentSemaphore.release(); // altrimenti ogni release utilizzato male fa incrementare i permessi
}
}
else {
this.reentrantLock.unlock();
}
if(semaphoreLock!=null) {
semaphoreLock.release(methodName, idTransazione);
}
if(this.isInstanceDebug()) {
debug(getPrefix(methodName, idTransazione)+" released");
}
}
void debug(String msg) {
System.out.println(msg);
}
}
@SuppressWarnings("serial")
class SemaphoreRuntimeException extends RuntimeException{
public SemaphoreRuntimeException() {
super();
}
public SemaphoreRuntimeException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public SemaphoreRuntimeException(String message, Throwable cause) {
super(message, cause);
}
public SemaphoreRuntimeException(String message) {
super(message);
}
public SemaphoreRuntimeException(Throwable cause) {
super(cause);
}
}