home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 77 / IOPROG_77.ISO / tips / Java / ConnectionPool / readme.txt
Encoding:
Text File  |  2003-12-19  |  13.6 KB  |  239 lines

  1. FUNZIONALITA' LIBRERIA:
  2. tramite poche istruzioni Φ possibile accedere ad un insieme di connessioni jdbc da poter utilizzare in maniera condivisa; questo perchΦ il pool dichiara, istanzia e gestisce le connessioni per noi, permettendo il riutilizzo delle stesse tramite un booleano indicante lo stato della connessione; al termine dell'elaborazione la connessione non viene chiusa ma semplicemente rilasciata; se alla richiesta di una connessione quelle attualmente in pool sono tutte occupate, e se il limite massimo di connessioni staibilito non Φ stato superato, ne verrα creata automaticamente una nuova.
  3. Complementare a questo, un thread che si occupa di controllare periodicamente il pool per verificare se esiste qualche connessione non rilasciata oltre un tempo massimo stabilito.
  4. I vantaggi principali dell'utilizzo del pool possono racchiudersi quindi in:
  5. -a fronte di una richiesta, evitare di ricreare ogni volta la connessione, prelevandola dal pool e reinserendola al termine dell'utilizzo
  6. -pi∙ thread possono richiedere contemporaneamente una connessione al pool.
  7. A corredo un utile tester per effettuare stress test e benchmark calcolando il tempo medio in secondi per aqcuisire e rilasciare una connessione utilizzando un pool piuttosto che ricreare ogni volta una singola connessione.
  8.  
  9.  
  10.  
  11.  
  12.  
  13. DESCRIZIONE CLASSI CONTENUTE NELL'ARCHIVIO JAVA connectionpool.jar:
  14. ConnectionItem            Bean che mappa la singola connessione.
  15. ConnectionManager        Thread dedicato al controllo periodico del pool delle connessioni alla ricerca di connessioni appese.
  16. ConnectionParameters        Bean che mappa i parametri acquisiti da file properties.
  17. ConnectionPool            Gestore del pool di connessioni.
  18. MaxNumberConnectionException    Eccezione sollevata per la richiesta di connessioni superiori al limite stabilito.
  19. Tester                Utility per effettuare stress test e benchmark fra connessioni in pool e connessioni singole.
  20. PropertiesFileHandler        Wrapper per la lettura di file properties.
  21.  
  22.  
  23.  
  24.  
  25.  
  26. La libreria, per poter funzionare, si basa sulle seguenti coppie chiave=valore inserite nel file connectionpool.properties:
  27. jdbc.driver=<inserire qui il driver da utilizzare per la connessione jdbc>
  28. jdbc.url=<inserire qui la stringa di connessione da utilizzare per la connessione jdbc>
  29. jdbc.user=<inserire qui l'utenza da utilizzare per la connessione jdbc>
  30. jdbc.password=<inserire qui la password da utilizzare per la connessione jdbc>
  31. pool.minConns=<inserire qui il numero di connessioni che verranno create e inserite nel pool all'avvio>
  32. pool.maxConns=<inserire qui il numero massimo di connessioni previste dal pool>
  33. pool.maxInUse=<inserire qui il numero massimo di secondi di utilizzo di una connessione prima di essere forzatamente rilasciata>
  34. manager.cleaningInterval=<inserire qui l'intervallo in secondi per l'esecuzione del controllo di connessioni non rilasciate nel pool>
  35. stress.try.number=<inserire qui il numero di connessioni da richiedere in modalitα pool/single per effettuare lo stress test>
  36. log4j.file.url=<inserire qui la url relativa al file di properties per la gestione del framework Log4J>
  37.  
  38.  
  39.  
  40.  
  41.  
  42. Tramite l'analisi degli attributi, del costuttore, del distruttore e dei metodi fondamentali (fra cui quelli richiamati dal client) della classe principale ConnectionPool, la classe che "avvolge" l'insieme delle connessioni JDBC, verrα illustrato il ciclo di vita del connection pool:
  43.  
  44.  
  45. questi gli attibuti:
  46.     //gestore del pool
  47.     private static ConnectionPool connectionPool = null;    
  48.     //collezione di istanze di ConnetionItem, la classe che conterrα la connessione JDBC aperta
  49.     private Hashtable pool = new Hashtable();            
  50.     //gestore delle connessioni
  51.     private ConnectionManager connectionManager = null;        
  52.     //bean contenente le chiavi acquisite dal file delle proprietα
  53.     private ConnectionParameters connectionParameters = new ConnectionParameters();    
  54.     //gestore del logging
  55.     private Logger logger = null;                
  56.  
  57.  
  58. nel metodo getInstance, applicando il pattern Singleton, a fronte di una richiesta da un client viene effettuato il controllo se Φ giα presente una istanza della classe: in caso negativo ne viene creata una, chiamando quindi il costruttore
  59. public static synchronized ConnectionPool getInstance() {
  60.     //gestione singola istanza della classe con pattern singleton
  61.     if (connectionPool == null) {
  62.         connectionPool = new ConnectionPool();
  63.     }
  64.     return connectionPool;
  65. }
  66.  
  67.  
  68. nel costruttore sono inserite le operazioni di inizializzazioni che permettono l'acquisizione dei parametri sopra descritti dal file delle proprietα;
  69. per la gestione del framework open source Log4J rimando al seguente link: http://jakarta.apache.org/log4j
  70. private ConnectionPool() {
  71.     super();
  72.     try {
  73.     //configurazione log4j
  74.     PropertiesFileHandler props = PropertiesFileHandler.getInstance("connectionpool.properties");        
  75.     setLogger(Logger.getLogger(this.getClass().getName()));
  76.     PropertyConfigurator.configure(props.readProperty("log4j.file.url"));
  77.             
  78.     getLogger().info("creazione istanza ConnectionPool");
  79.     getLogger().info("acquisizione parametri dal file di properties");                
  80.  
  81.          //acquisizione parametri dal file di properties   
  82.         getConnectionParameters().setDriver(props.readProperty("jdbc.driver"));
  83.         getConnectionParameters().setUrl(props.readProperty("jdbc.url"));
  84.         getConnectionParameters().setUser(props.readProperty("jdbc.user"));
  85.         getConnectionParameters().setPassword(props.readProperty("jdbc.password"));
  86.         getConnectionParameters().setMinConns(Integer.parseInt(props.readProperty("pool.minConns")));
  87.         getConnectionParameters().setMaxConns(Integer.parseInt(props.readProperty("pool.maxConns")));
  88.         getConnectionParameters().setMaxInUse(Integer.parseInt(props.readProperty("pool.maxInUse")));
  89.         getConnectionParameters().setCleaningInterval(Integer.parseInt(props.readProperty("manager.cleaningInterval")));                        
  90.  
  91.         //istanza e lancio del connection manager
  92.         getLogger().info("creazione e partenza istanza ConnectionManager con intervallo di " + getConnectionParameters().getCleaningInterval() + " secondi" );        
  93.         connectionManager = new ConnectionManager(getConnectionParameters().getCleaningInterval());
  94.         getConnectionManager().setAlive(true);         
  95.         (new Thread(getConnectionManager()).start());
  96.  
  97.     //registrazione driver del dbms comune a tutte le connessioni
  98.         Class.forName(getConnectionParameters().getDriver());
  99.         
  100.     //creazione pool di connessioni minimo; questo soddisferα le richieste fino al momento in cui saranno effettuate pool.minConns+1 richieste contemporanee
  101.         getLogger().info("creazione pool minimo di " + getConnectionParameters().getMinConns() + " connessioni");                        
  102.         for (int i = 0; i < getConnectionParameters().getMinConns(); i++) {
  103.             makeConnection();
  104.         }
  105.     } catch (Exception e) {
  106.         getLogger().fatal(e.getMessage());
  107.     }
  108. }
  109.  
  110.  
  111. nel metodo getFreeConnection viene effettuata la gestione di richiesta connessione: a fronte di una richiesta viene ciclato il pool alla ricerca di una connessione marcata con l'attributo booleano inUse a falso; se l'esito della ricerca Φ negativo, viene invocato il metodo per la creazione di una nuova connessione da inserire nel pool
  112. public synchronized Connection getFreeConnection() throws SQLException, MaxNumberConnectionException {
  113.     //ricerca connessione non in uso
  114.     for (Enumeration enum = getPool().elements(); enum.hasMoreElements();) {
  115.         ConnectionItem currItem = (ConnectionItem) enum.nextElement();
  116.         if (!currItem.isInUse()) {
  117.             //aggiornamento pool
  118.             currItem.setInUse(true);
  119.             getPool().put(currItem.getConnection().toString(), currItem);
  120.             getLogger().info("acquisizione Connection con progressivo " + currItem.getCounter());
  121.             return currItem.getConnection();
  122.         }
  123.     }
  124.     //non Φ stata trovata connessione non in uso, creazione di una nuova
  125.     getLogger().info("connessione attualmente non disponibile; richiesta creazione istanza Connection");    
  126.     return makeConnection();
  127. }
  128.  
  129.  
  130. il metodo makeConnection provvede alla creazione di una nuova connessione JDBC, incapsulata al solito nella classe ConnectionItem; se il numero delle connessioni presenti supera il valore massimo definito nella chiave pool.maxConns viene sollevata una eccezione di tipo MaxNumberConnectionException
  131. private Connection makeConnection() throws SQLException, MaxNumberConnectionException {
  132.     Connection newConnection = null;
  133.     //controllo su limite max stabilito di connessioni
  134.     if (getConnectionParameters().getMaxConns() == getPool().size()) {
  135.         getLogger().error("richiesta connessione eccedente al numero max stabilito del pool");
  136.         throw new MaxNumberConnectionException();
  137.     }
  138.     //creazione connessione
  139.     newConnection =
  140.         DriverManager.getConnection(
  141.             getConnectionParameters().getUrl(),
  142.             getConnectionParameters().getUser(),
  143.             getConnectionParameters().getPassword());
  144.     //inserimento nel pool
  145.     releaseConnection(newConnection);
  146.     return newConnection;
  147. }
  148.  
  149.  
  150. nel metodo releaseConnection viene effettuata la gestione di rilascio connessione: a fronte di un rilascio la connessione corrispondente non viene distrutta ma semplicemente marcata come non in uso tramite impostazione dell'attributo inUse a falso; in questo modo la stessa potrα essere riutilizzata evitando inutile tempo di elaborazione per ricrearla
  151. public synchronized void releaseConnection(Connection connection) {
  152.     //estrazione dal pool dell'item corrispondente
  153.     ConnectionItem currItem =
  154.         (ConnectionItem) getPool().get(connection.toString());
  155.     if (currItem == null) {
  156.         //ConnectionItem non trovato se richiamato da makeConnection per inizializzare l'item
  157.         currItem = new ConnectionItem();
  158.         currItem.setConnection(connection);
  159.         currItem.setCounter(getPool().size() + 1);
  160.         getLogger().info("creazione e apertura istanza Connection con progressivo: " + currItem.getCounter());
  161.     }
  162.     //aggiornamento del pool    
  163.     currItem.setInUse(false);
  164.     getPool().put(connection.toString(), currItem);
  165.     getLogger().info("rilascio istanza Connection con progressivo " + currItem.getCounter());
  166. }
  167.  
  168.  
  169. il metodo closeConnection provvede alla chiusura reale della connessione JDBC incapsulata nell'istanza di ConnectionItem fornita in input
  170. private void closeConnection(ConnectionItem currItem) throws SQLException {
  171.     //chiusura e rimozione dal pool della connessione
  172.     currItem.getConnection().close();
  173.     getLogger().info("chiusura e rimozione dal pool della Connection con progressivo " + currItem.getCounter());
  174.     getPool().remove(currItem.getConnection().toString());
  175.  
  176. }
  177.  
  178.  
  179. nel distruttore Φ inserita la chiamata al metodo per fermare il thread per il controllo delle connessioni e al metodo per chiudere definitivamente tutte le connessioni presenti correntemente nel pool
  180. protected void finalize() throws Throwable {
  181.     super.finalize();
  182.     getLogger().info("distruzione istanza ConnectionPool");    
  183.  
  184.     //stop del connection manager
  185.     getLogger().info("distruzione istanza ConnectionManager");    
  186.     getConnectionManager().setAlive(false);
  187.  
  188.     //libero il pool
  189.      closePool();
  190.      
  191. }
  192.  
  193.  
  194. il metodo cleanPool viene richiamato dal thread che si occupa di controllare lo stato delle connessioni in base al tempo di utilizzo:  la verifica consiste nel controllare, ogni intervallo di tempo definito dalla chiave manager.cleaningInterval, se esiste qualche connessione non rilasciata oltre al tempo massimo definito dalla chiave pool.maxInUse; in caso affermativo, la connessione viene annullata e marcata come non in uso per poter permettere poi il riutilizzo
  195. void cleanPool() throws SQLException {
  196.     //ricerca connessioni appese
  197.     getLogger().info("start pulizia del pool");
  198.     for (Enumeration enum = getPool().elements(); enum.hasMoreElements();) {
  199.         ConnectionItem currItem = (ConnectionItem) enum.nextElement();
  200.         if (currItem.isInUse()) {
  201.             java.util.Date now = new java.util.Date();
  202.             long diff = now.getTime() - currItem.getDate().getTime();
  203.             if (diff > (getConnectionParameters().getMaxInUse() * 1000)) {
  204.                 //connessione aperta da pi∙ tempo rispetto al max stabilito
  205.                 //rilascio forzato della connessione
  206.                 getLogger().error("rilascio forzato della connessione con progressivo " + currItem.getCounter() + " aperta da " + (diff/1000) + " secondi rispetto al max stabilito di " + getConnectionParameters().getMaxInUse() + " secondi");
  207.                 releaseConnection(currItem.getConnection());
  208.             }
  209.         }
  210.     }
  211.     getLogger().info("stop pulizia del pool; connessioni esaminate: " + getPool().size());    
  212. }
  213.  
  214.  
  215.  
  216.  
  217.  
  218. ESEMPIO DI CONSUMO DELLA LIBRERIA DA PARTE DI UN CLIENT:
  219. //dichiarazione connection pool
  220. it.favaroni.db.connectionpool.ConnectionPool connectionPool = null;
  221. //dichiarazione connessione jdbc
  222. java.sql.Connection connection = null;
  223. try {
  224.     //acquisizione connection pool
  225.         connectionPool = it.favaroni.db.connectionpool.ConnectionPool.getInstance();
  226.     //acquisizione dal pool della prima connessione disponibile        
  227.         connection = connectionPool.getFreeConnection();
  228.         /////////////////////////////////
  229.         //fai qualcosa con la connessione
  230.         /////////////////////////////////
  231. } catch (Exception e) {
  232.         /////////////////////////////////
  233.         //gestisci le eccezioni
  234.     /////////////////////////////////
  235. } finally {
  236.     //rilascio della connessione
  237.     connectionPool.releaseConnection(connection);
  238. }
  239.