RicezioneContenutiApplicativiHTTPtoSOAPService.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2025 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.services.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import jakarta.xml.soap.SOAPBody;
import jakarta.xml.soap.SOAPElement;

import org.apache.commons.lang3.StringUtils;
import org.openspcoop2.core.commons.CoreException;
import org.openspcoop2.core.config.DumpConfigurazione;
import org.openspcoop2.core.config.PortaDelegata;
import org.openspcoop2.core.constants.TipoPdD;
import org.openspcoop2.core.constants.TransferLengthModes;
import org.openspcoop2.core.id.IDPortaDelegata;
import org.openspcoop2.core.id.IDServizio;
import org.openspcoop2.core.registry.driver.IDServizioFactory;
import org.openspcoop2.core.transazioni.Transazione;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2MessageParseResult;
import org.openspcoop2.message.constants.MessageRole;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.exception.ParseException;
import org.openspcoop2.message.exception.ParseExceptionUtils;
import org.openspcoop2.message.soap.SoapUtils;
import org.openspcoop2.message.soap.TunnelSoapUtils;
import org.openspcoop2.monitor.sdk.transaction.FaseTracciamento;
import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.core.CostantiPdD;
import org.openspcoop2.pdd.core.GestoreRichieste;
import org.openspcoop2.pdd.core.PdDContext;
import org.openspcoop2.pdd.core.connettori.IConnettore;
import org.openspcoop2.pdd.core.connettori.RepositoryConnettori;
import org.openspcoop2.pdd.core.controllo_traffico.SogliaDimensioneMessaggio;
import org.openspcoop2.pdd.core.controllo_traffico.SogliaReadTimeout;
import org.openspcoop2.pdd.core.controllo_traffico.SoglieDimensioneMessaggi;
import org.openspcoop2.pdd.core.credenziali.Credenziali;
import org.openspcoop2.pdd.core.handlers.GestoreHandlers;
import org.openspcoop2.pdd.core.handlers.HandlerException;
import org.openspcoop2.pdd.core.handlers.PostOutResponseContext;
import org.openspcoop2.pdd.core.handlers.PreInAcceptRequestContext;
import org.openspcoop2.pdd.core.handlers.PreInRequestContext;
import org.openspcoop2.pdd.core.transazioni.TransactionContext;
import org.openspcoop2.pdd.logger.MsgDiagnosticiProperties;
import org.openspcoop2.pdd.logger.MsgDiagnostico;
import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
import org.openspcoop2.pdd.logger.transazioni.ConfigurazioneTracciamento;
import org.openspcoop2.pdd.logger.transazioni.InformazioniTransazione;
import org.openspcoop2.pdd.logger.transazioni.TracciamentoManager;
import org.openspcoop2.pdd.services.DirectVMProtocolInfo;
import org.openspcoop2.pdd.services.DumpRaw;
import org.openspcoop2.pdd.services.OpenSPCoop2Startup;
import org.openspcoop2.pdd.services.ServicesUtils;
import org.openspcoop2.pdd.services.connector.AsyncResponseCallbackClientEvent;
import org.openspcoop2.pdd.services.connector.ConnectorDispatcherErrorInfo;
import org.openspcoop2.pdd.services.connector.ConnectorDispatcherInfo;
import org.openspcoop2.pdd.services.connector.ConnectorDispatcherUtils;
import org.openspcoop2.pdd.services.connector.ConnectorException;
import org.openspcoop2.pdd.services.connector.IAsyncResponseCallback;
import org.openspcoop2.pdd.services.connector.RicezioneContenutiApplicativiHTTPtoSOAPConnector;
import org.openspcoop2.pdd.services.connector.messages.ConnectorInMessage;
import org.openspcoop2.pdd.services.connector.messages.ConnectorOutMessage;
import org.openspcoop2.pdd.services.connector.messages.DirectVMConnectorInMessage;
import org.openspcoop2.pdd.services.connector.messages.DirectVMConnectorOutMessage;
import org.openspcoop2.pdd.services.connector.messages.DumpRawConnectorInMessage;
import org.openspcoop2.pdd.services.connector.messages.DumpRawConnectorOutMessage;
import org.openspcoop2.pdd.services.core.RicezioneContenutiApplicativi;
import org.openspcoop2.pdd.services.core.RicezioneContenutiApplicativiContext;
import org.openspcoop2.pdd.services.error.RicezioneContenutiApplicativiInternalErrorGenerator;
import org.openspcoop2.protocol.basic.builder.EsitoBuilder;
import org.openspcoop2.protocol.basic.registry.ServiceIdentificationReader;
import org.openspcoop2.protocol.engine.SecurityTokenUtilities;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.builder.EsitoTransazione;
import org.openspcoop2.protocol.sdk.builder.InformazioniErroriInfrastrutturali;
import org.openspcoop2.protocol.sdk.constants.CodiceErroreIntegrazione;
import org.openspcoop2.protocol.sdk.constants.ErroreIntegrazione;
import org.openspcoop2.protocol.sdk.constants.ErroriIntegrazione;
import org.openspcoop2.protocol.sdk.constants.EsitoTransazioneName;
import org.openspcoop2.protocol.sdk.constants.IDService;
import org.openspcoop2.protocol.sdk.constants.IntegrationFunctionError;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.protocol.sdk.state.URLProtocolContext;
import org.openspcoop2.protocol.utils.EsitiProperties;
import org.openspcoop2.utils.LimitExceededIOException;
import org.openspcoop2.utils.LoggerWrapperFactory;
import org.openspcoop2.utils.TimeoutIOException;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.dch.MailcapActivationReader;
import org.openspcoop2.utils.io.DumpByteArrayOutputStream;
import org.openspcoop2.utils.io.notifier.NotifierInputStreamParams;
import org.openspcoop2.utils.transport.TransportRequestContext;
import org.openspcoop2.utils.transport.TransportUtils;
import org.openspcoop2.utils.transport.http.HttpConstants;
import org.slf4j.Logger;

/**
 * Servlet che serve per creare un tunnel da un servizio che non conosce SOAP verso OpenSPCoop (che utilizza SOAP)
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */

public class RicezioneContenutiApplicativiHTTPtoSOAPService implements IRicezioneService, IAsyncResponseCallback {

	private RicezioneContenutiApplicativiInternalErrorGenerator generatoreErrore;
	
	public static void forceXmlResponse(RicezioneContenutiApplicativiInternalErrorGenerator generatoreErrore) {
		generatoreErrore.setForceMessageTypeResponse(MessageType.XML); // forzo xml
		if(generatoreErrore.getProprietaErroreAppl()!=null) {
			generatoreErrore.getProprietaErroreAppl().setFaultAsXML(true); // siamo in una richiesta http senza SOAP, un SoapFault non ha senso
		}
	}
	
	public RicezioneContenutiApplicativiHTTPtoSOAPService(RicezioneContenutiApplicativiInternalErrorGenerator generatoreErrore){
		this.generatoreErrore = generatoreErrore;
		if(this.generatoreErrore!=null) {
			RicezioneContenutiApplicativiHTTPtoSOAPService.forceXmlResponse(this.generatoreErrore);
		}
	}
	
	private RicezioneContenutiApplicativiContext context = null;
	private IProtocolFactory<?> protocolFactory = null;
	private OpenSPCoop2Properties openSPCoopProperties = null;
	
	private ConnectorInMessage req = null;
	private ConnectorOutMessage res = null;
	private Date dataAccettazioneRichiesta = null;
	private Date dataIngressoRichiesta = null;
	
	private RequestInfo requestInfo = null;
	private PdDContext pddContext = null;
	private byte[] inputBody = null;
	private OpenSPCoop2Message requestMessage = null;
	private OpenSPCoop2Message responseMessage = null;
	
	private Logger logCore = null;
	private MsgDiagnostico msgDiag=null;
	private DumpRaw dumpRaw = null;
	
	private String idModulo = null;
	private PostOutResponseContext postOutResponseContext = null;
	private String idTransazione = null;
	
	@Override
	public void process(ConnectorInMessage reqParam, ConnectorOutMessage resParam, Date dataAccettazioneRichiestaParam, boolean async) throws ConnectorException {
		
		this.req = reqParam;
		this.res = resParam;
		this.dataAccettazioneRichiesta = dataAccettazioneRichiestaParam;
		
		// IDModulo
		this.idModulo = this.req.getIdModulo();
		IDService idModuloAsService = this.req.getIdModuloAsIDService();
		this.requestInfo = this.req.getRequestInfo();

		// Log
		this.logCore = OpenSPCoop2Logger.getLoggerOpenSPCoopCore();
		if(this.logCore==null)
			this.logCore = LoggerWrapperFactory.getLogger(this.idModulo);

		this.openSPCoopProperties = OpenSPCoop2Properties.getInstance();
		

		/* ------------  PreInHandler (PreInAcceptRequestContext) ------------- */
		
		PreInAcceptRequestContext preInAcceptRequestContext = null;
		SogliaReadTimeout sogliaReadTimeout = null;
		if (this.openSPCoopProperties != null && OpenSPCoop2Startup.initialize) {
			
			// build context
			preInAcceptRequestContext = new PreInAcceptRequestContext();
			preInAcceptRequestContext.setTipoPorta(TipoPdD.DELEGATA);
			preInAcceptRequestContext.setIdModulo(this.idModulo);
			preInAcceptRequestContext.setRequestInfo(this.requestInfo);	
			preInAcceptRequestContext.setLogCore(this.logCore);
			
			// valori che verranno aggiornati dopo
			try {
				if(this.openSPCoopProperties.isConnettoriUseLimitedInputStream()) {
					SogliaDimensioneMessaggio soglia = new SogliaDimensioneMessaggio();
					soglia.setSogliaKb(this.openSPCoopProperties.getLimitedInputStreamThresholdKb());
					soglia.setUseContentLengthHeader(this.openSPCoopProperties.isLimitedInputStreamUseContentLength());
					soglia.setUseContentLengthHeaderAcceptZeroValue(this.openSPCoopProperties.isLimitedInputStreamUseContentLengthAcceptZeroValue());
					soglia.setPolicyGlobale(true);
					soglia.setNomePolicy(CostantiPdD.GOVWAY_CORE);
					soglia.setIdPolicyConGruppo(CostantiPdD.GOVWAY_CORE);
					this.req.setRequestLimitedStream(soglia);
				}
				if(this.openSPCoopProperties.isConnettoriUseTimeoutInputStream()) {
					sogliaReadTimeout = new SogliaReadTimeout();
					sogliaReadTimeout.setSogliaMs(this.openSPCoopProperties.getReadConnectionTimeoutRicezioneContenutiApplicativi());
					sogliaReadTimeout.setConfigurazioneGlobale(true);
					sogliaReadTimeout.setIdConfigurazione(CostantiPdD.GOVWAY_CORE);
					this.req.setRequestReadTimeout(sogliaReadTimeout);
				}
				this.req.setThresholdContext(null, 
						this.openSPCoopProperties.getDumpBinarioInMemoryThreshold(), this.openSPCoopProperties.getDumpBinarioRepository());
			}catch(Throwable t) {
				this.logCore.error(t.getMessage(),t);
			}
			preInAcceptRequestContext.setReq(this.req);
			
			// invocazione handler
			GestoreHandlers.preInRequest(preInAcceptRequestContext, this.logCore, this.logCore);
			
		}
		
		
		// GeneratoreErrore
		try{
			if(this.generatoreErrore==null){
				this.generatoreErrore = 
						new RicezioneContenutiApplicativiInternalErrorGenerator(this.logCore, RicezioneContenutiApplicativiHTTPtoSOAPConnector.ID_MODULO, this.requestInfo);
				this.generatoreErrore.getProprietaErroreAppl().setFaultAsXML(true); // siamo in una richiesta http senza SOAP, un SoapFault non ha senso
			}
		}catch(Exception e){
			String msg = "Inizializzazione Generatore Errore fallita: "+Utilities.readFirstErrorValidMessageFromException(e);
			this.logCore.error(msg,e);
			ConnectorDispatcherErrorInfo cInfo = ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore, // il metodo doError gestisce il generatoreErrore a null
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
					get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_501_PDD_NON_INIZIALIZZATA), 
					IntegrationFunctionError.GOVWAY_NOT_INITIALIZED, e, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, null, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
		
		//	Proprieta' OpenSPCoop
		if (!OpenSPCoop2Startup.initialize || this.openSPCoopProperties == null) {
			String msg = "Inizializzazione di GovWay non correttamente effettuata: OpenSPCoopProperties";
			if(!OpenSPCoop2Startup.initialize) {
				msg = "Inizializzazione di GovWay non correttamente effettuata";
			}
			this.logCore.error(msg);
			ConnectorDispatcherErrorInfo cInfo = ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore, 
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
						get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_501_PDD_NON_INIZIALIZZATA), 
						IntegrationFunctionError.GOVWAY_NOT_INITIALIZED, null, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, null, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
		
		// Configurazione Reader
		ConfigurazionePdDManager configPdDManager = null;
		try{
			configPdDManager = ConfigurazionePdDManager.getInstance();
			if(configPdDManager==null || !configPdDManager.isInitializedConfigurazionePdDReader()){
				throw new CoreException("ConfigurazionePdDManager not initialized");
			}
		}catch(Throwable e){
			String msg = "Inizializzazione di GovWay non correttamente effettuata: ConfigurazionePdDManager";
			this.logCore.error(msg);
			ConnectorDispatcherErrorInfo cInfo = ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore, 
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
						get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_501_PDD_NON_INIZIALIZZATA), 
						IntegrationFunctionError.GOVWAY_NOT_INITIALIZED, e, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, null, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
			
		// PddContext from servlet
		Object oPddContextFromServlet = null;
		try{
			oPddContextFromServlet = this.req.getAttribute(CostantiPdD.OPENSPCOOP2_PDD_CONTEXT_HEADER_HTTP.getValue());
		}catch(Exception e){
			this.logCore.error("req.getAttribute("+CostantiPdD.OPENSPCOOP2_PDD_CONTEXT_HEADER_HTTP+") error: "+e.getMessage(),e);
		}
		PdDContext pddContextFromServlet = null;
		if(oPddContextFromServlet!=null){
			pddContextFromServlet = (PdDContext) oPddContextFromServlet;
		}
				
		// check requestInfo
		if(this.requestInfo==null) {
			this.res.close(false);
			String msg = "RequestInfo undefined";
			this.logCore.error(msg);
			return;
		}
		
		// Identifico Servizio per comprendere correttamente il messageType
		ServiceIdentificationReader serviceIdentificationReader = null;
		try{
			serviceIdentificationReader = ServicesUtils.getServiceIdentificationReader(this.logCore, this.requestInfo,
					configPdDManager.getRegistroServiziManager(), configPdDManager);
		}catch(Exception e){
			String msg = "Inizializzazione RegistryReader fallita: "+Utilities.readFirstErrorValidMessageFromException(e);
			this.logCore.error(msg,e);
			ConnectorDispatcherErrorInfo cInfo =  ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore,
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
						get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_501_PDD_NON_INIZIALIZZATA),
						IntegrationFunctionError.GOVWAY_NOT_INITIALIZED, e, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, pddContextFromServlet, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
	
		// Provo a creare un context (per l'id di transazione nei diagnostici)
		try {
			this.context = new RicezioneContenutiApplicativiContext(idModuloAsService,this.dataAccettazioneRichiesta,this.requestInfo);
			this.protocolFactory = this.req.getProtocolFactory();
			this.idTransazione = (String)this.context.getPddContext().getObject(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE);
		}catch(Throwable e) {
			this.context = null;
			this.protocolFactory = null;
			// non loggo l'errore tanto poi provo a ricreare il context subito dopo e li verra' registrato l'errore
		}

		try{
			GestoreRichieste.readRequestConfig(this.requestInfo);
		}catch(Exception e){
			String msg = "GestoreRichieste readRequestConfig fallita: "+Utilities.readFirstErrorValidMessageFromException(e);
			this.logCore.error(msg,e);
			ConnectorDispatcherErrorInfo cInfo = ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore,
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
						get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_500_ERRORE_INTERNO),
						IntegrationFunctionError.INTERNAL_REQUEST_ERROR, e, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, pddContextFromServlet, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
		
		if(this.idTransazione!=null) {
			try {
				if(this.openSPCoopProperties.isTransazioniEnabled()) {
					TransactionContext.createTransaction(this.idTransazione,"RicezioneContenutiApplicativiHTTPtoSOAP.1");
				}
				this.requestInfo.setIdTransazione(this.idTransazione);
				
				this.req.setThresholdContext((this.context!=null ? this.context.getPddContext(): null), 
						this.openSPCoopProperties.getDumpBinarioInMemoryThreshold(), this.openSPCoopProperties.getDumpBinarioRepository());
			
			}catch(Throwable e) {
				this.context = null;
				this.protocolFactory = null;
				// non loggo l'errore tanto poi provo a ricreare il context subito dopo e li verra' registrato l'errore
			}
		}
		
		// Logger dei messaggi diagnostici
		String nomePorta = this.requestInfo.getProtocolContext().getInterfaceName();
		this.msgDiag = MsgDiagnostico.newInstance(TipoPdD.DELEGATA,this.idModulo,nomePorta,this.requestInfo);
		this.msgDiag.setPrefixMsgPersonalizzati(MsgDiagnosticiProperties.MSG_DIAG_RICEZIONE_CONTENUTI_APPLICATIVI);
		if(this.context!=null && this.protocolFactory!=null) {
			this.msgDiag.setPddContext(this.context.getPddContext(), this.protocolFactory);
		}
		
		try{
			this.msgDiag.logPersonalizzato("ricezioneRichiesta.firstLog");
		}catch(Exception e){
			this.logCore.error("Errore generazione diagnostico di ingresso",e);
		}
		
		try{
			this.req.setDiagnosticProducer(this.context!=null ? this.context.getPddContext(): null, this.msgDiag);
		}catch(Throwable e){
			this.logCore.error("Errore registrazione diagnostico sulla richiesta",e);
		}
		
		// emitDiagnostic preAccept handler
		GestoreHandlers.emitDiagnostic(this.msgDiag, preInAcceptRequestContext, this.context!=null ? this.context.getPddContext() : null, 
				this.logCore, this.logCore);
		
		// Aggiorno RequestInfo
		try{
			this.msgDiag.mediumDebug("Accesso configurazione della richiesta in corso...");
		}catch(Exception e){
			this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
		}
		ConnectorDispatcherInfo cInfo = RicezioneContenutiApplicativiServiceUtils.updatePortaDelegataRequestInfo(this.requestInfo, this.logCore, this.req, this.res,
				this.generatoreErrore, serviceIdentificationReader, this.msgDiag, 
				this.context!=null ? this.context.getPddContext(): null);
		if(cInfo!=null){
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.context, this.logCore, this.req, pddContextFromServlet, this.dataAccettazioneRichiesta, cInfo);
			return; // l'errore in response viene impostato direttamente dentro il metodo
		}
		this.req.updateRequestInfo(this.requestInfo);	

		// Timeout e DumpRaw
		try{
			try{
				this.msgDiag.mediumDebug("Lettura configurazione dump binario ...");
			}catch(Exception e){
				this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
			}
			boolean dumpBinario = configPdDManager.dumpBinarioPD();
			PortaDelegata pd = null;
			if(this.requestInfo!=null && this.requestInfo.getProtocolContext()!=null && this.requestInfo.getProtocolContext().getInterfaceName()!=null) {
				IDPortaDelegata idPD = new IDPortaDelegata();
				idPD.setNome(this.requestInfo.getProtocolContext().getInterfaceName());
				pd = configPdDManager.getPortaDelegataSafeMethod(idPD, this.requestInfo);
			}

			// Limited
			try{
				this.msgDiag.mediumDebug("Lettura configurazione dimensione massima della richiesta ...");
			}catch(Exception e){
				this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
			}
			String azione = (this.requestInfo!=null && this.requestInfo.getIdServizio()!=null) ? this.requestInfo.getIdServizio().getAzione() : null;
			SoglieDimensioneMessaggi limitedInputStream = configPdDManager.getSoglieLimitedInputStream(pd, azione, this.idModulo,
					(this.context!=null && this.context.getPddContext()!=null) ? this.context.getPddContext() : null, 
							this.requestInfo,
							this.protocolFactory, this.logCore);
			if(limitedInputStream!=null) {
				this.req.setRequestLimitedStream(limitedInputStream.getRichiesta());
				if(this.context!=null && this.context.getPddContext()!=null) {
					this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.LIMITED_STREAM, limitedInputStream.getRisposta());
				}
			}
			else {
				if(!this.openSPCoopProperties.isLimitedInputStreamThresholdDefined()) {
					this.req.disableLimitedStream();
				}
			}
			
			// Timeout
			try{
				this.msgDiag.mediumDebug("Lettura configurazione timeout per la lettura della richiesta ...");
			}catch(Exception e){
				this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
			}
			boolean useTimeoutInputStream = configPdDManager.isConnettoriUseTimeoutInputStream(pd);
			if(useTimeoutInputStream) {
				sogliaReadTimeout = configPdDManager.getRequestReadTimeout(pd,
						this.requestInfo,
						this.protocolFactory, 
						this.context!=null ? this.context.getPddContext() : null,
								null);
				if(sogliaReadTimeout!=null && sogliaReadTimeout.getSogliaMs()>0) {
					this.req.setRequestReadTimeout(sogliaReadTimeout);
				}
				else {
					this.req.disableReadTimeout();
				}
			}
			else {
				this.req.disableReadTimeout();
			}
			
			// DumpRaw
			try{
				this.msgDiag.mediumDebug("Lettura configurazione dump ...");
			}catch(Exception e){
				this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
			}
			DumpConfigurazione dumpConfigurazione = configPdDManager.getDumpConfigurazione(pd);
			ConfigurazioneTracciamento configurazioneTracciamento = new ConfigurazioneTracciamento(this.logCore, configPdDManager, pd);
			boolean fileTraceHeaders = configurazioneTracciamento.isTransazioniFileTraceDumpBinarioHeaderEnabled();
			boolean fileTracePayload = configurazioneTracciamento.isTransazioniFileTraceDumpBinarioPayloadEnabled();
			this.dumpRaw = new DumpRaw(this.logCore, this.requestInfo.getIdentitaPdD(), this.idModulo, TipoPdD.DELEGATA, 
					dumpBinario, 
					dumpConfigurazione,
					fileTraceHeaders, fileTracePayload);
			if(this.dumpRaw.isActiveDumpRichiesta()) {
				this.req = new DumpRawConnectorInMessage(this.logCore, this.req, 
						(this.context!=null ? this.context.getPddContext(): null), 
						this.openSPCoopProperties.getDumpBinarioInMemoryThreshold(), this.openSPCoopProperties.getDumpBinarioRepository());
			}
			if(this.dumpRaw.isActiveDumpRisposta()) {
				this.res = new DumpRawConnectorOutMessage(this.logCore, this.res, 
						(this.context!=null ? this.context.getPddContext(): null), 
						this.openSPCoopProperties.getDumpBinarioInMemoryThreshold(), this.openSPCoopProperties.getDumpBinarioRepository(),
						this.dumpRaw);
			}
		}catch(Throwable e){
			String msg = "Inizializzazione di GovWay non correttamente effettuata: DumpRaw";
			this.logCore.error(msg,  e);
			cInfo = ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore, 
					ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.
						get5XX_ErroreProcessamento(msg,CodiceErroreIntegrazione.CODICE_501_PDD_NON_INIZIALIZZATA), 
						IntegrationFunctionError.GOVWAY_NOT_INITIALIZED, e, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.logCore, this.req, pddContextFromServlet, this.dataAccettazioneRichiesta, cInfo);
			return;
		}
		
		// Riporto in context il timeout della richiesta
		if(this.context!=null && this.context.getPddContext()!=null &&
			sogliaReadTimeout!=null && sogliaReadTimeout.getSogliaMs()>0) {
				this.context.getPddContext().put(CostantiPdD.REQUEST_READ_TIMEOUT, sogliaReadTimeout.getSogliaMs());
		}
		
		// Questo servizio è invocabile solo con API Soap
		if(!ServiceBinding.SOAP.equals(this.requestInfo.getIntegrationServiceBinding())){
			String msg = "Servizio utilizzabile solamente con API SOAP, riscontrata API REST";
			this.logCore.error(msg);
			ConnectorDispatcherErrorInfo cInfoError =  ConnectorDispatcherUtils.doError(this.requestInfo, this.generatoreErrore,
					ErroriIntegrazione.ERRORE_439_FUNZIONALITA_NOT_SUPPORTED_BY_PROTOCOL.getErrore439_FunzionalitaNotSupportedByProtocol(msg, this.protocolFactory),
					IntegrationFunctionError.NOT_SUPPORTED_BY_PROTOCOL, null, null, this.res, this.logCore, ConnectorDispatcherUtils.GENERAL_ERROR);
			this.res.close(false);
			RicezioneContenutiApplicativiServiceUtils.emitTransaction(this.context, this.logCore, this.req, pddContextFromServlet, this.dataAccettazioneRichiesta, cInfoError);
			return;
		}
		
		
		
		
		
		
		/* ------------  Lettura parametri della richiesta ------------- */
				
		String errorImbustamentoSoapNonRiuscito = null;
		MessageType messageTypeReq = null;
		String protocol = null;
		
		boolean completeProcess = false;
		try{
			
			/* --------------- Creo il context che genera l'id univoco ----------------------- */
			
			try{
				this.msgDiag.mediumDebug("Creazione contesto ...");
			}catch(Exception e){
				this.logCore.error(CostantiPdD.GOVWAY_CORE_ERRORE_GENERAZIONE_DIAGNOSTICO,e);
			}
			
			if(this.protocolFactory==null) {
				this.protocolFactory = this.req.getProtocolFactory();
			}
			protocol = this.protocolFactory.getProtocol();
			
			if(this.context==null) {
				this.context = new RicezioneContenutiApplicativiContext(idModuloAsService,this.dataAccettazioneRichiesta,this.requestInfo);
			}
			if(preInAcceptRequestContext!=null && preInAcceptRequestContext.getPreContext()!=null && !preInAcceptRequestContext.getPreContext().isEmpty()) {
				this.context.getPddContext().addAll(preInAcceptRequestContext.getPreContext(), false);
			}
			this.context.setTipoPorta(TipoPdD.DELEGATA);
			this.context.setForceFaultAsXML(true); // siamo in una richiesta http senza SOAP, un SoapFault non ha senso
			this.context.setIdModulo(this.idModulo);
			this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME, this.protocolFactory.getProtocol());
			this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.REQUEST_INFO, this.req.getRequestInfo());
			RicezionePropertiesConfig rConfig = RicezioneContenutiApplicativiServiceUtils.readPropertiesConfig(this.req.getRequestInfo(), this.logCore,null);
			if(rConfig!=null) {
	            if (rConfig.getApiImplementation() != null && !rConfig.getApiImplementation().isEmpty()) {
	               this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.PROPRIETA_CONFIGURAZIONE, rConfig.getApiImplementation());
	            }
	            if (rConfig.getSoggettoFruitore() != null && !rConfig.getSoggettoFruitore().isEmpty()) {
	            	this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.PROPRIETA_SOGGETTO_FRUITORE, rConfig.getSoggettoFruitore());
	            }
	            if (rConfig.getSoggettoErogatore() != null && !rConfig.getSoggettoErogatore().isEmpty()) {
	            	this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.PROPRIETA_SOGGETTO_EROGATORE, rConfig.getSoggettoErogatore());
	            }
			}
			this.msgDiag.setPddContext(this.context.getPddContext(),this.protocolFactory);		
			this.pddContext = this.context.getPddContext();
			
			try{
				if(this.openSPCoopProperties.isTransazioniEnabled()) {
					// NOTA: se gia' esiste con l'id di transazione, non viene ricreata
					TransactionContext.createTransaction((String)this.pddContext.getObject(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE),"RicezioneContenutiApplicativiHTTPtoSOAP.2");
				}
			}catch(Exception e){
				this.logCore.error("Errore durante la creazione della transazione",e);
			}
			
			try{
				this.msgDiag.logPersonalizzato("ricezioneRichiesta.firstAccessRequestStream");
			}catch(Exception e){
				this.logCore.error("Errore generazione diagnostico di ingresso (stream access)",e);
			}
			
			if(this.dumpRaw!=null && this.dumpRaw.isActiveDump()){
				this.dumpRaw.setPddContext(this.msgDiag.getPorta(), this.context.getPddContext());
				this.dumpRaw.serializeContext(this.context, protocol);
			}
			
			DirectVMConnectorInMessage vm = null;
			if(this.req instanceof DirectVMConnectorInMessage directvmconnectorinmessage){
				vm = directvmconnectorinmessage;
			}
			else if(this.req instanceof DumpRawConnectorInMessage dumprawconnectorinmessage &&
					dumprawconnectorinmessage.getWrappedConnectorInMessage() instanceof DirectVMConnectorInMessage directvmconnectorInmessage ){
				vm = directvmconnectorInmessage;
			}
			if(vm!=null && vm.getDirectVMProtocolInfo()!=null){
				vm.getDirectVMProtocolInfo().setInfo(this.pddContext);
			}
			
			
			
			
			/* ------------  PostOutResponseContext ------------- */
			this.postOutResponseContext = new PostOutResponseContext(this.logCore,this.protocolFactory);
			this.postOutResponseContext.setTipoPorta(TipoPdD.DELEGATA);
			this.postOutResponseContext.setPddContext(this.pddContext);
			this.postOutResponseContext.setIdModulo(this.idModulo);
			
			
			
			
			/* ------------  PreInHandler ------------- */
			
			// build context
			PreInRequestContext preInRequestContext = new PreInRequestContext(this.pddContext);
			if(pddContextFromServlet!=null){
				preInRequestContext.getPddContext().addAll(pddContextFromServlet, true);
			}
			preInRequestContext.setTipoPorta(TipoPdD.DELEGATA);
			preInRequestContext.setIdModulo(this.idModulo);
			preInRequestContext.setProtocolFactory(this.protocolFactory);
			preInRequestContext.setRequestInfo(this.requestInfo);
			Map<String, Object> transportContext = new HashMap<>();
			transportContext.put(PreInRequestContext.SERVLET_REQUEST, this.req);
			transportContext.put(PreInRequestContext.SERVLET_RESPONSE, this.res);
			preInRequestContext.setTransportContext(transportContext);	
			preInRequestContext.setLogCore(this.logCore);
			
			// invocazione handler
			GestoreHandlers.preInRequest(preInRequestContext, this.msgDiag, this.logCore);
			
			// aggiungo eventuali info inserite nel preInHandler
			this.pddContext.addAll(preInRequestContext.getPddContext(), false);
			
			// Lettura risposta parametri NotifierInputStream
			NotifierInputStreamParams notifierInputStreamParams = preInRequestContext.getNotifierInputStreamParams();
			this.context.setNotifierInputStreamParams(notifierInputStreamParams);
			
			// Controllo Content Length se attiva una policy di rate limiting
			this.req.checkContentLengthLimit();
			
			// Lettura richiesta con il dump
			if(this.dumpRaw!=null && this.dumpRaw.isActiveDumpRichiesta()){
				this.dumpRaw.serializeRequest(((DumpRawConnectorInMessage)this.req), false, notifierInputStreamParams);
				this.dataIngressoRichiesta = this.req.getDataIngressoRichiesta();
				this.context.setDataIngressoRichiesta(this.dataIngressoRichiesta);
			}

			
			
			


			
			
			
			
			/* ------------ Controllo ContentType -------------------- */
			
			this.msgDiag.logPersonalizzato("ricezioneRichiesta.elaborazioneDati.tipologiaMessaggio");
			messageTypeReq = MessageType.SOAP_11; // rendere parametrico ?
			
			
			
			
			/* ------------  Imbustamento Messaggio di Richiesta ------------- */
			boolean imbustamentoConAttachment = false;
			String tipoAttachment =  HttpConstants.CONTENT_TYPE_OPENSPCOOP2_TUNNEL_SOAP;
			// HeaderTrasporto
			String imb = TransportUtils.getFirstValue(this.req.getHeaderValues(this.openSPCoopProperties.getTunnelSOAPKeyWord_headerTrasporto()));
			if(imb!=null && "true".equals(imb.trim())){
				imbustamentoConAttachment = true;
				String mime = TransportUtils.getFirstValue(this.req.getHeaderValues(this.openSPCoopProperties.getTunnelSOAPKeyWordMimeType_headerTrasporto()));
				if(mime!=null) {
					tipoAttachment = mime.trim();
				}
			}
			if(imb==null){
				// Proprieta FORMBased
				imb = TransportUtils.getFirstValue(this.req.getParameterValues(this.openSPCoopProperties.getTunnelSOAPKeyWord_urlBased()));
				if(imb!=null && "true".equals(imb.trim())){
					imbustamentoConAttachment = true;
					// lettura eventuale tipo di attachment
					String mime = TransportUtils.getFirstValue(this.req.getParameterValues(this.openSPCoopProperties.getTunnelSOAPKeyWordMimeType_urlBased())); 
					if(mime!=null){
						tipoAttachment = mime.trim();
					}
				}
			}
			if(imb==null &&
				// Vedo se fosse indicato nel transport Context
				transportContext.get(this.openSPCoopProperties.getTunnelSOAPKeyWord_urlBased())!=null &&
				"true".equalsIgnoreCase((String)transportContext.get(this.openSPCoopProperties.getTunnelSOAPKeyWord_urlBased()))){
				imbustamentoConAttachment = true;
				// lettura eventuale tipo di attachment
				if(transportContext.get(this.openSPCoopProperties.getTunnelSOAPKeyWordMimeType_urlBased())!=null){
					tipoAttachment = (String) transportContext.get(this.openSPCoopProperties.getTunnelSOAPKeyWordMimeType_urlBased());
				}
			}

			String tipoLetturaRisposta = null;
			try{
				Utilities.printFreeMemory("RicezioneContenutiApplicativiHTTPtoSOAP - Pre costruzione richiesta");
				this.msgDiag.logPersonalizzato("ricezioneRichiesta.elaborazioneDati.inCorso");
				DumpByteArrayOutputStream bout = this.req.getRequest();
				if(bout!=null && bout.size()>0) {
					this.inputBody = bout.toByteArray();
					bout.clearResources();
					bout=null;
				}
				if( this.inputBody == null || this.inputBody.length<=0 ){
					throw new CoreException("Ricevuto nessun contenuto da imbustare");
				}
				this.req.close();
				this.dataIngressoRichiesta = this.req.getDataIngressoRichiesta();
				this.context.setDataIngressoRichiesta(this.dataIngressoRichiesta);
				
				if(imbustamentoConAttachment){
					tipoLetturaRisposta = "Costruzione messaggio SOAP per Tunnel con mimeType "+tipoAttachment;
					this.requestMessage = TunnelSoapUtils.imbustamentoMessaggioConAttachment(org.openspcoop2.pdd.core.Utilities.getOpenspcoop2MessageFactory(this.logCore,this.requestInfo, MessageRole.REQUEST),
							messageTypeReq,MessageRole.REQUEST,this.inputBody,tipoAttachment,
							MailcapActivationReader.existsDataContentHandler(tipoAttachment),this.req.getContentType(), this.openSPCoopProperties.getHeaderSoapActorIntegrazione());					
					this.requestMessage.setTransportRequestContext(this.requestInfo.getProtocolContext());				
				}else{
					tipoLetturaRisposta = "Imbustamento messaggio in un messaggio SOAP";
					String contentTypeForEnvelope = null; // renderlo parametrico soprattutto per soap1.2
					/** OLD String soapAction = "\"OpenSPCoop2\""; */ 
					String soapAction = "\"GovWay\"";
					OpenSPCoop2MessageParseResult pr = org.openspcoop2.pdd.core.Utilities.getOpenspcoop2MessageFactory(this.logCore,this.requestInfo, MessageRole.REQUEST).
							envelopingMessage(messageTypeReq, contentTypeForEnvelope, soapAction, 
							this.requestInfo.getProtocolContext(), this.inputBody, notifierInputStreamParams, 
							this.openSPCoopProperties.getAttachmentsProcessingMode(), 
							this.openSPCoopProperties.isDeleteInstructionTargetMachineXml(),
							this.openSPCoopProperties.useSoapMessageReader(), this.openSPCoopProperties.getSoapMessageReaderBufferThresholdKb());
					if(pr.getParseException()!=null){
						this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO_PARSE_EXCEPTION, pr.getParseException());
					}
					this.requestMessage = pr.getMessage_throwParseException();
				}
				
				if(this.requestInfo.getProtocolContext().getHeaders()==null) {
					this.requestInfo.getProtocolContext().setHeaders(new HashMap<>());
				}
				this.requestInfo.getProtocolContext().removeHeader(HttpConstants.CONTENT_TYPE);
				TransportUtils.setHeader(this.requestInfo.getProtocolContext().getHeaders(),HttpConstants.CONTENT_TYPE, this.requestMessage.getContentType());
				this.requestInfo.setIntegrationRequestMessageType(this.requestMessage.getMessageType());
				
				Utilities.printFreeMemory("RicezioneContenutiApplicativiHTTPtoSOAP - Post costruzione richiesta");
				this.requestMessage.setProtocolName(this.protocolFactory.getProtocol());
				this.requestMessage.setTransactionId(PdDContext.getValue(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE, this.pddContext));
				this.requestMessage.addContextProperty(org.openspcoop2.core.constants.Costanti.REQUEST_INFO,this.requestInfo); // serve nelle comunicazione non stateless (es. riscontro salvato) per poterlo rispedire
				this.requestMessage.addContextProperty(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE,this.pddContext.getObject(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE)); // serve nelle comunicazione non stateless (es. riscontro salvato) per poterlo rispedire
				Object nomePortaInvocataObject = this.context.getPddContext().getObject(CostantiPdD.NOME_PORTA_INVOCATA);
				if(nomePortaInvocataObject instanceof String) {
					this.requestMessage.addContextProperty(CostantiPdD.NOME_PORTA_INVOCATA, nomePortaInvocataObject );
				}
								
			}catch(Exception e){
				this.logCore.error(tipoLetturaRisposta +" con errore: "+e.getMessage(),e);
				errorImbustamentoSoapNonRiuscito=tipoLetturaRisposta +" con errore: "+e.getMessage();
				throw e;
			}
			
			
			/* --------------- SecurityToken --------------- */
			try {
				if(this.requestInfo!=null && this.requestInfo.getProtocolContext()!=null && this.requestInfo.getProtocolContext().getCredential()!=null && 
						this.requestInfo.getProtocolContext().getCredential().getCertificate()!=null &&
						this.requestInfo.getProtocolContext().getCredential().getCertificate().getCertificate()!=null) {
					SecurityTokenUtilities.newSecurityToken(this.pddContext);
				}
			}catch(Exception e){
				this.logCore.error("Costruzione SecurityToken non riuscito: "+e.getMessage(),e);
			}
			
			/* ------------  Elaborazione ------------- */
		
			// Contesto di Richiesta
			this.context.setCredenziali(new Credenziali(this.req.getCredential()));
			this.context.setGestioneRisposta(true); // siamo in una servlet, la risposta deve essere aspettata
			this.context.setInvocazionePDPerRiferimento(false); // la PD con questa servlet non effettuera' mai invocazioni per riferimento.
			this.context.setMessageRequest(this.requestMessage);
			this.context.setUrlProtocolContext(this.requestInfo.getProtocolContext());
			this.context.setMsgDiagnostico(this.msgDiag);
					
			// Log elaborazione dati completata
			this.msgDiag.logPersonalizzato("ricezioneRichiesta.elaborazioneDati.completata");
			
			// se il tracciamento lo prevedo emetto un log
			registraTracciaInRequest();
			
			// Invocazione...
			RicezioneContenutiApplicativi gestoreRichiesta = new RicezioneContenutiApplicativi(this.context, this.generatoreErrore, 
					async ? this : null);
			gestoreRichiesta.process(this.req);
			completeProcess = true;
						
		} catch (Throwable e) {
			
			if(this.context==null){
				// Errore durante la generazione dell'id
				this.context = RicezioneContenutiApplicativiContext.newRicezioneContenutiApplicativiContext(idModuloAsService,this.dataAccettazioneRichiesta,this.requestInfo);
				this.context.setDataIngressoRichiesta(this.dataIngressoRichiesta);
				this.context.setTipoPorta(TipoPdD.DELEGATA);
				this.context.setForceFaultAsXML(true); // siamo in una richiesta http senza SOAP, un SoapFault non ha senso
				this.context.setIdModulo(this.idModulo);
				this.context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.PROTOCOL_NAME, this.protocolFactory.getProtocol());
				this.pddContext = this.context.getPddContext();
				this.msgDiag.setPddContext(this.pddContext,this.protocolFactory);
				if(this.postOutResponseContext!=null){
					this.postOutResponseContext.setPddContext(this.pddContext);
				}
			}
			
			// Se viene lanciata una eccezione, riguarda la richiesta, altrimenti è gestita dopo nel finally.
			Throwable tParsing = null;
			ParseException parseException = null;
			if(this.pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO_PARSE_EXCEPTION)){
				parseException = (ParseException) this.pddContext.removeObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO_PARSE_EXCEPTION);
				if(parseException!=null) {
					tParsing = parseException.getParseException();
				}
			}
			if(tParsing==null && (this.requestMessage==null || this.requestMessage.getParseException() == null)){
				tParsing = ParseExceptionUtils.getParseException(e);
			}
					
			// Genero risposta con errore
			if(errorImbustamentoSoapNonRiuscito!=null){
				this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO, true);
				this.logCore.error("ImbustamentoSOAP",e);
				Throwable tMessage = null;
				if(tParsing!=null){
					tMessage = tParsing;
				}
				else{
					tMessage = e;
				}
				String msgErrore = tMessage.getMessage();
				if(msgErrore==null){
					msgErrore = tMessage.toString();
				}
				this.msgDiag.logErroreGenerico(errorImbustamentoSoapNonRiuscito+"  "+msgErrore, "ImbustamentoSOAP");
				
				IntegrationFunctionError integrationFunctionError = IntegrationFunctionError.UNPROCESSABLE_REQUEST_CONTENT;
				if( parseException!=null && parseException.getSourceException()!=null &&
						TimeoutIOException.isTimeoutIOException(parseException.getSourceException())) {
					integrationFunctionError = IntegrationFunctionError.REQUEST_TIMED_OUT;
				}
				else if( parseException!=null && parseException.getSourceException()!=null &&
						LimitExceededIOException.isLimitExceededIOException(parseException.getSourceException())) {
					integrationFunctionError = IntegrationFunctionError.REQUEST_SIZE_EXCEEDED;
				}
				
				this.responseMessage = this.generatoreErrore.build(this.pddContext,integrationFunctionError,
						ErroriIntegrazione.ERRORE_422_IMBUSTAMENTO_SOAP_NON_RIUSCITO_RICHIESTA_APPLICATIVA.
						getErrore422_MessaggioSOAPNonGenerabileTramiteImbustamentoSOAP(errorImbustamentoSoapNonRiuscito),tMessage,null);
			}
			else if(tParsing!=null){
				this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO, true);
				String msgErrore = tParsing.getMessage();
				if(msgErrore==null){
					msgErrore = tParsing.toString();
				}
				this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, msgErrore);
				this.logCore.error(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RICHIESTA,e);
				this.msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RICHIESTA);
				
				IntegrationFunctionError integrationFunctionError = IntegrationFunctionError.UNPROCESSABLE_REQUEST_CONTENT;
				if( parseException!=null && parseException.getSourceException()!=null &&
						TimeoutIOException.isTimeoutIOException(parseException.getSourceException())) {
					integrationFunctionError = IntegrationFunctionError.REQUEST_TIMED_OUT;
				}
				else if( parseException!=null && parseException.getSourceException()!=null &&
						LimitExceededIOException.isLimitExceededIOException(parseException.getSourceException())) {
					integrationFunctionError = IntegrationFunctionError.REQUEST_SIZE_EXCEEDED;
				}
				
				this.responseMessage = this.generatoreErrore.build(this.pddContext,integrationFunctionError,
						ErroriIntegrazione.ERRORE_432_PARSING_EXCEPTION_RICHIESTA.
						getErrore432_MessaggioRichiestaMalformato(tParsing),tParsing,null);
			}
			else if (e instanceof HandlerException he) {
				this.logCore.error("ErroreGenerale (HandlerException)",e);
				if(he.isEmettiDiagnostico()) {
					this.msgDiag.logErroreGenerico(e, "Generale(richiesta-handler)");
				}
				ErroreIntegrazione errore = he.convertToErroreIntegrazione();
				if(errore==null) {
					errore = ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.get5XX_ErroreProcessamento("Generale(richiesta)");
				}
				IntegrationFunctionError integrationError = he.getIntegrationFunctionError();
				if(integrationError==null) {
					integrationError = IntegrationFunctionError.BAD_REQUEST;
				}
				this.responseMessage = this.generatoreErrore.build(this.pddContext,integrationError,errore,e,null);
				he.customized(this.responseMessage);
			}
			else{
				this.logCore.error("ErroreGenerale",e);
				this.msgDiag.logErroreGenerico(e, "Generale(richiesta)");
				this.responseMessage = this.generatoreErrore.build(this.pddContext,IntegrationFunctionError.BAD_REQUEST,
						ErroriIntegrazione.ERRORE_426_SERVLET_ERROR.
						getErrore426_ServletError(true, e),e,null);
			}
		}
		finally{

			try {
				if(!completeProcess || !async) {
					this.completeEngine(AsyncResponseCallbackClientEvent.NONE,completeProcess);
				}

			}finally {
				try {
					GestoreRichieste.saveRequestConfig(this.requestInfo);
				}catch(Throwable e) {
					this.logCore.error("Errore durante il salvataggio dei dati della richiesta: "+e.getMessage(),e);
				}	
			}
		}
	}

	@Override
	public void asyncComplete(AsyncResponseCallbackClientEvent clientEvent, Object ... args) throws ConnectorException { // Questo metodo verrà chiamato dalla catena di metodi degli oggetti (IAsyncResponseCallback) fatta scaturire dal response callback dell'Async Client NIO
		this.completeEngine(clientEvent, true);
	}
	private void completeEngine(AsyncResponseCallbackClientEvent clientEvent, boolean completeProcess) throws ConnectorException {

		if(completeProcess) {
			this.responseMessage = this.context.getMessageResponse();
		}


		// Finally Request	
		if((this.requestMessage!=null && this.requestMessage.getParseException() != null) || 
				(this.pddContext!=null && this.pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO_PARSE_EXCEPTION))){
			this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO, true);
			ParseException parseException = null;
			if( this.requestMessage!=null && this.requestMessage.getParseException() != null ){
				parseException = this.requestMessage.getParseException();
			}
			else{
				parseException = (ParseException) this.pddContext.getObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RICHIESTA_NON_RICONOSCIUTO_PARSE_EXCEPTION);
			}
			String msgErrore = null;
			if (parseException != null && parseException.getParseException() != null)
				msgErrore = parseException.getParseException().getMessage();
			if(msgErrore==null){
				msgErrore = parseException.getParseException().toString();
			}
			this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, msgErrore);
			
			if (parseException != null)
				this.logCore.error(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RICHIESTA,parseException.getSourceException());
			this.msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RICHIESTA);
			
			IntegrationFunctionError integrationFunctionError = IntegrationFunctionError.UNPROCESSABLE_REQUEST_CONTENT;
			if( parseException!=null && parseException.getSourceException()!=null &&
					TimeoutIOException.isTimeoutIOException(parseException.getSourceException())) {
				integrationFunctionError = IntegrationFunctionError.REQUEST_TIMED_OUT;
			}
			else if( parseException!=null && parseException.getSourceException()!=null &&
					LimitExceededIOException.isLimitExceededIOException(parseException.getSourceException())) {
				integrationFunctionError = IntegrationFunctionError.REQUEST_SIZE_EXCEEDED;
			} else if (parseException != null) {
				this.responseMessage = this.generatoreErrore.build(this.pddContext, integrationFunctionError,
						ErroriIntegrazione.ERRORE_432_PARSING_EXCEPTION_RICHIESTA.
						getErrore432_MessaggioRichiestaMalformato(parseException.getParseException()),
						parseException.getParseException(),null);
			}
		}
		else if( (this.responseMessage!=null && this.responseMessage.getParseException() != null) ||
				(this.pddContext!=null && this.pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CONTENUTO_RISPOSTA_NON_RICONOSCIUTO_PARSE_EXCEPTION))){
			if(this.pddContext!=null) {
				this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RISPOSTA_NON_RICONOSCIUTO, true);
			}
			ParseException parseException = null;
			if( this.responseMessage!=null && this.responseMessage.getParseException() != null ){
				parseException = this.responseMessage.getParseException();
			}
			else{
				parseException = (ParseException) this.pddContext.getObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RISPOSTA_NON_RICONOSCIUTO_PARSE_EXCEPTION);
			}
			String msgErrore = parseException.getParseException().getMessage();
			if(msgErrore==null){
				msgErrore = parseException.getParseException().toString();
			}
			this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, msgErrore);
			this.logCore.error(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RISPOSTA,parseException.getSourceException());
			this.msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RISPOSTA);
			this.responseMessage = this.generatoreErrore.build(this.pddContext, IntegrationFunctionError.UNPROCESSABLE_RESPONSE_CONTENT,
					ErroriIntegrazione.ERRORE_440_PARSING_EXCEPTION_RISPOSTA.
					getErrore440_MessaggioRispostaMalformato(parseException.getParseException()),
					parseException.getParseException(),null);
		}
		
		try{
			// Se non sono stati recuperati i dati delle url, provo a recuperarli
			URLProtocolContext urlProtocolContext = this.context!=null ? this.context.getUrlProtocolContext() : null;
			if(urlProtocolContext==null){
				urlProtocolContext = this.req.getURLProtocolContext();
			}
			if(urlProtocolContext!=null){
				String urlInvocazione = urlProtocolContext.getUrlInvocazione_formBased();
				if(urlProtocolContext.getFunction()!=null){
					urlInvocazione = "["+urlProtocolContext.getFunction()+"] "+urlInvocazione;
				}
				this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.URL_INVOCAZIONE, urlInvocazione);
			}
		}catch(Throwable t){
			// ignore
		}
		try{
			Credenziali credenziali = this.context!=null ? this.context.getCredenziali() : null;
			if(credenziali==null){
				credenziali = new Credenziali(this.req.getCredential());
			}
			if(credenziali!=null && this.pddContext!=null){
				this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.CREDENZIALI_INVOCAZIONE, credenziali.toString());
			}
		}catch(Throwable t){
			// ignore
		}
		
		// *** GB ***
		try{
			this.req.close();
		}catch(Exception e){
			this.logCore.error("Request.close() error: "+e.getMessage(),e);
		}
		// *** GB ***
		
			
			
		// Imposto risposta

		Date dataPrimaSpedizioneRisposta = DateManager.getDate();
		Date dataRispostaSpedita = null; 
		Transazione transazioneDaAggiornare = null;
		
		if(this.context != null) {
			if(this.context.getMsgDiagnostico()!=null)
				this.msgDiag = this.context.getMsgDiagnostico();
			if(this.context.getResponseHeaders()==null)
				this.context.setResponseHeaders(new HashMap<>());
			
			ServicesUtils.setGovWayHeaderResponse(this.requestMessage!=null ? this.requestMessage.getServiceBinding() : this.requestInfo.getProtocolServiceBinding(),
					this.responseMessage, this.openSPCoopProperties,
					this.context.getResponseHeaders(), this.logCore, true, this.context.getPddContext(), this.requestInfo);
		}
		
		if(this.context != null && this.context.getResponseHeaders()!=null){
			Iterator<String> keys = this.context.getResponseHeaders().keySet().iterator();
			while (keys.hasNext()) {
				String key = keys.next();
				List<String> values = this.context.getResponseHeaders().get(key);
				if(values!=null && !values.isEmpty()) {
					for (int i = 0; i < values.size(); i++) {
						String value = values.get(i);
						String verbo = "";
						try{
							if(i==0) {
								verbo = "set";
								this.res.setHeader(key,value);
							}
							else {
								verbo = "add";
								this.res.addHeader(key,value);
							}
			    		}catch(Exception e){
			    			this.logCore.error("Response."+verbo+"Header("+key+","+value+") set failed: "+e.getMessage(),e);
			    		}	
					}
				}
	    	}	
		}
		if(this.context!=null && this.context.getProtocol()!=null){
			
			this.generatoreErrore.updateDominio(this.context.getIdentitaPdD());
			
			IDServizio idServizio = null;
			try{
				idServizio = IDServizioFactory.getInstance().getIDServizioFromValues(this.context.getProtocol().getTipoServizio(), 
						this.context.getProtocol().getServizio(), 
						this.context.getProtocol().getErogatore(), 
						this.context.getProtocol().getVersioneServizio());
			}catch(Exception e){ 
				// non dovrebbe succedere eccezione
			}
			if(idServizio!=null){
				idServizio.setAzione(this.context.getProtocol().getAzione());
				this.generatoreErrore.updateInformazioniCooperazione(this.context.getProtocol().getFruitore(), idServizio);
			}
						
			String servizioApplicativo = null;
			if(this.context.getIntegrazione()!=null){
				servizioApplicativo = this.context.getIntegrazione().getServizioApplicativoFruitore();
			}
			this.generatoreErrore.updateInformazioniCooperazione(servizioApplicativo);
			
			this.generatoreErrore.updateProprietaErroreApplicativo(this.context.getProprietaErroreAppl());
			
		}
		DirectVMConnectorOutMessage vm = null;
		if(this.res instanceof DirectVMConnectorOutMessage directvmconnectoroutmessage){
			vm = directvmconnectoroutmessage;
		}
		else if(this.req instanceof DumpRawConnectorOutMessage dumprawconnectoroutmessage &&
				dumprawconnectoroutmessage.getWrappedConnectorOutMessage() instanceof DirectVMConnectorOutMessage directvmconnectorOutmessage ){
			vm = directvmconnectorOutmessage;
		}
		if(vm!=null &&
			this.context!=null && this.context.getPddContext()!=null){
			DirectVMProtocolInfo pInfo = new DirectVMProtocolInfo();
			Object oIdTransazione = this.context.getPddContext().getObject(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE);
			if(oIdTransazione!=null){
				pInfo.setIdTransazione((String)oIdTransazione);
			}
			if(this.context.getProtocol()!=null){
				if(this.context.getProtocol().getIdRichiesta()!=null){
					pInfo.setIdMessaggioRichiesta(this.context.getProtocol().getIdRichiesta());
				}
				if(this.context.getProtocol().getIdRisposta()!=null){
					pInfo.setIdMessaggioRisposta(this.context.getProtocol().getIdRisposta());
				}
			}
			vm.setDirectVMProtocolInfo(pInfo);
		}
		
		InformazioniErroriInfrastrutturali informazioniErrori = ServicesUtils.readInformazioniErroriInfrastrutturali(this.pddContext);
		
		EsitoTransazione esito = null;
		String descrizioneSoapFault = "";
		int statoServletResponse = 200;
		Throwable erroreConsegnaRisposta = null; 
		boolean httpEmptyResponse = false;
		long lengthOutResponse = -1;
		boolean erroreConnessioneClient = false;
		boolean sendInvoked = false;
		boolean registraTracciaOutResponse = false;
		try{
			if(this.responseMessage!=null && !this.responseMessage.isForcedEmptyResponse() && (this.responseMessage.getForcedResponse()==null)){
					
				// force response code
				boolean forced = false;
				if(this.responseMessage.getForcedResponseCode()!=null){
					try{
						statoServletResponse = Integer.parseInt(this.responseMessage.getForcedResponseCode());
						forced = true;
					}catch(Exception e){
						// ignore
					}
				}
				
				if(ServiceBinding.SOAP.equals(this.responseMessage.getServiceBinding())) {
										
					SOAPBody body = this.responseMessage.castAsSoap().getSOAPBody();
					String contentTypeRisposta = null;
					byte[] risposta = null;
					if(body!=null && body.hasFault()){
						statoServletResponse = 500; // cmq e' un errore come l'errore applicativo
						String msgError = SoapUtils.safe_toString(this.responseMessage.getFactory(), body.getFault(), false, this.logCore);
						/**risposta=msgError.getBytes();*/
						org.openspcoop2.message.xml.MessageXMLUtils xmlUtils = org.openspcoop2.message.xml.MessageXMLUtils.getInstance(this.responseMessage.getFactory());
						risposta=xmlUtils.toByteArray(body.getFault(), true);
						/**System.out.println("ELABORATO:"+new String(risposta));*/
						contentTypeRisposta = this.responseMessage.getContentType();
						descrizioneSoapFault = " ("+msgError+")";	
					}else{
						risposta=TunnelSoapUtils.sbustamentoMessaggio(this.responseMessage);
						if(risposta==null || risposta.length<=0){
							// Si puo' entrare in questo caso, se nel messaggio Soap vi era presente solo l'header
							risposta = null;
							if(!forced)
								statoServletResponse = 202;
						}else{
							
							SOAPElement child = SoapUtils.getNotEmptyFirstChildSOAPElement(body);
							if(child!=null &&
								this.protocolFactory.createErroreApplicativoBuilder().isErroreApplicativo(child)){
								statoServletResponse = 500;
							}
							
							// Non serve la updateContentType. Il messaggio e' gia' stato serializzato ed il cType e' corretto.  
							if(TunnelSoapUtils.isTunnelOpenSPCoopSoap(this.responseMessage.getFactory(), body)){
								contentTypeRisposta = TunnelSoapUtils.getContentTypeTunnelOpenSPCoopSoap(body);
							}else{
								contentTypeRisposta = this.responseMessage.getContentType();
							}
						}
					}
					
					// transfer length
					if(risposta!=null){
						lengthOutResponse = risposta.length;
						ServicesUtils.setTransferLength(this.openSPCoopProperties.getTransferLengthModes_ricezioneContenutiApplicativi(), 
								this.req, this.res, Long.valueOf(risposta.length));
					}
					
					// httpstatus
					this.res.setStatus(statoServletResponse);
					
					// content type
					if(contentTypeRisposta!=null){
						this.res.setContentType(contentTypeRisposta);
					}
					
					// esito calcolato prima del sendResponse, per non consumare il messaggio
					esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(), 
							statoServletResponse, this.requestInfo.getIntegrationServiceBinding(),
							this.responseMessage, this.context.getProprietaErroreAppl(),informazioniErrori,
							this.pddContext);
					
					// httpHeaders
					this.res.sendResponseHeaders(this.responseMessage);					
					
					// se il tracciamento lo prevedo emetto un log
					registraTracciaOutResponse = true;
					transazioneDaAggiornare = registraTracciaOutResponse(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
							esito, statoServletResponse,
							erroreConsegnaRisposta, lengthOutResponse);
					
					// contenuto
					if(risposta!=null){
						sendInvoked = true;
						this.res.sendResponse(DumpByteArrayOutputStream.newInstance(risposta));
					}
				}
				else {
					
					// transfer length
					ServicesUtils.setTransferLength(this.openSPCoopProperties.getTransferLengthModes_ricezioneContenutiApplicativi(), 
							this.req, this.res, this.responseMessage);
					
					// content type
					// Alcune implementazioni richiedono di aggiornare il Content-Type
					this.responseMessage.updateContentType();
					ServicesUtils.setContentType(this.responseMessage, this.res);
					
					// http status
					boolean consume = true;
					if(this.responseMessage.castAsRest().isProblemDetailsForHttpApis_RFC7807() || 
							(MessageRole.FAULT.equals(this.responseMessage.getMessageRole()) &&
								(
								MessageType.XML.equals(this.responseMessage.getMessageType()) 
										|| 
								MessageType.JSON.equals(this.responseMessage.getMessageType())
								)
							)
						) {
						consume = false; // può essere usato nel post out response handler
						String contentAsString = null;
						try {
							contentAsString = this.responseMessage.castAsRest().getContentAsString();
						}catch(Throwable t) {
							this.logCore.error("Parsing errore non riuscito: "+t.getMessage(),t);
						}
						if(contentAsString!=null && StringUtils.isNotEmpty(contentAsString)) {
							descrizioneSoapFault = " ("+contentAsString+")";
						}
					}
					this.res.setStatus(statoServletResponse);
					
					// esito calcolato prima del sendResponse, per non consumare il messaggio
					esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(), 
							statoServletResponse, this.requestInfo.getIntegrationServiceBinding(),
							this.responseMessage, this.context.getProprietaErroreAppl(), informazioniErrori,
							this.pddContext);
					
					// se il tracciamento lo prevedo emetto un log
					registraTracciaOutResponse = true;
					transazioneDaAggiornare = registraTracciaOutResponse(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
							esito, statoServletResponse,
							erroreConsegnaRisposta, lengthOutResponse);
					
					// contenuto
					Utilities.printFreeMemory("RicezioneContenutiApplicativiDirect - Pre scrittura risposta");
					
					// Il contentLenght, nel caso di TransferLengthModes.CONTENT_LENGTH e' gia' stato calcolato
					// con una writeTo senza consume. Riuso il solito metodo per evitare differenze di serializzazione
					// e cambiare quindi il content length effettivo.
					sendInvoked = true;
					if(TransferLengthModes.CONTENT_LENGTH.equals(this.openSPCoopProperties.getTransferLengthModes_ricezioneContenutiApplicativi())){
						this.res.sendResponse(this.responseMessage, false);
					} else {
						this.res.sendResponse(this.responseMessage, consume);
					}
					Utilities.printFreeMemory("RicezioneContenutiApplicativiDirect - Post scrittura risposta");
					
				}
				
			}
			else if(this.responseMessage!=null && this.responseMessage.getForcedResponse()!=null) {
				byte[]response = this.responseMessage.getForcedResponse().getContent();
				/**if(response==null) {
					throw new Exception("Trovata configurazione 'forcedResponse' senza una vera risposta");
				}*/
				if(response!=null) {
					lengthOutResponse = response.length;
				}
			
				if(response!=null && response.length<1024) {
					// Se il messaggio non è troppo grande lo aggiungo al diagnostico
					try {
						descrizioneSoapFault = "("+new String(response)+")";
					}catch(Throwable t) {
						descrizioneSoapFault = "";
					}
				}
				
				if(this.responseMessage.getForcedResponse().getHeadersValues()!=null &&
						this.responseMessage.getForcedResponse().getHeadersValues().size()>0) {
					Iterator<String> keys = this.responseMessage.getForcedResponse().getHeadersValues().keySet().iterator();
					while (keys.hasNext()) {
						String key = keys.next();
						List<String> values = this.responseMessage.getForcedResponse().getHeadersValues().get(key);
						if(values!=null && !values.isEmpty()) {
							for (int i = 0; i < values.size(); i++) {
								String value = values.get(i);
								String verbo = "";
								try{
									if(i==0) {
										verbo = "set";
										this.res.setHeader(key,value);
									}
									else {
										verbo = "add";
										this.res.addHeader(key,value);
									}
					    		}catch(Exception e){
					    			this.logCore.error("Response(Forced)."+verbo+"Header("+key+","+value+") error: "+e.getMessage(),e);
					    		}	
							}
						}
			    	}	
				}
				
				if(this.responseMessage.getForcedResponse().getContentType()!=null) {
					this.res.setContentType(this.responseMessage.getForcedResponse().getContentType());
				}
				
				if(this.responseMessage.getForcedResponse().getResponseCode()!=null) {
					try{
						statoServletResponse = Integer.parseInt(this.responseMessage.getForcedResponse().getResponseCode());
					}catch(Exception e){
						// ignore
					}
				}
				else if(this.responseMessage!=null && this.responseMessage.getForcedResponseCode()!=null) {
					try{
						statoServletResponse = Integer.parseInt(this.responseMessage.getForcedResponseCode());
					}catch(Exception e){
						// ignore
					}
				}
				this.res.setStatus(statoServletResponse);
				
				if (this.context != null) {
					// esito calcolato prima del sendResponse, per non consumare il messaggio
					esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(), 
							statoServletResponse, this.requestInfo.getIntegrationServiceBinding(),
							this.responseMessage, this.context.getProprietaErroreAppl(),informazioniErrori,
							this.pddContext);
				}
				
				// se il tracciamento lo prevedo emetto un log
				registraTracciaOutResponse = true;
				transazioneDaAggiornare = registraTracciaOutResponse(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
						esito, statoServletResponse,
						erroreConsegnaRisposta, lengthOutResponse);
				
				if(response!=null) {
					sendInvoked = true;
					this.res.sendResponse(DumpByteArrayOutputStream.newInstance(response));
				}
				
			}			
			else{
				// httpstatus
				if(this.responseMessage!=null && this.responseMessage.getForcedResponseCode()!=null) {
					try{
						statoServletResponse = Integer.parseInt(this.responseMessage.getForcedResponseCode());
					}catch(Exception e){}
				}
				else {
					statoServletResponse = this.protocolFactory.createProtocolManager().getHttpReturnCodeEmptyResponseOneWay();
				}
				this.res.setStatus(statoServletResponse);
				httpEmptyResponse = true;
				esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(), 
						statoServletResponse, this.requestInfo.getIntegrationServiceBinding(),
						this.responseMessage, this.context.getProprietaErroreAppl(),informazioniErrori,
						this.pddContext);
				// carico-vuoto
				
				// se il tracciamento lo prevedo emetto un log
				registraTracciaOutResponse = true;
				transazioneDaAggiornare = registraTracciaOutResponse(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
						esito, statoServletResponse,
						erroreConsegnaRisposta, lengthOutResponse);

			}
			
		}catch(Throwable e){
			this.logCore.error("ErroreGenerale",e);
			erroreConsegnaRisposta = e;
			
			erroreConnessioneClient = ServicesUtils.isConnessioneClientNonDisponibile(e);
			
			try{
				esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(),EsitoTransazioneName.ERRORE_PROCESSAMENTO_PDD_5XX);
			}catch(Exception eBuildError){
				esito = EsitoTransazione.ESITO_TRANSAZIONE_ERROR;
			}
			
			// Genero risposta con errore
			try{
				if(!sendInvoked) {
					// nel caso sia già stato inoltrata una risposta non e' più possibile modificarlo cosi come tutti gli header etc...	
					byte [] rispostaErrore = null;
					List<Integer> returnCode = new ArrayList<>();
					InformazioniErroriInfrastrutturali informazioniErroriInfrastrutturaliError = null;
					if( (this.responseMessage!=null && this.responseMessage.getParseException() != null) ||
							(this.pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CONTENUTO_RISPOSTA_NON_RICONOSCIUTO_PARSE_EXCEPTION))){
						ParseException parseException = null;
						if( this.responseMessage!=null && this.responseMessage.getParseException() != null ){
							parseException = this.responseMessage.getParseException();
						}
						else{
							parseException = (ParseException) this.pddContext.getObject(org.openspcoop2.core.constants.Costanti.CONTENUTO_RISPOSTA_NON_RICONOSCIUTO_PARSE_EXCEPTION);
						}
						String msgErrore = parseException.getParseException().getMessage();
						if(msgErrore==null){
							msgErrore = parseException.getParseException().toString();
						}
						this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, msgErrore);
						this.logCore.error(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RISPOSTA,parseException.getSourceException());
						this.msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_PARSING_EXCEPTION_RISPOSTA);
											
						rispostaErrore = this.generatoreErrore.buildAsByteArray(this.pddContext, IntegrationFunctionError.UNPROCESSABLE_RESPONSE_CONTENT,
								ErroriIntegrazione.ERRORE_440_PARSING_EXCEPTION_RISPOSTA.
								getErrore440_MessaggioRispostaMalformato(parseException.getParseException()),
								returnCode);
						
						informazioniErroriInfrastrutturaliError = new InformazioniErroriInfrastrutturali();
						informazioniErroriInfrastrutturaliError.setContenutoRispostaNonRiconosciuto(true);
					} 
					else{
						IntegrationFunctionError ife = IntegrationFunctionError.INTERNAL_RESPONSE_ERROR;
						if(e instanceof HandlerException he &&
							he.getIntegrationFunctionError()!=null) {
							ife = he.getIntegrationFunctionError();
						}
						rispostaErrore = this.generatoreErrore.buildAsByteArray(this.pddContext, ife,
								ErroriIntegrazione.ERRORE_426_SERVLET_ERROR.
								getErrore426_ServletError(false, e),
								returnCode);
					}
					
					// transfer length
					lengthOutResponse = rispostaErrore.length;
					ServicesUtils.setTransferLength(this.openSPCoopProperties.getTransferLengthModes_ricezioneContenutiApplicativi(), 
							this.req, this.res, Long.valueOf(rispostaErrore.length));
					
					// httpstatus
					//statoServletResponse = 500; // Nella servlet con sbustamento non devo ritornare 500
					statoServletResponse = 200;
					if(returnCode!=null && !returnCode.isEmpty()){
						statoServletResponse = returnCode.get(0);
					}
					this.res.setStatus(statoServletResponse);
					
					// esito calcolato prima del sendResponse, per non consumare il messaggio
					if(informazioniErroriInfrastrutturaliError!=null) {
						esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(),
								statoServletResponse, this.requestInfo.getIntegrationServiceBinding(),
								null, this.context.getProprietaErroreAppl(), informazioniErroriInfrastrutturaliError,
								this.pddContext);
					}
					
					// content type
					this.res.setContentType("text/xml");
					
					// se il tracciamento lo prevedo emetto un log
					// se ho già provato prima a tracciare non lo faccio un'altra volta
					if(!registraTracciaOutResponse) {
						transazioneDaAggiornare = registraTracciaOutResponse(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
							esito, statoServletResponse,
							erroreConsegnaRisposta, lengthOutResponse);
					}
					
					// contenuto
					this.res.sendResponse(DumpByteArrayOutputStream.newInstance(rispostaErrore));
				}
						
			}catch(Throwable error){
				
				if(!erroreConnessioneClient){
					erroreConnessioneClient = ServicesUtils.isConnessioneClientNonDisponibile(error);
				}
				
				this.logCore.error("Generazione di un risposta errore non riuscita",error);
				statoServletResponse = 500; // ERRORE EFFETTIVO!
				try{
					this.res.setStatus(500);
				}catch(Exception eStatus){
					this.logCore.error("Response.setStatus(500) error: "+eStatus.getMessage(),eStatus);
				}
				byte[] ris = error.toString().getBytes();
				try{
					this.res.sendResponse(DumpByteArrayOutputStream.newInstance(ris));
				}catch(Exception erroreStreamChiuso){ 
					erroreConnessioneClient = true;
					//se lo stream non e' piu' disponibile non si potra' consegnare alcuna risposta
				}
				lengthOutResponse = ris.length;
			}
			
		}
		finally{
			if(!sendInvoked) {
				// nel caso sia già stato inoltrata una risposta non e' più possibile modificarlo cosi come tutti gli header etc...			
				statoServletResponse = this.res.getResponseStatus(); // puo' essere "trasformato" da api engine
			}
			this.msgDiag.addKeyword(CostantiPdD.KEY_CODICE_CONSEGNA, ""+statoServletResponse);
			this.msgDiag.addKeyword(CostantiPdD.KEY_SOAP_FAULT, descrizioneSoapFault);
			
			try{
				
				// Flush and close response
				// NOTA: per poter ottenere l'errore di BrokenPipe sempre, deve essere disabilitato il socketBufferOutput sul servlet container.
				// Se non lo si disabilta, l'errore viene ritornato solo se il messaggio supera la dimensione del buffer (default: 8192K)
				// Ad esempio in tomcat utilizzare (socketBuffer="-1"): 
				//    <Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}" 
	            //       connectionTimeout="20000" redirectPort="8443" socketBuffer="-1" />
				this.res.flush(true);
				this.res.close(clientEvent,true);
				
				dataRispostaSpedita = DateManager.getDate();
				
				// Emetto diagnostico
				if(erroreConsegnaRisposta!=null){
					
					// Risposta non ritornata al servizio applicativo, il socket verso il servizio applicativo era chiuso o cmq inutilizzabile
					this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_CONSEGNA, erroreConsegnaRisposta.toString()); // NOTA: lasciare e.toString()
					this.msgDiag.logPersonalizzato("consegnaRispostaApplicativaFallita");
					
				}else{
					if(httpEmptyResponse){
						this.msgDiag.logPersonalizzato("consegnaRispostaApplicativaVuota");
					}else{
						if(statoServletResponse>=300)
							this.msgDiag.logPersonalizzato("consegnaRispostaApplicativaKoEffettuata");
						else
							this.msgDiag.logPersonalizzato("consegnaRispostaApplicativaOkEffettuata");
					}
				}
				
			}catch(Exception e){
				
				erroreConnessioneClient = true;
				
				this.logCore.error("Chiusura stream non riuscita",e);
				
				// Risposta non ritornata al servizio applicativo, il socket verso il servizio applicativo era chiuso o cmq inutilizzabile
				this.msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_CONSEGNA, e.toString()); // NOTA: lasciare e.toString()
				
				this.msgDiag.logPersonalizzato("consegnaRispostaApplicativaFallita");
				
				erroreConsegnaRisposta = e;
				
				if(esito!=null){
					if(EsitoTransazioneName.OK.equals(esito.getName())){
						// non è ok, essendo andato in errore il flush
						try{
							esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(),EsitoTransazioneName.ERRORE_PROCESSAMENTO_PDD_5XX);
						}catch(Exception eBuildError){
							esito = EsitoTransazione.ESITO_TRANSAZIONE_ERROR;
						}
					}
				}
				else{
					// non dovrebbe mai essere null
				}
				
			} finally {
				if(dataRispostaSpedita==null) {
					dataRispostaSpedita = DateManager.getDate();
				}
			}
			
			if(this.dumpRaw!=null && this.dumpRaw.isActiveDumpRisposta()){
				this.dumpRaw.serializeResponse(((DumpRawConnectorOutMessage)this.res));
			}
			
		}
		
		if(erroreConnessioneClient){
			// forzo esito errore connessione client
			try{
				esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(),EsitoTransazioneName.ERRORE_CONNESSIONE_CLIENT_NON_DISPONIBILE);
			}catch(Exception eBuildError){
				esito = EsitoTransazione.ESITO_TRANSAZIONE_ERROR;
			}
		}
		else if(EsitoTransazioneName.OK.equals(esito.getName()) && this.context!=null && this.context.getPddContext()!=null && this.context.getPddContext().containsKey(org.openspcoop2.core.constants.Costanti.EMESSI_DIAGNOSTICI_ERRORE)) {
			// caso di errore generato durante il tracciamento dopo aver calcolato l'esito, in cui non viene sollevata una eccezione
			esito = ServicesUtils.updateEsitoConAnomalie(esito, this.logCore, this.protocolFactory);
		}
		
		
		
		
		
		
		
		// *** Chiudo connessione verso PdD Destinazione per casi stateless ***
		String location = "...";
		try{
			IConnettore c = null;
			if(this.context!=null && this.context.getPddContext()!=null && this.context.getPddContext().containsKey(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE)) {
				this.idTransazione = (String)this.context.getPddContext().getObject(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE);
			}
			if(this.idTransazione!=null) {
			/**if(this.context.getIdMessage()!=null){*/
				c = RepositoryConnettori.removeConnettorePD(
						/**this.context.getIdMessage()*/
						this.idTransazione
						);
			}
			if(c!=null){
				location = c.getLocation();
				c.disconnect();
			}
		}catch(Exception e){
			this.msgDiag.logDisconnectError(e, location);
		}
		
		

		
		
		
		
		
		
		
		/* ------------  PostOutResponseHandler ------------- */
		
		if(this.postOutResponseContext!=null){
			try {
				updateContext(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
						esito, statoServletResponse,
						erroreConsegnaRisposta, lengthOutResponse);
				this.postOutResponseContext.setTransazioneDaAggiornare(transazioneDaAggiornare);
			}catch(Exception e){
				this.msgDiag.logErroreGenerico(e,"postOutResponse, preparazione contesto");
			}
			
			GestoreHandlers.postOutResponse(this.postOutResponseContext, this.msgDiag, this.logCore);
		}
		
		
		
		
		
		
		
		
		
		
		// *** Rilascio risorse NotifierInputStream ***
		
		// request
		try{
			if(this.requestMessage!=null && this.requestMessage.getNotifierInputStream()!=null){
				this.requestMessage.getNotifierInputStream().close();
			}
		}catch(Exception e){
			this.msgDiag.logErroreGenerico(e,"Rilascio risorse NotifierInputStream richiesta");
		}
		
		// response
		try{
			if(this.responseMessage!=null && this.responseMessage.getNotifierInputStream()!=null){
				this.responseMessage.getNotifierInputStream().close();
			}
		}catch(Exception e){
			this.msgDiag.logErroreGenerico(e,"Rilascio risorse NotifierInputStream risposta");
		}
		
		
		
		
		
		
		
		
		
		// *** GB ***
		this.requestMessage = null;
		this.responseMessage = null;
		// *** GB ***
		

	}


	private void updateContext(Date dataPrimaSpedizioneRisposta, Date dataRispostaSpedita,
			EsitoTransazione esito, int statoServletResponse,
			Throwable erroreConsegnaRisposta, long lengthOutResponse) throws ConnectorException {
		if(this.postOutResponseContext!=null){
			this.postOutResponseContext.getPddContext().addObject(CostantiPdD.DATA_ACCETTAZIONE_RICHIESTA, this.dataAccettazioneRichiesta);
			if(this.dataIngressoRichiesta!=null){
				this.postOutResponseContext.getPddContext().addObject(CostantiPdD.DATA_INGRESSO_RICHIESTA, this.dataIngressoRichiesta);
			}
			this.postOutResponseContext.setDataElaborazioneMessaggio(DateManager.getDate());
			this.postOutResponseContext.setDataPrimaSpedizioneRisposta(dataPrimaSpedizioneRisposta);
			this.postOutResponseContext.setDataRispostaSpedita(dataRispostaSpedita);
			if(erroreConsegnaRisposta==null){
				this.postOutResponseContext.setEsito(esito);
			}else{
				try{
					esito = this.protocolFactory.createEsitoBuilder().getEsito(this.req.getURLProtocolContext(),EsitoTransazioneName.ERRORE_PROCESSAMENTO_PDD_5XX);
				}catch(Exception eBuildError){
					esito = EsitoTransazione.ESITO_TRANSAZIONE_ERROR;
				}
				this.postOutResponseContext.setEsito(esito);
			}
			this.postOutResponseContext.setReturnCode(statoServletResponse);
			this.postOutResponseContext.setResponseHeaders(this.context.getResponseHeaders());
			this.postOutResponseContext.setProtocollo(this.context.getProtocol());
			this.postOutResponseContext.setIntegrazione(this.context.getIntegrazione());
			if(this.context.getTipoPorta()!=null)
				this.postOutResponseContext.setTipoPorta(this.context.getTipoPorta());	
			this.postOutResponseContext.setIdModulo(this.idModulo);
			
			if(this.inputBody!=null){
				this.postOutResponseContext.setInputRequestMessageSize(Long.valueOf(this.inputBody.length));
			}
			if(this.requestMessage!=null){
				/**this.postOutResponseContext.setInputRequestMessageSize(this.requestMessage.getIncomingMessageContentLength());*/
				this.postOutResponseContext.setOutputRequestMessageSize(this.requestMessage.getOutgoingMessageContentLength());
			}else{
				this.postOutResponseContext.setInputRequestMessageSize(this.req.getContentLength()+0l);
			}
			
			if(erroreConsegnaRisposta==null && this.responseMessage!=null  && !this.responseMessage.isForcedEmptyResponse() && this.responseMessage.getForcedResponse()==null){
				this.postOutResponseContext.setInputResponseMessageSize(this.responseMessage.getIncomingMessageContentLength());
				this.postOutResponseContext.setOutputResponseMessageSize(lengthOutResponse); // sbustata!
				this.postOutResponseContext.setMessaggio(this.responseMessage);
			}
			else if(this.responseMessage!=null && this.responseMessage.getForcedResponse()!=null &&
					this.responseMessage.getForcedResponse().getContent()!=null) {
				this.postOutResponseContext.setInputResponseMessageSize(this.responseMessage.getIncomingMessageContentLength());
				this.postOutResponseContext.setOutputResponseMessageSize((long) this.responseMessage.getForcedResponse().getContent().length);
			}	
		}		
	}

	private Transazione registraTracciaOutResponse(Date dataPrimaSpedizioneRisposta, Date dataRispostaSpedita,
			EsitoTransazione esito, int statoServletResponse,
			Throwable erroreConsegnaRisposta, long lengthOutResponse) throws HandlerException {

		try {
		
			if(this.postOutResponseContext!=null) {
				updateContext(dataPrimaSpedizioneRisposta, dataRispostaSpedita,
						esito, statoServletResponse,
						erroreConsegnaRisposta, lengthOutResponse);
				
				TracciamentoManager tracciamentoManager = new TracciamentoManager(FaseTracciamento.OUT_RESPONSE);
				if(!tracciamentoManager.isTransazioniEnabled()) {
					return null;
				}
				
				InformazioniTransazione info = new InformazioniTransazione(this.postOutResponseContext);
				
				tracciamentoManager.invoke(info, this.postOutResponseContext.getEsito(), this.context.getResponseHeaders(), this.msgDiag);
				
				return info.getTransazioneDaAggiornare();
			}

		}catch(Exception e) {
			ServicesUtils.processTrackingException(e, this.postOutResponseContext.getLogCore(), FaseTracciamento.OUT_RESPONSE, this.context.getPddContext());
		}
		
		return null;
	}
	
	private void registraTracciaInRequest() throws HandlerException{
		
		try {
		
			TracciamentoManager tracciamentoManager = new TracciamentoManager(FaseTracciamento.IN_REQUEST);
			if(!tracciamentoManager.isTransazioniEnabled()) {
				return;
			}
				
			InformazioniTransazione info = new InformazioniTransazione();
			info.setContext(this.context.getPddContext());
			info.setTipoPorta(this.context.getTipoPorta());
			info.setProtocolFactory(this.protocolFactory);
			info.setProtocollo(this.context.getProtocol());
			info.setIntegrazione(this.context.getIntegrazione());
			info.setIdModulo(this.context.getIdModulo());
			
			TransportRequestContext transportRequestContext = null;
			if(this.context.getMessageRequest()!=null) {
				transportRequestContext = this.context.getMessageRequest().getTransportRequestContext();
			}
			String esitoContext = EsitoBuilder.getTipoContext(transportRequestContext, EsitiProperties.getInstance(this.logCore, this.protocolFactory), this.logCore);
			
			tracciamentoManager.invoke(info, esitoContext, this.msgDiag);
			
		}catch(Exception e) {
			ServicesUtils.processTrackingException(e, this.logCore, FaseTracciamento.IN_REQUEST, this.context.getPddContext());
		}
		
	}
}