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 }