TransactionManager.java

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



  20. package org.openspcoop2.pdd.core.node;

  21. import java.sql.Connection;

  22. import org.openspcoop2.core.id.IDSoggetto;
  23. import org.openspcoop2.pdd.config.DBManager;
  24. import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
  25. import org.openspcoop2.pdd.config.Resource;
  26. import org.openspcoop2.pdd.core.GestoreMessaggi;
  27. import org.openspcoop2.pdd.core.PdDContext;
  28. import org.openspcoop2.pdd.core.state.OpenSPCoopStateful;
  29. import org.openspcoop2.pdd.logger.MsgDiagnostico;
  30. import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
  31. import org.openspcoop2.pdd.mdb.ConsegnaContenutiApplicativi;
  32. import org.openspcoop2.pdd.mdb.Imbustamento;
  33. import org.openspcoop2.pdd.mdb.ImbustamentoRisposte;
  34. import org.openspcoop2.pdd.mdb.InoltroBuste;
  35. import org.openspcoop2.pdd.mdb.InoltroRisposte;
  36. import org.openspcoop2.pdd.mdb.Sbustamento;
  37. import org.openspcoop2.pdd.mdb.SbustamentoRisposte;
  38. import org.openspcoop2.pdd.services.core.RicezioneBuste;
  39. import org.openspcoop2.pdd.services.core.RicezioneContenutiApplicativi;
  40. import org.openspcoop2.protocol.sdk.state.StatefulMessage;
  41. import org.openspcoop2.utils.Utilities;
  42. import org.openspcoop2.utils.date.DateManager;


  43. /**
  44.  * Classe che implementa la logica transazionale di OpenSPCoop
  45.  *
  46.  * @author Poli Andrea (apoli@link.it)
  47.  * @author $Author$
  48.  * @version $Rev$, $Date$
  49.  */

  50. public class TransactionManager {

  51.     /** Logger utilizzato per debug. */
  52.     private static org.slf4j.Logger log = OpenSPCoop2Logger.getLoggerOpenSPCoopCore();
  53.    
  54.     /** Variabile che indica il Nome del modulo dell'architettura di OpenSPCoop rappresentato da questa classe */
  55.     public static final String ID_MODULO = "TransactionManager";

  56.     /**
  57.      * Quando un modulo di OpenSPCoop chiama questo metodo, si aspetta come risposta una indicazione
  58.      * se utilizzare o meno il messaggio ricevuto.
  59.      *
  60.      * @param idModulo Identificativo del Modulo OpenSPCoop
  61.      * @param idBusta Identificativo del Messaggio Ricevuto
  62.      * @param tipo Tipo di Messaggio da gestire (INBOX/OUTBOX)
  63.      * @param idJMS ID JMS del Messaggio ricevuto
  64.      * @return Il metodo ritorna true se il modulo e' autorizzato a processare il messaggio, false altrimenti.
  65.      *
  66.      */
  67.     public static boolean validityCheck(MsgDiagnostico msgDiag,String idModulo,String idBusta,String tipo,
  68.             String idJMS, PdDContext pddContext) throws Exception{
  69.         return TransactionManager.validityCheck(msgDiag,idModulo,idBusta,tipo,idJMS,pddContext,null);
  70.     }

  71.     /**
  72.      * Quando un modulo di OpenSPCoop chiama questo metodo, si aspetta come risposta una indicazione
  73.      * se utilizzare o meno il messaggio ricevuto.
  74.      *
  75.      * @param idModulo Identificativo del Modulo OpenSPCoop
  76.      * @param idBusta Identificativo del Messaggio Ricevuto
  77.      * @param tipo Tipo di Messaggio da gestire (INBOX/OUTBOX)
  78.      * @param idJMS ID JMS del Messaggio ricevuto
  79.      * @param servizioApplicativo Servizio Applicativo utilizzato come chiave insieme all'id
  80.      * @return Il metodo ritorna true se il modulo e' autorizzato a processare il messaggio, false altrimenti.
  81.      *
  82.      */
  83.     public static boolean validityCheck(MsgDiagnostico msgDiag,String idModulo,String idBusta,String tipo,
  84.             String idJMS, PdDContext pddContext,String servizioApplicativo) throws Exception{
  85.        
  86.         // Risorse
  87.         OpenSPCoop2Properties properties = OpenSPCoop2Properties.getInstance();
  88.         IDSoggetto dominio = properties.getIdentitaPortaDefaultWithoutProtocol();
  89.         String idModuloTransaction = TransactionManager.ID_MODULO + "_" + idModulo;
  90.        
  91.         DBManager dbManager = DBManager.getInstance();
  92.         Resource resource = null;

  93.         OpenSPCoopStateful openspcoopstate = new OpenSPCoopStateful();
  94.         StatefulMessage state = new StatefulMessage(null, TransactionManager.log);
  95.         openspcoopstate.setStatoRichiesta(state);
  96.         GestoreMessaggi msg = new GestoreMessaggi(openspcoopstate, true,idBusta,tipo,msgDiag,null);
  97.        
  98.         long scadenzaWhile = DateManager.getTimeMillis() + properties.getTransactionManagerAttesaAttiva();

  99.        
  100.         try{

  101.             // Algoritmo
  102.             int refreshOnlyCacheCount = 0;
  103.            
  104.             while( DateManager.getTimeMillis() < scadenzaWhile  ){
  105.                
  106.                 refreshOnlyCacheCount++;
  107.                 // Il riferimento messaggio e il proprietario puo' essere letto dalla cache.
  108.                 // Per migliorare le performance, se non e' presente nella cache si assume anche non presente nel database.
  109.                 // - Per rifMsg se si chiama il metodo mapRiferimentoIntoIDBusta(true) l'id viene cercato solo nella cache
  110.                 // - Per proprietario se si chiama il metodo getProprietario(true) viene cercato solo nella cache
  111.                 // La cache puo' cmq svuotarsi velocemente se la dimensione e' troppo piccola rispetto al numero di msg in parallelo gestiti.
  112.                 // Quindi ogni 20 volte (se checkInterval=500 ogni 10 secondi) viene controllato anche il database, oltre alla cache.
  113.                 boolean checkOnlyCache = properties.isAbilitataCacheGestoreMessaggi();
  114.                 if(refreshOnlyCacheCount==(properties.getTransactionManagerCheckDBInterval())){
  115.                     msgDiag.highDebug("Re-inizializzo contatore refreshOnlyCacheCount");
  116.                     refreshOnlyCacheCount = 1;
  117.                     checkOnlyCache = false;
  118.                 }
  119.                
  120.                 msgDiag.highDebug("Transaction su IDM["+idModulo+"] IDBusta["+idBusta+"] Tipo["+
  121.                         tipo+"] IDJMS["+idJMS+"] SA["+servizioApplicativo+"] ancora interval["
  122.                         +properties.getTransactionManagerCheckInterval()+"]  millisecondi "+(scadenzaWhile-DateManager.getTimeMillis()));
  123.                
  124.                 boolean needConnection = false;
  125.                 if(properties.singleConnectionTransactionManager()==false)
  126.                     needConnection = true;
  127.                 else if(resource==null)
  128.                     needConnection = true;
  129.                 if( (checkOnlyCache==false) && needConnection ){
  130.                     msgDiag.highDebug("Prendo Connessione per TransactionManager");
  131.                     try{
  132.                         resource = dbManager.getResource(dominio,idModuloTransaction,PdDContext.getValue(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE, pddContext));
  133.                     }catch(Exception e){
  134.                         throw new NodeException("Impossibile ottenere una Risorsa dal DBManager",e);
  135.                     }
  136.                     if(resource==null)
  137.                         throw new NodeException("Risorsa is null");
  138.                     if(resource.getResource() == null)
  139.                         throw new NodeException("Connessione is null");
  140.                     Connection connectionDB = (Connection) resource.getResource();
  141.                     state.setConnectionDB(connectionDB);
  142.            
  143.                 }
  144.                
  145.                 msgDiag.highDebug("getProprietario("+checkOnlyCache+")");
  146.                 String proprietarioMessaggio = msg.getProprietario(idModulo,checkOnlyCache);
  147.                 msgDiag.highDebug("getProprietario("+checkOnlyCache+") proprietario="+proprietarioMessaggio);
  148.            
  149.                 /*
  150.                 if( (idModulo.startsWith(RicezioneContenutiApplicativi.ID_MODULO)) ||
  151.                     (ConsegnaContenutiApplicativi.ID_MODULO.equals(idModulo)) ){
  152.                     //log.info("getProprietarioSerializable");
  153.                     proprietarioMessaggio = msg.getProprietario_SerializableRead(properties.getGestioneSerializableDB_AttesaAttiva(),
  154.                             properties.getGestioneSerializableDB_CheckInterval());
  155.                 } else {
  156.                     //log.info("getProprietario");
  157.                     proprietarioMessaggio = msg.getProprietario();
  158.                 }
  159.                 */
  160.                
  161.                 if(proprietarioMessaggio==null){
  162.                     // N.B.: anche se il thread eliminazione messaggi elimina un messaggio, poi comunque dopo 500 tentativi il
  163.                     //       messaggio verra' eliminato!
  164.                     msgDiag.highDebug("Messaggio per il modulo ["+idModulo+"]: Attesa attiva, modulo precendente [Punto di inizio]");
  165.                     // Attesa attiva del modulo:
  166.                     if((properties.singleConnectionTransactionManager()==false) && (checkOnlyCache==false)){
  167.                         msgDiag.highDebug("Rilascio connessione per TransactionManager");
  168.                         dbManager.releaseResource(dominio,idModuloTransaction,resource);
  169.                     }
  170.                     //try {
  171.                         //Utilities.sleep((new java.util.Random()).nextInt(properties.getTransactionManager_CheckInterval())); // random da 0ms a TransactionManagerCheckInterval ms
  172.                     //}catch(Exception eRandom){}
  173.                     Utilities.sleep(properties.getTransactionManagerCheckInterval());
  174.                 }
  175.                 else if(proprietarioMessaggio.equals(idModulo)){
  176.                     msgDiag.highDebug("Messaggio per il modulo ["+idModulo+"]: proprietario["+proprietarioMessaggio+"] OK");
  177.                     if( (properties.singleConnectionTransactionManager() && (resource!=null) ) || (checkOnlyCache==false) ){
  178.                         msgDiag.highDebug("Rilascio connessione per TransactionManager");
  179.                         dbManager.releaseResource(dominio,idModuloTransaction,resource);
  180.                     }
  181.                     return true; // messaggio per il modulo
  182.                 }
  183.                 else if( TransactionManager.isModuloPrecedente(idModulo,proprietarioMessaggio,false) == false ){
  184.                     msgDiag.highDebug("Messaggio per il modulo ["+idModulo+"]: proprietario["+proprietarioMessaggio+"], scarta");
  185.                     if( (properties.singleConnectionTransactionManager() && (resource!=null) ) || (checkOnlyCache==false) ){
  186.                         msgDiag.highDebug("Rilascio connessione per TransactionManager");
  187.                         dbManager.releaseResource(dominio,idModuloTransaction,resource);
  188.                     }
  189.                     return false; // scarta messaggio poiche' il messaggio e' gia stato consegnato al modulo successivo
  190.                 }
  191.                 else{
  192.                     // Se avevo onlyCache, non possedevo la connessione
  193.                     if(needConnection && checkOnlyCache){
  194.                         msgDiag.highDebug("Prendo Connessione per TransactionManager NeedForJMS");
  195.                         try{
  196.                             resource = dbManager.getResource(dominio,idModuloTransaction,PdDContext.getValue(org.openspcoop2.core.constants.Costanti.ID_TRANSAZIONE, pddContext));
  197.                         }catch(Exception e){
  198.                             throw new NodeException("Impossibile ottenere una Risorsa dal DBManager",e);
  199.                         }
  200.                         if(resource==null)
  201.                             throw new NodeException("Risorsa is null");
  202.                         if(resource.getResource() == null)
  203.                             throw new NodeException("Connessione is null");
  204.                         Connection connectionDB = (Connection) resource.getResource();
  205.                         state.setConnectionDB(connectionDB);
  206.                        
  207.                     }
  208.                    
  209.                     // Aggiornamento primo msg ricevuto
  210.                     String idJMSRicevutoPrecedentemente = null;
  211.                     if( ConsegnaContenutiApplicativi.ID_MODULO.equals(idModulo)  ){
  212.                         idJMSRicevutoPrecedentemente = msg.getIDJMSRicevuto(idModulo,servizioApplicativo);
  213.                     }else{
  214.                         idJMSRicevutoPrecedentemente =msg.getIDJMSRicevuto(idModulo);
  215.                     }

  216.                     if(idJMSRicevutoPrecedentemente == null){
  217.                         // Se null, e' la prima copia ricevuta, la memorizzo
  218.                         if( ConsegnaContenutiApplicativi.ID_MODULO.equals(idModulo)  ){
  219.                             msg.aggiornaIDHeaderJMS(idModulo,idJMS,servizioApplicativo);
  220.                         }else{
  221.                             msg.aggiornaIDHeaderJMS(idModulo,idJMS);
  222.                         }
  223.                     }else{
  224.                         // se non e' null guardo se e' una copia per me
  225.                         if(idJMSRicevutoPrecedentemente.equals(idJMS) == false){
  226.                             msgDiag.highDebug("Messaggio per il modulo ["+idModulo+"]: un altro messaggio JMS e' gia stato ricevuto, scarta");
  227.                             msgDiag.highDebug("Rilascio connessione per TransactionManager");
  228.                             dbManager.releaseResource(dominio,idModuloTransaction,resource);
  229.                             return false; // scarta messaggio
  230.                         }
  231.                     }

  232.                     msgDiag.highDebug("Messaggio per il modulo ["+idModulo+"]: Attesa attiva, modulo precedente["+proprietarioMessaggio+"]");
  233.                     // Attesa attiva del modulo:
  234.                     if( properties.singleConnectionTransactionManager()==false ){
  235.                         msgDiag.highDebug("Rilascio connessione per TransactionManager");
  236.                         dbManager.releaseResource(dominio,idModuloTransaction,resource);
  237.                     }
  238.                     Utilities.sleep(properties.getTransactionManagerCheckInterval());
  239.                 }

  240.             }

  241.             if( properties.singleConnectionTransactionManager() ){
  242.                 msgDiag.highDebug("Rilascio connessione per TransactionManager");
  243.                 dbManager.releaseResource(dominio,idModuloTransaction,resource);
  244.             }
  245.            
  246.             String msgTerminato = "TransactionManager: Attesa attiva terminata, probabilmente il messaggio e' stato gia gestito ed eliminato IDModulo["+idModulo+"] IDBusta["+idBusta+"] Tipo["+tipo+"]";
  247.             msgDiag.highDebug(msgTerminato);
  248.             TransactionManager.log.warn(msgTerminato);
  249.             return false;

  250.         }catch(Exception e){
  251.             TransactionManager.log.error("TransactionManager exception: "+e.getMessage(),e);
  252.             if(resource != null)
  253.                 dbManager.releaseResource(dominio,idModuloTransaction,resource);
  254.             throw e;
  255.         }
  256.     }


  257.     /**
  258.      * Restituisce true se il modulo <var>proprietarioMessaggio</var> e' un modulo precedente, nel flusso dei messaggi
  259.      * all'interno dell'architettura di OpenSPCoop del modulo <var>idModulo</var>
  260.      *
  261.      * @param idModulo Identificatore del nodo che effettua il test.
  262.      * @param proprietarioMessaggio 'Potenziale' modulo precedente
  263.      *
  264.      */
  265.     public static boolean isModuloPrecedente(String idModulo,String proprietarioMessaggio,boolean checkProprietarioNull) throws Exception{

  266.         if(idModulo.startsWith(RicezioneContenutiApplicativi.ID_MODULO)){
  267.             if(Imbustamento.ID_MODULO.equals(proprietarioMessaggio))
  268.                 return true;
  269.             else if(InoltroBuste.ID_MODULO.equals(proprietarioMessaggio))
  270.                 return true;
  271.             else if(SbustamentoRisposte.ID_MODULO.equals(proprietarioMessaggio))
  272.                 return true;
  273.             else if(Sbustamento.ID_MODULO.equals(proprietarioMessaggio))
  274.                 return true; // ricevute asincrone!
  275.             else
  276.                 return false;
  277.         }

  278.         else if(idModulo.startsWith(RicezioneBuste.ID_MODULO)){
  279.             if(Sbustamento.ID_MODULO.equals(proprietarioMessaggio))
  280.                 return true;
  281.             else if(ConsegnaContenutiApplicativi.ID_MODULO.equals(proprietarioMessaggio))
  282.                 return true;
  283.             else if(ImbustamentoRisposte.ID_MODULO.equals(proprietarioMessaggio))
  284.                 return true;
  285.             else
  286.                 return false;  
  287.         }

  288.         else if(Imbustamento.ID_MODULO.equals(idModulo)){
  289.             if(checkProprietarioNull){
  290.                 if(proprietarioMessaggio == null)
  291.                     return true; // punto di inizio
  292.             }
  293.             if(proprietarioMessaggio.startsWith(RicezioneContenutiApplicativi.ID_MODULO))
  294.                 return true;
  295.             else
  296.                 return false;
  297.         }

  298.         else if(ImbustamentoRisposte.ID_MODULO.equals(idModulo)){
  299.             if(ConsegnaContenutiApplicativi.ID_MODULO.equals(proprietarioMessaggio))
  300.                 return true;
  301.             else
  302.                 return false;
  303.         }

  304.         else if(Sbustamento.ID_MODULO.equals(idModulo)){
  305.             if(checkProprietarioNull){
  306.                 if(proprietarioMessaggio == null)
  307.                     return true; // punto di inizio
  308.             }
  309.             if(proprietarioMessaggio.startsWith(RicezioneBuste.ID_MODULO))
  310.                 return true;
  311.             else
  312.                 return false;
  313.         }

  314.         else if(SbustamentoRisposte.ID_MODULO.equals(idModulo)){
  315.             if(InoltroBuste.ID_MODULO.equals(proprietarioMessaggio))
  316.                 return true;
  317.             else
  318.                 return false;
  319.         }

  320.         else if(InoltroBuste.ID_MODULO.equals(idModulo)){
  321.             if(Imbustamento.ID_MODULO.equals(proprietarioMessaggio))
  322.                 return true;
  323.             else
  324.                 return false;
  325.         }

  326.         else if(InoltroRisposte.ID_MODULO.equals(idModulo)){
  327.             // E' un punto di uscita senza risposta
  328.             // qualsiasi nodo e' un potenziale precedente
  329.             return true;
  330.         }

  331.         else if(ConsegnaContenutiApplicativi.ID_MODULO.equals(idModulo)){
  332.             if(Sbustamento.ID_MODULO.equals(proprietarioMessaggio))
  333.                 return true;
  334.             else if(SbustamentoRisposte.ID_MODULO.equals(proprietarioMessaggio))
  335.                 return true;
  336.             else if(Imbustamento.ID_MODULO.equals(proprietarioMessaggio))
  337.                 return true;
  338.             else if(InoltroBuste.ID_MODULO.equals(proprietarioMessaggio))
  339.                 return true;
  340.             else
  341.                 return false;
  342.         }else{
  343.             return false;
  344.         }
  345.     }

  346. }