001: /*
002: * Created on 03/01/2004
003: *
004: * Swing Components - visit http://sf.net/projects/gfd
005: *
006: * Copyright (C) 2004 Igor Regis da Silva Simões
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: *
022: */
023:
024: package br.com.gfpshare.db;
025:
026: import java.sql.Connection;
027: import java.sql.DriverManager;
028: import java.sql.SQLException;
029: import java.util.Vector;
030:
031: /**
032: *
033: * Classe que gerencia conexões com um banco de dados, mantendo um pool de conexões.
034: *
035: * @created 03 de Janeiro de 2004
036: * @author Igor Regis da Silva Simões
037: * @see Connection
038: *
039: **/
040: public class ConnectionPool implements Runnable {
041:
042: private String driver, url, username, password;
043: private int maxConnections;
044: private boolean waitIfBusy, autoCommit;
045: private Vector<Connection> availableConnections, busyConnections;
046: private boolean connectionPending = false;
047:
048: /** Cria uma nova instância de ConnectionPool
049: *
050: * @param autoCommit Indica se as conexões devem ou não estar em modo xde autocommit
051: * @param driver Driver a ser usado para estabelecer a conexão com o banco de dados.
052: * @param url Endereço do banco de dados.
053: * @param username Nome do usuário a ser usado no banco de dados.
054: * @param password Senha a ser usada para se conectar ao banco de dados.
055: * @param initialConnections Número inicial de conexões.
056: * @param maxConnections Número máximo de conexões com o banco de dados.
057: * @param waitIfBusy Valor booleano que indica se o usário irá esperar uma conexão ser liberada para seu uso,
058: * em caso de todas as disponíveis estarem sendo usadas, ou se receberá uma mensagem de erro.
059: * @throws SQLException Pode lançar exceção caso haja problemas para a criação do Pool
060: */
061: public ConnectionPool(String driver, String url, String username,
062: String password, int initialConnections,
063: int maxConnections, boolean waitIfBusy, boolean autoCommit)
064: throws SQLException {
065:
066: this .driver = driver;
067: this .url = url;
068: this .username = username;
069: this .password = password;
070: this .maxConnections = maxConnections;
071: this .waitIfBusy = waitIfBusy;
072: this .autoCommit = autoCommit;
073:
074: if (initialConnections > maxConnections)
075: initialConnections = maxConnections;
076:
077: availableConnections = new Vector<Connection>(
078: initialConnections);
079: busyConnections = new Vector<Connection>();
080:
081: for (int i = 0; i < initialConnections; i++) {
082: availableConnections.addElement(makeNewConnection());
083: }
084: }
085:
086: /** Retorna uma referência para uma das conexões disponíveis no pool.
087: *
088: * @return Referência para uma das conexões disponíveis no pool.
089: * @throws SQLException Lança exceção quando não houverem conexões disponívies e o pool
090: * não estiver configurado para esperar pela liberação d outra conexão
091: */
092: public synchronized Connection getConnection() throws SQLException {
093: busyConnections.trimToSize();
094: System.out.println(busyConnections.size());
095:
096: if (!availableConnections.isEmpty()) {
097:
098: Connection existingConnection = availableConnections
099: .firstElement();
100: int lastIndex = availableConnections.size() - 1;
101: availableConnections.removeElementAt(lastIndex);
102:
103: // Se a conexão retornada está fechada, então notifique a todos que uma conexão estará
104: // disponível e tente novamente pegar uma conexão na lista de conexões estabelecidas.
105: // Caso contrário será retornada a conexão selecionada par aser utilizada.
106:
107: if (existingConnection.isClosed()) {
108: notifyAll(); // Libera todas as threads que estiverem esperando
109: return (getConnection());
110: } else {
111: busyConnections.addElement(existingConnection);
112:
113: return (existingConnection);
114: }
115: } else {
116:
117: // Três casos possíveis:
118: // 1) Não foi ultrapassado o limite máximo de conexões e se não existe nenhuma conexão
119: // sendo estabelecida no memento. Então uma conexão será estabelecida em segundo plano.
120: // 2) Se foi ultrapassado o limite do conexões e o flag waitIfBusy está como false então
121: // lance uma SQLException.
122: // 3) Foi ultrapassado o limite máximo de conexõesand e o flag waitIfBusy está como true;
123: // então espere a liberação de uma nova conexão como no passo 2.
124:
125: if ((totalConnections() < maxConnections)
126: && !connectionPending) { // 1)
127: makeBackgroundConnection();
128:
129: } else if (!waitIfBusy) { // 2)
130: throw new SQLException("Limite de conexões alcançado!");
131: }
132:
133: try { // 3)
134: wait();
135: } catch (InterruptedException ie) {
136: //Não fazemos nada
137: }
138:
139: return (getConnection()); // alguém liberou uma conexão, então tente denovo...
140: }
141: }
142:
143: /**
144: * Cria uma conexão em segundo plano, desta forma a thread pode acessar a conexão que for liberada
145: * antes, seja a que esta sendo estabelecida ou uma outra que for liberado por outro usuário.
146: * @throws SQLException Em caso de erro ao criar a conexão
147: */
148: private void makeBackgroundConnection() throws SQLException {
149: connectionPending = true;
150: try {
151: Thread connectThread = new Thread(this );
152: connectThread.start();
153: } catch (OutOfMemoryError oome) {
154: System.gc();
155: throw new SQLException(
156: "Não há mais memória disponível para criar mais conexões");
157: }
158: }
159:
160: /**
161: * Cria uma nova conexão em uma thread separada.
162: *
163: **/
164: public void run() {
165: try {
166: Connection connection = makeNewConnection();
167:
168: synchronized (this ) {
169: availableConnections.addElement(connection);
170: connectionPending = false;
171: notifyAll();
172: }
173:
174: } catch (SQLException sqle) {
175: //TODO Tratamento de Exceção
176: }
177: }
178:
179: /**
180: * Cria de fato uma nova conexão; esta função é chamada pelo construtor desta classe ou pelas suas
181: * threads, para a criação de conexões em segundo plano.
182: * @return Retorna uma conexão com o banco de dados
183: * @throws SQLException em caso de erro ao abrir a conexão
184: **/
185: private Connection makeNewConnection() throws SQLException {
186: try {
187: Class.forName(driver);
188: Connection connection = DriverManager.getConnection(url,
189: username, password);
190: connection.setAutoCommit(autoCommit);
191: return (connection);
192: } catch (ClassNotFoundException cnfe) {
193: throw new SQLException("Can't find class for driver: "
194: + driver);
195: }
196: }
197:
198: /**
199: * Libera uma conexão para ser usada.
200: *
201: * @param connection Conexão a ser liberada.
202: *
203: **/
204: public synchronized void free(Connection connection) {
205: try {
206: connection.commit();
207: } catch (SQLException sqle) {
208: System.out
209: .println("Erro no ConnectionPool.free(Connection)");
210: sqle.printStackTrace();
211: }
212:
213: busyConnections.removeElement(connection);
214: availableConnections.addElement(connection);
215: notifyAll(); //Libera threads que estão esperando por uma conexão livre...
216: }
217:
218: /**
219: * Retornao número total de conexões existentes no pool.
220: *
221: * @return Retorna a soma das conexões livres e das conexões acupadas.
222: *
223: **/
224: public synchronized int totalConnections() {
225: return (availableConnections.size() + busyConnections.size());
226: }
227:
228: /** Fecha todas as conexões existentes no pool.
229: * @throws SQLException Lança uma exceção casso haja falha no fechamento das conexões
230: */
231: public synchronized void closeAllConnections() throws SQLException {
232: closeConnections(availableConnections);
233: availableConnections.removeAllElements();
234: closeConnections(busyConnections);
235: busyConnections.removeAllElements();
236: }
237:
238: /**
239: * Fecha as conexões existentes em um Vector.
240: * @param connections Vector de conexões que serão fechadas.
241: * @throws SQLException em caso de erro ao fechar as conexões
242: **/
243: private void closeConnections(Vector<Connection> connections)
244: throws SQLException {
245: for (Connection connection : connections)
246: if (!connection.isClosed())
247: connection.close();
248: }
249:
250: /**
251: * Retorna uma string com o caminh com o banco de dados, o nome do usuário, o número de conexões livres
252: * o número de conexões ocupadas e o número máximo de conexões.
253: *
254: * @return Retorna uma descrição deste objeto em forma textual.
255: *
256: **/
257: @Override
258: public synchronized String toString() {
259: return ("ConnectionPool(" + url + "," + username + ")"
260: + ", available=" + availableConnections.size()
261: + ", busy=" + busyConnections.size() + ", max=" + maxConnections);
262: }
263: }
|