SavedMessagePSUtilities.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */




package org.openspcoop2.pdd.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;

import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.context.SerializedContext;
import org.openspcoop2.message.context.SerializedParameter;
import org.openspcoop2.message.exception.MessageException;
import org.openspcoop2.message.exception.MessageNotSupportedException;
import org.openspcoop2.pdd.core.state.OpenSPCoopStateless;
import org.openspcoop2.protocol.engine.constants.Costanti;
import org.openspcoop2.protocol.sdk.Context;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.protocol.sdk.state.RequestInfoConfigUtilities;
import org.openspcoop2.protocol.sdk.state.StateMessage;
import org.openspcoop2.utils.MapKey;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.beans.WriteToSerializerType;
import org.openspcoop2.utils.serialization.JavaSerializer;


/**
 * SavedMessagePSUtilities
 *
 * @author Poli Andrea (apoli@link.it)
 * @author Tronci Fabio (tronci@link.it)
 * @author Lorenzo Nardi (nardi@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */



public class SavedMessagePSUtilities {
	
	private SavedMessagePSUtilities() {}

	public static void save(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean isRichiesta, boolean portaDiTipoStateless, boolean consumeMessage, Timestamp oraRegistrazione) throws UtilsException{

		if( !portaDiTipoStateless ) {
			StateMessage stateMsg = (isRichiesta) ?  
					(StateMessage)savedMessage.openspcoopstate.getStatoRichiesta() :
						(StateMessage)savedMessage.openspcoopstate.getStatoRisposta();
			Connection connectionDB = stateMsg.getConnectionDB();

			saveStateful(savedMessage, 
					msg, consumeMessage, oraRegistrazione,
					connectionDB, stateMsg);
			
		}else { /** if (portaDiTipoStateless){ */

			if (isRichiesta) ((OpenSPCoopStateless)savedMessage.openspcoopstate).setRichiestaMsg(msg);
			else ((OpenSPCoopStateless)savedMessage.openspcoopstate).setRispostaMsg(msg);


		}

	}  
	
	private static void saveStateful(SavedMessage savedMessage, 
			OpenSPCoop2Message msg, boolean consumeMessage, Timestamp oraRegistrazione,
			Connection connectionDB, StateMessage stateMsg) throws UtilsException {
		PreparedStatement pstmt = null;
		try{
			// Save proprieta' msg
			StringBuilder query = new StringBuilder();
			query.append("INSERT INTO  ");
			query.append(GestoreMessaggi.DEFINIZIONE_MESSAGGI);
			if(savedMessage.saveOnFS)
				query.append(" (ID_MESSAGGIO,TIPO,CONTENT_TYPE,ORA_REGISTRAZIONE) VALUES ( ? , ? , ? , ? )");
			else
				query.append(" (ID_MESSAGGIO,TIPO,CONTENT_TYPE,ORA_REGISTRAZIONE,MSG_BYTES,MSG_CONTEXT) VALUES ( ? , ? , ? , ? , ? , ?)");

			pstmt = connectionDB.prepareStatement(query.toString());
			pstmt.setString(1,savedMessage.idMessaggio);
			if(Costanti.INBOX.equals(savedMessage.box))
				pstmt.setString(2,Costanti.INBOX);
			else
				pstmt.setString(2,Costanti.OUTBOX);		

			//Sposto il set del contentType dopo la writeTo del messaggio 
			//cosi nel caso di attachment lo trovo corretto.

			saveNormalizeRequestInfoBeforeSerialization(savedMessage,
					msg, consumeMessage,
					pstmt);

			// Set del contentType nella query
			String contentType = readContentType(msg);
			pstmt.setString(3,contentType);
			
			// Set Ora Registrazione
			pstmt.setTimestamp(4,oraRegistrazione);

			// Add PreparedStatement
			stateMsg.getPreparedStatement().put("INSERT (MSG_OP_STEP1a) saveMessage["+savedMessage.idMessaggio+","+savedMessage.box+"]",pstmt);

		}catch(Exception e){
			try{
				if( pstmt != null )
					pstmt.close();
			} catch(Exception err) {
				// close
			}
			String errorMsg = "SOAP_MESSAGE, save : "+savedMessage.box+"/"+savedMessage.idMessaggio+": "+e.getMessage();		
			savedMessage.logError(errorMsg,e);
			throw new UtilsException(errorMsg,e);
		}
	}
	
	private static void saveNormalizeRequestInfoBeforeSerialization(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean consumeMessage,
			PreparedStatement pstmt) throws UtilsException, MessageException, IOException, SQLException {
		// Elimino dalla RequestInfo i dati "cached"
		RequestInfo requestInfoBackup = RequestInfoConfigUtilities.normalizeRequestInfoBeforeSerialization(msg);
		try {
			save(savedMessage,
					msg, consumeMessage,
					pstmt);
		}finally {
			if(requestInfoBackup!=null) {
				RequestInfoConfigUtilities.restoreRequestInfoAfterSerialization(msg, requestInfoBackup);
			}
		}
	}
	private static void save(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean consumeMessage,
			PreparedStatement pstmt) throws UtilsException, MessageException, IOException, SQLException {
		if(savedMessage.saveOnFS){
			// SAVE IN FILE SYSTEM
			
			String saveDir = savedMessage.getBaseDir();
			savedMessage.checkInizializzazioneWorkingDir(saveDir);
			
			// Save bytes message
			String pathBytes = saveDir + savedMessage.keyMsgBytes;
			savedMessage.saveMessageBytes(pathBytes,msg, consumeMessage,false);
			
			// Save message context
			String pathContext = saveDir + savedMessage.keyMsgContext;
			savedMessage.saveMessageContext(pathContext,msg,false);
			
		}else{
			// SAVE IN DB
			
			// Save bytes message
			java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
			msg.writeTo(bout,consumeMessage);
			bout.flush();
			bout.close();
			/** System.out.println("---------SALVO RISPOSTA: "+msgByte.toString()); */
			savedMessage.adapter.setBinaryData(pstmt,5,bout.toByteArray());
			
			// Save message context
			bout = new java.io.ByteArrayOutputStream();
			msg.serializeResourcesTo(bout);
			bout.flush();
			bout.close();
			/** System.out.println("---------SALVO CONTEXT: "+msgByte.toString()); */
			savedMessage.adapter.setBinaryData(pstmt,6,bout.toByteArray());
		}
	}
	private static String readContentType(OpenSPCoop2Message msg) throws UtilsException, MessageException, MessageNotSupportedException {
		
		String prefix = "Rilevata una richiesta "+msg.getServiceBinding();
		
		String contentType = msg.getContentType();
		if(contentType==null || "".equals(contentType)){
			if(ServiceBinding.REST.equals(msg.getServiceBinding())){
				if(MessageType.BINARY.equals(msg.getMessageType())) {
					if(msg.castAsRest().hasContent()) {
						throw new UtilsException(prefix+" "+msg.getMessageType()+" con payload per la quale non è stato fornito un ContentType"); // sul DB e' required la colonna
					}
					else {
						contentType = SavedMessage.REST_CONTENT_TYPE_EMPTY;
					}
				}
				else {
					throw new UtilsException(prefix+" "+msg.getMessageType()+" per la quale non è stato fornito un ContentType"); // sul DB e' required la colonna
				}
			}
			else {
				throw new UtilsException(prefix+" per la quale non è stato fornito un ContentType"); // sul DB e' required la colonna
			}
		}
		return contentType;
	}

	
	public static void updateResponse(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean consumeMessage) throws UtilsException{

		StateMessage stateMsg = (StateMessage)savedMessage.openspcoopstate.getStatoRisposta();
		Connection connectionDB = stateMsg.getConnectionDB();

		PreparedStatement pstmt = null;
		try{
			// Save proprieta' msg
			StringBuilder query = new StringBuilder();
			query.append("UPDATE ");
			query.append(GestoreMessaggi.DEFINIZIONE_MESSAGGI);
			query.append(" SET ");
			query.append(" RESPONSE_CONTENT_TYPE=? ");
			if(!savedMessage.saveOnFS) {
				query.append(" , RESPONSE_MSG_BYTES=? ");
				query.append(" , RESPONSE_MSG_CONTEXT=? ");
			}
			query.append(" WHERE ID_MESSAGGIO=? AND TIPO=?");


			pstmt = connectionDB.prepareStatement(query.toString());
			int index = 1;
			
			// Set del contentType nella query
			String contentType = readContentType(msg);
			pstmt.setString(index++,contentType);
			
			index = updateResponseNormalizeRequestInfoBeforeSerialization(savedMessage,
					msg, consumeMessage,
					pstmt, index);
			
			pstmt.setString(index++,savedMessage.idMessaggio);
			if(Costanti.INBOX.equals(savedMessage.box))
				pstmt.setString(index,Costanti.INBOX);
			else
				pstmt.setString(index,Costanti.OUTBOX);		


			// Add PreparedStatement
			stateMsg.getPreparedStatement().put("UPDATE (RESPONSE) saveMessage["+savedMessage.idMessaggio+","+savedMessage.box+"]",pstmt);
	
		}catch(Exception e){
			try{
				if( pstmt != null )
					pstmt.close();
			} catch(Exception err) {
				// close
			}
			String errorMsg = "SOAP_MESSAGE, update response : "+savedMessage.box+"/"+savedMessage.idMessaggio+": "+e.getMessage();		
			savedMessage.logError(errorMsg,e);
			throw new UtilsException(errorMsg,e);
		}

	}     
	
	private static int updateResponseNormalizeRequestInfoBeforeSerialization(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean consumeMessage,
			PreparedStatement pstmt, int index) throws UtilsException, MessageException, IOException, SQLException {
		// Elimino dalla RequestInfo i dati "cached"
		RequestInfo requestInfoBackup = RequestInfoConfigUtilities.normalizeRequestInfoBeforeSerialization(msg);
		try {
			return updateResponse(savedMessage,
					msg, consumeMessage,
					pstmt, index);
		}finally {
			if(requestInfoBackup!=null) {
				RequestInfoConfigUtilities.restoreRequestInfoAfterSerialization(msg, requestInfoBackup);
			}
		}
	}
	private static int updateResponse(SavedMessage savedMessage,
			OpenSPCoop2Message msg, boolean consumeMessage,
			PreparedStatement pstmt, int index) throws UtilsException, MessageException, IOException, SQLException {
		if(savedMessage.saveOnFS){
			// SAVE IN FILE SYSTEM
			
			String saveDir = savedMessage.getBaseDir();
			savedMessage.checkInizializzazioneWorkingDir(saveDir);
			
			// Save bytes message
			String pathBytes = saveDir + savedMessage.keyMsgResponseBytes;
			savedMessage.saveMessageBytes(pathBytes,msg, consumeMessage, false);
			
			// Save message context
			String pathContext = saveDir + savedMessage.keyMsgResponseContext;
			savedMessage.saveMessageContext(pathContext,msg, false);
			
		}else{
			// SAVE IN DB
			
			// Save bytes message
			java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
			msg.writeTo(bout,consumeMessage);
			bout.flush();
			bout.close();
			/** System.out.println("---------SALVO RISPOSTA: "+msgByte.toString()); */
			savedMessage.adapter.setBinaryData(pstmt,index++,bout.toByteArray());
			
			// Save message context
			bout = new java.io.ByteArrayOutputStream();
			msg.serializeResourcesTo(bout);
			bout.flush();
			bout.close();
			/** System.out.println("---------SALVO CONTEXT: "+msgByte.toString()); */
			savedMessage.adapter.setBinaryData(pstmt,index++,bout.toByteArray());
		}
		return index;
	}
	
	
	
	
	
	
	
	public static void updateTransactionContext(SavedMessage savedMessage,Context transactionContext) throws UtilsException{

		if(transactionContext==null || transactionContext.isEmpty()) {
			return;
		}
		
		SerializedContext sc = buildSerializedContext(transactionContext);
		
		if(savedMessage.saveOnFS) {
			
			String saveDir = savedMessage.getBaseDir();
			savedMessage.checkInizializzazioneWorkingDir(saveDir);
			
			// Save bytes message
			String pathBytes = saveDir + savedMessage.keyMsgTransactionContext;
			savedMessage.saveTransactionContext(pathBytes,sc, false);
			
		}
		else {
		
			StateMessage stateMsg = (StateMessage)savedMessage.openspcoopstate.getStatoRisposta();
			Connection connectionDB = stateMsg.getConnectionDB();
	
			// Save proprieta' msg
			StringBuilder query = new StringBuilder();
			query.append("UPDATE ");
			query.append(GestoreMessaggi.DEFINIZIONE_MESSAGGI);
			query.append(" SET ");
			query.append(" TRANSACTION_CONTEXT=? ");
			query.append(" WHERE ID_MESSAGGIO=? AND TIPO=?");
			
			try (PreparedStatement pstmt = connectionDB.prepareStatement(query.toString());){
				int index = 1;
				
				java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
				sc.writeTo(bout, WriteToSerializerType.XML_JAXB);
				bout.flush();
				bout.close();
				
				/** System.out.println("---------SALVO TRANSACTION CONTEXT: "+msgByte.toString()); */
				savedMessage.adapter.setBinaryData(pstmt,index++,bout.toByteArray());
				
				pstmt.setString(index++,savedMessage.idMessaggio);
				if(Costanti.INBOX.equals(savedMessage.box))
					pstmt.setString(index,Costanti.INBOX);
				else
					pstmt.setString(index,Costanti.OUTBOX);		
				
				pstmt.executeUpdate();
		
			}catch(Exception e){
				String errorMsg = "SOAP_MESSAGE, update transaction context : "+savedMessage.box+"/"+savedMessage.idMessaggio+": "+e.getMessage();		
				savedMessage.logError(errorMsg,e);
				throw new UtilsException(errorMsg,e);
			}
			
		}

	}  
	private static SerializedContext buildSerializedContext(Context transactionContext) throws UtilsException{
		
		try {
		
			SerializedContext sc = new SerializedContext();
			JavaSerializer jSerializer = new JavaSerializer();
			for (MapKey<String> key : transactionContext.keySet()) {
				Object o = transactionContext.get(key);
				if(!CostantiPdD.SALVA_CONTESTO_IDENTIFICATIVO_MESSAGGIO_NOTIFICA.equals(key) && 
						(o instanceof Serializable)
						) {
					SerializedParameter p = new SerializedParameter();
					p.setNome(key.getValue());
					p.setClasse(o.getClass().getName());
					ByteArrayOutputStream bout = new ByteArrayOutputStream();
					jSerialize(jSerializer, o, bout, p);
					bout.flush();
					bout.close();
					p.setBase(bout.toByteArray());
					sc.addProperty(p);
				}
			}
			return sc;
			
		}catch(Exception e){
			throw new UtilsException(e.getMessage(),e);
		}
	}
	private static void jSerialize(JavaSerializer jSerializer, Object o, ByteArrayOutputStream bout, SerializedParameter p) throws UtilsException {
		try {
			jSerializer.writeObject(o, bout);
		}catch(Exception t) {
			throw new UtilsException("Serialization error (nome:"+p.getNome()+" classe:"+p.getClasse()+"): "+t.getMessage(), t);
		}
	}
}