001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: DefaultConnectionPool.java 468638 2006-10-28 06:52:06Z minchau $
020 */
021 package org.apache.xalan.lib.sql;
022
023 import java.sql.Connection;
024 import java.sql.DatabaseMetaData;
025 import java.sql.Driver;
026 import java.sql.DriverManager;
027 import java.sql.SQLException;
028 import java.util.Enumeration;
029 import java.util.Iterator;
030 import java.util.Properties;
031 import java.util.Vector;
032
033 import org.apache.xalan.res.XSLMessages;
034 import org.apache.xalan.res.XSLTErrorResources;
035
036 /**
037 * For internal connectiones, i.e. Connection information supplies in the
038 * Stylesheet. The Default Connection Pool will be used.
039 */
040 public class DefaultConnectionPool implements ConnectionPool
041 {
042 /**
043 * A placeholder thast will keep the driver loaded
044 * between calls.
045 */
046 private Driver m_Driver = null;
047 /**
048 */
049 private static final boolean DEBUG = false;
050
051 /**
052 * The basic information to make a JDBC Connection
053 */
054 private String m_driver = new String("");
055 /**
056 */
057 private String m_url = new String("");
058
059
060 /**
061 * The mimimum size of the connection pool, if the
062 * number of available connections falls below this
063 * mark, min connections will be allocated. The Connection
064 * Pool will always be somewhere between MinSize and MinSize*2
065 */
066 private int m_PoolMinSize = 1;
067
068
069 /**
070 * Always implement the properties mechinism, if the Password
071 * or Username is set seperatly then we will add them to the
072 * property manually.
073 */
074 private Properties m_ConnectionProtocol = new Properties();
075
076 /**
077 * Storage for the PooledConnections
078 */
079 private Vector m_pool = new Vector();
080
081 /**
082 * Are we active ??
083 */
084 private boolean m_IsActive = false;
085
086 /**
087 */
088 public DefaultConnectionPool( ) {}
089
090
091 /**
092 * Return our current Active state
093 *
094 */
095 public boolean isEnabled( )
096 {
097 return m_IsActive;
098 }
099
100 /**
101 * Set the driver call to be used to create connections
102 * @param d
103 *
104 */
105 public void setDriver( String d )
106 {
107 m_driver = d;
108 }
109
110 /**
111 * Set the url used to connect to the database
112 * @param url
113 *
114 */
115 public void setURL( String url )
116 {
117 m_url = url;
118 }
119
120 /**
121 * Go through the connection pool and release any connections
122 * that are not InUse;
123 *
124 */
125 public void freeUnused( )
126 {
127 // Iterate over the entire pool closing the
128 // JDBC Connections.
129 Iterator i = m_pool.iterator();
130 while(i.hasNext())
131 {
132 PooledConnection pcon =
133 (PooledConnection) i.next();
134
135 // If the PooledConnection is not in use, close it
136 if ( pcon.inUse() == false )
137 {
138 if (DEBUG)
139 {
140 System.err.println("Closing JDBC Connection ");
141 }
142
143 pcon.close();
144 i.remove();
145 }
146 }
147
148 }
149
150 /**
151 * Is our ConnectionPool have any connections that are still in Use ??
152 *
153 */
154 public boolean hasActiveConnections( )
155 {
156 return (m_pool.size() > 0);
157 }
158
159
160 /**
161 * Set the password in the property set.
162 * @param p
163 *
164 */
165 public void setPassword( String p )
166 {
167 m_ConnectionProtocol.put("password", p);
168 }
169
170 /**
171 * Set the user name in the property set
172 * @param u
173 *
174 */
175 public void setUser( String u )
176 {
177 m_ConnectionProtocol.put("user", u);
178 }
179
180 /**
181 * The Protocol string is used to pass in other connection
182 * properties. A properties file is a general purpose container
183 *
184 * @param p
185 *
186 */
187 public void setProtocol( Properties p )
188 {
189 Enumeration e = p.keys();
190 while (e.hasMoreElements())
191 {
192 String key = (String) e.nextElement();
193 m_ConnectionProtocol.put(key, p.getProperty(key));
194 }
195 }
196
197
198 /**
199 * Override the current number of connections to keep in the pool. This
200 * setting will only have effect on a new pool or when a new connection
201 * is requested and there is less connections that this setting.
202 * @param n
203 *
204 */
205 public void setMinConnections( int n )
206 {
207 m_PoolMinSize = n;
208 }
209
210 /**
211 * Try to aquire a new connection, if it succeeds then return
212 * true, else return false.
213 * Note: This method will cause the connection pool to be built.
214 *
215 */
216 public boolean testConnection( )
217 {
218 try
219 {
220 if (DEBUG)
221 {
222 System.out.println("Testing Connection");
223 }
224
225 Connection conn = getConnection();
226
227 if (DEBUG)
228 {
229 DatabaseMetaData dma = conn.getMetaData();
230
231 System.out.println("\nConnected to " + dma.getURL());
232 System.out.println("Driver " + dma.getDriverName());
233 System.out.println("Version " + dma.getDriverVersion());
234 System.out.println("");
235 }
236
237 if (conn == null) return false;
238
239 releaseConnection(conn);
240
241 if (DEBUG)
242 {
243 System.out.println("Testing Connection, SUCCESS");
244 }
245
246 return true;
247 }
248 catch(Exception e)
249 {
250 if (DEBUG)
251 {
252 System.out.println("Testing Connection, FAILED");
253 e.printStackTrace();
254 }
255
256 return false;
257 }
258
259 }
260
261
262 // Find an available connection
263 /**
264 * @return Connection
265 * @throws SQLException
266 * @throws IllegalArgumentException
267 */
268 public synchronized Connection getConnection( )throws IllegalArgumentException, SQLException
269 {
270
271 PooledConnection pcon = null;
272
273 // We will fill up the pool any time it is less than the
274 // Minimum. THis could be cause by the enableing and disabling
275 // or the pool.
276 //
277 if ( m_pool.size() < m_PoolMinSize ) { initializePool(); }
278
279 // find a connection not in use
280 for ( int x = 0; x < m_pool.size(); x++ )
281 {
282
283 pcon = (PooledConnection) m_pool.elementAt(x);
284
285 // Check to see if the Connection is in use
286 if ( pcon.inUse() == false )
287 {
288 // Mark it as in use
289 pcon.setInUse(true);
290 // return the JDBC Connection stored in the
291 // PooledConnection object
292 return pcon.getConnection();
293 }
294 }
295
296 // Could not find a free connection,
297 // create and add a new one
298
299 // Create a new JDBC Connection
300 Connection con = createConnection();
301
302 // Create a new PooledConnection, passing it the JDBC
303 // Connection
304 pcon = new PooledConnection(con);
305
306 // Mark the connection as in use
307 pcon.setInUse(true);
308
309 // Add the new PooledConnection object to the pool
310 m_pool.addElement(pcon);
311
312 // return the new Connection
313 return pcon.getConnection();
314 }
315
316 /**
317 * @param con
318 *
319 * @throws SQLException
320 */
321 public synchronized void releaseConnection( Connection con )throws SQLException
322 {
323
324 // find the PooledConnection Object
325 for ( int x = 0; x < m_pool.size(); x++ )
326 {
327
328 PooledConnection pcon =
329 (PooledConnection) m_pool.elementAt(x);
330
331 // Check for correct Connection
332 if ( pcon.getConnection() == con )
333 {
334 if (DEBUG)
335 {
336 System.out.println("Releasing Connection " + x);
337 }
338
339 if (! isEnabled())
340 {
341 con.close();
342 m_pool.removeElementAt(x);
343 if (DEBUG)
344 {
345 System.out.println("-->Inactive Pool, Closing connection");
346 }
347
348 }
349 else
350 {
351 // Set it's inuse attribute to false, which
352 // releases it for use
353 pcon.setInUse(false);
354 }
355
356 break;
357 }
358 }
359 }
360
361
362 /**
363 * @param con
364 *
365 * @throws SQLException
366 */
367 public synchronized void releaseConnectionOnError( Connection con )throws SQLException
368 {
369
370 // find the PooledConnection Object
371 for ( int x = 0; x < m_pool.size(); x++ )
372 {
373
374 PooledConnection pcon =
375 (PooledConnection) m_pool.elementAt(x);
376
377 // Check for correct Connection
378 if ( pcon.getConnection() == con )
379 {
380 if (DEBUG)
381 {
382 System.out.println("Releasing Connection On Error" + x);
383 }
384
385 con.close();
386 m_pool.removeElementAt(x);
387 if (DEBUG)
388 {
389 System.out.println("-->Inactive Pool, Closing connection");
390 }
391 break;
392 }
393 }
394 }
395
396
397 /**
398 *
399 * @throws SQLException
400 */
401 private Connection createConnection( )throws SQLException
402 {
403 Connection con = null;
404
405 // Create a Connection directly from the Driver that was loaded
406 // with the context class loader. This is to support JDK1.4
407 con = m_Driver.connect(m_url, m_ConnectionProtocol );
408
409 return con;
410 }
411
412 // Initialize the pool
413 /**
414 *
415 * @throws IllegalArgumentException
416 * @throws SQLException
417 */
418 public synchronized void initializePool( )throws IllegalArgumentException, SQLException
419 {
420
421 // Check our initial values
422 if ( m_driver == null )
423 {
424 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DRIVER_NAME_SPECIFIED, null));
425 // "No Driver Name Specified!");
426 }
427
428 if ( m_url == null )
429 {
430 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_URL_SPECIFIED, null));
431 // "No URL Specified!");
432 }
433
434 if ( m_PoolMinSize < 1 )
435 {
436 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_POOLSIZE_LESS_THAN_ONE, null));
437 // "Pool size is less than 1!");
438 }
439
440 // Create the Connections
441 // Load the Driver class file
442
443 try
444 {
445 // We have also had problems with drivers unloading
446 // load an instance that will get freed with the class.
447 m_Driver = (Driver) ObjectFactory.newInstance(
448 m_driver, ObjectFactory.findClassLoader(), true);
449
450 // Register the Driver that was loaded with the Context Classloader
451 // but we will ask for connections directly from the Driver
452 // instance
453 DriverManager.registerDriver(m_Driver);
454 }
455 catch(ObjectFactory.ConfigurationError e)
456 {
457 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
458 // "Invalid Driver Name Specified!");
459 }
460 catch(Exception e)
461 {
462 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
463 }
464
465 // IF we are not active, don't actuall build a pool yet
466 // Just set up the driver and periphal items.
467 if ( !m_IsActive) return;
468
469 // Create Connections based on the size member
470 do
471 {
472
473 Connection con = createConnection();
474
475 if ( con != null )
476 {
477
478 // Create a PooledConnection to encapsulate the
479 // real JDBC Connection
480 PooledConnection pcon = new PooledConnection(con);
481
482 // Add the Connection the pool.
483 addConnection(pcon);
484
485 if (DEBUG) System.out.println("Adding DB Connection to the Pool");
486 }
487 }
488 while (m_pool.size() < m_PoolMinSize);
489 }
490
491 // Adds the PooledConnection to the pool
492 /**
493 * @param value
494 *
495 */
496 private void addConnection( PooledConnection value )
497 {
498 // Add the PooledConnection Object to the vector
499 m_pool.addElement(value);
500 }
501
502
503 /**
504 *
505 * @throws Throwable
506 */
507 protected void finalize( )throws Throwable
508 {
509 if (DEBUG)
510 {
511 System.out.println("In Default Connection Pool, Finalize");
512 }
513
514 // Iterate over the entire pool closing the
515 // JDBC Connections.
516 for ( int x = 0; x < m_pool.size(); x++ )
517 {
518
519 if (DEBUG)
520 {
521 System.out.println("Closing JDBC Connection " + x);
522 }
523
524 PooledConnection pcon =
525 (PooledConnection) m_pool.elementAt(x);
526
527 // If the PooledConnection is not in use, close it
528 if ( pcon.inUse() == false ) { pcon.close(); }
529 else
530 {
531 if (DEBUG)
532 {
533 System.out.println("--> Force close");
534 }
535
536 // If it still in use, sleep for 30 seconds and
537 // force close.
538 try
539 {
540 java.lang.Thread.sleep(30000);
541 pcon.close();
542 }
543 catch (InterruptedException ie)
544 {
545 if (DEBUG) System.err.println(ie.getMessage());
546 }
547 }
548 }
549
550 if (DEBUG)
551 {
552 System.out.println("Exit Default Connection Pool, Finalize");
553 }
554
555 super.finalize();
556 }
557
558 /**
559 * The Pool can be Enabled and Disabled. Disabling the pool
560 * closes all the outstanding Unused connections and any new
561 * connections will be closed upon release.
562 *
563 * @param flag Control the Connection Pool.
564 * If it is enabled then Connections will actuall be held
565 * around. If disabled then all unused connections will be instantly
566 * closed and as connections are released they are closed and removed
567 * from the pool.
568 *
569 *
570 */
571 public void setPoolEnabled( boolean flag )
572 {
573 m_IsActive = flag;
574 if ( ! flag )
575 freeUnused();
576 }
577
578 }