|
Segue una descrizione del ciclo di vita del connection pool tramite l'analisi degli attributi, del costruttore, del distruttore e dei metodi fondamentali (fra cui quelli richiamati dal client) della classe principale ConnectionPool, la classe che raccoglie l'insieme delle connessioni JDBC. gli attibuti: //gestore del pool private static ConnectionPool connectionPool = null; //collezione di istanze di ConnetionItem, la classe che conterrà la connessione JDBC aperta private Hashtable pool = new Hashtable(); //gestore delle connessioni private ConnectionManager connectionManager = null; //bean contenente le chiavi acquisite dal file delle proprietà private ConnectionParameters connectionParameters = new ConnectionParameters(); //gestore del logging private Logger logger = null; 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 public static synchronized ConnectionPool getInstance() { //gestione singola istanza della classe con pattern singleton if (connectionPool == null) { connectionPool = new ConnectionPool(); } return connectionPool; } nel costruttore sono inserite le operazioni di inizializzazioni che permettono l'acquisizione dei parametri sopra descritti dal file delle proprietà; per la gestione del framework open source Log4J rimando al seguente link: http://jakarta.apache.org/log4j private ConnectionPool() { super(); try { //configurazione log4j PropertiesFileHandler props = PropertiesFileHandler.getInstance("connectionpool.properties"); setLogger(Logger.getLogger(this.getClass().getName())); PropertyConfigurator.configure(props.readProperty("log4j.file.url")); getLogger().info("creazione istanza ConnectionPool"); getLogger().info("acquisizione parametri dal file di properties"); //acquisizione parametri dal file di properties getConnectionParameters().setDriver(props.readProperty("jdbc.driver")); getConnectionParameters().setUrl(props.readProperty("jdbc.url")); getConnectionParameters().setUser(props.readProperty("jdbc.user")); getConnectionParameters().setPassword(props.readProperty("jdbc.password")); getConnectionParameters().setMinConns(Integer.parseInt(props.readProperty ("pool.minConns"))); getConnectionParameters().setMaxConns(Integer.parseInt(props.readProperty ("pool.maxConns"))); getConnectionParameters().setMaxInUse(Integer.parseInt(props.readProperty ("pool.maxInUse"))); getConnectionParameters().setCleaningInterval(Integer.parseInt(props.readProperty ("manager.cleaningInterval"))); //istanza e lancio del connection manager getLogger().info("creazione e partenza istanza ConnectionManager con intervallo di " + getConnectionParameters().getCleaningInterval() + " secondi" ); connectionManager = new ConnectionManager(getConnectionParameters().getCleaningInterval()); getConnectionManager().setAlive(true); (new Thread(getConnectionManager()).start()); //registrazione driver del dbms comune a tutte le connessioni Class.forName(getConnectionParameters().getDriver()); //creazione pool di connessioni minimo; questo soddisferà le richieste fino al momento in cui //saranno effettuate pool.minConns+1 richieste contemporanee getLogger().info("creazione pool minimo di " + getConnectionParameters().getMinConns() + " connessioni"); for (int i = 0; i < getConnectionParameters().getMinConns(); i++) { makeConnection(); } } catch (Exception e) { getLogger().fatal(e.getMessage()); } } 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 public synchronized Connection getFreeConnection() throws SQLException, MaxNumberConnectionException { //ricerca connessione non in uso for (Enumeration enum = getPool().elements(); enum.hasMoreElements();) { ConnectionItem currItem = (ConnectionItem) enum.nextElement(); if (!currItem.isInUse()) { //aggiornamento pool currItem.setInUse(true); getPool().put(currItem.getConnection().toString(), currItem); getLogger().info("acquisizione Connection con progressivo " + currItem.getCounter()); return currItem.getConnection(); } } //non è stata trovata connessione non in uso, creazione di una nuova getLogger().info("connessione attualmente non disponibile; richiesta creazione istanza Connection"); return makeConnection(); } 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 private Connection makeConnection() throws SQLException, MaxNumberConnectionException { Connection newConnection = null; //controllo su limite max stabilito di connessioni if (getConnectionParameters().getMaxConns() == getPool().size()) { getLogger().error("richiesta connessione eccedente al numero max stabilito del pool"); throw new MaxNumberConnectionException(); } //creazione connessione newConnection DriverManager.getConnection( getConnectionParameters().getUrl(), getConnectionParameters().getUser(), getConnectionParameters().getPassword()); //inserimento nel pool releaseConnection(newConnection); return newConnection; } 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 public synchronized void releaseConnection(Connection connection) { //estrazione dal pool dell'item corrispondente ConnectionItem currItem (ConnectionItem) getPool().get(connection.toString()); if (currItem == null) { //ConnectionItem non trovato se richiamato da makeConnection per inizializzare l'item currItem = new ConnectionItem(); currItem.setConnection(connection); currItem.setCounter(getPool().size() + 1); getLogger().info("creazione e apertura istanza Connection con progressivo:
" + currItem.getCounter()); } //aggiornamento del pool currItem.setInUse(false); getPool().put(connection.toString(), currItem); getLogger().info("rilascio istanza Connection con progressivo " + currItem.getCounter()); } il metodo closeConnection provvede alla chiusura reale della connessione JDBC incapsulata nell'istanza di ConnectionItem fornita in input private void closeConnection(ConnectionItem currItem) throws SQLException { //chiusura e rimozione dal pool della connessione currItem.getConnection().close(); getLogger().info("chiusura e rimozione dal pool della Connection con progressivo " + currItem.getCounter()); getPool().remove(currItem.getConnection().toString()); } 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 protected void finalize() throws Throwable { super.finalize(); getLogger().info("distruzione istanza ConnectionPool"); //stop del connection manager getLogger().info("distruzione istanza ConnectionManager"); getConnectionManager().setAlive(false); //libero il pool closePool(); } 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 void cleanPool() throws SQLException { //ricerca connessioni appese getLogger().info("start pulizia del pool"); for (Enumeration enum = getPool().elements(); enum.hasMoreElements();) { ConnectionItem currItem = (ConnectionItem) enum.nextElement(); if (currItem.isInUse()) { java.util.Date now = new java.util.Date(); long diff = now.getTime() - currItem.getDate().getTime(); if (diff > (getConnectionParameters().getMaxInUse() * 1000)) { //connessione aperta da più tempo rispetto al max stabilito //rilascio forzato della connessione getLogger().error("rilascio forzato della connessione con progressivo " + currItem.getCounter() + " aperta da " + (diff/1000) + " secondi rispetto al max stabilito di " + getConnectionParameters().getMaxInUse() + " secondi"); releaseConnection(currItem.getConnection()); } } } getLogger().info("stop pulizia del pool; connessioni esaminate: " + getPool().size()); }
|