DBManager.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.config;

  21. import java.sql.Connection;
  22. import java.sql.SQLException;
  23. import java.sql.Statement;
  24. import java.text.SimpleDateFormat;
  25. import java.util.Enumeration;

  26. import javax.naming.RefAddr;
  27. import javax.sql.DataSource;

  28. import org.openspcoop2.core.commons.CoreException;
  29. import org.openspcoop2.core.commons.IMonitoraggioRisorsa;
  30. import org.openspcoop2.core.constants.CostantiDB;
  31. import org.openspcoop2.core.id.IDSoggetto;
  32. import org.openspcoop2.pdd.logger.MsgDiagnostico;
  33. import org.openspcoop2.pdd.logger.OpenSPCoop2Logger;
  34. import org.openspcoop2.utils.LoggerWrapperFactory;
  35. import org.openspcoop2.utils.UtilsException;
  36. import org.openspcoop2.utils.date.DateManager;
  37. import org.openspcoop2.utils.date.DateUtils;
  38. import org.openspcoop2.utils.id.UniqueIdentifierException;
  39. import org.openspcoop2.utils.jdbc.JDBCUtilities;
  40. import org.openspcoop2.utils.resources.GestoreJNDI;
  41. import org.slf4j.Logger;


  42. /**
  43.  * Contiene la gestione delle connessioni ad un Database.
  44.  * Il nome della risorsa JNDI da cui e' possibili attingere connessioni verso il Database,
  45.  * viene selezionato attraverso le impostazioni lette dal file 'govway.properties'
  46.  * e gestite attraverso l'utilizzo della classe  {@link org.openspcoop2.pdd.config.OpenSPCoop2Properties}.
  47.  *
  48.  *
  49.  * @author Poli Andrea (apoli@link.it)
  50.  * @author $Author$
  51.  * @version $Rev$, $Date$
  52.  */

  53. public class DBManager implements IMonitoraggioRisorsa {

  54.     private static final String ID_MODULO = "DBManager";
  55.    
  56.     /** DBManager */
  57.     private static DBManager manager = null;
  58.     /** TransactionIsolation level */
  59.     private static int transactionIsolationLevel = -1;
  60.     public static void setTransactionIsolationLevel(int transactionIsolationLevel) {
  61.         DBManager.transactionIsolationLevel = transactionIsolationLevel;
  62.     }

  63.     /** NomeJNDIC DataSource dove attingere connessioni */
  64.     private String dataSourceJndiName = null;
  65.     /** DataSource dove attingere connessioni */
  66.     private DataSource dataSource = null;
  67.     /** MsgDiagnostico */
  68.     private MsgDiagnostico msgDiag = null;

  69.     /** Informazione sui proprietari che hanno richiesto una connessione */
  70.     private static java.util.concurrent.ConcurrentHashMap<String,Resource> risorseInGestione = new java.util.concurrent.ConcurrentHashMap<>();
  71.    
  72.     /** Informazioni sui check */
  73.     private static boolean getConnectionCheckAutoCommitDisabled;
  74.     private static boolean getConnectionCheckTransactionIsolation;
  75.     private static int getConnectionCheckTransactionIsolationExpected;
  76.    
  77.     /** Stato di inizializzazione del manager */
  78.     private static boolean initialized = false;

  79.     public static String[] getStatoRisorse() {  
  80.         return getStatoRisorse(DBManager.risorseInGestione);
  81.     }
  82.     public static String[] getStatoRisorse(java.util.concurrent.ConcurrentMap<String,Resource> risorseInGestione) {
  83.         String[] sNull = null;
  84.         Object[] o = risorseInGestione.values().toArray(new Resource[0]);
  85.         if(! (o instanceof Resource[]))
  86.             return sNull;
  87.         Resource[] resources = (Resource[]) o;
  88.         if(resources.length<=0)
  89.             return sNull;
  90.    
  91.         String [] r = new String[resources.length];
  92.         for(int i=0; i<resources.length; i++){
  93.             Resource rr = resources[i];
  94.             r[i] = rr.getIdentificativoPorta()+"."+rr.getModuloFunzionale();
  95.             if(rr.getIdTransazione()!=null){
  96.                 r[i] = r[i] +"."+rr.getIdTransazione();
  97.             }
  98.             SimpleDateFormat dateformat = DateUtils.getSimpleDateFormatMs();
  99.             r[i] = r[i] +" ("+dateformat.format(rr.getDate())+")";
  100.         }
  101.         return r;
  102.        
  103.     }


  104.     /**
  105.      * Viene chiamato in causa per istanziare il datasource
  106.      * @throws SQLException
  107.      *
  108.      *
  109.      */
  110.     private DBManager(String jndiName,java.util.Properties context) throws UtilsException, SQLException {

  111.         this.msgDiag = MsgDiagnostico.newInstance(ID_MODULO);

  112.         GestoreJNDI jndi = new GestoreJNDI(context);
  113.         Object oSearch = jndi.lookup(jndiName);
  114.         if(oSearch==null){
  115.             throw new UtilsException("Lookup jndiResource ["+jndiName+"] not found");
  116.         }
  117.         try{
  118.             this.dataSourceJndiName = jndiName;
  119.             this.dataSource = (DataSource) oSearch;
  120.         }catch(Throwable t){
  121.             StringBuilder bf = new StringBuilder();
  122.             if(oSearch instanceof javax.naming.Reference){
  123.                 javax.naming.Reference r = (javax.naming.Reference) oSearch;
  124.                 bf.append(" (Factory=");
  125.                 bf.append(r.getFactoryClassName());
  126.                 bf.append(" FactoryLocation=");
  127.                 bf.append(r.getFactoryClassLocation());
  128.                 Enumeration<RefAddr> enR = r.getAll();
  129.                 if(enR!=null){
  130.                     while (enR.hasMoreElements()) {
  131.                         RefAddr refAddr = enR.nextElement();
  132.                         bf.append(" [").
  133.                             append("type=").
  134.                             append(refAddr.getType()).
  135.                             append(" content=").
  136.                             append(refAddr.getContent()).
  137.                             append("]");
  138.                     }
  139.                 }
  140.                 bf.append(")");
  141.             }
  142.             throw new UtilsException("lookup failed (object class: "+oSearch.getClass().getName()+")"+bf.toString()+": "+t.getMessage(),t);
  143.         }
  144.         /**this.jndiName = jndiName;*/
  145.        
  146.         // Prelevo livello di transaction isolation
  147.         try(java.sql.Connection connectionTest = this.dataSource.getConnection();){
  148.             DBManager.setTransactionIsolationLevel(connectionTest.getTransactionIsolation());
  149.         }      

  150.     }
  151.    
  152.    
  153.     public static boolean isInitialized() {
  154.         return DBManager.initialized;
  155.     }

  156.     private static void setInitialized(boolean initialized) {
  157.         DBManager.initialized = initialized;        
  158.     }



  159.     /**
  160.      * Il Metodo si occupa di inizializzare il propertiesReader del QueueManager
  161.      *
  162.      * @param jndiName Nome JNDI del Datasource
  163.      * @param context Contesto JNDI da utilizzare
  164.      * @throws SQLException
  165.      * @throws UtilsException
  166.      *
  167.      */
  168.     public static void initialize(String jndiName,java.util.Properties context) throws UtilsException, SQLException {
  169.         DBManager.manager = new DBManager(jndiName,context);    
  170.         DBManager.setInitialized(true);
  171.        
  172.         OpenSPCoop2Properties properties = OpenSPCoop2Properties.getInstance();
  173.         if(properties!=null) {
  174.             DBManager.getConnectionCheckAutoCommitDisabled = properties.isDataSourceGetConnectionCheckAutoCommitDisabled();
  175.             DBManager.getConnectionCheckTransactionIsolation = properties.isDataSourceGetConnectionCheckTransactionIsolationLevel();
  176.             if(DBManager.getConnectionCheckTransactionIsolation) {
  177.                 DBManager.getConnectionCheckTransactionIsolationExpected = properties.getDataSourceGetConnectionCheckTransactionIsolationLevelExpected();
  178.             }
  179.         }
  180.     }

  181.     /**
  182.      * Ritorna l'istanza di questo DBManager
  183.      *
  184.      * @return Istanza di DBManager
  185.      *
  186.      */
  187.     public static DBManager getInstance(){
  188.         if(DBManager.manager==null) {
  189.             // spotbugs warning 'SING_SINGLETON_GETTER_NOT_SYNCHRONIZED': l'istanza viene creata allo startup
  190.             synchronized (DBManager.class) {
  191.                 return DBManager.manager;
  192.             }
  193.         }
  194.         return DBManager.manager;
  195.     }

  196.     /**
  197.      * Ritorna il livello di isolamento di default del DataSource
  198.      *
  199.      * @return livello di isolamento di default del DataSource
  200.      *
  201.      */
  202.     public static int getTransactionIsolationLevel(){
  203.         return DBManager.transactionIsolationLevel;
  204.     }







  205.     /**
  206.      * Viene chiamato in causa per ottenere una connessione al DB
  207.      *
  208.      * @param idPDD Identificatore della porta di dominio.
  209.      * @param modulo Modulo che richiede una connessione al database.
  210.      * @return java.sql.Connection aperta sul database.
  211.      *
  212.      */
  213.     public Resource getResource(IDSoggetto idPDD,String modulo,String idTransazione) throws UtilsException {
  214.         return this.getResource(idPDD, modulo, idTransazione, true);
  215.     }
  216.     public Resource getResource(IDSoggetto idPDD,String modulo,String idTransazione, boolean logError) throws UtilsException {

  217.         if(this.dataSource == null)
  218.             throw new UtilsException("Datasource non istanziato");

  219.         Resource risorsa = null;
  220.         try {
  221.             risorsa = DBManager.buildResource(this.dataSourceJndiName, this.dataSource, idPDD, modulo, idTransazione);
  222.                
  223.             DBManager.risorseInGestione.put(risorsa.getId(), risorsa);
  224.            
  225.             /**if(this.dataSource instanceof SharedPoolDataSource)
  226.                 System.out.println("IDLE["+((SharedPoolDataSource)this.dataSource).getNumIdle()+"] ACTIVE["+((SharedPoolDataSource)this.dataSource).getNumActive()+"]");*/
  227.                        
  228.         }
  229.         catch(Exception e) {
  230.             if(logError) {
  231.                 this.msgDiag.aggiornaFiltri();
  232.                 this.msgDiag.setDominio(idPDD);
  233.                 this.msgDiag.setFunzione("DBManager."+modulo);
  234.                 this.msgDiag.logFatalError(e, "Richiesta connessione al datasource");
  235.             }
  236.             throw new UtilsException(e.getMessage(),e);
  237.         }
  238.        
  239.         return risorsa;
  240.     }

  241.     public static Resource buildResource(String dataSourceJndiName, DataSource dataSource, IDSoggetto idPDD,String modulo,String idTransazione) throws UtilsException, SQLException, UniqueIdentifierException {
  242.         if(dataSourceJndiName!=null) {
  243.             // log
  244.         }
  245.         /**System.out.println("### buildResource ["+dataSourceJndiName+"] modulo:"+modulo+" idTransazione:"+idTransazione+" ...");*/
  246.         Connection connectionDB = dataSource.getConnection();
  247.         checkConnection(connectionDB);
  248.         /**System.out.println("### buildResource ["+dataSourceJndiName+"] modulo:"+modulo+" idTransazione:"+idTransazione+" OK");*/
  249.         return buildResource("DBRuntimeManager", connectionDB, idPDD, modulo, idTransazione);
  250.     }
  251.     private static void checkConnection(Connection connectionDB) throws UtilsException {
  252.         if(connectionDB==null)
  253.             throw new UtilsException("is null");
  254.     }
  255.     public static Resource buildResource(String managerId, Connection connectionDB, IDSoggetto idPDD,String modulo,String idTransazione) throws SQLException, UniqueIdentifierException {
  256.        
  257.         /**if(modulo==null || !modulo.contains("Timer") || modulo.contains("TimerConsegnaContenutiApplicativi")) {
  258.             System.out.println("### buildResource managerId["+managerId+"] modulo:"+modulo+" idTransazione:"+idTransazione+" ...");
  259.         }*/
  260.        
  261.         if(connectionDB!=null) {
  262.             if(DBManager.getConnectionCheckAutoCommitDisabled &&
  263.                 !connectionDB.getAutoCommit()) {
  264.                 Logger log = OpenSPCoop2Logger.getLoggerOpenSPCoopResources();
  265.                 if(log!=null) {
  266.                     String msg = getPrefixLog(managerId, idPDD,modulo,idTransazione)+" Connessione ottenuta possiede autoCommit enabled ?";
  267.                     log.error(msg);
  268.                 }
  269.             }
  270.             if(DBManager.getConnectionCheckTransactionIsolation &&
  271.                 DBManager.getConnectionCheckTransactionIsolationExpected != connectionDB.getTransactionIsolation()) {
  272.                 Logger log = OpenSPCoop2Logger.getLoggerOpenSPCoopResources();
  273.                 if(log!=null) {
  274.                     String msg = getPrefixLog(managerId, idPDD,modulo,idTransazione)+" Connessione ottenuta possiede un transaction isolation level '"+connectionDB.getTransactionIsolation()+"' differente da quello atteso '"+DBManager.getConnectionCheckTransactionIsolationExpected+"'";
  275.                     log.error(msg);
  276.                 }
  277.             }
  278.         }
  279.        
  280.         Resource risorsa = new Resource();
  281.         String idUnivoco = Resource.generaIdentificatoreUnivoco(idPDD, modulo);
  282.         risorsa.setId(idUnivoco);
  283.         risorsa.setDate(DateManager.getDate());
  284.         risorsa.setIdentificativoPorta(idPDD);
  285.         risorsa.setModuloFunzionale(modulo);
  286.         risorsa.setResource(connectionDB);
  287.         risorsa.setResourceType(Connection.class.getName());
  288.         risorsa.setIdTransazione(idTransazione);
  289.        
  290.         return risorsa;

  291.     }
  292.     private static String getPrefixLog(String managerId, IDSoggetto idPDD,String modulo,String idTransazione) {
  293.         StringBuilder sb = new StringBuilder("");
  294.         if(managerId!=null) {
  295.             if(sb.length()>0) {
  296.                 sb.append(" ");
  297.             }
  298.             sb.append("[").append(managerId).append("]");
  299.         }
  300.         if(idTransazione!=null) {
  301.             if(sb.length()>0) {
  302.                 sb.append(" ");
  303.             }
  304.             sb.append("<").append(idTransazione).append(">");
  305.         }
  306.         if(modulo!=null) {
  307.             if(sb.length()>0) {
  308.                 sb.append(" ");
  309.             }
  310.             sb.append("modulo:").append(modulo);
  311.         }
  312.         if(idPDD!=null) {
  313.             if(sb.length()>0) {
  314.                 sb.append(" ");
  315.             }
  316.             sb.append("soggetto:").append(idPDD.toString());
  317.         }
  318.         return sb.toString();
  319.     }




  320.     /**
  321.      * Viene chiamato in causa per rilasciare una connessione al DB, effettuando precedentemente un commit
  322.      *
  323.      * @param idPDD Identificatore della porta di dominio.
  324.      * @param modulo Modulo che richiede il rilascio di una connessione al database.
  325.      * @param resource Connessione da rilasciare.
  326.      *
  327.      */
  328.     public void releaseResource(IDSoggetto idPDD,String modulo,Resource resource){
  329.         this.releaseResource(idPDD, modulo, resource, true);
  330.     }
  331.     public void releaseResource(IDSoggetto idPDD,String modulo,Resource resource, boolean logError){
  332.         try {
  333.             if(resource!=null){
  334.                
  335.                 if(resource.getResource()!=null){
  336.                     Connection connectionDB = (Connection) resource.getResource();
  337.                     Logger log = OpenSPCoop2Logger.getLoggerOpenSPCoopResources()!=null ? OpenSPCoop2Logger.getLoggerOpenSPCoopResources() : LoggerWrapperFactory.getLogger(DBManager.class);
  338.                     boolean checkAutocommit = (OpenSPCoop2Properties.getInstance()==null) || OpenSPCoop2Properties.getInstance().isJdbcCloseConnectionCheckAutocommit();
  339.                     boolean checkIsClosed = (OpenSPCoop2Properties.getInstance()==null) || OpenSPCoop2Properties.getInstance().isJdbcCloseConnectionCheckIsClosed();
  340.                     JDBCUtilities.closeConnection(log, connectionDB, checkAutocommit, checkIsClosed);
  341.                 }
  342.                 if(DBManager.risorseInGestione.containsKey(resource.getId()))
  343.                     DBManager.risorseInGestione.remove(resource.getId());
  344.                
  345.                 /**System.out.println("### releaseResource ["+this.dataSourceJndiName+"] modulo:"+modulo+" idTransazione:"+resource.getId()+" OK");
  346.                 if(this.dataSource instanceof SharedPoolDataSource)
  347.                     System.out.println("CLOSE IDLE["+((SharedPoolDataSource)this.dataSource).getNumIdle()+"] ACTIVE["+((SharedPoolDataSource)this.dataSource).getNumActive()+"]");*/
  348.                                    
  349.             }

  350.         }
  351.         catch(SQLException e) {
  352.             if(logError) {
  353.                 this.msgDiag.aggiornaFiltri();
  354.                 this.msgDiag.setDominio(idPDD);
  355.                 this.msgDiag.setFunzione("DBManager."+modulo);
  356.                 this.msgDiag.logFatalError(e, "Rilasciata connessione al datasource");
  357.             }
  358.         }
  359.     }

  360.    
  361.    
  362.     /**
  363.      * Metodo che verica la connessione ad una risorsa.
  364.      * Se la connessione non e' presente, viene lanciata una eccezione che contiene il motivo della mancata connessione
  365.      *
  366.      * @throws DriverException eccezione che contiene il motivo della mancata connessione
  367.      */
  368.     @Override
  369.     public void isAlive() throws CoreException{
  370.         // Verifico la connessione
  371.         Resource resource = null;
  372.         Statement stmtTest = null;
  373.         IDSoggetto idSoggettAlive = new IDSoggetto();
  374.         idSoggettAlive.setCodicePorta(ID_MODULO);
  375.         idSoggettAlive.setTipo(ID_MODULO);
  376.         idSoggettAlive.setNome(ID_MODULO);
  377.         try {
  378.             resource = getResource(idSoggettAlive);
  379.             if(resource == null)
  380.                 throw new CoreException("Resource is null");
  381.             if(resource.getResource() == null)
  382.                 throw new CoreException("Connessione is null");
  383.             Connection con = (Connection) resource.getResource();
  384.             // test:
  385.             stmtTest = con.createStatement();
  386.             stmtTest.execute("SELECT * from "+CostantiDB.DB_INFO);
  387.                    
  388.         } catch (Exception e) {
  389.             throw new CoreException("Connessione al database GovWay non disponibile: "+e.getMessage(),e);

  390.         }finally{
  391.             try{
  392.                 if(stmtTest!=null)
  393.                     stmtTest.close();
  394.             }catch(Exception e){
  395.                 // close
  396.             }
  397.             try{
  398.                 this.releaseResource(idSoggettAlive, "CheckIsAlive", resource);
  399.             }catch(Exception e){
  400.                 // close
  401.             }
  402.         }
  403.     }
  404.     private Resource getResource(IDSoggetto idSoggettAlive) throws CoreException {
  405.         try{
  406.             return this.getResource(idSoggettAlive, "CheckIsAlive", null,
  407.                     false); // verra' loggato nel servizio di check, altrimenti ad ogni test viene registrato l'errore
  408.         }catch(Exception e){
  409.             throw new CoreException(e.getMessage(),e);
  410.         }
  411.     }


  412.     public DataSource getDataSource() {
  413.         return this.dataSource;
  414.     }
  415. }