GestoreCacheResponseCaching.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 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.pdd.core.response_caching;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openspcoop2.core.commons.CoreException;
import org.openspcoop2.core.config.constants.CostantiConfigurazione;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.cache.Cache;
import org.openspcoop2.utils.cache.CacheAlgorithm;
import org.openspcoop2.utils.cache.CacheType;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.date.DateUtils;
import org.openspcoop2.utils.id.UniqueIdentifierManager;
import org.slf4j.Logger;
/**
* GestoreCacheResponseCaching
*
* @author Poli Andrea (poli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class GestoreCacheResponseCaching {
/** Chiave della cache per l'autenticazione Buste */
private static final String RESPONSE_CACHING_CACHE_NAME = "responseCaching";
/** Cache */
private static Cache cache = null;
private static final org.openspcoop2.utils.Semaphore lock_cache = new org.openspcoop2.utils.Semaphore("GestoreCacheResponseCaching");
/* --------------- Cache --------------------*/
public static boolean isCacheAbilitata() {
return cache!=null;
}
public static void resetCache() throws Exception{
try{
if(cache!=null){
cache.clear();
}
}catch(Exception e){
throw new Exception("Reset della cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static String printStatsCache(String separator) throws Exception{
try{
if(cache!=null){
try{
return cache.printStats(separator);
}catch(Exception e){
throw new Exception(e.getMessage(),e);
}
}else{
throw new Exception("Cache non abilitata");
}
}catch(Exception e){
throw new Exception("Visualizzazione Statistiche riguardante la cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static void abilitaCache() throws Exception{
try{
if(cache!=null)
throw new Exception("Cache gia' abilitata");
else{
_abilitaCache();
}
}catch(Exception e){
throw new Exception("Abilitazione cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
private static synchronized void _abilitaCache() throws Exception{
try{
if(cache==null) {
cache = new Cache(CacheType.JCS, RESPONSE_CACHING_CACHE_NAME); // lascio JCS come default abilitato via jmx
cache.build();
}
}catch(Exception e){
throw new Exception("Abilitazione cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static void abilitaCache(Long dimensioneCache,Boolean algoritmoCacheLRU,Long itemIdleTime,Long itemLifeSecond, Logger log) throws Exception{
try{
if(cache!=null)
throw new Exception("Cache gia' abilitata");
else{
int dimensione = -1;
if(dimensioneCache!=null){
dimensione = dimensioneCache.intValue();
}
initCache(CacheType.JCS, dimensione, algoritmoCacheLRU, itemIdleTime, itemLifeSecond, log); // lascio JCS come default abilitato via jmx
}
}catch(Exception e){
throw new Exception("Abilitazione cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static void disabilitaCache() throws Exception{
try{
if(cache==null)
throw new Exception("Cache gia' disabilitata");
else{
_disabilitaCache();
}
}catch(Exception e){
throw new Exception("Disabilitazione cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
private static synchronized void _disabilitaCache() throws Exception{
try{
if(cache!=null) {
cache.clear();
cache = null;
}
}catch(Exception e){
throw new Exception("Disabilitazione cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static String listKeysCache(String separator) throws Exception{
try{
if(cache!=null){
try{
return cache.printKeys(separator);
}catch(Exception e){
throw new Exception(e.getMessage(),e);
}
}else{
throw new Exception("Cache non abilitata");
}
}catch(Exception e){
throw new Exception("Visualizzazione chiavi presenti nella cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static String getObjectCache(String key) throws Exception{
try{
if(cache!=null){
try{
Object o = cache.get(key);
if(o!=null){
return o.toString();
}else{
return "oggetto con chiave ["+key+"] non presente";
}
}catch(Exception e){
throw new Exception(e.getMessage(),e);
}
}else{
throw new Exception("Cache non abilitata");
}
}catch(Exception e){
throw new Exception("Visualizzazione oggetto presente nella cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
public static void removeObjectCache(String key) throws Exception{
try{
if(cache!=null){
try{
cache.remove(key);
}catch(Exception e){
throw new Exception(e.getMessage(),e);
}
}else{
throw new Exception("Cache non abilitata");
}
}catch(Exception e){
throw new Exception("Rimozione oggetto presente nella cache per i dati contenenti le risposte salvate non riuscita: "+e.getMessage(),e);
}
}
/*----------------- INIZIALIZZAZIONE --------------------*/
public static void initialize(Logger log) throws Exception{
GestoreCacheResponseCaching.initialize(null, false, -1,null,-1l,-1l, log);
}
public static void initialize(CacheType cacheType, int dimensioneCache,String algoritmoCache,
long idleTime, long itemLifeSecond, Logger log) throws Exception{
GestoreCacheResponseCaching.initialize(cacheType, true, dimensioneCache,algoritmoCache,idleTime,itemLifeSecond, log);
}
private static void initialize(CacheType cacheType, boolean cacheAbilitata,int dimensioneCache,String algoritmoCache,
long idleTime, long itemLifeSecond, Logger log) throws Exception{
// Inizializzazione Cache
if(cacheAbilitata){
GestoreCacheResponseCaching.initCache(cacheType, dimensioneCache, algoritmoCache, idleTime, itemLifeSecond, log);
}
}
private static void initCache(CacheType cacheType, Integer dimensioneCache,String algoritmoCache,Long itemIdleTime,Long itemLifeSecond,Logger alog) throws Exception{
initCache(cacheType, dimensioneCache, CostantiConfigurazione.CACHE_LRU.toString().equalsIgnoreCase(algoritmoCache), itemIdleTime, itemLifeSecond, alog);
}
private static void initCache(CacheType cacheType, Integer dimensioneCache,boolean algoritmoCacheLRU,Long itemIdleTime,Long itemLifeSecond,Logger alog) throws Exception{
cache = new Cache(cacheType, RESPONSE_CACHING_CACHE_NAME);
// dimensione
if(dimensioneCache!=null && dimensioneCache>0){
try{
String msg = "Dimensione della cache (ResponseCaching) impostata al valore: "+dimensioneCache;
alog.info(msg);
cache.setCacheSize(dimensioneCache);
}catch(Exception error){
String msg = "Parametro errato per la dimensione della cache (ResponseCaching): "+error.getMessage();
alog.error(msg);
throw new Exception(msg,error);
}
}
// algoritno
String msg = "Algoritmo di cache (ResponseCaching) impostato al valore: LRU";
if(!algoritmoCacheLRU){
msg = "Algoritmo di cache (ResponseCaching) impostato al valore: MRU";
}
alog.info(msg);
if(!algoritmoCacheLRU)
cache.setCacheAlgoritm(CacheAlgorithm.MRU);
else
cache.setCacheAlgoritm(CacheAlgorithm.LRU);
// idle time
if(itemIdleTime!=null && itemIdleTime>0){
try{
msg = "Attributo 'IdleTime' (ResponseCaching) impostato al valore: "+itemIdleTime;
alog.info(msg);
cache.setItemIdleTime(itemIdleTime);
}catch(Exception error){
msg = "Parametro errato per l'attributo 'IdleTime' (ResponseCaching): "+error.getMessage();
alog.error(msg);
throw new Exception(msg,error);
}
}
// LifeSecond
long longItemLife = -1;
if(itemLifeSecond!=null && itemLifeSecond>0){
longItemLife = itemLifeSecond.longValue();
}
try{
msg = "Attributo 'MaxLifeSecond' (ResponseCaching) impostato al valore: "+longItemLife;
alog.info(msg);
cache.setItemLifeTime(longItemLife);
}catch(Exception error){
msg = "Parametro errato per l'attributo 'MaxLifeSecond' (ResponseCaching): "+error.getMessage();
alog.error(msg);
throw new Exception(msg,error);
}
cache.build();
}
@SuppressWarnings("deprecation")
@Deprecated
public static void disableSyncronizedGet() throws UtilsException {
if(cache==null) {
throw new UtilsException("Cache disabled");
}
cache.disableSyncronizedGet();
}
@SuppressWarnings("deprecation")
@Deprecated
public static boolean isDisableSyncronizedGet() throws UtilsException {
if(cache==null) {
throw new UtilsException("Cache disabled");
}
return cache.isDisableSyncronizedGet();
}
private static GestoreCacheResponseCaching staticInstance = null;
public static synchronized void initialize() {
if(staticInstance==null){
staticInstance = new GestoreCacheResponseCaching();
}
}
public static GestoreCacheResponseCaching getInstance() throws CoreException{
if(staticInstance==null){
// spotbugs warning 'SING_SINGLETON_GETTER_NOT_SYNCHRONIZED': l'istanza viene creata allo startup
synchronized (GestoreCacheResponseCaching.class) {
throw new CoreException("GestoreCacheResponseCaching non inizializzato");
}
}
return staticInstance;
}
private Logger log;
private GestoreCacheResponseCaching() {
this.log = OpenSPCoop2Logger.getLoggerOpenSPCoopCore();
}
/* ********************** ENGINE ************************** */
public String save(String digest, ResponseCached response) throws Exception{
String uuid = null;
try{
if(cache==null) {
throw new Exception("Cache per il salvataggio delle risposte non abilitata");
}
if(digest == null)
throw new Exception("Digest non definito");
if(response == null)
throw new Exception("Risposta non definita");
String digestKey = formatKeyDigest(digest);
// Fix: devo prima verificare se ho la chiave in cache prima di mettermi in sincronizzazione.
Object o = cache.get(digestKey);
if(o!=null) {
org.openspcoop2.utils.cache.CacheResponse responseCache =
(org.openspcoop2.utils.cache.CacheResponse) o;
return ((ResponseCached)responseCache.getObject()).getUuid(); // already saved concurrent thread
}
//synchronized (cache) {
lock_cache.acquire("save");
try {
o = cache.get(digestKey);
if(o!=null) {
org.openspcoop2.utils.cache.CacheResponse responseCache =
(org.openspcoop2.utils.cache.CacheResponse) o;
return ((ResponseCached)responseCache.getObject()).getUuid(); // already saved concurrent thread
}
uuid = UniqueIdentifierManager.newUniqueIdentifier().getAsString();
String uuidKey = formatKeyUUID(uuid);
try{
cache.put(uuidKey,digestKey);
}catch(UtilsException e){
this.log.error("Errore durante l'inserimento in cache con chiave ["+uuidKey+"] valore["+digestKey+"]: "+e.getMessage());
}
try{
org.openspcoop2.utils.cache.CacheResponse responseCache = new org.openspcoop2.utils.cache.CacheResponse();
response.setUuid(uuid);
response.setDigest(digest);
responseCache.setObject((java.io.Serializable)response);
cache.put(digestKey,responseCache);
}catch(UtilsException e){
this.log.error("Errore durante l'inserimento in cache con chiave ["+digestKey+"] della risposta: "+e.getMessage());
try {
cache.remove(uuidKey); // precedentemente aggiunto
}catch(Throwable tIgnore) {}
}
}finally {
lock_cache.release("save");
}
}
catch(Exception e){
this.log.error(e.getMessage(),e);
throw new Exception("Salvataggio in Cache fallito: "+e.getMessage(),e);
}
return uuid;
}
public ResponseCached readByUUID(String uuid) throws Exception{
try{
if(cache==null) {
throw new Exception("Cache per il salvataggio delle risposte non abilitata");
}
if(uuid == null)
throw new Exception("UUID non definito");
String uuidKey = formatKeyUUID(uuid);
// Fix: devo prima verificare se ho la chiave in cache prima di mettermi in sincronizzazione.
Object oDigest = cache.get(uuidKey);
if(oDigest==null) {
return null; // messaggio non precedentemente salvato
}
String digest = (String) oDigest;
String digestKey = formatKeyDigest(digest);
org.openspcoop2.utils.cache.CacheResponse response =
(org.openspcoop2.utils.cache.CacheResponse) cache.get(digestKey);
if(response == null || response.getObject()==null){
// la rimozione la faccio in modalita' sincronizzata
}
else {
ResponseCached responseCached = (ResponseCached) response.getObject();
Date now = DateManager.getDate();
if(now.after(responseCached.getScadenza())) {
// la rimozione la faccio in modalita' sincronizzata
}
else {
return responseCached;
}
}
//synchronized (cache) {
lock_cache.acquire("readByUUID");
try {
oDigest = cache.get(uuidKey);
if(oDigest==null) {
return null; // messaggio non precedentemente salvato
}
digest = (String) oDigest;
digestKey = formatKeyDigest(digest);
response =
(org.openspcoop2.utils.cache.CacheResponse) cache.get(digestKey);
if(response == null || response.getObject()==null){
this.log.error("In cache non è presente il mapping uuid ("+uuidKey+") - digest ("+digestKey+") ma poi non esiste l'oggetto??");
cache.remove(uuidKey);
return null;
}
ResponseCached responseCached = (ResponseCached) response.getObject();
Date now = DateManager.getDate();
if(now.after(responseCached.getScadenza())) {
SimpleDateFormat dateformat = DateUtils.getSimpleDateFormatMs();
String scadenza = dateformat.format(responseCached.getScadenza());
String nowS = dateformat.format(now);
this.log.debug("In cache è presente un messaggio [uuid ("+uuidKey+") - digest ("+digestKey+")] scaduto (scadenza:"+scadenza+") (now:"+nowS+")");
cache.remove(uuidKey);
cache.remove(digestKey);
return null;
}
return responseCached;
}finally {
lock_cache.release("readByUUID");
}
}
catch(Exception e){
this.log.error(e.getMessage(),e);
throw new Exception("Lettura messaggio in Cache fallito: "+e.getMessage(),e);
}
}
public void removeByUUID(String uuid) throws Exception{
try{
if(cache==null) {
throw new Exception("Cache per il salvataggio delle risposte non abilitata");
}
if(uuid == null)
throw new Exception("UUID non definito");
String uuidKey = formatKeyUUID(uuid);
// Fix: devo prima verificare se ho la chiave in cache prima di mettermi in sincronizzazione.
Object oDigest = cache.get(uuidKey);
if(oDigest==null) {
return; // messaggio non precedentemente salvato
}
//synchronized (cache) {
lock_cache.acquire("removeByUUID");
try {
oDigest = cache.get(uuidKey);
if(oDigest==null) {
return; // messaggio non precedentemente salvato
}
String digest = (String) oDigest;
String digestKey = formatKeyDigest(digest);
org.openspcoop2.utils.cache.CacheResponse response =
(org.openspcoop2.utils.cache.CacheResponse) cache.get(digestKey);
if(response == null || response.getObject()==null){
this.log.error("In cache non è presente il mapping uuid ("+uuidKey+") - digest ("+digestKey+") ma poi non esiste l'oggetto??");
cache.remove(uuidKey);
return;
}
cache.remove(uuidKey);
cache.remove(digestKey);
}finally{
lock_cache.release("removeByUUID");
}
}
catch(Exception e){
this.log.error(e.getMessage(),e);
throw new Exception("Rimozione messaggio in Cache fallito: "+e.getMessage(),e);
}
}
public ResponseCached readByDigest(String digest) throws Exception{
try{
if(cache==null) {
throw new Exception("Cache per il salvataggio delle risposte non abilitata");
}
if(digest == null)
throw new Exception("Digest non definito");
String digestKey = formatKeyDigest(digest);
// Fix: devo prima verificare se ho la chiave in cache prima di mettermi in sincronizzazione.
org.openspcoop2.utils.cache.CacheResponse response =
(org.openspcoop2.utils.cache.CacheResponse) cache.get(digestKey);
if(response == null || response.getObject()==null){
return null;
}
ResponseCached responseCached = (ResponseCached) response.getObject();
Date now = DateManager.getDate();
if(now.after(responseCached.getScadenza())) {
// effettuo il controllo in modalita' sincronizzazione per fare le rimozioni
}
else {
return responseCached;
}
//synchronized (cache) {
lock_cache.acquire("readByDigest");
try {
response =
(org.openspcoop2.utils.cache.CacheResponse) cache.get(digestKey);
if(response == null || response.getObject()==null){
return null;
}
responseCached = (ResponseCached) response.getObject();
now = DateManager.getDate();
if(now.after(responseCached.getScadenza())) {
String uuidKey = formatKeyUUID(responseCached.getUuid());
SimpleDateFormat dateformat = DateUtils.getSimpleDateFormatMs();
String scadenza = dateformat.format(responseCached.getScadenza());
String nowS = dateformat.format(now);
this.log.debug("In cache è presente un messaggio [uuid ("+uuidKey+") - digest ("+digestKey+")] scaduto (scadenza:"+scadenza+") (now:"+nowS+")");
cache.remove(uuidKey);
cache.remove(digestKey);
return null;
}
return responseCached;
}finally {
lock_cache.release("readByDigest");
}
}
catch(Exception e){
this.log.error(e.getMessage(),e);
throw new Exception("Lettura messaggio in Cache fallito: "+e.getMessage(),e);
}
}
public static String formatKeyUUID(String uuid) {
String prefix = "uuid:";
if(uuid.startsWith(prefix)==false) {
return prefix+uuid;
}
else {
return uuid;
}
}
public static String formatKeyDigest(String digest) {
String prefix = "digest:";
if(digest.startsWith(prefix)==false) {
return prefix+digest;
}
else {
return digest;
}
}
}