GestoreConsegnaMultipla.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.transazioni;

import java.security.SecureRandom;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

import org.openspcoop2.core.commons.CoreException;
import org.openspcoop2.core.commons.dao.DAOFactory;
import org.openspcoop2.core.commons.dao.DAOFactoryProperties;
import org.openspcoop2.core.config.OpenspcoopAppender;
import org.openspcoop2.core.config.PortaApplicativa;
import org.openspcoop2.core.config.Property;
import org.openspcoop2.core.config.Tracciamento;
import org.openspcoop2.core.config.utils.OpenSPCoopAppenderUtilities;
import org.openspcoop2.core.id.IDPortaApplicativa;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.core.transazioni.TransazioneApplicativoServer;
import org.openspcoop2.core.transazioni.dao.ITransazioneApplicativoServerService;
import org.openspcoop2.core.transazioni.utils.TransactionServerUtils;
import org.openspcoop2.generic_project.utils.ServiceManagerProperties;
import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
import org.openspcoop2.pdd.config.DBConsegneMessageBoxManager;
import org.openspcoop2.pdd.config.DBConsegnePreseInCaricoManager;
import org.openspcoop2.pdd.config.DBTransazioniManager;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.config.Resource;
import org.openspcoop2.pdd.core.handlers.transazioni.ExceptionSerialzerFileSystem;
import org.openspcoop2.pdd.core.state.IOpenSPCoopState;
import org.openspcoop2.pdd.core.state.OpenSPCoopState;
import org.openspcoop2.pdd.core.state.OpenSPCoopStateDBManager;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.protocol.sdk.Context;
import org.openspcoop2.protocol.sdk.ProtocolException;
import org.openspcoop2.protocol.sdk.constants.EsitoTransazioneName;
import org.openspcoop2.protocol.sdk.diagnostica.IDiagnosticProducer;
import org.openspcoop2.protocol.sdk.dump.IDumpProducer;
import org.openspcoop2.protocol.sdk.dump.Messaggio;
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.EsitiConfigUtils;
import org.openspcoop2.protocol.utils.EsitiProperties;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.jdbc.JDBCUtilities;
import org.slf4j.Logger;

/**     
 * GestoreConsegnaMultipla
 *
 * @author Poli Andrea (poli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class GestoreConsegnaMultipla {

	private static final String ID_MODULO = "GestoreConsegnaMultipla";

	private static DAOFactory daoFactory = null;
	private static DAOFactory daoFactoryDevNull = null;
	private static ServiceManagerProperties daoFactoryServiceManagerPropertiesTransazioni = null;
	private static ServiceManagerProperties daoFactoryDevNullServiceManagerPropertiesTransazioni = null;
	/**private static Logger daoFactoryLoggerTransazioni = null;*/
	private static Logger daoFactoryLoggerTransazioniSql = null;
	private static Logger daoFactoryLoggerTransazioniDevNull = null;

	private static OpenSPCoop2Properties openspcoopProperties = null;

	private static GestoreConsegnaMultipla gestoreConsegnaMultipla = null;
	private static synchronized void init() throws TransactionMultiDeliverException{
		if(gestoreConsegnaMultipla==null){
			String tipoDatabase = null;
			boolean debug = false;
			Logger daoFactoryLoggerTransazioni = null;
			try{
				openspcoopProperties = OpenSPCoop2Properties.getInstance();

				tipoDatabase = openspcoopProperties.getDatabaseType();
				/**System.out.println("DS["+this.datasource+"] TIPODB["+this.tipoDatabase+"]");*/

				if(tipoDatabase==null){
					throw new TransactionMultiDeliverException("Tipo Database non definito");
				}

				openspcoopProperties = OpenSPCoop2Properties.getInstance();

				debug = openspcoopProperties.isTransazioniDebug();

				daoFactoryLoggerTransazioni = OpenSPCoop2Logger.getLoggerOpenSPCoopTransazioni(debug);
				daoFactoryLoggerTransazioniSql = OpenSPCoop2Logger.getLoggerOpenSPCoopTransazioniSql(debug);
				daoFactoryLoggerTransazioniDevNull = OpenSPCoop2Logger.getLoggerOpenSPCoopTransazioniDevNull();
				
				daoFactory = DAOFactory.getInstance(daoFactoryLoggerTransazioniSql);
				DAOFactoryProperties daoFactoryProperties = DAOFactoryProperties.getInstance(daoFactoryLoggerTransazioniSql);
				daoFactoryServiceManagerPropertiesTransazioni = daoFactoryProperties.getServiceManagerProperties(org.openspcoop2.core.transazioni.utils.ProjectInfo.getInstance());
				daoFactoryServiceManagerPropertiesTransazioni.setShowSql(debug);	
				daoFactoryServiceManagerPropertiesTransazioni.setDatabaseType(DBTransazioniManager.getInstance().getTipoDatabase());
				
				daoFactoryDevNull = DAOFactory.getInstance(daoFactoryLoggerTransazioniDevNull);
				DAOFactoryProperties daoFactoryPropertiesDevNull = DAOFactoryProperties.getInstance(daoFactoryLoggerTransazioniDevNull);
				daoFactoryDevNullServiceManagerPropertiesTransazioni = daoFactoryPropertiesDevNull.getServiceManagerProperties(org.openspcoop2.core.transazioni.utils.ProjectInfo.getInstance());
				daoFactoryDevNullServiceManagerPropertiesTransazioni.setShowSql(debug);	
				daoFactoryDevNullServiceManagerPropertiesTransazioni.setDatabaseType(DBTransazioniManager.getInstance().getTipoDatabase());
				

			}catch(Exception e){
				throw new TransactionMultiDeliverException("Inizializzazione risorse database non riuscita: "+e.getMessage(),e);
			}
			try{
				gestoreConsegnaMultipla = new GestoreConsegnaMultipla(daoFactoryLoggerTransazioni, daoFactoryLoggerTransazioniSql,
						tipoDatabase,
						debug);
			}catch(Exception e){
				throw new TransactionMultiDeliverException("Inizializzazione GestoreConsegnaMultipla non riuscita: "+e.getMessage(),e);
			}
		}
	}
	public static GestoreConsegnaMultipla getInstance() throws TransactionMultiDeliverException {
		if(gestoreConsegnaMultipla==null){
			// spotbugs warning 'SING_SINGLETON_GETTER_NOT_SYNCHRONIZED': l'istanza viene creata allo startup
			synchronized (GestoreConsegnaMultipla.class) {
				init();
			}
		}
		return gestoreConsegnaMultipla;
	}


	
	
	private static java.util.Random rndInstance = null;
	private static synchronized void initRandom() {
		if(rndInstance==null) {
			rndInstance = new SecureRandom();
		}
	}
	public static java.util.Random getRandom() {
		if(rndInstance==null) {
			initRandom();
		}
		return rndInstance;
	}
	




	private Logger log = null;
	private Logger logSql = null;
	private String tipoDatabase = null;
	private boolean debug = false;

	private IDiagnosticProducer msgDiagnosticiOpenSPCoopAppender = null;
	private IDumpProducer dumpOpenSPCoopAppender = null;
	private boolean transazioniRegistrazioneDumpHeadersCompactEnabled = false;

	private GestoreConsegnaMultipla(Logger log,Logger logSql,
			String tipoDatabase,boolean debug) throws TransactionMultiDeliverException{

		this.log = log;
		this.logSql = logSql;
		this.tipoDatabase = tipoDatabase;
		this.debug = debug;

		boolean usePdDConnection = true;

		try{

			// Init
			this.msgDiagnosticiOpenSPCoopAppender = new org.openspcoop2.pdd.logger.MsgDiagnosticoOpenSPCoopProtocolAppender();
			OpenspcoopAppender diagnosticoOpenSPCoopAppender = new OpenspcoopAppender();
			diagnosticoOpenSPCoopAppender.setTipo("__gestoreConsegnaMultipla");
			List<Property> diagnosticoOpenSPCoopAppenderProperties = new ArrayList<>();

			// Verra poi utilizzata la connessione ottenuta ogni volta che il timer viene eseguito, infatti si usa usePdDConnection
			OpenSPCoopAppenderUtilities.addParameters(this.logSql, diagnosticoOpenSPCoopAppenderProperties, 
					null, // nessun datasource
					null, null, null, null,  // nessuna connection
					this.tipoDatabase,
					usePdDConnection, // viene usata la connessione della PdD
					this.debug
					);
			OpenSPCoopAppenderUtilities.addCheckProperties(diagnosticoOpenSPCoopAppenderProperties, false);

			diagnosticoOpenSPCoopAppender.setPropertyList(diagnosticoOpenSPCoopAppenderProperties);
			this.msgDiagnosticiOpenSPCoopAppender.initializeAppender(diagnosticoOpenSPCoopAppender);
			this.msgDiagnosticiOpenSPCoopAppender.isAlive();

		}catch(Exception e){
			throw new TransactionMultiDeliverException("Errore durante l'inizializzazione del DiagnosticoAppender: "+e.getMessage(),e);
		} 

		try{

			// Init
			this.dumpOpenSPCoopAppender = new org.openspcoop2.pdd.logger.DumpOpenSPCoopProtocolAppender();
			OpenspcoopAppender dumpOpenSPCoopAppenderEngine = new OpenspcoopAppender();
			dumpOpenSPCoopAppenderEngine.setTipo("__gestoreConsegnaMultipla");
			List<Property> dumpOpenSPCoopAppenderProperties = new ArrayList<>();

			// Verra poi utilizzata la connessione ottenuta ogni volta che il timer viene eseguito, infatti si usa usePdDConnection
			OpenSPCoopAppenderUtilities.addParameters(this.logSql, dumpOpenSPCoopAppenderProperties, 
					null, // nessun datasource
					null, null, null, null,  // nessuna connection
					this.tipoDatabase,
					usePdDConnection, // viene usata la connessione della PdD
					this.debug
					);
			OpenSPCoopAppenderUtilities.addCheckProperties(dumpOpenSPCoopAppenderProperties, false);

			dumpOpenSPCoopAppenderEngine.setPropertyList(dumpOpenSPCoopAppenderProperties);
			this.dumpOpenSPCoopAppender.initializeAppender(dumpOpenSPCoopAppenderEngine);
			this.dumpOpenSPCoopAppender.isAlive();
			
			// Indicazioni sulle modalita' di salvataggio degli header del dump
			this.transazioniRegistrazioneDumpHeadersCompactEnabled = openspcoopProperties.isTransazioniRegistrazioneDumpHeadersCompactEnabled();

		}catch(Exception e){
			throw new TransactionMultiDeliverException("Errore durante l'inizializzazione del DumpAppender: "+e.getMessage(),e);
		} 

	}


	// *** SAFE ***

	public void safeCreate(TransazioneApplicativoServer transazioneApplicativoServer, IDPortaApplicativa idPA, IOpenSPCoopState state, RequestInfo requestInfo, Context context) {

		// cluster id
		transazioneApplicativoServer.setClusterIdPresaInCarico(openspcoopProperties.getClusterId(false));
		
		getConnectionAndSave(transazioneApplicativoServer, transazioneApplicativoServer.getProtocollo(), false, false, idPA, state, null, requestInfo, context, "create",
				OpenSPCoopStateDBManager.runtime);

	}
	
	public void safeUpdateConsegna(TransazioneApplicativoServer transazioneApplicativoServer, IDPortaApplicativa idPA, IOpenSPCoopState state, RequestInfo requestInfo, Context context) {

		// cluster id
		transazioneApplicativoServer.setClusterIdConsegna(openspcoopProperties.getClusterId(false));
		
		getConnectionAndSave(transazioneApplicativoServer, transazioneApplicativoServer.getProtocollo(), true, true, idPA, state, null, requestInfo, context, "updateDeliveredMessage",
				OpenSPCoopStateDBManager.consegnePreseInCarico); // l'informazione dovrebbe esistere!

	}
	
	public void safeUpdatePrelievoIM(TransazioneApplicativoServer transazioneApplicativoServer, IDPortaApplicativa idPA, IOpenSPCoopState state, Context context) {

		// cluster id
		transazioneApplicativoServer.setClusterIdPrelievoIm(openspcoopProperties.getClusterId(false));
		
		getConnectionAndSave(transazioneApplicativoServer, transazioneApplicativoServer.getProtocollo(), true, false, idPA, state, null, null, context, "updateRetrieveMessageByMessageBox",
				OpenSPCoopStateDBManager.messageBox);

	}
	
	public void safeUpdateEliminazioneIM(TransazioneApplicativoServer transazioneApplicativoServer, IDPortaApplicativa idPA, IOpenSPCoopState state, Context context) {

		// cluster id
		transazioneApplicativoServer.setClusterIdEliminazioneIm(openspcoopProperties.getClusterId(false));
		
		getConnectionAndSave(transazioneApplicativoServer, transazioneApplicativoServer.getProtocollo(), true, false, idPA, state, null, null, context, "updateDeletedMessageByMessageBox",
				OpenSPCoopStateDBManager.messageBox);

	}
	
	public void safeUpdateMessaggioScaduto(TransazioneApplicativoServer transazioneApplicativoServer, IDPortaApplicativa idPA, IOpenSPCoopState state) {

		getConnectionAndSave(transazioneApplicativoServer, transazioneApplicativoServer.getProtocollo(), true, false, idPA, state, null, null, null, "updateExpiredMessage",
				OpenSPCoopStateDBManager.runtime);

	}

	
	public void safeSave(org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico diagnostico, IDPortaApplicativa idPA, IState state, RequestInfo requestInfo, Context context) {

		getConnectionAndSave(diagnostico, diagnostico.getProtocollo(), false, false, idPA, null, state, requestInfo, context, "saveDiagnostic",
				OpenSPCoopStateDBManager.consegnePreseInCarico);

	}

	public void safeSave(Messaggio dumpMessaggio, IDPortaApplicativa idPA, IState state, RequestInfo requestInfo, Context context) {

		getConnectionAndSave(dumpMessaggio, dumpMessaggio.getProtocollo(), false, false, idPA, null, state, requestInfo, context, "saveContent",
				OpenSPCoopStateDBManager.consegnePreseInCarico);

	}

	private void getConnectionAndSave(Object o, String protocol, boolean update, boolean throwNotFoundIfNotExists, IDPortaApplicativa idPA, 
			IOpenSPCoopState openspcoopState, IState state, RequestInfo requestInfo, Context context,
			String tipoOperazione,
			OpenSPCoopStateDBManager dbManagerSource) {
		TransazioniSAProcessTimes times = null;
		long timeStart = -1;
		boolean buildDetailsSA = false;
		boolean buildDetailsUpdateTransaction = false;
		if(openspcoopProperties.isTransazioniRegistrazioneSlowLog()) {
			times = new TransazioniSAProcessTimes();
			timeStart = DateManager.getTimeMillis();
			buildDetailsSA = openspcoopProperties.isTransazioniRegistrazioneSlowLogConnettoriMultipliProcessTransactionSADetails();
			buildDetailsUpdateTransaction = openspcoopProperties.isTransazioniRegistrazioneSlowLogConnettoriMultipliUpdateTransactionDetails();
		}
		try {
			getConnectionAndSave(o, protocol, update, throwNotFoundIfNotExists, idPA, 
					openspcoopState, state, requestInfo, context,
					tipoOperazione, times, buildDetailsSA,  buildDetailsUpdateTransaction,
					dbManagerSource);
		}finally {
			if(times!=null) {
				long timeEnd =  DateManager.getTimeMillis();
				long timeProcess = timeEnd-timeStart;
				if(timeProcess>=openspcoopProperties.getTransazioniRegistrazioneSlowLogThresholdMs()) {
					StringBuilder sb = new StringBuilder();
					sb.append(timeProcess);
					if(times.idTransazione!=null) {
						sb.append(" <").append(times.idTransazione).append(">");
					}
					if(times.servizioApplicativoErogatore!=null) {
						sb.append(" (sa:").append(times.servizioApplicativoErogatore).append(")");
					}
					sb.append(" [GestoreConsegnaMultipla."+tipoOperazione+"]");
					sb.append(" ").append(times.toString());
					String msg = sb.toString();
					OpenSPCoop2Logger.getLoggerOpenSPCoopTransazioniSlowLog().info(msg);
				}
			}
		}
	}
	@SuppressWarnings("resource")
	private void getConnectionAndSave(Object o, String protocol, boolean update, boolean throwNotFoundIfNotExists, IDPortaApplicativa idPA, 
			IOpenSPCoopState openspcoopState, IState state, RequestInfo requestInfo, Context context,
			String tipoOperazione, TransazioniSAProcessTimes times, boolean buildDetailsSA, boolean buildDetailsUpdateTransaction,
			OpenSPCoopStateDBManager dbManagerSource) {

		EsitiProperties esitiProperties = null;
		try {
			if(requestInfo!=null && requestInfo.getProtocolFactory()!=null) {
				esitiProperties = EsitiProperties.getInstance(this.log, requestInfo.getProtocolFactory());
				if(esitiProperties==null &&
					// per i casi di restart nodi in cui vengono modificati gli id delle chiavi, mentre serializzato nel messaggio e' rimasto il precedente
					requestInfo.getProtocolFactory().getProtocol()!=null) {
					esitiProperties = EsitiProperties.getInstanceFromProtocolName(this.log, requestInfo.getProtocolFactory().getProtocol());
				}
			}
			else {
				esitiProperties = EsitiProperties.getInstanceFromProtocolName(this.log, protocol);
			}
		}catch(Throwable e) {
			this.log.error("Errore avvenuto durante la lettura del gestore degli esiti: "+e.getMessage() ,e);
		}
		if(esitiProperties==null) {
			return;
		}
		
		try{
			ConfigurazionePdDManager configPdDManager = ConfigurazionePdDManager.getInstance(); 
			Tracciamento configTracciamento = configPdDManager.getOpenSPCoopAppenderTracciamento();
			StringBuilder bf = new StringBuilder();
			String esitiConfig = configTracciamento!=null ? configTracciamento.getEsiti() : null;
			if(idPA!=null) {
				PortaApplicativa pa = configPdDManager.getPortaApplicativaSafeMethod(idPA, requestInfo);
				if(pa!=null && pa.getTracciamento()!=null && pa.getTracciamento().getEsiti()!=null) {
					esitiConfig = pa.getTracciamento().getEsiti();
				}
			}
			List<String> esitiDaRegistrare = EsitiConfigUtils.getRegistrazioneEsiti(esitiConfig, this.log, bf);
			
			int code = esitiProperties.convertoToCode(EsitoTransazioneName.CONSEGNA_MULTIPLA);
			String codeAsString = code+"";
			if(esitiDaRegistrare!=null && !esitiDaRegistrare.isEmpty() && !esitiDaRegistrare.contains(codeAsString)){
				String msg = "Non devo registrare l'informazione, esito '"+codeAsString+"' disabilitato nel tracciamento";
				this.log.debug(msg);
				return;
			}
			
		}catch(Throwable e) {
			this.log.debug("Errore avvenuto durante la lettura della configurazione delle transazioni da salvare: "+e.getMessage(),e); 
		}
		
		String richiedenteConnessione = null;
		if(o!=null) {
			if(o instanceof TransazioneApplicativoServer) {
				richiedenteConnessione = tipoOperazione+ "_" +((TransazioneApplicativoServer)o).getIdTransazione();
				richiedenteConnessione = richiedenteConnessione + "_" + ((TransazioneApplicativoServer)o).getServizioApplicativoErogatore();
				if(times!=null) {
					times.idTransazione = ((TransazioneApplicativoServer)o).getIdTransazione();
					times.servizioApplicativoErogatore = ((TransazioneApplicativoServer)o).getServizioApplicativoErogatore();
				}
			}
			else if(o instanceof org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico) {
				richiedenteConnessione = tipoOperazione+ "_" +((org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico)o).getIdTransazione();
				if(times!=null) {
					times.idTransazione = ((org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico)o).getIdTransazione();
				}
			}
			else if(o instanceof Messaggio) {
				richiedenteConnessione = tipoOperazione+ "_" +((Messaggio)o).getIdTransazione();
				if(times!=null) {
					times.idTransazione = ((Messaggio)o).getIdTransazione();
				}
			}
		}
		
		Resource dbResource = null;
		DBTransazioniManager dbManagerTransazioni = null;
		DBConsegnePreseInCaricoManager dbConsegnePreseInCaricoManager = null;
		DBConsegneMessageBoxManager dbConsegneMessageBoxManager = null;
		if(dbManagerSource!=null) {
			switch (dbManagerSource) {
			case runtime:
				dbManagerTransazioni = DBTransazioniManager.getInstance();
				break;
			case consegnePreseInCarico:
				dbConsegnePreseInCaricoManager = DBConsegnePreseInCaricoManager.getInstanceTransazioni();
				break;
			case smistatoreMessaggiPresiInCarico: // non utilizzato
				dbConsegnePreseInCaricoManager = DBConsegnePreseInCaricoManager.getInstanceTransazioni();
				break;
			case messageBox:
				dbConsegneMessageBoxManager = DBConsegneMessageBoxManager.getInstanceTransazioni();
				break;
			}
		}
		else {
			dbManagerTransazioni = DBTransazioniManager.getInstance();
		}
		IDSoggetto idDominio = openspcoopProperties.getIdentitaPortaDefault(protocol, requestInfo);
		ExceptionSerialzerFileSystem exceptionSerializerFileSystem = new ExceptionSerialzerFileSystem(this.log);
		Connection con = null;
		boolean isMessaggioConsegnato = false;
		boolean possibileTerminazioneSingleIntegrationManagerMessage = false;
		boolean consegnaInErrore = false;
		TransazioneApplicativoServer transazioneApplicativoServer = null;
		boolean useConnectionRuntime = false;
		try{
			long timeStart = -1;
			try{
				if(times!=null) {
					timeStart = DateManager.getTimeMillis();
				}
				boolean checkPddRuntimeDatasource = false;
				if(dbManagerTransazioni!=null) {
					checkPddRuntimeDatasource = dbManagerTransazioni.useRuntimePdD();
				}
				else if(dbConsegnePreseInCaricoManager!=null) {
					checkPddRuntimeDatasource = dbConsegnePreseInCaricoManager.useRuntimePdD();
				}
				else if(dbConsegneMessageBoxManager!=null) {
					checkPddRuntimeDatasource = dbConsegneMessageBoxManager.useRuntimePdD();
				}
				
				if(checkPddRuntimeDatasource) {
					if(openspcoopState!=null) {
						if(openspcoopState instanceof OpenSPCoopState) {
							OpenSPCoopState s = (OpenSPCoopState) openspcoopState;
							if(s.getConnectionDB()!=null && !s.getConnectionDB().isClosed()) {
								con = s.getConnectionDB();
								useConnectionRuntime = true;
							}
						}
					}
					else if(state instanceof StateMessage) {
						StateMessage s = (StateMessage) state;
						if(s.getConnectionDB()!=null && !s.getConnectionDB().isClosed()) {
							con = s.getConnectionDB();
							useConnectionRuntime = true;
						}
					}
				}
				
				if(!useConnectionRuntime){
					if(dbManagerTransazioni!=null) {
						dbResource = dbManagerTransazioni.getResource(idDominio, ID_MODULO, richiedenteConnessione);
					}
					else if(dbConsegnePreseInCaricoManager!=null) {
						dbResource = dbConsegnePreseInCaricoManager.getResource(idDominio, ID_MODULO, richiedenteConnessione);
					}
					else if(dbConsegneMessageBoxManager!=null){
						dbResource = dbConsegneMessageBoxManager.getResource(idDominio, ID_MODULO, richiedenteConnessione);
					}
					
					if(dbResource!=null) {
						con = (Connection) dbResource.getResource();
					}
				}
				
			}finally {	
				if(times!=null) {
					long timeEnd =  DateManager.getTimeMillis();
					long timeProcess = timeEnd-timeStart;
					times.getConnection = timeProcess;
				}
			}
				
			boolean autoCommit = false;
			if(con!=null) {
				con.setAutoCommit(autoCommit);
			}

			if(o instanceof TransazioneApplicativoServer) {
				int conflict = 0;
				int iteration = 1; // inizio da 1 per i log
				boolean updateEffettuato = false;
				try{
					if(times!=null) {
						timeStart = DateManager.getTimeMillis();
						if(buildDetailsSA) {
							times.saDetails = new ArrayList<>();
						}
					}
					
					org.openspcoop2.core.transazioni.dao.jdbc.JDBCServiceManager jdbcServiceManager =
							(org.openspcoop2.core.transazioni.dao.jdbc.JDBCServiceManager) daoFactory.getServiceManager(org.openspcoop2.core.transazioni.utils.ProjectInfo.getInstance(), 
									con, autoCommit,
									daoFactoryServiceManagerPropertiesTransazioni,
									daoFactoryLoggerTransazioniDevNull);
					jdbcServiceManager.getJdbcProperties().setShowSql(this.debug);
					ITransazioneApplicativoServerService transazioneService = jdbcServiceManager.getTransazioneApplicativoServerService();
	
					// ** Aggiorno campi dipendenti da questa invocazione **
					// Campi che non possono essere gestiti a livello 'core'
	
					transazioneApplicativoServer = (TransazioneApplicativoServer) o;
	
					// dettaglio esito
					this.setDettaglioEsito(transazioneApplicativoServer, esitiProperties, context);
					
					// consegna terminata
					if(transazioneApplicativoServer.isConsegnaTerminata()) {
						isMessaggioConsegnato = true;
					}
					else if(transazioneApplicativoServer.getDataEliminazioneIm()!=null
							||
							transazioneApplicativoServer.getDataMessaggioScaduto()!=null) {
						isMessaggioConsegnato = true;
						possibileTerminazioneSingleIntegrationManagerMessage = true;
					}
					else if(transazioneApplicativoServer.isConsegnaTrasparente() && transazioneApplicativoServer.getDataUscitaRichiesta()!=null) {
						// !transazioneApplicativoServer.isConsegnaTerminata() altrimenti entrava nel primo if
						consegnaInErrore = true;
					}
	
					boolean useSelectForUpdate = true;
					/*
					 * Grazie alla select for update riesco a mettere il lock solamente sulla riga interessata
					 */
					
					int oldTransactionIsolation = -1;
					if(!useSelectForUpdate) {
						try{
							oldTransactionIsolation = con.getTransactionIsolation();
							/** già effettuato fuori dal metodo connectionDB.setAutoCommit(false);*/
							JDBCUtilities.setTransactionIsolationSerializable(daoFactoryServiceManagerPropertiesTransazioni.getDatabase(), con);
						} catch(Exception er) {
							throw new CoreException("(setIsolation) "+er.getMessage(),er);
						}
					}
					
					long gestioneSerializableDBAttesaAttiva = openspcoopProperties.getGestioneSerializableDB_AttesaAttiva();
					int gestioneSerializableDBCheckInterval = openspcoopProperties.getGestioneSerializableDB_CheckInterval();
					
					long scadenzaWhile = DateManager.getTimeMillis() + gestioneSerializableDBAttesaAttiva;
					
					Throwable lastT = null;
					while(!updateEffettuato && DateManager.getTimeMillis() < scadenzaWhile){
	
						List<String> timeDetails = null;
						try{
							
							if(times!=null && buildDetailsSA) {
								timeDetails = new ArrayList<>();
							}
							
							boolean transazioneAggiornata = TransactionServerUtils.save(transazioneService, (TransazioneApplicativoServer)o, update, throwNotFoundIfNotExists, false, 
									useSelectForUpdate, timeDetails);
														
							long timeStartCommit = -1;
							if(timeDetails!=null) {
								timeStartCommit = DateManager.getTimeMillis();
							}
							if(con!=null) {
								con.commit();
							}
							if(timeDetails!=null) {
								long timeEnd =  DateManager.getTimeMillis();
								long timeProcess = timeEnd-timeStartCommit;
								timeDetails.add("commit:"+timeProcess);
							}
							
							if(!transazioneAggiornata) {
								isMessaggioConsegnato = false; // per gestire eventuali errori durante il  recupero da file system
							}
	
							updateEffettuato = true;
	
						} catch(Throwable e) {
							conflict++;
							lastT = e;
							if(timeDetails!=null) {
								String errorMsg = e.getMessage();
								if(errorMsg!=null) {
									if(errorMsg.length()>100) {
										errorMsg = errorMsg.substring(0, 97)+"...";
									}
									timeDetails.add("error:"+errorMsg);
								}
							}
							/**System.out.println("Serializable error:"+e.getMessage());*/
							try{
								if(con!=null) {
									con.rollback();
								}
							} catch(Exception er) {}
						}finally {
							if(timeDetails!=null && !timeDetails.isEmpty()) {
								for (String detail : timeDetails) {
									times.saDetails.add("i"+iteration+"-"+detail);
								}
							}
						}
	
						if(!updateEffettuato){
							// Per aiutare ad evitare conflitti
							try{
								Utilities.sleep(getRandom().nextInt(gestioneSerializableDBCheckInterval)); // random da 0ms a checkIntervalms
							}catch(Exception eRandom){
								// ignore
							}
							iteration++;
						}
					}
					// Ripristino Transazione
					if(!useSelectForUpdate) {
						try{
							if(con!=null) {
								con.setTransactionIsolation(oldTransactionIsolation);
							}
							/** già effettuato fuori dal metodo connectionDB.setAutoCommit(true);*/
						} catch(Exception er) {
							throw new CoreException("(ripristinoIsolation) "+er.getMessage(),er);
						}
					}
					if(lastT!=null && !updateEffettuato) {
						// registro ultimo errore avvenuto durante il ciclo
						String msgError = "[id:"+transazioneApplicativoServer.getIdTransazione()+"][sa:"+transazioneApplicativoServer.getServizioApplicativoErogatore()+"]["+transazioneApplicativoServer.getConnettoreNome()+"] 'updateTransazioneSA' failed: "+lastT.getMessage();
						daoFactoryLoggerTransazioniSql.error(msgError,lastT);
						this.log.error(msgError,lastT);
						throw lastT;
					}
				}finally {	
					if(times!=null) {
						long timeEnd =  DateManager.getTimeMillis();
						long timeProcess = timeEnd-timeStart;
						if(update) {
							times.update = timeProcess;
						}
						else {
							times.insert = timeProcess;
						}
						if(buildDetailsSA) {
							times.saDetails.add("iteration:"+iteration);
							times.saDetails.add("conflicts:"+conflict);
							times.saDetails.add("updated:"+updateEffettuato);
						}
					}
				}
			}
			else if(o instanceof org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico) {
				try{
					if(times!=null) {
						timeStart = DateManager.getTimeMillis();
					}
					
					this.msgDiagnosticiOpenSPCoopAppender.log(con, (org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico)o);
					
					if(con!=null) {
						con.commit();
					}
				}finally {	
					if(times!=null) {
						long timeEnd =  DateManager.getTimeMillis();
						long timeProcess = timeEnd-timeStart;
						times.insert = timeProcess;
					}
				}
			}
			else if(o instanceof Messaggio) {
				try{
					if(times!=null) {
						timeStart = DateManager.getTimeMillis();
					}
					
					this.dumpOpenSPCoopAppender.dump(con, (Messaggio)o, this.transazioniRegistrazioneDumpHeadersCompactEnabled);
					
					if(con!=null) {
						con.commit();
					}
				}finally {	
					if(times!=null) {
						long timeEnd =  DateManager.getTimeMillis();
						long timeProcess = timeEnd-timeStart;
						times.insert = timeProcess;
					}
				}
			}

		}catch(Throwable e){
			try{
				if(con!=null) {
					con.rollback();
				}
			}catch(Exception eRollback){
				// ignore
			}

			if(o instanceof TransazioneApplicativoServer) {
				
				isMessaggioConsegnato = false; // per gestire eventuali errori durante il  recupero da file system
				
				TransazioneApplicativoServer serverInfo = (TransazioneApplicativoServer) o;
				try{
					if(serverInfo.getIdTransazione()!=null && serverInfo.getServizioApplicativoErogatore()!=null) {
						// NOTA: volutamente salvo serverInfo per poter reimplementare la logica di cui sopra
						serverInfo.setProtocollo(protocol);
						exceptionSerializerFileSystem.registrazioneFileSystemTransazioneApplicativoServerEmessoPdD(serverInfo, serverInfo.getIdTransazione(), serverInfo.getServizioApplicativoErogatore());
					}
				} catch (Throwable eClose) {
					// ignore
				}
				// Effettuo il log anche nel core per evitare che un eventuale filtro a OFF sul core della PdD eviti la scrittura di questi errori
				String msg = "Errore durante la scrittura dell'informazione del server '"+serverInfo.getServizioApplicativoErogatore()+"' associato alla transazione sul database: " + e.getLocalizedMessage();
				this.log.error("["+serverInfo.getIdTransazione()+"] "+msg,e);
			}
			else if(o instanceof org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico) {
				org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico msgDiag = (org.openspcoop2.protocol.sdk.diagnostica.MsgDiagnostico) o;
				try{
					if(msgDiag.getIdTransazione()!=null && msgDiag.getApplicativo()!=null) {
						exceptionSerializerFileSystem.registrazioneFileSystemDiagnosticoEmessoPdD(msgDiag, msgDiag.getIdTransazione(), msgDiag.getApplicativo());
					}
				} catch (Throwable eClose) {
					// ignore
				}
				// Effettuo il log anche nel core per evitare che un eventuale filtro a OFF sul core della PdD eviti la scrittura di questi errori
				String msg = "Errore durante la scrittura del diagnostico relativo al server '"+msgDiag.getApplicativo()+"' associato alla transazione sul database: " + e.getLocalizedMessage();
				this.log.error("["+msgDiag.getIdTransazione()+"] "+msg,e);
			}
			else if(o instanceof Messaggio) {
				Messaggio messaggio = (Messaggio) o;
				try{
					if(messaggio.getIdTransazione()!=null && messaggio.getServizioApplicativoErogatore()!=null) {
						exceptionSerializerFileSystem.registrazioneFileSystemDumpEmessoPdD(messaggio, messaggio.getIdTransazione(), 
								messaggio.getServizioApplicativoErogatore(), messaggio.getDataConsegna());
					}
				} catch (Throwable eClose) {
					// ignore
				}
				// Effettuo il log anche nel core per evitare che un eventuale filtro a OFF sul core della PdD eviti la scrittura di questi errori
				String msg = "Errore durante la scrittura del messaggio relativo al server '"+messaggio.getServizioApplicativoErogatore()+"' associato alla transazione sul database: " + e.getLocalizedMessage();
				this.log.error("["+messaggio.getIdTransazione()+"] "+msg,e);
			}

		}finally{
			
			if(isMessaggioConsegnato || consegnaInErrore) {

				long timeStart = -1;
				try{
					List<String> timeDetails = null;
					if(times!=null) {
						timeStart = DateManager.getTimeMillis();
						if(buildDetailsUpdateTransaction) {
							times.transazioneDetails = new ArrayList<>();
							timeDetails = times.transazioneDetails;
						}
					}
				
					// aggiorno esito transazione (non viene sollevata alcuna eccezione)
					boolean transazioneAggiornata = safeAggiornaInformazioneConsegnaTerminata(transazioneApplicativoServer, con, esitiProperties, possibileTerminazioneSingleIntegrationManagerMessage, consegnaInErrore, timeDetails);
					if(!transazioneAggiornata) {
						
						String prefix = "[id:"+transazioneApplicativoServer.getIdTransazione()+"][sa:"+transazioneApplicativoServer.getServizioApplicativoErogatore()+"]["+transazioneApplicativoServer.getConnettoreNome()+"] 'gestisciErroreAggiornamentoInformazioneConsegnaTerminata'";
						
						boolean existsTransaction = false;
						try {
							existsTransaction = TransactionServerUtils.existsTransaction(transazioneApplicativoServer.getIdTransazione(), con, this.tipoDatabase, this.log, 
									true); // la sessione viene chiusa dopo
						}catch(Throwable t) {
							String msgError = prefix+" compresione esistenza transazione fallita";
							this.log.error(msgError, t);
						}
						
						boolean serializeTransactionInfo = false;
						try {
							serializeTransactionInfo = true;
							try{
								if(transazioneApplicativoServer.getIdTransazione()!=null && transazioneApplicativoServer.getServizioApplicativoErogatore()!=null) {
									// NOTA: volutamente salvo serverInfo per poter reimplementare la logica di cui sopra
									transazioneApplicativoServer.setProtocollo(protocol);
									/**System.out.println("["+transazioneApplicativoServer.getConnettoreNome()+"] ["+transazioneApplicativoServer.getIdTransazione()+"] SAVE PER RECUPERO");*/
									exceptionSerializerFileSystem.registrazioneFileSystemTransazioneApplicativoServerConsegnaTerminata(transazioneApplicativoServer, transazioneApplicativoServer.getIdTransazione(), transazioneApplicativoServer.getServizioApplicativoErogatore());
								}
							} catch (Throwable eClose) {
								// ignore
							}
							
						}catch(Throwable t) {
							String msgError = prefix+" compresione esistenza transazione fallita";
							this.log.error(msgError,t );
						}
						
						String msgError = prefix+" effettuata gestione dell'errore (existsTransaction="+existsTransaction+" serializeTransactionInfo="+serializeTransactionInfo+")";
						this.log.error(msgError);
						
					}
				}finally {	
					if(times!=null) {
						long timeEnd =  DateManager.getTimeMillis();
						long timeProcess = timeEnd-timeStart;
						times.updateTransazione = timeProcess;
					}
				}
			}
			
			try{
				if(con!=null) {
					con.setAutoCommit(true);
				}
			}catch(Exception eRollback){
				// ignore
			}
			try {
				if(!useConnectionRuntime) {
					if(dbManagerTransazioni!=null) {
						dbManagerTransazioni.releaseResource(idDominio, ID_MODULO, dbResource);
					}
					else if(dbConsegnePreseInCaricoManager!=null) {
						dbConsegnePreseInCaricoManager.releaseResource(idDominio, ID_MODULO, dbResource);
					}
					else if(dbConsegneMessageBoxManager!=null){
						dbConsegneMessageBoxManager.releaseResource(idDominio, ID_MODULO, dbResource);
					}
				}
			} catch (Exception e) {
				// ignore
			}
		}


	}

	private void setDettaglioEsito(TransazioneApplicativoServer transazioneApplicativoServer, EsitiProperties esitiProperties, Context context) throws ProtocolException {
		
		if(transazioneApplicativoServer.getDataMessaggioScaduto()!=null) {
			return; // non devo aggiornare l'esito
		}
		else if(transazioneApplicativoServer.getDataPrelievoIm()!=null) {
			return; // non devo aggiornare l'esito
		}
		else if(transazioneApplicativoServer.getDataEliminazioneIm()!=null) {
			transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.OK));
		}
		else {
			if(transazioneApplicativoServer.getDataUscitaRichiesta()!=null) {
				if(transazioneApplicativoServer.getFault()!=null) {
					transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.ERRORE_APPLICATIVO));
				}
				else if(transazioneApplicativoServer.getCodiceRisposta()!=null) {
					int code = -1;
					try {
						code = Integer.valueOf(transazioneApplicativoServer.getCodiceRisposta());
					}catch(Exception e) {
						// ignore
					}
					if(code>0) {
						if(code<=299) {
							transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.OK));
						}
						else if(transazioneApplicativoServer.isConsegnaTerminata()) {
							
							if(code>299 && code<=399) {
								transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.HTTP_3xx));
							}
							else if(code>399 && code<=499) {
								transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.HTTP_4xx));
							}
							else { /**if(code>499) {*/
								transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.HTTP_5xx));
							}
							
						}
						else {
							setErroreInvocazione(transazioneApplicativoServer, esitiProperties, context);
						}
					}
					else {
						// altro tipo di codice
						if(transazioneApplicativoServer.isConsegnaTerminata()) {
							transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.OK));
						}
						else {
							setErroreInvocazione(transazioneApplicativoServer, esitiProperties, context);
						}
					}
				}
				else {
					// senza codice di risposta
					if(transazioneApplicativoServer.isConsegnaTerminata()) {
						transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(EsitoTransazioneName.OK));
					}
					else {
						setErroreInvocazione(transazioneApplicativoServer, esitiProperties, context);
					}
				}
			}
		}
		
		if(transazioneApplicativoServer.getDettaglioEsito()>0) {
			// nop
		}
		
	}
	
	private void setErroreInvocazione(TransazioneApplicativoServer transazioneApplicativoServer, EsitiProperties esitiProperties, Context context) throws ProtocolException {
		EsitoTransazioneName name = EsitoTransazioneName.ERRORE_INVOCAZIONE;
		if(context!=null &&
				context.containsKey(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_NAME_CONTROLLO_TRAFFICO_VIOLAZIONE)
			){

			String timeoutExceededMessage = null;
			if(context.containsKey(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_NAME_CONTROLLO_TRAFFICO_VIOLAZIONE)) {
				timeoutExceededMessage = (String) context.get(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_NAME_CONTROLLO_TRAFFICO_VIOLAZIONE);
				if(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_VALUE_CONNECTION_TIMEOUT.equals(timeoutExceededMessage)) {
					name = EsitoTransazioneName.ERRORE_CONNECTION_TIMEOUT;
				}
				else if(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_VALUE_REQUEST_READ_TIMEOUT.equals(timeoutExceededMessage)) {
					name = EsitoTransazioneName.ERRORE_REQUEST_TIMEOUT;
				}
				else if(org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_VALUE_RESPONSE_READ_TIMEOUT.equals(timeoutExceededMessage) ||
						org.openspcoop2.core.controllo_traffico.constants.Costanti.PDD_CONTEXT_VALUE_READ_TIMEOUT.equals(timeoutExceededMessage)) {
					name = EsitoTransazioneName.ERRORE_RESPONSE_TIMEOUT;
				}
			}
			
		}
		
		transazioneApplicativoServer.setDettaglioEsito(esitiProperties.convertoToCode(name));
	}

	private static boolean debugStaticValue = true;
	public static boolean isDebug() {
		return debugStaticValue;
	}
	public static void setDebug(boolean debug) {
		GestoreConsegnaMultipla.debugStaticValue = debug;
	}
	private boolean safeAggiornaInformazioneConsegnaTerminata(TransazioneApplicativoServer transazioneApplicativoServer, Connection con, EsitiProperties esitiProperties,
			 boolean possibileTerminazioneSingleIntegrationManagerMessage,boolean consegnaInErrore,
			 List<String> timeDetails) {
		
		boolean debugMode = debugStaticValue; // un eventuale errore deve essere sempre registrato
		
		DAOFactory daoF = debugMode ? daoFactory : daoFactoryDevNull;
		Logger logFactory = debugMode ? daoFactoryLoggerTransazioniSql : daoFactoryLoggerTransazioniDevNull;
		ServiceManagerProperties smp = debugMode ? daoFactoryServiceManagerPropertiesTransazioni : daoFactoryDevNullServiceManagerPropertiesTransazioni;
		
		int esitoConsegnaMultipla = -1;
		int esitoConsegnaMultiplaInCorso = -1;
		int esitoConsegnaMultiplaFallita = -1;
		int esitoConsegnaMultiplaCompletata = -1;
		int ok = -1;
		int esitoIntegrationManagerSingolo = -1;
		boolean esitiLetti = false;
		try {
			esitoConsegnaMultipla = esitiProperties.convertoToCode(EsitoTransazioneName.CONSEGNA_MULTIPLA);
			esitoConsegnaMultiplaInCorso = esitiProperties.convertoToCode(EsitoTransazioneName.CONSEGNA_MULTIPLA_IN_CORSO);
			esitoConsegnaMultiplaFallita = esitiProperties.convertoToCode(EsitoTransazioneName.CONSEGNA_MULTIPLA_FALLITA);
			esitoConsegnaMultiplaCompletata = esitiProperties.convertoToCode(EsitoTransazioneName.CONSEGNA_MULTIPLA_COMPLETATA);
			ok = esitiProperties.convertoToCode(EsitoTransazioneName.OK);
			esitoIntegrationManagerSingolo = esitiProperties.convertoToCode(EsitoTransazioneName.MESSAGE_BOX);
			esitiLetti = true;			
		}catch(Exception er) {
			// errore che non dovrebbe succedere
			String msg = "Errore durante l'aggiornamento delle transazione relativamente all'informazione del server '"+transazioneApplicativoServer.getServizioApplicativoErogatore()+"': (readEsiti) " + er.getMessage();
			this.log.error("[id:"+transazioneApplicativoServer.getIdTransazione()+"][sa:"+transazioneApplicativoServer.getServizioApplicativoErogatore()+"]["+transazioneApplicativoServer.getConnettoreNome()+"] "+msg,er);
		}
		
		if(esitiLetti) {
			return TransactionServerUtils.safeAggiornaInformazioneConsegnaTerminata(transazioneApplicativoServer, con, 
					this.tipoDatabase, this.log,
					daoF,logFactory,smp,
					this.debug,
					esitoConsegnaMultipla, esitoConsegnaMultiplaInCorso, esitoConsegnaMultiplaFallita, esitoConsegnaMultiplaCompletata, ok,
					esitoIntegrationManagerSingolo, possibileTerminazioneSingleIntegrationManagerMessage, consegnaInErrore,
					openspcoopProperties.getGestioneSerializableDB_AttesaAttiva(),openspcoopProperties.getGestioneSerializableDB_CheckInterval(),
					timeDetails);
		}
		return false;
	}

}

class TransazioniSAProcessTimes{

	String idTransazione;
	String servizioApplicativoErogatore;
	long getConnection = -1;
	long insert = -1;
	long update = -1;
	List<String> saDetails = null;
	long updateTransazione = -1;
	List<String> transazioneDetails = null;
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		if(this.getConnection>=0) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			sb.append("getConnection:").append(this.getConnection);
		}
		if(this.insert>=0) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			sb.append("insert:").append(this.insert);
		}
		if(this.update>=0) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			sb.append("update:").append(this.update);
		}
		if(this.saDetails!=null && !this.saDetails.isEmpty()) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			if(this.insert>=0) {
				sb.append("insertDetails:{");
			}
			else if(this.update>=0) {
				sb.append("updateDetails:{");
			}
			else {
				sb.append("details:{");
			}
			boolean first = true;
			for (String det : this.saDetails) {
				if(!first) {
					sb.append(" ");
				}
				sb.append(det);
				first=false;
			}
			sb.append("}");
		}
		if(this.updateTransazione>=0) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			sb.append("updateTransaction:").append(this.updateTransazione);
		}
		if(this.transazioneDetails!=null && !this.transazioneDetails.isEmpty()) {
			if(sb.length()>0) {
				sb.append(" ");
			}
			sb.append("updateTransactionDetails:{");
			boolean first = true;
			for (String det : this.transazioneDetails) {
				if(!first) {
					sb.append(" ");
				}
				sb.append(det);
				first=false;
			}
			sb.append("}");
		}
		return sb.toString();
	}
}