FileTraceManager.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.logger.filetrace;

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

import org.apache.commons.lang.StringUtils;
import org.openspcoop2.core.constants.Costanti;
import org.openspcoop2.core.constants.TipoPdD;
import org.openspcoop2.core.transazioni.Transazione;
import org.openspcoop2.core.transazioni.constants.TipoMessaggio;
import org.openspcoop2.core.transazioni.utils.CredenzialiMittente;
import org.openspcoop2.monitor.sdk.transaction.FaseTracciamento;
import org.openspcoop2.pdd.core.token.InformazioniNegoziazioneToken;
import org.openspcoop2.pdd.core.token.InformazioniToken;
import org.openspcoop2.pdd.core.token.attribute_authority.InformazioniAttributi;
import org.openspcoop2.pdd.core.transazioni.Transaction;
import org.openspcoop2.protocol.sdk.Busta;
import org.openspcoop2.protocol.sdk.Context;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.ProtocolException;
import org.openspcoop2.protocol.sdk.SecurityToken;
import org.openspcoop2.protocol.sdk.dump.Messaggio;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.DynamicStringReplace;
import org.openspcoop2.utils.UtilsException;
import org.slf4j.Logger;

/**     
 * TransazioneLogTraceManager
 *
 * @author Poli Andrea (poli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class FileTraceManager {
	
	private Logger log;
	private FileTraceConfig config;
	
	private Map<String, Object> dynamicMap = new HashMap<>();
	private Info t;
	private Info tBase64;
	

	public FileTraceManager(Logger log, FileTraceConfig config) {
		this.config = config;
		this.log = log;
	}
	
	public void buildTransazioneInfo(IProtocolFactory<?> protocolFactory, Transazione transazioneDTO, Transaction transaction, 
			InformazioniToken informazioniToken,
			InformazioniAttributi informazioniAttributi,
			InformazioniNegoziazioneToken informazioniNegoziazioneToken,
			SecurityToken securityToken,
			Context context,
			Map<String, List<String>> headerInUscita,
			FaseTracciamento trackingPhase) throws ProtocolException {
		
		Messaggio richiestaIngresso = null;
		Messaggio richiestaUscita = null;
		Messaggio rispostaIngresso = null;
		Messaggio rispostaUscita = null;
		
		for(int i=0; i<transaction.sizeMessaggi(); i++){
			Messaggio messaggio = transaction.getMessaggio(i);
			TipoMessaggio tipoMessaggio = messaggio.getTipoMessaggio();
			switch (tipoMessaggio) {
			case RICHIESTA_INGRESSO_DUMP_BINARIO:
				richiestaIngresso = messaggio;
				break;
			case RICHIESTA_USCITA_DUMP_BINARIO:
				richiestaUscita = messaggio;
				break;
			case RISPOSTA_INGRESSO_DUMP_BINARIO:
				rispostaIngresso = messaggio;
				break;
			case RISPOSTA_USCITA_DUMP_BINARIO:
				rispostaUscita = messaggio;
				break;
			default:
				break;
			}
			
		}
		
		Map<String, List<String>> headerRichiestaUscita = null;
		Map<String, List<String>> headerRispostaUscita = null;
		if(FaseTracciamento.OUT_REQUEST.equals(trackingPhase)) {
			headerRichiestaUscita = headerInUscita;
		}
		else if(FaseTracciamento.OUT_RESPONSE.equals(trackingPhase)) {
			headerRispostaUscita = headerInUscita;
		}
		
		CredenzialiMittente credenzialiMittente = transaction.getCredenzialiMittente();
		
		InfoConfigurazione infoConfigurazione = new InfoConfigurazione(transazioneDTO, context, credenzialiMittente);
		
		boolean base64 = true;
		
		this.t = new Info(this.log, protocolFactory, transazioneDTO, credenzialiMittente, 
				informazioniToken,
				informazioniAttributi,
				informazioniNegoziazioneToken,
				securityToken,
				transaction.getTracciaRichiesta(), transaction.getTracciaRisposta(),
				transaction.getMsgDiagnostici(),
				richiestaIngresso, 
				richiestaUscita, headerRichiestaUscita,
				rispostaIngresso, 
				rispostaUscita, headerRispostaUscita,
				infoConfigurazione,
				this.config, trackingPhase, 
				!base64);
		this.tBase64 = new Info(this.log, protocolFactory, transazioneDTO, credenzialiMittente, 
				informazioniToken,
				informazioniAttributi,
				informazioniNegoziazioneToken,
				securityToken,
				transaction.getTracciaRichiesta(), transaction.getTracciaRisposta(),
				transaction.getMsgDiagnostici(),
				richiestaIngresso, 
				richiestaUscita, headerRichiestaUscita,
				rispostaIngresso, 
				rispostaUscita, headerRispostaUscita,
				infoConfigurazione, 
				this.config, trackingPhase,  
				base64);
		
		this.dynamicMap.put("log", this.t);
		this.dynamicMap.put("logBase64", this.tBase64);
		
	}
		
	public void cleanResourcesForOnlyFileTrace(Transaction transaction) throws ProtocolException {
			
		List<TipoMessaggio> tipiDaEliminareHeaders = transaction.getMessaggi_headers_onlyLogFileTrace();
		List<TipoMessaggio> tipiDaEliminareBody = transaction.getMessaggi_body_onlyLogFileTrace();
			
		List<TipoMessaggio> messaggiDaEliminare = new ArrayList<>();
		
		for(int i=0; i<transaction.sizeMessaggi(); i++){
			Messaggio messaggio = transaction.getMessaggio(i);
			TipoMessaggio tipoMessaggio = messaggio.getTipoMessaggio();
			
			boolean onlyLogFileTraceHeaders = false;
			if(tipiDaEliminareHeaders!=null && !tipiDaEliminareHeaders.isEmpty()) {
				for (int j = 0; j < tipiDaEliminareHeaders.size(); j++) {
					TipoMessaggio tipoMessaggioHeaders = tipiDaEliminareHeaders.get(j);
					if(tipoMessaggioHeaders.equals(tipoMessaggio)) {
						onlyLogFileTraceHeaders = true;
						break;
					}
				}
			}
			if(onlyLogFileTraceHeaders) {
				tipiDaEliminareHeaders.remove(tipoMessaggio);	
			}
			
			boolean onlyLogFileTraceBody = false;
			if(tipiDaEliminareBody!=null && !tipiDaEliminareBody.isEmpty()) {
				for (int j = 0; j < tipiDaEliminareBody.size(); j++) {
					TipoMessaggio tipoMessaggioBody = tipiDaEliminareBody.get(j);
					if(tipoMessaggioBody.equals(tipoMessaggio)) {
						onlyLogFileTraceBody = true;
						break;
					}
				}
			}
			if(onlyLogFileTraceBody) {
				tipiDaEliminareBody.remove(tipoMessaggio);	
			}
			
			if(onlyLogFileTraceHeaders && onlyLogFileTraceBody) {
				messaggiDaEliminare.add(tipoMessaggio);
			}
			else if(onlyLogFileTraceHeaders) {
				messaggio.getHeaders().clear();
			}
			else if(onlyLogFileTraceBody) {
				if(messaggio.getBody()!=null) {
					messaggio.getBody().unlock();
					messaggio.getBody().clearResources();
					messaggio.setBody(null);
				}
				messaggio.setContentType(null);
			}
		}
		
		if(messaggiDaEliminare!=null) {
			while(!messaggiDaEliminare.isEmpty()) {
				TipoMessaggio tipo = messaggiDaEliminare.remove(0);
				if(transaction.sizeMessaggi()>0) {
					for (int i = 0; i < transaction.sizeMessaggi(); i++) {
						if(tipo.equals(transaction.getMessaggio(i).getTipoMessaggio())) {
							if(transaction.getMessaggio(i).getBody()!=null){
								transaction.getMessaggio(i).getBody().unlock();
								transaction.getMessaggio(i).getBody().clearResources();
								transaction.getMessaggio(i).setBody(null);
							}
							transaction.removeMessaggio(i);
							break;
						}
					}
				}
			}
		}

	}
	
	public void invoke(TipoPdD tipoPdD, Context context, RequestInfo requestInfo, Busta busta, FaseTracciamento faseTracciamento) throws UtilsException {
		this.invoke(tipoPdD, context, requestInfo, busta, faseTracciamento, null);
	}
	public void invoke(TipoPdD tipoPdD, Context context, RequestInfo requestInfo, Busta busta, FaseTracciamento faseTracciamento, Map<String, String> outputMap) throws UtilsException {
		List<String> topic = null;
		Map<String, Topic> topicMap = null;
		switch (tipoPdD) {
		case DELEGATA:
			topic = this.config.getTopicFruizioni();
			topicMap = this.config.getTopicFruizioneMap();
			break;
		case APPLICATIVA:
			topic = this.config.getTopicErogazioni();
			topicMap = this.config.getTopicErogazioniMap();
			break;
		default:
			break;
		}
		if(topic!=null && !topic.isEmpty()) {
			
			boolean requestSent = isRequestSent(context);
			
			List<Topic> topicInvoke = new ArrayList<>();
			for (String topicName : topic) {
				Topic topicConfig = topicMap.get(topicName);
				if(add(topicConfig, faseTracciamento, requestSent)) {
					topicInvoke.add(topicConfig);
				}
				/**else {
					System.out.println("SKIP["+topicName+"] in fase ["+faseTracciamento+"]");
				}*/
			}
			
			invoke(topicInvoke, 
					requestInfo, context, busta,
					outputMap);
		}
	}
	
	private boolean isRequestSent(Context context) {
		boolean requestSent = false;
		if(context!=null && context.containsKey(Costanti.RICHIESTA_INOLTRATA_BACKEND)) {
			Object o = context.getObject(Costanti.RICHIESTA_INOLTRATA_BACKEND);
			if(o instanceof String) {
				String s = (String) o;
				if(Costanti.RICHIESTA_INOLTRATA_BACKEND_VALORE.equals(s)) {
					requestSent = true;
				}
			}
		}
		return requestSent;
	}
	
	private boolean add(Topic topicConfig, FaseTracciamento faseTracciamento, boolean requestSent) {
		if(!topicConfig.isEnabled(faseTracciamento)) {
			/**System.out.println("R1");*/
			return false;
		}
		
		if(topicConfig.isOnlyRequestSent() &&
			!requestSent) {
			/**System.out.println("R1");*/
			return false;
		}
		if(topicConfig.isOnlyInRequestContentDefined()) {
			boolean contentDefined = this.t.getInRequestSize()>0;
			if(!contentDefined) {
				/**System.out.println("R3");*/
				return false;
			}
		}
		if(topicConfig.isOnlyOutRequestContentDefined()) {
			boolean contentDefined = this.t.getOutRequestSize()>0;
			if(!contentDefined) {
				/**System.out.println("R4");*/
				return false;
			}
		}
		if(topicConfig.isOnlyInResponseContentDefined()) {
			boolean contentDefined = this.t.getInResponseSize()>0;
			if(!contentDefined) {
				/**System.out.println("R5");*/
				return false;
			}
		}
		if(topicConfig.isOnlyOutResponseContentDefined()) {
			boolean contentDefined = this.t.getOutResponseSize()>0;
			if(!contentDefined) {
				/**System.out.println("R6");*/
				return false;
			}
		}
		
		return true;
	}
	
	private void invoke(List<Topic> topicInvoke, 
			RequestInfo requestInfo, Context context, Busta busta,
			Map<String, String> outputMap) throws UtilsException {
		if(topicInvoke!=null && !topicInvoke.isEmpty()) {
			
			this.resolveProperties(requestInfo, context, busta);
			
			for (Topic topicConfig : topicInvoke) {
				String value = this.resolve(topicConfig.getFormat());
				if(outputMap!=null) {
					outputMap.put(topicConfig.getNome(), value);
				}
				else {
					switch (this.config.getLogSeverity()) {
					case trace:
						topicConfig.getLog().trace(value);		
						break;
					case debug:
						topicConfig.getLog().debug(value);		
						break;
					case info:
						topicConfig.getLog().info(value);		
						break;
					case warn:
						topicConfig.getLog().warn(value);		
						break;
					case error:
						topicConfig.getLog().error(value);		
						break;
					}
				}
			}
		}
	}
		
	private void resolveProperties(RequestInfo requestInfo, Context context, Busta busta) throws UtilsException {
		
		Map<String, String> propertiesEncryptionModes = this.config.getPropertiesEncryptionMode();
		FileTraceEncrypt encrypt = null;
		if(!propertiesEncryptionModes.isEmpty()) {
			encrypt = new FileTraceEncrypt(this.log, requestInfo, context, busta);
		}
		
		List<String> properties = this.config.getPropertiesSortKeys();
		if(properties!=null && !properties.isEmpty()) {
			for (String sortKey : properties) {
				String pName = this.config.getPropertiesNames().get(sortKey);
				String pValue = this.config.getPropertiesValues().get(sortKey);
				/**System.out.println("SORT ["+sortKey+"] ["+pName+"]");*/
				String resolvedValue = this.resolve(pValue);
				
				String mode = propertiesEncryptionModes.get(sortKey);
				if(encrypt!=null && mode!=null && StringUtils.isNotEmpty(mode)) {
					FileTraceEncryptConfig encryptionConfig = this.config.getEncryptionMode().get(mode);
					resolvedValue = encrypt.encrypt(encryptionConfig, resolvedValue);
				}
				
				this.t.addProperty(pName, resolvedValue);
				this.tBase64.addProperty(pName, resolvedValue);
			}
		}
	}
	
	private String resolve(String format) throws UtilsException {
		boolean complexField = false;
		return DynamicStringReplace.replace(format, this.dynamicMap, true, complexField);
	}
}