Riscontri.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.protocol.engine.driver;


  21. import java.sql.Connection;
  22. import java.sql.PreparedStatement;
  23. import java.sql.ResultSet;
  24. import java.util.ArrayList;
  25. import java.util.Date;
  26. import java.util.List;

  27. import org.openspcoop2.protocol.engine.Configurazione;
  28. import org.openspcoop2.protocol.engine.constants.Costanti;
  29. import org.openspcoop2.protocol.sdk.ProtocolException;
  30. import org.openspcoop2.protocol.sdk.constants.ProfiloDiCollaborazione;
  31. import org.openspcoop2.protocol.sdk.state.IState;
  32. import org.openspcoop2.protocol.sdk.state.StateMessage;
  33. import org.openspcoop2.utils.LoggerWrapperFactory;
  34. import org.openspcoop2.utils.Utilities;
  35. import org.openspcoop2.utils.date.DateManager;
  36. import org.openspcoop2.utils.jdbc.JDBCUtilities;
  37. import org.openspcoop2.utils.sql.ISQLQueryObject;
  38. import org.openspcoop2.utils.sql.SQLObjectFactory;
  39. import org.slf4j.Logger;

  40. /**
  41.  * Sono inclusi i metodi per la gestione dei Riscontri.
  42.  * La gestione dei riscontri puo' essere classificati nella seguente maniera :
  43.  * <ul>
  44.  * <li> Riscontri da ricevere, dove un mittente ha inviato una busta e sta' attendendo un ACK!
  45.  * <li> Riscontri da inviare (modalita' NON PIGGYBACKING), quando una porta di dominio riceve una busta
  46.  *      con profilo 'ConfermaRicezione'==true, deve generare un riscontro apposito.
  47.  * <li> Riscontri da inviare (modalita' PIGGYBACKING), quando una porta di dominio riceve una busta
  48.  *      con profilo 'ConfermaRicezione'==true, deve salvarsi le informazioni per generare successivi riscontri.
  49.  * </ul>
  50.  * Tutti i metodi hanno bisogno di una connessione ad un DB, precedentemente impostata
  51.  * e passata attraverso l'apposito metodo.
  52.  *
  53.  *
  54.  * @author Poli Andrea (apoli@link.it)
  55.  * @author Tronci Fabio (tronci@link.it)
  56.  * @author $Author$
  57.  * @version $Rev$, $Date$
  58.  */

  59. public class Riscontri  {



  60.     /** Logger utilizzato per debug. */
  61.     private Logger log = null;
  62.    
  63.     /** Se IState e' un'istanza di StatefulMessage possiede una Connessione SQL in autoCommit mode su cui effettuare query
  64.      *  Altrimenti, e' un'istanza di StatelessMessage e nn necessita di connessioni  */
  65.     private IState state;



  66.     /* ********  C O S T R U T T O R E  ******** */

  67.     /**
  68.      * Costruttore.
  69.      *
  70.      * @param state Oggetto che rappresenta lo stato di una busta
  71.      *
  72.      */
  73.     public Riscontri(IState state){
  74.         this(state,Configurazione.getLibraryLog());
  75.     }
  76.     /**
  77.      * Costruttore.
  78.      *
  79.      * @param state Oggetto che rappresenta lo stato di una busta
  80.      *
  81.      */
  82.     public Riscontri(IState state ,Logger alog){
  83.         this.state = state;
  84.         if(alog!=null){
  85.             this.log = alog;
  86.         }else{
  87.             this.log = LoggerWrapperFactory.getLogger(Riscontri.class.getName());
  88.         }
  89.     }

  90.     public void updateState(IState state) {
  91.         this.state = state;
  92.     }
  93.    
  94.     /* ********  R I S C O N T R I    D A    R I C E V E R E  ******** */

  95.     /**
  96.      * Aggiunge un riscontro da ricevere nella tabella per la gestione della fase di ricezione riscontro.
  97.      *
  98.      * @param id identificativo della busta.
  99.      * @param timestamp data di invio della busta.
  100.      *
  101.      */
  102.     public void registraRiscontroDaRicevere(String id , Date timestamp)throws ProtocolException{

  103.         StateMessage stateMSG = (StateMessage)this.state;
  104.         Connection connectionDB = stateMSG.getConnectionDB();
  105.        
  106.         PreparedStatement pstmt = null;
  107.        
  108.         try{    

  109.             java.sql.Timestamp oraInvio = new java.sql.Timestamp(timestamp.getTime());

  110.             StringBuilder query = new StringBuilder();
  111.             query.append("INSERT INTO  ");
  112.             query.append(Costanti.RISCONTRI_DA_RICEVERE);
  113.             query.append(" VALUES ( ? , ? )");


  114.             pstmt = connectionDB.prepareStatement(query.toString());
  115.             pstmt.setString(1,id);
  116.             pstmt.setTimestamp(2,oraInvio);

  117.             //  Add PreparedStatement (LASCIARE UPDATE per ordine esecuzione)
  118.             stateMSG.getPreparedStatement().put("UPDATE saveRiscontroDaRicevere_"+id,pstmt);


  119.             // Registrazione nella tabella History
  120.             History historyBuste = new History(this.state);
  121.             historyBuste.registraBustaInviata(id);
  122.    
  123.         } catch(Exception e) {
  124.             String errorMsg = "RISCONTRI, Errore di registrazione "+id+": "+e.getMessage();    
  125.             this.log.error(errorMsg,e);
  126.             try{
  127.                 if( pstmt != null )
  128.                     pstmt.close();
  129.             } catch(Exception er) {
  130.                 // Eccezione SQL.
  131.             }
  132.             throw new ProtocolException(errorMsg,e);
  133.         }

  134.     }


  135.     /**
  136.      * In caso esista nella tabella dei riscontri da ricevere, un riscontro scaduto,
  137.      * si occupa di ritornare un array di riscontro da reinviare, aggiornando le loro data di registrazione.
  138.      * Il controllo non e' serializzato, quindi possono essere ritornate anche busta gia' riscontrate, in seguito al controllo.
  139.      *
  140.      * @param timeout Minuti dopo il quale una data risulta scaduta.
  141.      * @return un List di {@link org.openspcoop2.protocol.sdk.Busta} contenente le informazioni necessarie per il re-invio delle buste,
  142.      *         se esistono riscontro scaduti.
  143.      */
  144.     public List<BustaNonRiscontrata> getBustePerUlterioreInoltro(long timeout, int limit, int offset, boolean logQuery)throws ProtocolException{
  145.        
  146.         StateMessage stateMSG = (StateMessage)this.state;
  147.         Connection connectionDB = stateMSG.getConnectionDB();

  148.         PreparedStatement pstmt = null;
  149.         ResultSet rs = null;
  150.         java.util.List<String> IDBuste = new java.util.ArrayList<>();
  151.         String queryString = null;
  152.         try{    

  153.             long nowTime = DateManager.getTimeMillis() - (timeout * 60 * 1000);
  154.             java.sql.Timestamp scadenzaRiscontro = new java.sql.Timestamp(nowTime);


  155.             if(Configurazione.getSqlQueryObjectType()==null){
  156.                 StringBuilder query = new StringBuilder();
  157.                 query.append("SELECT ID_MESSAGGIO FROM ");
  158.                 query.append(Costanti.RISCONTRI_DA_RICEVERE);
  159.                 query.append(" WHERE DATA_INVIO < ? ");
  160.                 queryString = query.toString();
  161.             }else{

  162.                 ISQLQueryObject sqlQueryObject = SQLObjectFactory.createSQLQueryObject(Configurazione.getSqlQueryObjectType());
  163.                 sqlQueryObject.addSelectField("ID_MESSAGGIO");
  164.                 sqlQueryObject.addSelectField("DATA_INVIO");
  165.                 sqlQueryObject.addFromTable(Costanti.RISCONTRI_DA_RICEVERE);
  166.                 sqlQueryObject.addWhereCondition("DATA_INVIO < ?"); // order by e' obbligatorio essendoci l'offset
  167.                 sqlQueryObject.setANDLogicOperator(true);
  168.                 sqlQueryObject.addOrderBy("DATA_INVIO");
  169.                 sqlQueryObject.setSortType(true);
  170.                 sqlQueryObject.setLimit(limit);
  171.                 sqlQueryObject.setOffset(offset);
  172.                 queryString = sqlQueryObject.createSQLQuery();
  173.             }


  174.             //System.out.println("QUERY RISCONTRI IS: ["+queryString+"] 1["+scadenzaRiscontro+"]");

  175.             pstmt = connectionDB.prepareStatement(queryString);
  176.             pstmt.setTimestamp(1,scadenzaRiscontro);

  177.             // Esecuzione comando SQL
  178.             long startDateSQLCommand = DateManager.getTimeMillis();
  179.             if(logQuery)
  180.                 this.log.debug("[QUERY] (Riscontri) ["+queryString+"] 1["+scadenzaRiscontro+"]...");
  181.             rs = pstmt.executeQuery();      
  182.             long endDateSQLCommand = DateManager.getTimeMillis();
  183.             long secondSQLCommand = (endDateSQLCommand - startDateSQLCommand) / 1000;
  184.             if(logQuery)
  185.                 this.log.debug("[QUERY] (Riscontri) ["+queryString+"] 1["+scadenzaRiscontro+"] effettuata in "+secondSQLCommand+" secondi");

  186.             if(rs == null) {
  187.                 pstmt.close();
  188.                 throw new ProtocolException("RS NULL?");
  189.             }
  190.             int countLimit = 0;
  191.             int countOffset = 0;
  192.             while(rs.next()){
  193.                 if(Configurazione.getSqlQueryObjectType()==null){
  194.                     // OFFSET APPLICATIVO
  195.                     if( countOffset>=offset ){
  196.                         String id = rs.getString("ID_MESSAGGIO");
  197.                         IDBuste.add(id);
  198.                         // LIMIT Applicativo
  199.                         countLimit++;
  200.                         if(countLimit==limit)
  201.                             break;
  202.                     }
  203.                     else{
  204.                         countOffset++;
  205.                     }
  206.                 }else{
  207.                     String id = rs.getString("ID_MESSAGGIO");
  208.                     IDBuste.add(id);
  209.                 }
  210.             }
  211.             rs.close();
  212.             pstmt.close();

  213.         } catch(Exception e) {
  214.             try{
  215.                 if( rs != null )
  216.                     rs.close();
  217.             } catch(Exception er) {
  218.                 // close
  219.             }
  220.             try{
  221.                 if( pstmt != null )
  222.                     pstmt.close();
  223.             } catch(Exception er) {
  224.                 // close
  225.             }
  226.             String errorMsg = "[Riscontri.getBustePerUlterioreInoltro] errore, queryString["+queryString+"]: "+e.getMessage();
  227.             this.log.error(errorMsg,e);
  228.             throw new ProtocolException(errorMsg,e);
  229.         }
  230.        
  231.        
  232.         List<BustaNonRiscontrata> listaBustaNonRiscontrata = new ArrayList<BustaNonRiscontrata>();
  233.         for (int i = 0; i < IDBuste.size(); i++) {
  234.             BustaNonRiscontrata bustaNonRiscontrata = new BustaNonRiscontrata();
  235.             bustaNonRiscontrata.setIdentificativo(IDBuste.get(i));
  236.             bustaNonRiscontrata.setProfiloCollaborazione(ProfiloDiCollaborazione.ONEWAY);
  237.             listaBustaNonRiscontrata.add(bustaNonRiscontrata);
  238.         }
  239.        
  240.         return listaBustaNonRiscontrata;
  241.        
  242.     }
  243.    
  244.    
  245.    
  246.     /**
  247.      * Valida un riscontro ricevuto, identificato dall'identificativo della busta.
  248.      * In caso di validazione del riscontro, l'entry nella tabella di gestione dei riscontri da ricevere,
  249.      * con chiave di accesso uguale al parametro <var>idRiscontro</var> viene cancellata.
  250.      *
  251.      * @param idRiscontro identificativo del riscontro da validare.
  252.      * @deprecated utilizzare la versione non serializable
  253.      */
  254.     @Deprecated
  255.     public void validazioneRiscontroRicevuto_serializable(String idRiscontro) throws ProtocolException{
  256.         validazioneRiscontroRicevuto_serializable(idRiscontro,60l,100);
  257.     }


  258.     /**
  259.      * Valida un riscontro ricevuto, identificato dall'identificativo della busta.
  260.      * In caso di validazione del riscontro, l'entry nella tabella di gestione dei riscontri da ricevere,
  261.      * con chiave di accesso uguale al parametro <var>idRiscontro</var> viene cancellata.
  262.      *
  263.      * @param idRiscontro identificativo del riscontro da validare.
  264.      * @param attesaAttiva AttesaAttiva per la gestione del livello di serializable
  265.      * @param checkInterval Intervallo di check per la gestione  del livello di serializable
  266.      * @deprecated utilizzare la versione non serializable
  267.      */
  268.     @Deprecated
  269.     public void validazioneRiscontroRicevuto_serializable(String idRiscontro,long attesaAttiva,int checkInterval) throws ProtocolException{
  270.         StateMessage stateMSG = (StateMessage)this.state;
  271.         Connection connectionDB = stateMSG.getConnectionDB();


  272.         /*
  273.   Viene realizzato con livello di isolamento SERIALIZABLE, per essere sicuri
  274.   che esecuzioni parallele non leggano dati inconsistenti.
  275.   Con il livello SERIALIZABLE, se ritorna una eccezione, deve essere riprovato
  276.          */
  277.         // setAutoCommit e livello Isolamento
  278.         int oldTransactionIsolation = -1;
  279.         try{
  280.             oldTransactionIsolation =connectionDB.getTransactionIsolation();
  281.             connectionDB.setAutoCommit(false);
  282.             JDBCUtilities.setTransactionIsolationSerializable(Configurazione.getSqlQueryObjectType(), connectionDB);
  283.         } catch(Exception er) {
  284.             String errorMsg = "RISCONTRI, Errore durante la validazioneRiscontroRicevuto(setIsolation): "+er.getMessage();      
  285.             this.log.error(errorMsg,er);
  286.             throw new ProtocolException(errorMsg,er);
  287.         }


  288.         boolean deleteRiscontroOK = false;

  289.         long scadenzaWhile = DateManager.getTimeMillis() + attesaAttiva;

  290.         while(deleteRiscontroOK==false && DateManager.getTimeMillis() < scadenzaWhile){

  291.             PreparedStatement pstmtDelete = null;
  292.             try{

  293.                 // Eliminazione dalla tabella Riscontri
  294.                 StringBuilder query = new StringBuilder();
  295.                 query.delete(0,query.capacity());
  296.                 query.append("DELETE FROM ");
  297.                 query.append(Costanti.RISCONTRI_DA_RICEVERE);
  298.                 query.append(" WHERE ID_MESSAGGIO = ?");
  299.                 pstmtDelete = connectionDB.prepareStatement(query.toString());
  300.                 pstmtDelete.setString(1,idRiscontro);
  301.                 pstmtDelete.execute();
  302.                 pstmtDelete.close();

  303.                 // Eliminazione dalla tabella History
  304.                 History historyBuste = new History(this.state,this.log);
  305.                 historyBuste.eliminaBustaInviata(idRiscontro);
  306.                
  307.                 stateMSG.executePreparedStatement();

  308.                 // Chiusura Transazione
  309.                 connectionDB.commit();

  310.                 // ID Costruito
  311.                 deleteRiscontroOK = true;

  312.             } catch(Exception e) {
  313.                 try{
  314.                     if( pstmtDelete != null  )
  315.                         pstmtDelete.close();
  316.                 } catch(Exception er) {
  317.                     // close
  318.                 }
  319.                 try{
  320.                     connectionDB.rollback();
  321.                 } catch(Exception er) {
  322.                     // ignore
  323.                 }
  324.             }

  325.             if(deleteRiscontroOK == false){
  326.                 // Per aiutare ad evitare conflitti
  327.                 try{
  328.                     Utilities.sleep(ProtocolRandomUtilities.getRandom().nextInt(checkInterval)); // random da 0ms a checkIntervalms
  329.                 }catch(Exception eRandom){
  330.                     // ignore
  331.                 }
  332.             }
  333.         }

  334.         // Ripristino Transazione
  335.         try{
  336.             connectionDB.setTransactionIsolation(oldTransactionIsolation);
  337.             connectionDB.setAutoCommit(true);
  338.         } catch(Exception er) {
  339.             String errorMsg = "RISCONTRI, Errore durante la validazioneRiscontroRicevuto(ripristinoIsolation): "+er.getMessage();      
  340.             this.log.error(errorMsg,er);
  341.             throw new ProtocolException(errorMsg,er);
  342.         }
  343.     }  


  344.     /**
  345.      * Valida un riscontro ricevuto, identificato dall'identificativo della busta.
  346.      * In caso di validazione del riscontro, l'entry nella tabella di gestione dei riscontri da ricevere,
  347.      * con chiave di accesso uguale al parametro <var>idRiscontro</var> viene cancellata.
  348.      *
  349.      * @param idRiscontro identificativo del riscontro da validare.
  350.      */
  351.     public void validazioneRiscontroRicevuto(String idRiscontro) throws ProtocolException{
  352.        
  353.         StateMessage stateMSG = (StateMessage)this.state;
  354.         Connection connectionDB = stateMSG.getConnectionDB();

  355.         PreparedStatement pstmtDelete = null;
  356.         try{

  357.             // Eliminazione dalla tabella Riscontri
  358.             StringBuilder query = new StringBuilder();
  359.             query.delete(0,query.capacity());
  360.             query.append("DELETE FROM ");
  361.             query.append(Costanti.RISCONTRI_DA_RICEVERE);
  362.             query.append(" WHERE ID_MESSAGGIO = ?");
  363.             pstmtDelete = connectionDB.prepareStatement(query.toString());
  364.             pstmtDelete.setString(1,idRiscontro);

  365.             //  Add PreparedStatement (LASCIARE UPDATE per ordine esecuzione)
  366.             stateMSG.getPreparedStatement().put("UPDATE validazioneRiscontroRicevuto_"+idRiscontro,pstmtDelete);

  367.             // Eliminazione dalla tabella History
  368.             History historyBuste = new History(stateMSG,this.log);
  369.             historyBuste.eliminaBustaInviataPerRiscontri(idRiscontro);
  370.         } catch(Exception e) {
  371.             try{
  372.                 if( pstmtDelete != null  )
  373.                     pstmtDelete.close();
  374.             } catch(Exception er) {
  375.                 // close
  376.             }
  377.             String errorMsg = "RISCONTRI, Errore durante la validazioneRiscontroRicevuto: "+e.getMessage();    
  378.             this.log.error(errorMsg,e);
  379.             throw new ProtocolException(errorMsg,e);
  380.         }
  381.     }
  382. }