1 package atg.metier.dao.jdbc;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import atg.metier.dao.jdbc.exception.ATGDaoBaseIndisponibleException;
6 import atg.service.constante.AtgConstantes;
7 import atg.service.log.AtgLogManager;
8
9 /**
10 * <p>Titre : Pool de connexions aux bases de données en JDBCs</p>
11 * <p>Description : Gestion de pool de connexion</p>
12 * <p>Copyright : FERRARI Olivier</p>
13 * @author BOSC Frédéric repris et modifié par FERRARI Olivier
14 * @version 1.0
15 * Ce logiciel est régi par la licence CeCILL soumise au droit français et
16 * respectant les principes de diffusion des logiciels libres. Vous pouvez
17 * utiliser, modifier et/ou redistribuer ce programme sous les conditions
18 * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
19 * sur le site http://www.cecill.info.
20 *
21 * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
22 * pris connaissance de la licence CeCILL, et que vous en avez accepté les
23 * termes.
24 */
25
26 public class ATGDaoPoolDataSource
27 extends atg.util.service.ATGBasicClass {
28
29 //***************************************************************************
30 // Attributs
31 //***************************************************************************
32
33 /**
34 * Référence du pool par défaut
35 */
36 public static final String CSTE_DEFAULT_REFERENCE = "Reference par defaut";
37
38 /**
39 * Instances uniques (pour une référence donnée) du pool de connections
40 */
41
42 private static java.util.Hashtable instances = new java.util.Hashtable();
43
44 /**
45 * Commande à executer sur la connexion nouvellement créées
46 */
47 private static java.util.Hashtable connexionRequests = new java.util.
48 Hashtable();
49
50 /**
51 * nombre de connections maximum crées sur la base de données
52 */
53 private static final int MAX_CONNECTION = 10;
54
55 /**
56 * nombre de connections automatiquement crées au démarrage du pool
57 */
58 private static final int START_CONNECTION = 1;
59
60 /**
61 * Delai d'attente pour l'obtention d'une connection, exemple 3000 = 3 secondes
62 */
63 private static final int MAX_WAIT = 3000;
64
65 /**
66 * delai au dela duquel une connexion est consideree comme perdue, exemple 60000 = 60 secondes
67 */
68 private static final int MAX_CTRL_TIME = 60000;
69
70 /**
71 * Attribut de la connexion standard
72 */
73 private String DRIVER_NAME = null;
74 private String USER_NAME = null;
75 private String USER_PWD = null;
76 private String DB_NAME = null;
77
78 /**
79 * liste des connections créées mais non utilisées
80 */
81 private java.util.Vector freeConnections = null;
82
83 /**
84 * nombre de connections en cours (demandées mais pas encore libérées)
85 */
86 private int checkedOut = 0;
87
88 /**
89 * driver permettant d'obtenir une connection
90 */
91 protected java.sql.Driver driver = null;
92
93 /**
94 * référence du pool de données
95 */
96 protected String reference = null;
97
98 /**
99 * ensemble des connections creees sur la base de donnees
100 */
101 protected java.util.Hashtable ctrlConnexion = null;
102
103 //***************************************************************************
104 // Constructeurs
105 //***************************************************************************
106
107 /**
108 * Constructeur.
109 * Récupère les informations relatives aux connections.
110 * @param newReference Référence du pool de connection
111 */
112 private ATGDaoPoolDataSource(String newReference) {
113 reference = newReference;
114 ctrlConnexion = new java.util.Hashtable();
115
116 if (newReference.equals(CSTE_DEFAULT_REFERENCE)) {
117 DRIVER_NAME = atg.service.constante.AtgConstantesWF.DB_DRIVER_NAME;
118 USER_NAME = atg.service.constante.AtgConstantesWF.DB_USER_NAME;
119 USER_PWD = atg.service.constante.AtgConstantesWF.DB_PWD;
120 DB_NAME = atg.service.constante.AtgConstantesWF.DB_DB_NAME;
121 }
122 else {
123 DRIVER_NAME = atg.service.constante.AtgConstantesWF.getValue(
124 newReference +
125 "_DB_DRIVER_NAME");
126 USER_NAME = atg.service.constante.AtgConstantesWF.getValue(newReference +
127 "_DB_USER_NAME");
128 USER_PWD = atg.service.constante.AtgConstantesWF.getValue(newReference +
129 "_DB_PWD");
130 DB_NAME = atg.service.constante.AtgConstantesWF.getValue(newReference +
131 "_DB_DB_NAME");
132 }
133 }
134
135 //***************************************************************************
136 // Méthodes statiques
137 //***************************************************************************
138
139 /**
140 * Fixe la commande à executer lors de la création d'une nouvelle connexion.
141 * @param reference Référence du pool de connection
142 * @param connexionRequest Commande sql à executer
143 */
144 static public void setConnexionRequest(String reference,
145 String connexionRequest) {
146 connexionRequests.put(reference, connexionRequest);
147 }
148
149 /**
150 * Fixe la commande à executer lors de la création d'une nouvelle connexion.
151 * La référence de la base correspond à la référence par défaut.
152 * @param connexionRequest Commande sql à executer
153 */
154 static public void setConnexionRequest(String connexionRequest) {
155 connexionRequests.put(CSTE_DEFAULT_REFERENCE, connexionRequest);
156 }
157
158 /**
159 * Renvoie l'instance unique du pool de connections pour la référence donnée.
160 * @param reference Référence du pool de connection
161 * @return StdPoolDataSource Instance d'un pool d'accès aux données
162 */
163 static public ATGDaoPoolDataSource getInstance(String reference) {
164 if ( (instances.get(reference) != null)) {
165
166 return (ATGDaoPoolDataSource) (instances.get(reference));
167 }
168 else {
169
170 ATGDaoPoolDataSource instance = new ATGDaoPoolDataSource(reference);
171
172 instance.initPool();
173
174 instances.put(reference, instance);
175
176 return instance;
177 }
178 }
179
180 /**
181 * Renvoie l'instance unique du pool de connections par défaut.
182 * @return StdPoolDataSource Instance d'un pool d'accès aux données
183 */
184 static public ATGDaoPoolDataSource getInstance() {
185 return getInstance(CSTE_DEFAULT_REFERENCE);
186 }
187
188 //***************************************************************************
189 // Méthodes publiques
190 //***************************************************************************
191
192 /**
193 * Renvoie une connection à la base de donnees.
194 * @return java.sql.Connection Connection à la base de données
195 */
196 public java.sql.Connection getConnection() throws
197 ATGDaoBaseIndisponibleException {
198 try {
199 java.sql.Connection conn = getConnection(MAX_WAIT);
200 if (conn != null)
201 addCtrlConnexion(conn);
202
203 return conn;
204 }
205 catch (ATGDaoBaseIndisponibleException exception) {
206
207 logSevere("Impossible de fournir une connection correcte : " +
208 exception.getCause() + " / " + exception.getMessage());
209 throw new ATGDaoBaseIndisponibleException();
210 }
211 }
212
213 //***************************************************************************
214 // Méthodes protected
215 //***************************************************************************
216
217 /**
218 * Initialisation du pool.
219 * Appelé lors de l'instanciation.
220 */
221 protected void initPool() {
222 freeConnections = new java.util.Vector();
223
224
225
226 for (int i = 0; i < START_CONNECTION; i++) {
227 try {
228 freeConnections.addElement(newConnection());
229 }
230 catch (ATGDaoBaseIndisponibleException exception) {
231 logSevere("Impossible d'initialiser le pool de Connection !" +
232 exception.getCause() + " / " + exception.getMessage());
233 }
234 }
235 }
236
237 /**
238 * Renvoie une connection à la base de données.
239 * Attend au maximum 'timeout' millisecondes l'obtention de la connection.
240 * @param timeout Temps d'attente maximum de la connection
241 * @throws java.sql.SQLException Exception SQL
242 * @return java.sql.Connection Connection à la base de données
243 */
244 protected synchronized java.sql.Connection getConnection(long timeout) throws
245 ATGDaoBaseIndisponibleException {
246 long startTime = System.currentTimeMillis();
247 long remaining = timeout;
248 java.sql.Connection conn = null;
249
250 while ( (conn = getPooledConnection()) == null) {
251
252 try {
253
254
255 logFinest("Attente d'une connection (" + timeout + ").");
256 wait(timeout);
257 }
258 catch (InterruptedException exception) {
259
260 }
261
262
263
264 remaining = timeout - (System.currentTimeMillis() - startTime);
265
266 if (remaining <= 0) {
267
268 throw new ATGDaoBaseIndisponibleException("getConnection() time-out");
269 }
270 }
271
272 if (!isConnectionOk(conn)) {
273
274 return getConnection(remaining);
275 }
276
277
278 checkedOut++;
279
280 return conn;
281 }
282
283 /**
284 * Contrôle si la connection specifiée est valide.
285 * @param conn Connection à tester
286 * @return boolean Connection valide ou non
287 */
288 protected boolean isConnectionOk(java.sql.Connection conn) {
289 java.sql.Statement testStmt = null;
290
291
292 try {
293
294 if (!conn.isClosed()) {
295
296 testStmt = conn.createStatement();
297 testStmt.close();
298 }
299 else
300 return false;
301 }
302 catch (java.sql.SQLException exceptionConnection) {
303 if (testStmt != null) {
304 try {
305 testStmt.close();
306 }
307 catch (java.sql.SQLException exceptionStatement) {
308
309 }
310 }
311
312 return false;
313 }
314
315 return true;
316 }
317
318 /**
319 * Renvoie une connection.
320 * @throws ATGDaoBaseIndisponibleException
321 * @return java.sql.Connection Connection à la base de données
322 */
323 protected java.sql.Connection getPooledConnection() throws
324 ATGDaoBaseIndisponibleException {
325 java.sql.Connection conn = null;
326
327
328 logFinest("Une connection est demandee au pool.");
329
330
331 if (freeConnections.size() > 0) {
332
333 conn = (java.sql.Connection) freeConnections.firstElement();
334 freeConnections.removeElementAt(0);
335 }
336 else {
337
338
339 if (checkedOut == MAX_CONNECTION) {
340 logWarning(
341 "Attention, le nombre maximum de connexions creees sur la base est atteint : " +
342 MAX_CONNECTION);
343 ckeckCtrlConnexion();
344 }
345
346
347
348
349
350
351 if (checkedOut < MAX_CONNECTION)
352 conn = newConnection();
353 }
354
355 return conn;
356 }
357
358 /**
359 * Création d'une nouvelle connection.
360 * @throws ATGDaoBaseIndisponibleException
361 * @return java.sql.Connection Nouvelle connection à la base de données
362 */
363 protected java.sql.Connection newConnection() throws
364 ATGDaoBaseIndisponibleException {
365
366 if (driver == null) {
367 try {
368 Class clazz = Class.forName(DRIVER_NAME);
369 logFinest("Classe du driver trouvee : " + clazz.toString());
370 driver = (java.sql.Driver) clazz.newInstance();
371 }
372 catch (Throwable exception) {
373 logSevere("Impossible de creer l'instance : " + DRIVER_NAME + " " +
374 exception.getMessage());
375 throw new ATGDaoBaseIndisponibleException(exception.getMessage());
376 }
377 }
378
379
380 java.util.Properties props = new java.util.Properties();
381 if (USER_NAME != null)
382 props.put("user", USER_NAME);
383 if (USER_PWD != null)
384 props.put("password", USER_PWD);
385
386
387 Connection conn = null;
388 try {
389 conn = driver.connect(DB_NAME, props);
390 }
391 catch (SQLException ex) {
392 ex.printStackTrace();
393 logSevere("Connection JDBC impossible à créer par le pool " +
394 DRIVER_NAME + " " + USER_NAME + " " + USER_PWD + " " + DB_NAME);
395 throw new ATGDaoBaseIndisponibleException("Impossible de créer une connexion par pool");
396 }
397
398
399 logFinest("Une nouvelle connection JDBC est creee par le pool " +
400 DRIVER_NAME + " " + USER_NAME + " " + USER_PWD + " " + DB_NAME);
401
402 if (conn == null)
403 logSevere("Impossible de creer une connection : " + DRIVER_NAME + " " +
404 USER_NAME + " " + USER_PWD + " " + DB_NAME);
405 else {
406
407
408 try {
409 if (connexionRequests.get(reference) != null) {
410 java.sql.PreparedStatement statement = conn.prepareStatement( (String) (
411 connexionRequests.get(reference)));
412 statement.executeUpdate();
413 logFinest(
414 "La commande d'alteration de la connexion a ete realisee : " +
415 (String) (connexionRequests.get(reference)) + ".");
416 }
417 else
418 logFinest(
419 "Il n'existe pas de commande d'alteration sur la connexion.");
420 }
421 catch (Exception exception) {
422 logSevere(
423 "Impossible d'effectuer la requete d'alteration de la connexion ! " +
424 exception.getMessage());
425 }
426 }
427
428 return conn;
429 }
430
431 /**
432 * Libére la connection spécifiée.
433 * @param conn Connection à libérer
434 */
435 public synchronized void release(java.sql.Connection conn) {
436
437 logFinest("Une connection JDBC est liberee : " +
438 (freeConnections.size() + 1) + " connections sont libres.");
439
440
441 freeConnections.addElement(conn);
442 checkedOut--;
443
444
445
446 notifyAll();
447 }
448
449 /**
450 * Retourne le nombre de bases de données.
451 * @return int Nombre de bases de données
452 */
453 public static int getNumberOfDataBase() {
454 if (instances == null)
455 return 0;
456 else
457 return instances.size();
458 }
459
460 /**
461 * Ajoute une connexion au mécanisme de contrôle.
462 * @param conn Connexion à ajouter
463 */
464 public void addCtrlConnexion(java.sql.Connection conn) {
465 ctrlConnexion.put(conn, new Long(System.currentTimeMillis()));
466 }
467
468 /**
469 * Vérifie si toutes les connexions ont bien été rendues au pool.
470 */
471 public void ckeckCtrlConnexion() {
472
473 java.util.Enumeration conns = ctrlConnexion.keys();
474
475
476 do {
477 Object conn = conns.nextElement();
478 long delta = System.currentTimeMillis() -
479 ( (Long) (ctrlConnexion.get(conn))).longValue();
480 if ( (!freeConnections.contains(conn)) && (delta > MAX_CTRL_TIME)) {
481
482
483 ctrlConnexion.remove(conn);
484 logSevere("La connexion " + conn +
485 " est consideree comme perdue : elle n'a pas ete rendue au pool.");
486
487 checkedOut--;
488
489
490 try {
491 ( (java.sql.Connection) conn).close();
492 }
493 catch (java.sql.SQLException sqlException) {
494
495 }
496 }
497 }
498 while (conns.hasMoreElements());
499 }
500
501 /**
502 * Retourne le temps maximum d'attente d'une connexion par défaut.
503 * @return int Temps maximum d'attente d'une connexion par défaut.
504 */
505 public static int getMaxWait() {
506 return MAX_WAIT;
507 }
508
509 /**
510 * Retourne le nombre maximun de connexions créées par le pool.
511 * @return int Nombre maximun de connexions créées par le pool.
512 */
513 public static int getMaxConnection() {
514 return MAX_CONNECTION;
515 }
516
517 /**
518 * Retourne le nombre de connexions que créait le pool au démarrage.
519 * @return int Nombre de connexions que créait le pool au démarrage.
520 */
521 public static int getStartConnection() {
522 return START_CONNECTION;
523 }
524
525 protected static java.util.logging.Logger logger_ = null;
526 /**
527 * Ecriture des logs
528 */
529
530 protected java.util.logging.Logger getLogger() {
531 if (logger_ == null)
532 logger_ = AtgLogManager.getLog(AtgConstantes.
533 ATG_LOG_CATEGORY_METIER_DAO_JDBC);
534
535 return logger_;
536 }
537
538 }