PolicyGroupByActiveThreadsDB.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.controllo_traffico.policy.driver;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.openspcoop2.core.commons.DBUtils;
import org.openspcoop2.core.controllo_traffico.beans.ActivePolicy;
import org.openspcoop2.core.controllo_traffico.beans.DatiCollezionati;
import org.openspcoop2.core.controllo_traffico.beans.IDUnivocoGroupBy;
import org.openspcoop2.core.controllo_traffico.beans.IDUnivocoGroupByPolicy;
import org.openspcoop2.core.controllo_traffico.beans.MisurazioniTransazione;
import org.openspcoop2.core.controllo_traffico.beans.UniqueIdentifierUtilities;
import org.openspcoop2.core.controllo_traffico.constants.Costanti;
import org.openspcoop2.core.controllo_traffico.driver.IPolicyGroupByActiveThreadsInMemory;
import org.openspcoop2.core.controllo_traffico.driver.PolicyException;
import org.openspcoop2.core.controllo_traffico.driver.PolicyGroupByActiveThreadsType;
import org.openspcoop2.core.controllo_traffico.driver.PolicyNotFoundException;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.pdd.config.DBManager;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.config.Resource;
import org.openspcoop2.pdd.core.controllo_traffico.policy.PolicyDateUtils;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.protocol.sdk.state.IState;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.protocol.sdk.state.StateMessage;
import org.openspcoop2.protocol.utils.EsitiProperties;
import org.openspcoop2.utils.Map;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.date.DateUtils;
import org.openspcoop2.utils.jdbc.IJDBCAdapter;
import org.openspcoop2.utils.jdbc.JDBCAdapterFactory;
import org.openspcoop2.utils.jdbc.JDBCUtilities;
import org.openspcoop2.utils.serialization.JavaDeserializer;
import org.openspcoop2.utils.serialization.JavaSerializer;
import org.openspcoop2.utils.sql.ISQLQueryObject;
import org.openspcoop2.utils.sql.SQLObjectFactory;
import org.slf4j.Logger;

/**     
 * PolicyGroupByActiveThreads
 *
 * @author Poli Andrea (poli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class PolicyGroupByActiveThreadsDB implements Serializable,IPolicyGroupByActiveThreadsInMemory {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private static final String CT_MAP_TABLE = "ct_map";
	private static final String CT_MAP_COLUMN_KEY = "map_key";
	private static final String CT_MAP_COLUMN_UPDATE_TIME = "map_update_time";
	private static final String CT_MAP_COLUMN_VALUE = "map_value";

	private static java.util.Random _rnd = null;
	private static synchronized void initRandom() {
		if(_rnd==null) {
			_rnd = new SecureRandom();
		}
	}
	public static java.util.Random getRandom() {
		if(_rnd==null) {
			initRandom();
		}
		return _rnd;
	}
	
	private boolean mapExists = false;
	
	private ActivePolicy activePolicy;
	private PolicyGroupByActiveThreadsType tipoGestore;
	private String uniqueIdMap_idActivePolicy;
	private Date uniqueIdMap_updateTime;
	private IState state;
	private IDSoggetto dominio;
	private String idTransazione;
	private transient OpenSPCoop2Properties _op2Properties; 
	private String tipoDatabase;
	private IJDBCAdapter jdbcAdapter;
	private transient JavaSerializer _javaSerializer = null;
	private transient JavaDeserializer _javaDeserializer = null;
	private Logger log;
	private Logger logSql;
	private boolean debug;
	private boolean transactionMode = true;
	
	private long attesaAttivaJDBC;
	private int checkIntervalloJDBC;

	public PolicyGroupByActiveThreadsDB(ActivePolicy activePolicy, PolicyGroupByActiveThreadsType tipoGestore, String uniqueIdMap, IState state,IDSoggetto dominio, String idTransazione) throws PolicyException {
		this.activePolicy = activePolicy;
		this.tipoGestore = tipoGestore;
		this.uniqueIdMap_idActivePolicy = UniqueIdentifierUtilities.extractIdActivePolicy(uniqueIdMap);
		try {
			this.uniqueIdMap_updateTime = UniqueIdentifierUtilities.extractUpdateTimeActivePolicy(uniqueIdMap);
		}catch(Exception e) {
			throw new PolicyException(e.getMessage(),e);
		}
		this.state = state;
		this.dominio = dominio;
		this.idTransazione = idTransazione;
		this._op2Properties = OpenSPCoop2Properties.getInstance();
		this.tipoDatabase = this._op2Properties.getDatabaseType();
		try {
			this.jdbcAdapter = JDBCAdapterFactory.createJDBCAdapter(this.tipoDatabase);
		}catch(Exception e){
			throw new PolicyException("[createJDBCAdapter] "+e.getMessage(),e);
		}
		this._javaSerializer = new JavaSerializer();
		this._javaDeserializer = new JavaDeserializer();
		this.debug = this._op2Properties.isControlloTrafficoDebug();
		this.log = OpenSPCoop2Logger.getLoggerOpenSPCoopControlloTraffico(this.debug);
		this.logSql = OpenSPCoop2Logger.getLoggerOpenSPCoopControlloTrafficoSql(this.debug);
		
		this.transactionMode = this._op2Properties.isControlloTrafficoGestorePolicyInMemoryDatabase_useTransaction();
		this.attesaAttivaJDBC = this._op2Properties.getControlloTrafficoGestorePolicyInMemoryDatabase_serializableDB_AttesaAttiva();
		this.checkIntervalloJDBC = this._op2Properties.getControlloTrafficoGestorePolicyInMemoryDatabase_serializableDB_CheckInterval();
				
	}
	
	private synchronized void initJavaSerializer() {
		if(this._javaSerializer==null) {
			this._javaSerializer = new JavaSerializer();
		}
	}
	public JavaSerializer getJavaSerializer(){
		if(this._javaSerializer==null) {
			initJavaSerializer();
		}
		return this._javaSerializer;
	}
	
	private synchronized void initJavaDeserializer() {
		if(this._javaDeserializer==null) {
			this._javaDeserializer = new JavaDeserializer();
		}
	}
	public JavaDeserializer getJavaDeserializer(){
		if(this._javaDeserializer==null) {
			initJavaDeserializer();
		}
		return this._javaDeserializer;
	}
	
	private synchronized void initOpenSPCoop2Properties() {
		if(this._op2Properties==null) {
			this._op2Properties = OpenSPCoop2Properties.getInstance();
		}
	}
	public OpenSPCoop2Properties getOpenSPCoop2Properties(){
		if(this._op2Properties==null) {
			initOpenSPCoop2Properties();
		}
		return this._op2Properties;
	}
	
	
	
	@Override
	public ActivePolicy getActivePolicy() {
		return this.activePolicy;
	}
	
	@Override
	public java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> getMapActiveThreads(){
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "getMapActiveThreads", this.idTransazione, null);
			
			//checkMap(resource.con);
			
			return _getMapActiveThreads(resource.con);
			
		}
		catch(Throwable e) {
			this.log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "getMapActiveThreads", null);
			}
		}
	}
	
	@Override
	public void initMap(java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> map) {
		
		// Non serve essendo già persistente, si rischia di salvare una precedente immagine.
		
		/*
		if(map!=null && map.size()>0){
			PolicyConnessioneRuntime resource = null;
			try {
				resource = getConnessione(this.state, this.dominio, "initMap", this.idTransazione);
				
				checkMap(resource.con);
				
				_initMap(resource.con, map);
				
			}
			catch(Throwable e) {
				this.log.error(e.getMessage(),e);
				throw new RuntimeException(e.getMessage(),e);
			}
			finally {
				if(resource!=null) {
					releaseConnessione(resource, this.dominio, "initMap");
				}
			}
		}
		*/
		
	}
	
	@Override
	public void resetCounters(){
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "resetCounters", this.idTransazione, null);
			
			//checkMap(resource.con);
			
			_resetCounters(resource.con);
			
		}
		catch(Throwable e) {
			this.log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "resetCounters", null);
			}
		}
		
	}
	
	@Override
	public void remove() throws UtilsException{
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "remove", this.idTransazione, null);
			
			//checkMap(resource.con);
			
			_remove(resource.con);
			
		}
		catch(Throwable e) {
			this.log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "remove", null);
			}
		}
		
	}
	
	@Override
	public DatiCollezionati registerStartRequest(Logger log, String idTransazione, IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx) throws PolicyException{
		
		PolicyConnessioneRuntime resource = null;
		DatiCollezionati datiCollezionatiReaded = null;
		try {
			resource = getConnessione(this.state, this.dominio, "registerStartRequest", idTransazione, ctx);
			
			checkMap(resource.con);
			
			// mi salvo fuori dal synchronized l'attuale stato
			datiCollezionatiReaded = _registerStartRequest(resource.con, log, idTransazione, datiGroupBy, ctx); 
			
		}finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "registerStartRequest", ctx);
			}
		}
		
		// Tutti i restanti controlli sono effettuati usando il valore di datiCollezionatiReaded, che e' gia' stato modificato
		// Inoltre e' stato re-inserito nella map come oggetto nuovo, quindi il valore dentro il metodo non subira' trasformazioni (essendo stato fatto il deserialize da db)
		// E' possibile procedere con l'analisi rispetto al valore che possiedono il counter dentro questo scope.
		
		return datiCollezionatiReaded;

	}
	
	@Override
	public DatiCollezionati updateDatiStartRequestApplicabile(Logger log, String idTransazione, IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx) throws PolicyException,PolicyNotFoundException{
		
		PolicyConnessioneRuntime resource = null;
		DatiCollezionati datiCollezionatiReaded = null;
		try {
			resource = getConnessione(this.state, this.dominio, "updateDatiStartRequestApplicabile", idTransazione, ctx);
			
			//checkMap(resource.con);
			
			// mi salvo fuori dal synchronized l'attuale stato
			datiCollezionatiReaded = _updateDatiStartRequestApplicabile(resource.con, log, idTransazione, datiGroupBy, ctx);
			
		}finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "updateDatiStartRequestApplicabile", ctx);
			}
		}
		
		// Tutti i restanti controlli sono effettuati usando il valore di datiCollezionatiReaded, che e' gia' stato modificato
		// Inoltre e' stato re-inserito nella map come oggetto nuovo, quindi il valore dentro il metodo non subira' trasformazioni (essendo stato fatto il deserialize da db)
		// E' possibile procedere con l'analisi rispetto al valore che possiedono il counter dentro questo scope.
		
		return datiCollezionatiReaded;

	}
	
	@Override
	public void registerStopRequest(Logger log, String idTransazione,IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx, 
			MisurazioniTransazione dati, boolean isApplicabile, boolean isViolata) throws PolicyException,PolicyNotFoundException{
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "registerStopRequest", idTransazione, ctx);
			
			//checkMap(resource.con);
			
			_registerStopRequest(resource.con, log, idTransazione,datiGroupBy, ctx,
					dati, isApplicabile, isViolata);
			
		}finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "registerStopRequest", ctx);
			}
		}
		
	}

	
	@Override
	public long getActiveThreads(){
		return this.getActiveThreads(null);
	}
	@Override
	public long getActiveThreads(IDUnivocoGroupByPolicy filtro){
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "getActiveThreads", this.idTransazione, null);
			
			//checkMap(resource.con);
			
			return _getActiveThreads(resource.con, filtro);
			
		}
		catch(Throwable e) {
			this.log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "getActiveThreads", null);
			}
		}
		
	}
	
	@Override
	public String printInfos(Logger log, String separatorGroups) throws UtilsException{
		
		PolicyConnessioneRuntime resource = null;
		try {
			resource = getConnessione(this.state, this.dominio, "printInfos", this.idTransazione, null);
			
			//checkMap(resource.con);
			
			return _printInfos(resource.con, log, separatorGroups);
			
		}
		catch(Throwable e) {
			this.log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		finally {
			if(resource!=null) {
				releaseConnessione(resource, this.dominio, "printInfos", null);
			}
		}
		
	}
	
	private PolicyConnessioneRuntime getConnessione(IState state,IDSoggetto dominio, String funzione, String idTransazione, Map<Object> ctx) throws PolicyException {
		
		DBManager dbManager = null;
		Resource r = null;
		String modulo = null;
		try {
			Connection con = null;
				
			if(state!=null) {
				if(state instanceof StateMessage) {
					StateMessage s = (StateMessage) state;
					if(s.getConnectionDB()!=null && !s.getConnectionDB().isClosed()) {
						con = s.getConnectionDB();
						if(con!=null) {
							PolicyConnessioneRuntime cr = new PolicyConnessioneRuntime();
							cr.con = con;
							return cr;
						}
					}
				}
			}
			
			if(dominio==null) {
				
				String protocolName = null;
				if(ctx!=null && ctx.containsKey(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME)) {
					protocolName = (String) ctx.getObject(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME);
				}
				
				RequestInfo requestInfo = null;
				if(ctx!=null && ctx.containsKey(org.openspcoop2.core.constants.Costanti.REQUEST_INFO)) {
					requestInfo = (RequestInfo) ctx.getObject(org.openspcoop2.core.constants.Costanti.REQUEST_INFO);
				}
				
				dominio = OpenSPCoop2Properties.getInstance().getIdentitaPortaDefault(protocolName, requestInfo);
			}
			
			modulo = "RateLimitingActiveThreadsDB"+"."+funzione;
			dbManager = DBManager.getInstance();
			r = dbManager.getResource(dominio, modulo, idTransazione);
			if(r==null){
				throw new Exception("Risorsa al database non disponibile");
			}
			con = (Connection) r.getResource();
			if(con == null)
				throw new Exception("Connessione non disponibile");	
			
			PolicyConnessioneRuntime cr = new PolicyConnessioneRuntime();
			cr.con = con;
			cr.r = r;
			return cr;
		}catch(Throwable e) {
			if(r!=null) {
				try {
					dbManager.releaseResource(dominio, modulo, r);
				}catch(Throwable eClose) {
					// ignore
				}
			}
			throw new PolicyException(e.getMessage(),e);
		}
		
	}
	
	private void releaseConnessione(PolicyConnessioneRuntime p, IDSoggetto dominio, String funzione, Map<Object> ctx) {
		if(p.r!=null) {
			if(dominio==null) {
				String protocolName = null;
				if(ctx!=null && ctx.containsKey(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME)) {
					protocolName = (String) ctx.getObject(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME);
				}				
				
				RequestInfo requestInfo = null;
				if(ctx!=null && ctx.containsKey(org.openspcoop2.core.constants.Costanti.REQUEST_INFO)) {
					requestInfo = (RequestInfo) ctx.getObject(org.openspcoop2.core.constants.Costanti.REQUEST_INFO);
				}
				
				dominio = OpenSPCoop2Properties.getInstance().getIdentitaPortaDefault(protocolName, requestInfo);
			}
			DBManager.getInstance().releaseResource(dominio, "RateLimitingActiveThreadsDB"+"."+funzione, p.r);
		}
	}
	
	private transient org.openspcoop2.utils.Semaphore _lock_checkMap = null;
	private synchronized void initLockCheckMap() {
		if(this._lock_checkMap==null) {
			this._lock_checkMap = new org.openspcoop2.utils.Semaphore("PolicyGroupByActiveThreadsDB_checkMap"); 
		}
	}
	public org.openspcoop2.utils.Semaphore getLockCheckMap(){
		if(this._lock_checkMap==null) {
			initLockCheckMap();
		}
		return this._lock_checkMap;
	}
	
	private void checkMap(Connection con) throws PolicyException {
		if(!this.mapExists) {
			_checkMap(con);
		}
	}
	private void _checkMap(Connection con) throws PolicyException {
		
		this.getLockCheckMap().acquireThrowRuntime("checkMap");
		try {

			if(!this.mapExists) {
			
				PreparedStatement pstmt = null;
				ResultSet rs = null;
				try {
				
					ISQLQueryObject sqlGet = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
					sqlGet.addSelectField(CT_MAP_COLUMN_KEY);
					sqlGet.addFromTable(CT_MAP_TABLE);
					sqlGet.setANDLogicOperator(true);
					sqlGet.addWhereCondition(CT_MAP_COLUMN_KEY+"=?");
					String query = sqlGet.createSQLQuery();
					if(this.debug) {
						this.logSql.debug("[checkMap"+""+this.idTransazione+"] execute "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
					}
					pstmt = con.prepareStatement(query);
					pstmt.setString(1, this.uniqueIdMap_idActivePolicy);
					rs = pstmt.executeQuery();
					if(rs == null) {
						pstmt.close();
						throw new UtilsException("CheckMap failed: ResultSet is null?");		
					}
					boolean exist = rs.next();
					if(this.debug) {
						this.logSql.debug("[checkMap"+""+this.idTransazione+"] executed (result:"+exist+") "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
					}
					rs.close();
					pstmt.close();
					
					if(!exist) {
						
						try{
							if(con.isClosed()){
								throw new UtilsException("Connessione risulta già chiusa");
							}
						}catch(Exception e){
							throw new UtilsException("CheckMap failed: connection closed; "+e.getMessage(),e);
						}

						boolean originalConnectionAutocommit = false;
						boolean autoCommitModificato = false;
						try{
							originalConnectionAutocommit = con.getAutoCommit();
						}catch(Exception e){
							throw new UtilsException("CheckMap failed: autocommit mode disabled; "+e.getMessage(),e); 
						}
						if(originalConnectionAutocommit==false){
							throw new UtilsException("CheckMap failed: autocommit mode disabled; non e' possibile fornire una connessione con autocommit disabilitato poiche' l'utility ha necessita' di effettuare operazioni di commit/rollback)");		
						}
						
						int originalConnectionTransactionIsolation = -1;
						boolean transactionIsolationModificato = false;
						try{
							originalConnectionTransactionIsolation = con.getTransactionIsolation();
						}catch(Exception e){
							throw new UtilsException("Lettura livello di isolamento transazione della Connessione non riuscito: "+e.getMessage(),e); 
						}
						
						try {
							
							try{				

								//System.out.println("SET TRANSACTION SERIALIZABLE ("+conDB.getTransactionIsolation()+","+conDB.getAutoCommit()+")");
								// Il rollback, non servirebbe, pero le WrappedConnection di JBoss hanno un bug, per cui alcune risorse non vengono rilasciate.
								// Con il rollback tali risorse vengono rilasciate, e poi effettivamente la ConnectionSottostante emette una eccezione.
								try{
									con.rollback();
								}catch(Exception e){
									//System.out.println("ROLLBACK ERROR: "+e.getMessage());
								}
								
								JDBCUtilities.setTransactionIsolationSerializable(this.tipoDatabase, con);
								transactionIsolationModificato = true;
																
								if(originalConnectionAutocommit){
									con.setAutoCommit(false);
									autoCommitModificato = true;
								}
								
							} catch(Exception er) {
								throw new UtilsException("CheckMap failed: setting connection error; "+er.getMessage(),er);		
							}
							
							long scadenzaWhile = DateManager.getTimeMillis()
									+ this.attesaAttivaJDBC;
							boolean transactionInsertFinished = false;
							int iteration = 0;
							
							while(transactionInsertFinished==false && DateManager.getTimeMillis() < scadenzaWhile){
								
								try {
									sqlGet = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
									sqlGet.addSelectField(CT_MAP_COLUMN_KEY);
									sqlGet.addFromTable(CT_MAP_TABLE);
									sqlGet.setANDLogicOperator(true);
									sqlGet.addWhereCondition(CT_MAP_COLUMN_KEY+"=?");
									sqlGet.setSelectForUpdate(true);
									
									query = sqlGet.createSQLQuery();
									if(this.debug) {
										this.logSql.debug("[checkMap"+""+this.idTransazione+"] (forUpdate) execute "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
									}
									pstmt = con.prepareStatement(query);
									pstmt.setString(1, this.uniqueIdMap_idActivePolicy);
									rs = pstmt.executeQuery();
									if(rs == null) {
										pstmt.close();
										throw new UtilsException("CheckMap failed: ResultSet is null?");		
									}
									exist = rs.next();
									if(this.debug) {
										this.logSql.debug("[checkMap"+""+this.idTransazione+"] (forUpdate) executed (result:"+exist+") "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
									}
									//System.out.println("@check SELECT result:"+exist);
									rs.close();
									pstmt.close();
									
									if(!exist) {
										
										java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> mapActiveThreads = new HashMap<IDUnivocoGroupByPolicy, DatiCollezionati>();
										ByteArrayOutputStream bout = new ByteArrayOutputStream();
										this.getJavaSerializer().writeObject(mapActiveThreads, bout);
										bout.flush();
										bout.close();
										
										StringBuilder queryInsert = new StringBuilder();
										ISQLQueryObject sqlInsert = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
										sqlInsert.addInsertTable(CT_MAP_TABLE);
										sqlInsert.addInsertField(CT_MAP_COLUMN_KEY, "?");
										sqlInsert.addInsertField(CT_MAP_COLUMN_UPDATE_TIME, "?");
										sqlInsert.addInsertField(CT_MAP_COLUMN_VALUE, "?");
										queryInsert.append(sqlInsert.createSQLInsert());
										//System.out.println("INSERT ["+queryInsert.toString()+"]");
										
										if(this.debug) {
											this.logSql.debug("[checkMap"+""+this.idTransazione+"] execute "+DBUtils.formatSQLString(queryInsert.toString(), 
													this.uniqueIdMap_idActivePolicy, 
													DateUtils.getSimpleDateFormatMs().format(this.uniqueIdMap_updateTime), 
													("blob-size:"+bout.size())));
										}
										
										pstmt = con.prepareStatement(queryInsert.toString());
										pstmt.setString(1, this.uniqueIdMap_idActivePolicy);
										Timestamp t = new Timestamp(this.uniqueIdMap_updateTime.getTime());
										pstmt.setTimestamp(2, t);
										this.jdbcAdapter.setBinaryData(pstmt, 3, bout.toByteArray());
										int rows = pstmt.executeUpdate();
										pstmt.close();
										
										if(this.debug) {
											this.logSql.debug("[checkMap"+""+this.idTransazione+"] executed (rows:"+rows+") "+DBUtils.formatSQLString(queryInsert.toString(), 
													this.uniqueIdMap_idActivePolicy, 
													DateUtils.getSimpleDateFormatMs().format(this.uniqueIdMap_updateTime), 
													("blob-size:"+bout.size())));
										}
										
										//System.out.println("@check INSERT rows:"+rows);
										
									}
									
									// Chiusura Transazione
									con.commit();
	
									// ID Costruito
									transactionInsertFinished = true;
									
								} catch(Throwable e) {
									if(this.debug) {
										this.logSql.debug("Transaction error: "+e.getMessage(),e);
									}
									//System.out.println("ERRORE INSERT ("+iteration+"): "+e.getMessage());
									//log.info("ERROR GET SERIAL SQL ["+e.getMessage()+"]");
									try{
										if( rs != null )
											rs.close();
									} catch(Exception er) {
										// close
									}
									try{
										if( pstmt != null )
											pstmt.close();
									} catch(Exception er) {
										// close
									}
									try{
										con.rollback();
									} catch(Exception er) {
										// ignore
									}
								}

								if(transactionInsertFinished == false){
									// Per aiutare ad evitare conflitti
									try{
										int sleep = (getRandom()).nextInt(this.checkIntervalloJDBC);
										//System.out.println("Sleep: "+sleep);
										Utilities.sleep(sleep); // random
									}catch(Exception eRandom){
										// ignore
									}
								}
								
								iteration++;
								
							}
							
							if(!transactionInsertFinished) {
								throw new Exception("Check non riuscito dopo '"+iteration+"' tentativi");
							}
							
						}
						finally{

							// Ripristino Transazione
							try{
								if(transactionIsolationModificato){
									con.setTransactionIsolation(originalConnectionTransactionIsolation);
								}
								if(autoCommitModificato){
									con.setAutoCommit(originalConnectionAutocommit);
								}
							} catch(Exception er) {
								//System.out.println("ERROR UNSET:"+er.getMessage());
								//throw new UtilsException("CheckMap failed: unsetting connection error; "+er.getMessage());
								if(this.log!=null) {
									this.log.error("CheckMap failed: unsetting connection error; "+er.getMessage(),er);
								}
							}
						}
						
					}
					else {
						
						this.mapExists = true;
						
					}
					
				}catch(Throwable e) {
					throw new PolicyException(e.getMessage(),e);
				}
				
				
				
			}
			
		}finally {
			this.getLockCheckMap().release("checkMap");
		}
	}
	
	private java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> _getMapActiveThreads(Connection con){
		try {
			return _updateMap(con, OperationType.getMapActiveThreads,
					this.log, this.idTransazione, null, null,
					null, false, false,
					null,
					null).map;
		}catch(Throwable e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	@SuppressWarnings("unused")
	private void _initMap(Connection con, java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> map) {
		try {
			if(map!=null && map.size()>0){
				_updateMap(con, OperationType.initMap,
						this.log, this.idTransazione, null, null,
						null, false, false,
						null,
						map);
			}
		}catch(Throwable e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	private DatiCollezionati _registerStartRequest(Connection con,Logger log, String idTransazione, IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx) throws PolicyException {
		try {
			return _updateMap(con, OperationType.registerStartRequest,
					log, idTransazione, datiGroupBy, ctx,
					null, false, false,
					null,
					null).datiCollezionatiReaded;
		}catch(PolicyNotFoundException e) {
			throw new PolicyException(e.getMessage(),e);
		}
	}
	private DatiCollezionati _updateDatiStartRequestApplicabile(Connection con,Logger log, String idTransazione, IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx) throws PolicyException,PolicyNotFoundException{
		return _updateMap(con, OperationType.updateDatiStartRequestApplicabile,
				log, idTransazione, datiGroupBy, ctx,
				null, false, false,
				null,
				null).datiCollezionatiReaded;
	}
	private void _registerStopRequest(Connection con,Logger log, String idTransazione,IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx, 
			MisurazioniTransazione dati, boolean isApplicabile, boolean isViolata) throws PolicyException,PolicyNotFoundException{
		_updateMap(con, OperationType.registerStopRequest,
				log, idTransazione, datiGroupBy, ctx,
				dati, isApplicabile, isViolata,
				null,
				null);
	}
	private void _resetCounters(Connection con){
		try {
			_updateMap(con, OperationType.resetCounters,
					this.log, this.idTransazione, null, null,
					null, false, false,
					null,
					null);
		}catch(Throwable e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	private void _remove(Connection con){
		try {
			_updateMap(con, OperationType.remove,
					this.log, this.idTransazione, null, null,
					null, false, false,
					null,
					null);
		}catch(Throwable e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	private long _getActiveThreads(Connection con,IDUnivocoGroupByPolicy filtro){
		try {
			return _updateMap(con, OperationType.getActiveThreads,
					this.log, this.idTransazione, filtro, null,
					null, false, false,
					null,
					null).counter;
		}catch(Throwable e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	private String _printInfos(Connection con,Logger log, String separatorGroups) throws UtilsException{
		try {
			return _updateMap(con, OperationType.printInfos,
					log, this.idTransazione, null, null,
					null, false, false,
					separatorGroups,
					null).info;
		}catch(Exception e) {
			throw new UtilsException(e.getMessage(),e);
		}
	}
	@SuppressWarnings({ "unchecked"})
	private UpdateResult _updateMap(Connection con, OperationType opType,
			Logger log, String idTransazione, IDUnivocoGroupByPolicy datiGroupBy, Map<Object> ctx,
			MisurazioniTransazione dati, boolean isApplicabile, boolean isViolata,
			String separatorGroups,
			java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> map) throws PolicyException, PolicyNotFoundException {
			
		UpdateResult updateResult = new UpdateResult();
		
		try{
			if(con.isClosed()){
				throw new UtilsException("Connessione risulta già chiusa");
			}
		}catch(Exception e){
			throw new PolicyException("CheckMap failed: connection closed; "+e.getMessage(),e);
		}

		boolean originalConnectionAutocommit = false;
		boolean autoCommitModificato = false;
		try{
			originalConnectionAutocommit = con.getAutoCommit();
		}catch(Exception e){
			throw new PolicyException("CheckMap failed: autocommit mode disabled; "+e.getMessage(),e); 
		}
		if(originalConnectionAutocommit==false){
			throw new PolicyException("CheckMap failed: autocommit mode disabled; non e' possibile fornire una connessione con autocommit disabilitato poiche' l'utility ha necessita' di effettuare operazioni di commit/rollback)");		
		}
				
		try {
			
			if(this.transactionMode) {
				try{				
	
					//System.out.println("SET TRANSACTION SERIALIZABLE ("+conDB.getTransactionIsolation()+","+conDB.getAutoCommit()+")");
					// Il rollback, non servirebbe, pero le WrappedConnection di JBoss hanno un bug, per cui alcune risorse non vengono rilasciate.
					// Con il rollback tali risorse vengono rilasciate, e poi effettivamente la ConnectionSottostante emette una eccezione.
					try{
						con.rollback();
					}catch(Exception e){
						//System.out.println("ROLLBACK ERROR: "+e.getMessage());
					}
								
					if(originalConnectionAutocommit){
						con.setAutoCommit(false);
						autoCommitModificato = true;
					}
					
				} catch(Exception er) {
					throw new PolicyException("CheckMap failed: setting connection error; "+er.getMessage(),er);		
				}
			}
			
			long scadenzaWhile = DateManager.getTimeMillis()
					+ this.attesaAttivaJDBC;
			boolean transactionUpdateFinished = false;
			
			int iteration = 0;
			
			PolicyNotFoundException policyNotFoundException = null;
			PolicyException policyException = null;
			
			while(transactionUpdateFinished==false && DateManager.getTimeMillis() < scadenzaWhile){
				
				try {			
					ISQLQueryObject sqlGet = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
					sqlGet.addSelectField(CT_MAP_COLUMN_VALUE);
					sqlGet.addSelectField(CT_MAP_COLUMN_UPDATE_TIME);
					sqlGet.addFromTable(CT_MAP_TABLE);
					sqlGet.setANDLogicOperator(true);
					sqlGet.addWhereCondition(CT_MAP_COLUMN_KEY+"=?");
					if(this.transactionMode) {
						sqlGet.setSelectForUpdate(true);
					}
					
					ResultSet rsMap = null;
					java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> mapActiveThreads = null;
					boolean updateDate = false;
					String query = sqlGet.createSQLQuery();
					if(this.debug) {
						this.logSql.debug("[updateMap"+""+this.idTransazione+"] (forUpdate) execute "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
					}
					try (PreparedStatement pstmtMap = con.prepareStatement(query)){
						pstmtMap.setString(1, this.uniqueIdMap_idActivePolicy);
						try{
							rsMap = pstmtMap.executeQuery();
							if(rsMap == null) {
								throw new UtilsException("CheckMap failed: ResultSet is null?");		
							}
							boolean exist = rsMap.next();
							if(this.debug) {
								this.logSql.debug("[updateMap"+""+this.idTransazione+"] (forUpdate) executed (result:"+exist+") "+DBUtils.formatSQLString(query, this.uniqueIdMap_idActivePolicy));
							}
							//System.out.println("@update SELECT ["+opType+"] result:"+exist);
							
							if(!exist) {
								if(!OperationType.getMapActiveThreads.equals(opType) &&
										!OperationType.resetCounters.equals(opType)  &&
										!OperationType.remove.equals(opType) &&
										!OperationType.getActiveThreads.equals(opType)  &&
										!OperationType.printInfos.equals(opType)) {
									throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' not found ?");
								}
							}
							else {
								Timestamp tCheck = rsMap.getTimestamp(CT_MAP_COLUMN_UPDATE_TIME);
								if(this.uniqueIdMap_updateTime.equals(tCheck)) {
									try(InputStream is = this.jdbcAdapter.getBinaryStream(rsMap, CT_MAP_COLUMN_VALUE);){
										mapActiveThreads = (java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati>) this.getJavaDeserializer().readObject(is, java.util.Map.class);
									}
								}
								else {
									// data aggiornata
									mapActiveThreads = new HashMap<IDUnivocoGroupByPolicy, DatiCollezionati>();
									updateDate = true;
								}
								if(mapActiveThreads == null) {
									if(!OperationType.getMapActiveThreads.equals(opType) &&
											!OperationType.resetCounters.equals(opType)  &&
											!OperationType.getActiveThreads.equals(opType)  &&
											!OperationType.printInfos.equals(opType)) {
										throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' null ?");
									}
								}
							}
						}finally {
							try{
								if( rsMap != null ) {
									rsMap.close();
								}
							} catch(Throwable er) {
								// close rs
							}
						}
					} // close pstmt
					
					
					DatiCollezionati datiCollezionati = null;
					boolean deleteMap = false;
					boolean updateMap = false;
					switch (opType) {
					
					case getMapActiveThreads:
						
						updateResult.map = mapActiveThreads;
						break;
					
					case initMap:
						
						if(map!=null && map.size()>0){
							
							if(mapActiveThreads == null) {
								throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' null ?");
							}
							
							mapActiveThreads.putAll(map);
							updateMap = true;
						}
						
						break;
						
					case registerStartRequest:
						
						if(mapActiveThreads == null) {
							throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' null ?");
						}
						
						try {
							if(mapActiveThreads.containsKey(datiGroupBy)){
								//System.out.println("<"+idTransazione+">registerStartRequest CHECK CONTAINS ["+datiGroupBy+"]=true");
								datiCollezionati = mapActiveThreads.get(datiGroupBy);	
							}
							else{
								//System.out.println("<"+idTransazione+">registerStartRequest CHECK CONTAINS ["+datiGroupBy+"]=false");
								Date gestorePolicyConfigDate = PolicyDateUtils.readGestorePolicyConfigDateIntoContext(ctx);
								datiCollezionati = new DatiCollezionati(this.activePolicy.getInstanceConfiguration().getUpdateTime(), gestorePolicyConfigDate);
								//System.out.println("<"+idTransazione+">registerStartRequest PUT");
								mapActiveThreads.put(datiGroupBy, datiCollezionati); // registro nuova immagine
							}
							
							// incremento il numero di thread
							//System.out.println("<"+idTransazione+">registerStartRequest in datiCollezionati ...");
							datiCollezionati.registerStartRequest(log, this.activePolicy, ctx);
							//System.out.println("<"+idTransazione+">registerStartRequest in datiCollezionati ok: "+datiCollezionati.getActiveRequestCounter());
							
							updateMap = true;
							
							// mi salvo fuori dal synchronized l'attuale stato
							updateResult.datiCollezionatiReaded = (DatiCollezionati) datiCollezionati.newInstance(); 
							
						}catch(Exception e) {
							policyException = new PolicyException(e.getMessage(),e);
							transactionUpdateFinished = true;
						}
												
						break;

					case updateDatiStartRequestApplicabile:
						
						if(mapActiveThreads == null) {
							throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' null ?");
						}
						
						try {
							if(mapActiveThreads.containsKey(datiGroupBy)==false){
								//System.out.println("<"+idTransazione+">updateDatiStartRequestApplicabile Non sono presenti alcun threads registrati per la richiesta con dati identificativi ["+datiGroupBy.toString()+"]");
								policyNotFoundException = new PolicyNotFoundException("Non sono presenti alcun threads registrati per la richiesta con dati identificativi ["+datiGroupBy.toString()+"]");
								transactionUpdateFinished = true;
							}
							else{
								datiCollezionati = mapActiveThreads.get(datiGroupBy);	
							
								// incremento il numero dei contatori
								//System.out.println("<"+idTransazione+">updateDatiStartRequestApplicabile updateDatiStartRequestApplicabile ...");
								boolean updated = datiCollezionati.updateDatiStartRequestApplicabile(log, this.activePolicy, ctx);
								//System.out.println("<"+idTransazione+">updateDatiStartRequestApplicabile updateDatiStartRequestApplicabile ok");
														
								if(updated) {
									
									updateMap = true;
									
									// mi salvo fuori dal synchronized l'attuale stato
									updateResult.datiCollezionatiReaded = (DatiCollezionati) datiCollezionati.newInstance();
								}
							}
						}
						catch(Exception e) {
							policyException = new PolicyException(e.getMessage(),e);
							transactionUpdateFinished = true;
						}
						
						break;
						
					case registerStopRequest:
						
						if(mapActiveThreads == null) {
							throw new Exception("Map with id '"+this.uniqueIdMap_idActivePolicy+"' null ?");
						}
						
						try {
							if(mapActiveThreads.containsKey(datiGroupBy)==false){
								//System.out.println("<"+idTransazione+">registerStopRequest Non sono presenti alcun threads registrati per la richiesta con dati identificativi ["+datiGroupBy.toString()+"]");
								policyNotFoundException = new PolicyNotFoundException("Non sono presenti alcun threads registrati per la richiesta con dati identificativi ["+datiGroupBy.toString()+"]");
								transactionUpdateFinished = true;
							}
							else{
								//System.out.println("<"+idTransazione+">registerStopRequest get ...");
								datiCollezionati = mapActiveThreads.get(datiGroupBy);	
								//System.out.println("<"+idTransazione+">registerStopRequest registerEndRequest ...");
								datiCollezionati.registerEndRequest(log, this.activePolicy, ctx, dati);
								//System.out.println("<"+idTransazione+">registerStopRequest registerEndRequest ok");
								if(isApplicabile){
									//System.out.println("<"+idTransazione+">registerStopRequest updateDatiEndRequestApplicabile ...");
									EsitiProperties esitiProperties = EsitiProperties.getInstanceFromProtocolName(log,dati.getProtocollo());
									List<Integer> esitiCodeOk = esitiProperties.getEsitiCodeOk_senzaFaultApplicativo();
									List<Integer> esitiCodeKo_senzaFaultApplicativo = esitiProperties.getEsitiCodeKo_senzaFaultApplicativo();
									List<Integer> esitiCodeFaultApplicativo = esitiProperties.getEsitiCodeFaultApplicativo();
									datiCollezionati.updateDatiEndRequestApplicabile(log, this.activePolicy, ctx, dati,
											esitiCodeOk,esitiCodeKo_senzaFaultApplicativo, esitiCodeFaultApplicativo, isViolata);
									//System.out.println("<"+idTransazione+">registerStopRequest updateDatiEndRequestApplicabile ok");
								}
								
								updateMap = true;
							}
						}catch(Exception e) {
							policyException = new PolicyException(e.getMessage(),e);
							transactionUpdateFinished = true;
						}
						
						break;
						
					case resetCounters:
						
						if(mapActiveThreads!=null && mapActiveThreads.size()>0){
							Iterator<DatiCollezionati> datiCollezionatiIter = mapActiveThreads.values().iterator();
							while (datiCollezionatiIter.hasNext()) {
								DatiCollezionati item = (DatiCollezionati) datiCollezionatiIter.next();
								item.resetCounters();
							}
							
							updateMap = true;
						}
						
						break;
						
						
					case remove:
						
						if(mapActiveThreads!=null) {
							deleteMap = true;
						}
						
						break;
						
					case getActiveThreads:
						
						try {
							long counter = 0l;
							
							if(mapActiveThreads!=null && !mapActiveThreads.isEmpty()) {
								for (IDUnivocoGroupByPolicy check : mapActiveThreads.keySet()) {
									
									if(datiGroupBy!=null){
										IDUnivocoGroupBy<IDUnivocoGroupByPolicy> idAstype = (IDUnivocoGroupBy<IDUnivocoGroupByPolicy>) check;
										if(!idAstype.match(datiGroupBy)){
											continue;
										}
									}
									
									counter = counter + mapActiveThreads.get(check).getActiveRequestCounter();
								}
							}
							
							// mi appoggio a questa struttura
							updateResult.counter = counter;
							
						}catch(Exception e) {
							policyException = new PolicyException(e.getMessage(),e);
							transactionUpdateFinished = true;
						}
						
						break;
						
					case printInfos:
						
						StringBuilder bf = new StringBuilder();
						if(mapActiveThreads!=null && !mapActiveThreads.isEmpty()) {
							for (IDUnivocoGroupByPolicy check : mapActiveThreads.keySet()) {
								bf.append(separatorGroups);
								bf.append("\n");
								bf.append(Costanti.LABEL_MODALITA_SINCRONIZZAZIONE).append(" ").append(this.tipoGestore.toLabel());
								bf.append("\n");
								bf.append("Criterio di Collezionamento dei Dati\n");
								bf.append(check.toString(true));
								bf.append("\n");
								mapActiveThreads.get(check).checkDate(log, this.activePolicy); // imposta correttamente gli intervalli
								bf.append(mapActiveThreads.get(check).toString());
								bf.append("\n");
							}
						}
						if(bf.length()<=0){
							bf.append("Nessuna informazione disponibile");
							updateResult.info = bf.toString();
						}
						else{
							updateResult.info = bf.toString()+separatorGroups;
						}
						
						break;
					}
					
					if(updateMap) {
					
						ByteArrayOutputStream bout = new ByteArrayOutputStream();
						this.getJavaSerializer().writeObject(mapActiveThreads, bout);
						bout.flush();
						bout.close();
						
						ISQLQueryObject sqlUpdate = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
						sqlUpdate.addUpdateTable(CT_MAP_TABLE);
						//sqlUpdate.addUpdateField(CT_MAP_COLUMN_KEY, "?");
						if(updateDate) {
							sqlUpdate.addUpdateField(CT_MAP_COLUMN_UPDATE_TIME, "?");
						}
						sqlUpdate.addUpdateField(CT_MAP_COLUMN_VALUE, "?");
						sqlUpdate.setANDLogicOperator(true);
						sqlUpdate.addWhereCondition(CT_MAP_COLUMN_KEY+"=?");
						String queryUpdate = sqlUpdate.createSQLUpdate();
						
						if(this.debug) {
							if(updateDate) {
								this.logSql.debug("[updateMap"+""+this.idTransazione+"] execute "+DBUtils.formatSQLString(queryUpdate,
										DateUtils.getSimpleDateFormatMs().format(this.uniqueIdMap_updateTime),
										("blob-size:"+bout.size()), 
										this.uniqueIdMap_idActivePolicy));
							}
							else {
								this.logSql.debug("[updateMap"+""+this.idTransazione+"] execute "+DBUtils.formatSQLString(queryUpdate,
										("blob-size:"+bout.size()), 
										this.uniqueIdMap_idActivePolicy));
							}
						}
						
						PreparedStatement pstmtUpdateMap = null;
						int rows = -1;
						try {
							//System.out.println("INSERT ["+queryInsert.toString()+"]");
							pstmtUpdateMap = con.prepareStatement(queryUpdate);
							int index = 1;
							if(updateDate) {
								Timestamp t = new Timestamp(this.uniqueIdMap_updateTime.getTime());
								pstmtUpdateMap.setTimestamp(index++, t);
							}
							this.jdbcAdapter.setBinaryData(pstmtUpdateMap, index++, bout.toByteArray());
							pstmtUpdateMap.setString(index++, this.uniqueIdMap_idActivePolicy);
							rows = pstmtUpdateMap.executeUpdate();
						}
						finally {
							try {
								if(pstmtUpdateMap!=null) {
									pstmtUpdateMap.close();
								}
							}catch(Throwable t) {
								// ignore
							}
						}
						
						if(this.debug) {
							if(updateDate) {
								this.logSql.debug("[updateMap"+""+this.idTransazione+"] executed (rows:"+rows+") "+DBUtils.formatSQLString(queryUpdate,
										DateUtils.getSimpleDateFormatMs().format(this.uniqueIdMap_updateTime),
										("blob-size:"+bout.size()), 
										this.uniqueIdMap_idActivePolicy));
							}
							else {
								this.logSql.debug("[updateMap"+""+this.idTransazione+"] executed (rows:"+rows+") "+DBUtils.formatSQLString(queryUpdate,
										("blob-size:"+bout.size()), 
										this.uniqueIdMap_idActivePolicy));
							}
						}
						
						//System.out.println("@update UPDATE ["+opType+"]: "+rows);
					}
					
					if(deleteMap) {
						
						ISQLQueryObject sqlDelete = SQLObjectFactory.createSQLQueryObject(this.tipoDatabase);
						sqlDelete.addDeleteTable(CT_MAP_TABLE);
						sqlDelete.setANDLogicOperator(true);
						sqlDelete.addWhereCondition(CT_MAP_COLUMN_KEY+"=?");
						String queryDelete = sqlDelete.createSQLDelete();
						
						if(this.debug) {
							this.logSql.debug("[deleteMap"+""+this.idTransazione+"] execute "+DBUtils.formatSQLString(queryDelete,
										this.uniqueIdMap_idActivePolicy));
						}
						
						//System.out.println("INSERT ["+queryInsert.toString()+"]");
						PreparedStatement pstmtDeleteMap = null;
						int rows = -1;
						try {
							pstmtDeleteMap = con.prepareStatement(queryDelete);
							int index = 1;
							pstmtDeleteMap.setString(index++, this.uniqueIdMap_idActivePolicy);
							rows = pstmtDeleteMap.executeUpdate();
						}
						finally {
							try {
								if(pstmtDeleteMap!=null) {
									pstmtDeleteMap.close();
								}
							}catch(Throwable t) {
								// ignore
							}
						}
						
						if(this.debug) {
							this.logSql.debug("[updateMap"+""+this.idTransazione+"] executed (rows:"+rows+") "+DBUtils.formatSQLString(queryDelete,
										this.uniqueIdMap_idActivePolicy));
						}
						
					}
					
					if(this.transactionMode) {
						// Chiusura Transazione
						con.commit();
					}
					
					// ID Costruito
					transactionUpdateFinished = true;
				} catch(Throwable e) {
					//System.out.println("ERRORE UPDATE ("+iteration+"): "+e.getMessage());
					//log.info("ERROR GET SERIAL SQL ["+e.getMessage()+"]",e);
					if(this.transactionMode) {
						try{
							con.rollback();
						} catch(Exception er) {
							// ignore
						}
					}
					else {
						throw new PolicyException("Operazione non riuscita: "+e.getMessage(),e);
					}
				}

				if(transactionUpdateFinished == false){
					if(this.transactionMode) {
						// Per aiutare ad evitare conflitti
						try{
							int sleep = (getRandom()).nextInt(this.checkIntervalloJDBC);
							//System.out.println("Sleep: "+sleep);
							Utilities.sleep(sleep); // random
						}catch(Exception eRandom){
							// ignore
						}
					}
					else {
						throw new PolicyException("Operazione non riuscita");
					}
				}
				
				iteration++;
				
				if(policyNotFoundException!=null) {
					throw policyNotFoundException;
				}
				if(policyException!=null) {
					throw policyException;
				}
				
			}
			
			if(!transactionUpdateFinished) {
				throw new PolicyException("Operazione non riuscita dopo '"+iteration+"' tentativi");
			}
			
		}
		finally{

			// Ripristino Transazione
			if(this.transactionMode) {
				try{
					if(autoCommitModificato){
						con.setAutoCommit(originalConnectionAutocommit);
					}
				} catch(Exception er) {
					//System.out.println("ERROR UNSET:"+er.getMessage());
					//throw new PolicyException("CheckMap failed: unsetting connection error; "+er.getMessage());
					if(this.log!=null) {
						this.log.error("CheckMap failed: unsetting connection error; "+er.getMessage(),er);
					}
				}
			}
		}

		return updateResult;
		
	}
}
	
class PolicyConnessioneRuntime{
	
	Resource r = null;
	Connection con = null;
	
}

class UpdateResult{
	
	DatiCollezionati datiCollezionatiReaded;
	long counter;
	String info;
	java.util.Map<IDUnivocoGroupByPolicy, DatiCollezionati> map;
	
}

enum OperationType {
	
	registerStartRequest,
	updateDatiStartRequestApplicabile,
	registerStopRequest,
	getActiveThreads,
	printInfos,
	resetCounters,
	remove,
	getMapActiveThreads,
	initMap
	
}