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 = -1;
  67.     public long getInstanceLockAcquisitionTimeoutMs() {
  68.         return this.instanceLockAcquisitionTimeoutMs>0 ? this.instanceLockAcquisitionTimeoutMs : defaultLockAcquisitionTimeoutMs;
  69.     }
  70.     public void setInstanceLockAcquisitionTimeoutMs(long timeoutMs) {
  71.         this.instanceLockAcquisitionTimeoutMs = timeoutMs;
  72.     }
  73.    
  74.     private long instanceLockHoldTimeoutMs = -1;
  75.     public long getInstanceLockHoldTimeoutMs() {
  76.         return this.instanceLockHoldTimeoutMs>0 ? this.instanceLockHoldTimeoutMs : defaultLockHoldTimeoutMs;
  77.     }
  78.     public void setInstanceLockHoldTimeoutMs(long timeoutMs) {
  79.         this.instanceLockHoldTimeoutMs = timeoutMs;
  80.     }

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

  88.     private static SemaphoreType semaphoreType = SemaphoreType.Semaphore;
  89.     public static SemaphoreType getSemaphoreType() {
  90.         return semaphoreType;
  91.     }
  92.     public static void setSemaphoreType(SemaphoreType semaphoreType) {
  93.         Semaphore.semaphoreType = semaphoreType;
  94.     }

  95.     private static boolean fair = true;
  96.     public static boolean isFair() {
  97.         return fair;
  98.     }
  99.     public static void setFair(boolean fair) {
  100.         Semaphore.fair = fair;
  101.     }

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

  268. @SuppressWarnings("serial")
  269. class SemaphoreRuntimeException extends RuntimeException{

  270.     public SemaphoreRuntimeException() {
  271.         super();
  272.     }

  273.     public SemaphoreRuntimeException(String message, Throwable cause, boolean enableSuppression,
  274.             boolean writableStackTrace) {
  275.         super(message, cause, enableSuppression, writableStackTrace);
  276.     }

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

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

  283.     public SemaphoreRuntimeException(Throwable cause) {
  284.         super(cause);
  285.     }
  286.    
  287. }