Semaphore.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.utils;

  21. import java.util.concurrent.TimeUnit;
  22. import java.util.concurrent.locks.ReentrantLock;

  23. import org.slf4j.Logger;

  24. /**
  25.  * Semaphore
  26.  *
  27.  *
  28.  * @author Poli Andrea (apoli@link.it)
  29.  * @author $Author$
  30.  * @version $Rev$, $Date$
  31.  */
  32. public class Semaphore {

  33.     public static final long DEFAULT_LOCK_ACQUISITION_TIMEOUT_MS = 30000;
  34.     private static long defaultLockAcquisitionTimeoutMs = DEFAULT_LOCK_ACQUISITION_TIMEOUT_MS;
  35.     public static long getDefaultLockAcquisitionTimeoutMs() {
  36.         return defaultLockAcquisitionTimeoutMs;
  37.     }
  38.     public static void setDefaultLockAcquisitionTimeoutMs(long timeoutMs) {
  39.         defaultLockAcquisitionTimeoutMs = timeoutMs;
  40.     }
  41.    
  42.     public static final long DEFAULT_LOCK_HOLD_TIMEOUT_MS = 60000;
  43.     private static long defaultLockHoldTimeoutMs = DEFAULT_LOCK_HOLD_TIMEOUT_MS;
  44.     public static long getDefaultLockHoldTimeoutMs() {
  45.         return defaultLockHoldTimeoutMs;
  46.     }
  47.     public static void setDefaultLockHoldTimeoutMs(long timeoutMs) {
  48.         defaultLockHoldTimeoutMs = timeoutMs;
  49.     }
  50.    
  51.     private static Logger logDebug = null;
  52.     public static Logger getLogDebug() {
  53.         return logDebug;
  54.     }
  55.     public static void setLogDebug(Logger logDebug) {
  56.         Semaphore.logDebug = logDebug;
  57.     }

  58.     private static boolean defaultDebug = false;
  59.     public static boolean isDefaultDebug() {
  60.         return defaultDebug;
  61.     }
  62.     public static void setDefaultDebug(boolean d) {
  63.         defaultDebug = d;
  64.     }
  65.    
  66.     private Long instanceLockAcquisitionTimeoutMs = null;
  67.     public long getInstanceLockAcquisitionTimeoutMs() {
  68.         return this.instanceLockAcquisitionTimeoutMs != null && this.instanceLockAcquisitionTimeoutMs.longValue()>0 ? this.instanceLockAcquisitionTimeoutMs : defaultLockAcquisitionTimeoutMs;
  69.     }
  70.     public void setInstanceLockAcquisitionTimeoutMs(Long timeoutMs) {
  71.         this.instanceLockAcquisitionTimeoutMs = timeoutMs;
  72.     }
  73.     public void setInstanceLockAcquisitionTimeoutMs(long timeoutMs) {
  74.         this.instanceLockAcquisitionTimeoutMs = timeoutMs;
  75.     }
  76.    
  77.     private long instanceLockHoldTimeoutMs = -1;
  78.     public long getInstanceLockHoldTimeoutMs() {
  79.         return this.instanceLockHoldTimeoutMs>0 ? this.instanceLockHoldTimeoutMs : defaultLockHoldTimeoutMs;
  80.     }
  81.     public void setInstanceLockHoldTimeoutMs(long timeoutMs) {
  82.         this.instanceLockHoldTimeoutMs = timeoutMs;
  83.     }

  84.     private boolean instanceDebug = false;
  85.     public boolean isInstanceDebug() {
  86.         return this.instanceDebug || defaultDebug;
  87.     }
  88.     public void setInstanceDebug(boolean d) {
  89.         this.instanceDebug = d;
  90.     }

  91.     private static SemaphoreType semaphoreType = SemaphoreType.Semaphore;
  92.     public static SemaphoreType getSemaphoreType() {
  93.         return semaphoreType;
  94.     }
  95.     public static void setSemaphoreType(SemaphoreType semaphoreType) {
  96.         Semaphore.semaphoreType = semaphoreType;
  97.     }

  98.     private static boolean fair = true;
  99.     public static boolean isFair() {
  100.         return fair;
  101.     }
  102.     public static void setFair(boolean fair) {
  103.         Semaphore.fair = fair;
  104.     }

  105.     private final java.util.concurrent.Semaphore concurrentSemaphore;
  106.     private final java.util.concurrent.locks.ReentrantLock reentrantLock;
  107.     private String semaphoreName = null;
  108.     private int permits = -1;
  109.    
  110.     public Semaphore(String name) {
  111.         this(name, Semaphore.semaphoreType, Semaphore.fair);
  112.     }
  113.     public Semaphore(String name, boolean fair) {
  114.         this(name, Semaphore.semaphoreType, fair);
  115.     }
  116.     public Semaphore(String name, SemaphoreType semaphoreType, boolean fair) {
  117.         this.semaphoreName = name;
  118.         switch (semaphoreType) {
  119.         case ReentrantLock:
  120.             this.reentrantLock = new ReentrantLock(fair);
  121.             this.concurrentSemaphore = null;
  122.             break;
  123.         case Semaphore:
  124.             this.reentrantLock = null;
  125.             this.concurrentSemaphore = new java.util.concurrent.Semaphore(1, fair);
  126.             break;
  127.         default:
  128.             this.reentrantLock = null;
  129.             this.concurrentSemaphore = null;
  130.         }
  131.         this.permits = 1;
  132.     }
  133.    
  134.     public Semaphore(String name, int permits) {
  135.         this(name, permits, Semaphore.fair);
  136.     }
  137.     public Semaphore(String name, int permits, boolean fair) {
  138.         this.semaphoreName = name;
  139.         this.concurrentSemaphore = new java.util.concurrent.Semaphore(permits, fair);
  140.         this.reentrantLock = null;
  141.         this.permits = permits;
  142.     }
  143.    
  144.     public boolean hasQueuedThreads() {
  145.         if(this.concurrentSemaphore!=null) {
  146.             return this.concurrentSemaphore.hasQueuedThreads();
  147.         }
  148.         else {
  149.             return this.reentrantLock.hasQueuedThreads();
  150.         }
  151.     }
  152.     public boolean available() {
  153.         if(this.concurrentSemaphore!=null) {
  154.             return this.concurrentSemaphore.availablePermits()>0;
  155.         }
  156.         else {
  157.             return !this.reentrantLock.isLocked();
  158.         }
  159.     }
  160.     public int availablePermits() {
  161.         if(this.concurrentSemaphore!=null) {
  162.             return this.concurrentSemaphore.availablePermits();
  163.         }
  164.         else {
  165.             return -1;
  166.         }
  167.     }
  168.    
  169.     String getPrefix(String methodName, String idTransazione) {
  170.         String idTr = "";
  171.         if(idTransazione!=null) {
  172.             idTr = " (idTransazione:"+idTransazione+")";
  173.         }
  174.         return this.semaphoreName+"."+methodName+" [Thread:"+Thread.currentThread().getName()+"]"+idTr+" ";
  175.     }
  176.    
  177.     public SemaphoreLock acquire(String methodName) throws UtilsException {
  178.         return this.acquire(methodName, null);
  179.     }
  180.     public SemaphoreLock acquire(String methodName, String idTransazione) throws UtilsException {
  181.         try {
  182.             if(this.getInstanceLockAcquisitionTimeoutMs()<=0) {
  183.                 return acquireWithoutTimeout(methodName, idTransazione);
  184.             }
  185.             else {
  186.                 return acquireWithTimeout(methodName, idTransazione);
  187.             }
  188.         }
  189.         catch(InterruptedException ie) {
  190.             if(this.isInstanceDebug()) {
  191.                 debug(getPrefix(methodName, idTransazione)+" acquire("+this.getInstanceLockAcquisitionTimeoutMs()+"ms) failed: "+ie.getMessage());
  192.             }
  193.             Thread.currentThread().interrupt();
  194.             throw new UtilsException(ie.getMessage(),ie);
  195.         }
  196.     }
  197.     private SemaphoreLock acquireWithoutTimeout(String methodName, String idTransazione) throws InterruptedException {
  198.         if(this.isInstanceDebug()) {
  199.             debug(getPrefix(methodName, idTransazione)+" acquire ...");
  200.         }
  201.         if(this.concurrentSemaphore!=null) {
  202.             this.concurrentSemaphore.acquire();
  203.         }
  204.         else {
  205.             this.reentrantLock.lock();
  206.         }
  207.         if(this.isInstanceDebug()) {
  208.             debug(getPrefix(methodName, idTransazione)+" acquired");
  209.         }
  210.         return new SemaphoreLock(this, methodName, idTransazione);
  211.     }
  212.     private SemaphoreLock acquireWithTimeout(String methodName, String idTransazione) throws InterruptedException {
  213.         if(this.isInstanceDebug()) {
  214.             debug(getPrefix(methodName, idTransazione)+" acquire("+this.getInstanceLockAcquisitionTimeoutMs()+"ms) ...");
  215.         }
  216.         boolean acquire = false;
  217.         if(this.concurrentSemaphore!=null) {
  218.             acquire = this.concurrentSemaphore.tryAcquire(this.getInstanceLockAcquisitionTimeoutMs(), TimeUnit.MILLISECONDS);
  219.         }
  220.         else {
  221.             acquire = this.reentrantLock.tryLock(this.getInstanceLockAcquisitionTimeoutMs(), TimeUnit.MILLISECONDS);
  222.         }
  223.         if(!acquire) {
  224.             throw new InterruptedException("["+this.semaphoreName+"] Could not acquire semaphore after "+this.getInstanceLockAcquisitionTimeoutMs()+"ms");
  225.         }
  226.         if(this.isInstanceDebug()) {
  227.             debug(getPrefix(methodName, idTransazione)+" acquired");
  228.         }
  229.         return new SemaphoreLock(this, methodName, idTransazione);
  230.     }
  231.    
  232.     public SemaphoreLock acquireThrowRuntime(String methodName) throws SemaphoreRuntimeException {
  233.         return this.acquireThrowRuntime(methodName, null);
  234.     }
  235.     public SemaphoreLock acquireThrowRuntime(String methodName, String idTransazione) throws SemaphoreRuntimeException {
  236.         try {
  237.             return this.acquire(methodName, idTransazione);
  238.         }
  239.         catch(Exception t) {
  240.             throw new SemaphoreRuntimeException(t.getMessage(),t);
  241.         }
  242.     }
  243.    
  244.     public void release(SemaphoreLock semaphoreLock, String methodName) {
  245.         this.release(semaphoreLock, methodName, null);
  246.     }
  247.     public void release(SemaphoreLock semaphoreLock, String methodName, String idTransazione) {
  248.         if(this.isInstanceDebug()) {
  249.             debug(getPrefix(methodName, idTransazione)+" release ...");
  250.         }
  251.         if(this.concurrentSemaphore!=null) {
  252.             if(this.concurrentSemaphore.availablePermits()<this.permits) {
  253.                 this.concurrentSemaphore.release(); // altrimenti ogni release utilizzato male fa incrementare i permessi
  254.             }
  255.         }
  256.         else {
  257.             this.reentrantLock.unlock();
  258.         }
  259.         if(semaphoreLock!=null) {
  260.             semaphoreLock.release(methodName, idTransazione);
  261.         }
  262.         if(this.isInstanceDebug()) {
  263.             debug(getPrefix(methodName, idTransazione)+" released");
  264.         }
  265.     }
  266.    
  267.     void debug(String msg) {
  268.         System.out.println(msg);
  269.     }
  270. }

  271. @SuppressWarnings("serial")
  272. class SemaphoreRuntimeException extends RuntimeException{

  273.     public SemaphoreRuntimeException() {
  274.         super();
  275.     }

  276.     public SemaphoreRuntimeException(String message, Throwable cause, boolean enableSuppression,
  277.             boolean writableStackTrace) {
  278.         super(message, cause, enableSuppression, writableStackTrace);
  279.     }

  280.     public SemaphoreRuntimeException(String message, Throwable cause) {
  281.         super(message, cause);
  282.     }

  283.     public SemaphoreRuntimeException(String message) {
  284.         super(message);
  285.     }

  286.     public SemaphoreRuntimeException(Throwable cause) {
  287.         super(cause);
  288.     }
  289.    
  290. }