1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.util.pool.service;
35
36 import fr.paris.lutece.portal.service.util.AppException;
37
38 import org.apache.commons.collections.CollectionUtils;
39 import org.apache.logging.log4j.Logger;
40
41 import java.io.PrintWriter;
42
43 import java.sql.Connection;
44 import java.sql.DriverManager;
45 import java.sql.SQLException;
46 import java.sql.Statement;
47
48 import java.util.ArrayList;
49 import java.util.List;
50
51 import javax.sql.DataSource;
52
53
54
55
56
57
58
59
60 public class ConnectionPool implements DataSource
61 {
62 private static final String DEFAULT_CHECK_VALID_CONNECTION_SQL = "SELECT 1";
63 private String _strUrl;
64 private String _strUser;
65 private String _strPassword;
66 private int _nMaxConns;
67 private int _nTimeOut;
68 private Logger _logger;
69 private int _nCheckedOut;
70 private List<Connection> _freeConnections = new ArrayList<>( );
71 private String _strCheckValidConnectionSql;
72 private PrintWriter _logWriter;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 public ConnectionPool( String strName, String strUrl, String strUser, String strPassword, int nMaxConns, int nInitConns, int nTimeOut, Logger logger,
97 String strCheckValidConnectionSql )
98 {
99 _strUrl = strUrl;
100 _strUser = strUser;
101 _strPassword = strPassword;
102 _nMaxConns = nMaxConns;
103 _nTimeOut = ( nTimeOut > 0 ) ? nTimeOut : 5;
104 _logger = logger;
105 initPool( nInitConns );
106 _logger.info( "New pool created : {}", strName );
107
108 _strCheckValidConnectionSql = ( ( strCheckValidConnectionSql != null ) && !strCheckValidConnectionSql.equals( "" ) ) ? strCheckValidConnectionSql
109 : DEFAULT_CHECK_VALID_CONNECTION_SQL;
110
111 String lf = System.getProperty( "line.separator" );
112 _logger.debug( "{} url={}{} user= {}{} initconns= {}{} maxConns={}{} logintimeout={}", lf, strUrl, lf, _strUser, lf,
113 nInitConns, lf, _nMaxConns, lf, _nTimeOut );
114 _logger.debug( ( ) -> getStats( ) );
115 }
116
117
118
119
120
121
122
123 private void initPool( int initConns )
124 {
125 for ( int i = 0; i < initConns; i++ )
126 {
127 try
128 {
129 Connection pc = newConnection( );
130 _freeConnections.add( pc );
131 }
132 catch( SQLException e )
133 {
134 throw new AppException( "SQL Error executing command : " + e.toString( ), e );
135 }
136 }
137 }
138
139
140
141
142
143
144
145
146 @Override
147 public Connection getConnection( ) throws SQLException
148 {
149 _logger.debug( "Request for connection received" );
150
151 try
152 {
153 return getConnection( _nTimeOut * 1000L );
154 }
155 catch( SQLException e )
156 {
157 _logger.error( "Exception getting connection", e );
158 throw e;
159 }
160 }
161
162
163
164
165
166
167
168
169
170
171 private synchronized Connection getConnection( long timeout ) throws SQLException
172 {
173
174
175
176 long startTime = System.currentTimeMillis( );
177 long remaining = timeout;
178 Connection conn = null;
179
180 while ( ( conn = getPooledConnection( ) ) == null )
181 {
182 try
183 {
184 _logger.debug( "Waiting for connection. Timeout= {}", remaining );
185
186 wait( remaining );
187 }
188 catch( InterruptedException e )
189 {
190 _logger.debug( "A connection has been released by another thread.", e );
191 }
192
193 remaining = timeout - ( System.currentTimeMillis( ) - startTime );
194
195 if ( remaining <= 0 )
196 {
197
198 _logger.debug( "Time-out while waiting for connection" );
199 throw new SQLException( "getConnection() timed-out" );
200 }
201 }
202
203
204 if ( !isConnectionOK( conn ) )
205 {
206
207 _logger.error( "Removed selected bad connection from pool" );
208
209 return getConnection( remaining );
210 }
211
212 _nCheckedOut++;
213 _logger.debug( "Delivered connection from pool" );
214 _logger.debug( getStats( ) );
215
216 return conn;
217 }
218
219
220
221
222
223
224
225
226 private boolean isConnectionOK( Connection conn )
227 {
228 Statement testStmt = null;
229
230 try
231 {
232 if ( !conn.isClosed( ) )
233 {
234
235 testStmt = conn.createStatement( );
236 testStmt.executeQuery( _strCheckValidConnectionSql );
237 testStmt.close( );
238 }
239 else
240 {
241 return false;
242 }
243 }
244 catch( SQLException e )
245 {
246 if ( testStmt != null )
247 {
248 try
249 {
250 testStmt.close( );
251 }
252 catch( SQLException se )
253 {
254 throw new AppException( "ConnectionService : SQL Error executing command : " + se.toString( ) );
255 }
256 }
257
258 _logger.error( "Pooled Connection was not okay", e );
259
260 return false;
261 }
262
263 return true;
264 }
265
266
267
268
269
270
271
272
273 private Connection getPooledConnection( ) throws SQLException
274 {
275 Connection conn = null;
276
277 if ( CollectionUtils.isNotEmpty( _freeConnections ) )
278 {
279
280
281 conn = _freeConnections.get( 0 );
282 _freeConnections.remove( 0 );
283 }
284 else
285 if ( ( _nMaxConns == 0 ) || ( _nCheckedOut < _nMaxConns ) )
286 {
287 conn = newConnection( );
288 }
289
290 return conn;
291 }
292
293
294
295
296
297
298
299
300
301 private Connection newConnection( ) throws SQLException
302 {
303 Connection conn;
304
305 if ( _strUser == null )
306 {
307 conn = DriverManager.getConnection( _strUrl );
308 }
309 else
310 {
311 conn = DriverManager.getConnection( _strUrl, _strUser, _strPassword );
312 }
313
314
315 conn = LuteceConnectionFactory.newInstance( this, conn );
316 _logger.info( "New connection created. Connections count is : " + ( getConnectionCount( ) + 1 ) );
317 return conn;
318 }
319
320
321
322
323
324
325
326 public synchronized void freeConnection( Connection conn )
327 {
328
329 _freeConnections.add( conn );
330 _nCheckedOut--;
331 notifyAll( );
332 _logger.debug( "Returned connection to pool" );
333 _logger.debug( ( ) -> getStats( ) );
334 }
335
336
337
338
339 public synchronized void release( )
340 {
341 for ( Connection connection : _freeConnections )
342 {
343 try
344 {
345 if ( connection instanceof LuteceConnection )
346 {
347 ( (LuteceConnection) connection ).closeConnection( );
348 }
349 else
350 {
351 connection.close( );
352 }
353
354 _logger.debug( "Closed connection" );
355 }
356 catch( SQLException e )
357 {
358 _logger.error( "Couldn't close connection", e );
359 }
360 }
361
362 _freeConnections.clear( );
363 }
364
365
366
367
368
369
370 private String getStats( )
371 {
372 return "Total connections: " + getConnectionCount( ) + " Available: " + getFreeConnectionCount( ) + " Checked-out: " + getBusyConnectionCount( );
373 }
374
375
376
377
378
379
380 public int getConnectionCount( )
381 {
382 return getFreeConnectionCount( ) + getBusyConnectionCount( );
383 }
384
385
386
387
388
389
390 public int getFreeConnectionCount( )
391 {
392 return _freeConnections.size( );
393 }
394
395
396
397
398
399
400 public int getBusyConnectionCount( )
401 {
402 return _nCheckedOut;
403 }
404
405
406
407
408
409
410 public int getMaxConnectionCount( )
411 {
412 return _nMaxConns;
413 }
414
415
416
417
418
419
420
421
422
423
424
425
426 @Override
427 public Connection getConnection( String username, String password ) throws SQLException
428 {
429 return getConnection( );
430 }
431
432
433
434
435
436
437
438
439 @Override
440 public PrintWriter getLogWriter( ) throws SQLException
441 {
442 _logger.debug( "ConnectionPool : DataSource getLogWriter called" );
443
444 return _logWriter;
445 }
446
447
448
449
450
451
452
453
454
455 @Override
456 public void setLogWriter( PrintWriter out ) throws SQLException
457 {
458 _logger.debug( "ConnectionPool : DataSource setLogWriter called" );
459 _logWriter = out;
460 }
461
462
463
464
465
466
467
468
469
470 @Override
471 public void setLoginTimeout( int seconds ) throws SQLException
472 {
473
474 }
475
476
477
478
479
480
481
482
483 @Override
484 public int getLoginTimeout( ) throws SQLException
485 {
486 return _nTimeOut;
487 }
488
489
490
491
492
493
494
495
496
497
498
499
500 @Override
501 public <T> T unwrap( Class<T> iface ) throws SQLException
502 {
503 return null;
504 }
505
506
507
508
509
510
511
512
513
514
515 @Override
516 public boolean isWrapperFor( Class<?> iface ) throws SQLException
517 {
518 return false;
519 }
520
521
522
523
524
525
526 public java.util.logging.Logger getParentLogger( )
527 {
528 return java.util.logging.Logger.getLogger( java.util.logging.Logger.GLOBAL_LOGGER_NAME );
529 }
530 }