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