DiagnosticInputStream.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;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.openspcoop2.pdd.core.CostantiPdD;
import org.openspcoop2.utils.Map;
import org.openspcoop2.utils.MapKey;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.date.DateManager;
import org.slf4j.Logger;

/**
 * DiagnosticInputStream
 *
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class DiagnosticInputStream extends FilterInputStream {

	private static final String COMPLETATA = "completata";
	private static final String FALLITA = "fallita";
	
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_REQUEST_COMPLETE_DATE = Map.newMapKey("DiagnosticInputStreamRequestCompleteDate");
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_REQUEST_ERROR_DATE = Map.newMapKey("DiagnosticInputStreamRequestErrorDate");
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_REQUEST_START_DATE = Map.newMapKey("DiagnosticInputStreamRequestStartDate");
	
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_RESPONSE_COMPLETE_DATE = Map.newMapKey("DiagnosticInputStreamResponseCompleteDate");
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_RESPONSE_ERROR_DATE = Map.newMapKey("DiagnosticInputStreamResponseErrorDate");
	public static final MapKey<String> DIAGNOSTIC_INPUT_STREAM_RESPONSE_START_DATE = Map.newMapKey("DiagnosticInputStreamResponseStartDate");
	
	private InputStream isWrapped = null;
	private String idModuloFunzionale;
	private String identificativo;
	private boolean request;
	private MsgDiagnostico msgDiagnostico;
	private Logger log;
	private Map<Object> ctx;
	private boolean readSomeBytes = false;
	
	private static boolean setDateEmptyStream = true;
	public static boolean isSetDateEmptyStream() {
		return setDateEmptyStream;
	}
	public static void setSetDateEmptyStream(boolean setDateEmptyStream) {
		DiagnosticInputStream.setDateEmptyStream = setDateEmptyStream;
	}

	public DiagnosticInputStream(InputStream inputStream, String idModuloFunzionale, String identificativo, boolean request,
			MsgDiagnostico msgDiagnostico, Logger log, Map<Object> ctx) {
		super(inputStream);
		this.idModuloFunzionale = idModuloFunzionale;
		this.identificativo = identificativo;
		this.request = request;
		this.msgDiagnostico = msgDiagnostico;
		this.log = log;
		this.ctx = ctx;
		
		this.isWrapped = inputStream;
		
		registerStartDate();
	}
	
	public InputStream getIsWrapped() {
		return this.isWrapped;
	}

	@Override
	public int read() throws IOException {
		try {
			int res = super.read();
			if (res == -1) {
				registerDate(null);
				emitLog(null);
				emitDiagnostic(null);
			}
			else {
				if(!this.readSomeBytes) {
					this.readSomeBytes=true;
				}
			}
			return res;
		}catch(IOException io) {
			registerDate(io);
			emitLog(io);
			emitDiagnostic(io);
			throw io;
		}catch(Throwable t) {
			registerDate(t);
			emitLog(t);
			emitDiagnostic(t);
			throw t;
		}
	}

	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		try {
			int res = super.read(b, off, len);
			if (res == -1) {
				registerDate(null);
				emitLog(null);
				emitDiagnostic(null);
			}
			else {
				if(!this.readSomeBytes) {
					this.readSomeBytes=true;
				}
			}
			return res;
		}catch(IOException io) {
			registerDate(io);
			emitLog(io);
			emitDiagnostic(io);
			throw io;
		}catch(Throwable t) {
			registerDate(t);
			emitLog(t);
			emitDiagnostic(t);
			throw t;
		}
	}

	private void registerStartDate() {
		if(this.ctx!=null && this.isWrapped!=null) {
			Date d = DateManager.getDate();
			this.ctx.put(this.request ? DIAGNOSTIC_INPUT_STREAM_REQUEST_START_DATE : DIAGNOSTIC_INPUT_STREAM_RESPONSE_START_DATE, d);
		}
	}
	
	private void registerDate(Throwable t) {
		if(this.ctx!=null && 
				(this.readSomeBytes 
						|| 
				DiagnosticInputStream.setDateEmptyStream) // per far si che le due date siano le stesse
			) {
			Date d = DateManager.getDate();
			if(t!=null) {
				this.ctx.put(this.request ? DIAGNOSTIC_INPUT_STREAM_REQUEST_ERROR_DATE : DIAGNOSTIC_INPUT_STREAM_RESPONSE_ERROR_DATE, d);
			}
			else {
				this.ctx.put(this.request ? DIAGNOSTIC_INPUT_STREAM_REQUEST_COMPLETE_DATE : DIAGNOSTIC_INPUT_STREAM_RESPONSE_COMPLETE_DATE, d);
			}
		}
	}
	
	private void emitLog(Throwable t) {
		if(this.log!=null) {
			StringBuilder sb = new StringBuilder();
			sb.append("Lettura payload '").append(this.identificativo).append("' ");
			if(t!=null) {
				sb.append("fallita: ").append(t.getMessage());
			}
			else {
				sb.append("completata con successo");
			}
			String msg = sb.toString();
			if(t!=null) {
				this.log.error(msg, t);
			}else{
				this.log.debug(msg);
			}
		}
	}
	private void emitDiagnostic(Throwable t) {
		if(this.msgDiagnostico!=null && 
				this.idModuloFunzionale!=null && StringUtils.isNotEmpty(this.idModuloFunzionale) &&
				this.identificativo!=null && StringUtils.isNotEmpty(this.identificativo)) {
			try {
				if(t!=null) {
					String msg = Utilities.readFirstErrorValidMessageFromException(t);
					this.msgDiagnostico.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, msg);
					this.msgDiagnostico.logPersonalizzato(this.idModuloFunzionale, this.identificativo+"."+FALLITA);
				}
				else {
					this.msgDiagnostico.logPersonalizzato(this.idModuloFunzionale, this.identificativo+"."+COMPLETATA);
				}
			}catch(Throwable tLog) {
				if(this.log!=null) {
					this.log.error("Emissione diagnostico per lettura payload '"+this.identificativo+"' fallita: "+tLog.getMessage(),tLog);
				}
			}
		}
	}
}