1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.lib.sql;
  58. import java.sql.Connection;
  59. import java.sql.DatabaseMetaData;
  60. import java.sql.DriverManager;
  61. import java.sql.Driver;
  62. import java.sql.SQLException;
  63. import java.util.Enumeration;
  64. import java.util.Properties;
  65. import java.util.Vector;
  66. import java.lang.reflect.Method;
  67. import org.apache.xalan.res.XSLMessages;
  68. import org.apache.xalan.res.XSLTErrorResources;
  69. /**
  70. * For internal connectiones, i.e. Connection information supplies in the
  71. * Stylesheet. The Default Connection Pool will be used.
  72. */
  73. public class DefaultConnectionPool implements ConnectionPool
  74. {
  75. /**
  76. * A placeholder thast will keep the driver loaded
  77. * between calls.
  78. */
  79. private Object m_Driver = null;
  80. /**
  81. */
  82. private static final boolean DEBUG = false;
  83. /**
  84. * The basic information to make a JDBC Connection
  85. */
  86. private String m_driver = new String("");
  87. /**
  88. */
  89. private String m_url = new String("");
  90. /**
  91. * The mimimum size of the connection pool, if the
  92. * number of available connections falls below this
  93. * mark, min connections will be allocated. The Connection
  94. * Pool will always be somewhere between MinSize and MinSize*2
  95. */
  96. private int m_PoolMinSize = 1;
  97. /**
  98. * Always implement the properties mechinism, if the Password
  99. * or Username is set seperatly then we will add them to the
  100. * property manually.
  101. */
  102. private Properties m_ConnectionProtocol = new Properties();
  103. /**
  104. * Storage for the PooledConnections
  105. */
  106. private Vector m_pool = new Vector();
  107. /**
  108. * Are we active ??
  109. */
  110. private boolean m_IsActive = false;
  111. /**
  112. */
  113. public DefaultConnectionPool( ) {}
  114. /**
  115. * Return our current Active state
  116. * @return
  117. */
  118. public boolean isEnabled( )
  119. {
  120. return m_IsActive;
  121. }
  122. /**
  123. * Set the driver call to be used to create connections
  124. * @param d
  125. * @return
  126. */
  127. public void setDriver( String d )
  128. {
  129. m_driver = d;
  130. }
  131. /**
  132. * Set the url used to connect to the database
  133. * @param url
  134. * @return
  135. */
  136. public void setURL( String url )
  137. {
  138. m_url = url;
  139. }
  140. /**
  141. * Go through the connection pool and release any connections
  142. * that are not InUse;
  143. * @return
  144. */
  145. public void freeUnused( )
  146. {
  147. // Iterate over the entire pool closing the
  148. // JDBC Connections.
  149. for ( int x = 0; x < m_pool.size(); x++ )
  150. {
  151. PooledConnection pcon =
  152. (PooledConnection) m_pool.elementAt(x);
  153. // If the PooledConnection is not in use, close it
  154. if ( pcon.inUse() == false )
  155. {
  156. if (DEBUG)
  157. {
  158. System.err.println("Closing JDBC Connection " + x);
  159. }
  160. pcon.close();
  161. }
  162. }
  163. }
  164. /**
  165. * Is our ConnectionPool have any connections that are still in Use ??
  166. * @return
  167. */
  168. public boolean hasActiveConnections( )
  169. {
  170. return (m_pool.size() > 0);
  171. }
  172. /**
  173. * Set the password in the property set.
  174. * @param p
  175. * @return
  176. */
  177. public void setPassword( String p )
  178. {
  179. m_ConnectionProtocol.put("password", p);
  180. }
  181. /**
  182. * Set the user name in the property set
  183. * @param u
  184. * @return
  185. */
  186. public void setUser( String u )
  187. {
  188. m_ConnectionProtocol.put("user", u);
  189. }
  190. /**
  191. * The Protocol string is used to pass in other connection
  192. * properties. A properties file is a general purpose container
  193. *
  194. * @param p
  195. * @return
  196. */
  197. public void setProtocol( Properties p )
  198. {
  199. Enumeration e = p.keys();
  200. while (e.hasMoreElements())
  201. {
  202. String key = (String) e.nextElement();
  203. m_ConnectionProtocol.put(key, p.getProperty(key));
  204. }
  205. }
  206. /**
  207. * Override the current number of connections to keep in the pool. This
  208. * setting will only have effect on a new pool or when a new connection
  209. * is requested and there is less connections that this setting.
  210. * @param n
  211. * @return
  212. */
  213. public void setMinConnections( int n )
  214. {
  215. m_PoolMinSize = n;
  216. }
  217. /**
  218. * Try to aquire a new connection, if it succeeds then return
  219. * true, else return false.
  220. * Note: This method will cause the connection pool to be built.
  221. * @return
  222. */
  223. public boolean testConnection( )
  224. {
  225. try
  226. {
  227. if (DEBUG)
  228. {
  229. System.out.println("Testing Connection");
  230. }
  231. Connection conn = getConnection();
  232. if (DEBUG)
  233. {
  234. DatabaseMetaData dma = conn.getMetaData();
  235. System.out.println("\nConnected to " + dma.getURL());
  236. System.out.println("Driver " + dma.getDriverName());
  237. System.out.println("Version " + dma.getDriverVersion());
  238. System.out.println("");
  239. }
  240. if (conn == null) return false;
  241. releaseConnection(conn);
  242. if (DEBUG)
  243. {
  244. System.out.println("Testing Connection, SUCCESS");
  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. return false;
  256. }
  257. }
  258. // Find an available connection
  259. /**
  260. * @return Connection
  261. * @throws SQLException
  262. * @throws IllegalArgumentException
  263. */
  264. public synchronized Connection getConnection( )throws IllegalArgumentException, SQLException
  265. {
  266. PooledConnection pcon = null;
  267. // We will fill up the pool any time it is less than the
  268. // Minimum. THis could be cause by the enableing and disabling
  269. // or the pool.
  270. //
  271. if ( m_pool.size() < m_PoolMinSize ) { initializePool(); }
  272. // find a connection not in use
  273. for ( int x = 0; x < m_pool.size(); x++ )
  274. {
  275. pcon = (PooledConnection) m_pool.elementAt(x);
  276. // Check to see if the Connection is in use
  277. if ( pcon.inUse() == false )
  278. {
  279. // Mark it as in use
  280. pcon.setInUse(true);
  281. // return the JDBC Connection stored in the
  282. // PooledConnection object
  283. return pcon.getConnection();
  284. }
  285. }
  286. // Could not find a free connection,
  287. // create and add a new one
  288. // Create a new JDBC Connection
  289. Connection con = createConnection();
  290. // Create a new PooledConnection, passing it the JDBC
  291. // Connection
  292. pcon = new PooledConnection(con);
  293. // Mark the connection as in use
  294. pcon.setInUse(true);
  295. // Add the new PooledConnection object to the pool
  296. m_pool.addElement(pcon);
  297. // return the new Connection
  298. return pcon.getConnection();
  299. }
  300. /**
  301. * @param con
  302. * @return
  303. * @throws SQLException
  304. */
  305. public synchronized void releaseConnection( Connection con )throws SQLException
  306. {
  307. // find the PooledConnection Object
  308. for ( int x = 0; x < m_pool.size(); x++ )
  309. {
  310. PooledConnection pcon =
  311. (PooledConnection) m_pool.elementAt(x);
  312. // Check for correct Connection
  313. if ( pcon.getConnection() == con )
  314. {
  315. if (DEBUG)
  316. {
  317. System.out.println("Releasing Connection " + x);
  318. }
  319. if (! isEnabled())
  320. {
  321. con.close();
  322. m_pool.removeElementAt(x);
  323. if (DEBUG)
  324. {
  325. System.out.println("-->Inactive Pool, Closing connection");
  326. }
  327. }
  328. else
  329. {
  330. // Set it's inuse attribute to false, which
  331. // releases it for use
  332. pcon.setInUse(false);
  333. }
  334. break;
  335. }
  336. }
  337. }
  338. /**
  339. * @param con
  340. * @return
  341. * @throws SQLException
  342. */
  343. public synchronized void releaseConnectionOnError( Connection con )throws SQLException
  344. {
  345. // find the PooledConnection Object
  346. for ( int x = 0; x < m_pool.size(); x++ )
  347. {
  348. PooledConnection pcon =
  349. (PooledConnection) m_pool.elementAt(x);
  350. // Check for correct Connection
  351. if ( pcon.getConnection() == con )
  352. {
  353. if (DEBUG)
  354. {
  355. System.out.println("Releasing Connection On Error" + x);
  356. }
  357. con.close();
  358. m_pool.removeElementAt(x);
  359. if (DEBUG)
  360. {
  361. System.out.println("-->Inactive Pool, Closing connection");
  362. }
  363. break;
  364. }
  365. }
  366. }
  367. /**
  368. * @return
  369. * @throws SQLException
  370. */
  371. private Connection createConnection( )throws SQLException
  372. {
  373. Connection con = null;
  374. // Create a Connection
  375. con = DriverManager.getConnection( m_url, m_ConnectionProtocol );
  376. return con;
  377. }
  378. // Initialize the pool
  379. /**
  380. * @return
  381. * @throws IllegalArgumentException
  382. * @throws SQLException
  383. */
  384. public synchronized void initializePool( )throws IllegalArgumentException, SQLException
  385. {
  386. // Check our initial values
  387. if ( m_driver == null )
  388. {
  389. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DRIVER_NAME_SPECIFIED, null));
  390. // "No Driver Name Specified!");
  391. }
  392. if ( m_url == null )
  393. {
  394. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_URL_SPECIFIED, null));
  395. // "No URL Specified!");
  396. }
  397. if ( m_PoolMinSize < 1 )
  398. {
  399. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_POOLSIZE_LESS_THAN_ONE, null));
  400. // "Pool size is less than 1!");
  401. }
  402. // Create the Connections
  403. // Load the Driver class file
  404. try
  405. {
  406. // We need to implement the context classloader
  407. Class cls = null;
  408. try
  409. {
  410. Method m = Thread.class.getMethod("getContextClassLoader", null);
  411. ClassLoader classLoader = (ClassLoader) m.invoke(Thread.currentThread(), null);
  412. cls = classLoader.loadClass(m_driver);
  413. }
  414. catch (Exception e)
  415. {
  416. cls = Class.forName(m_driver);
  417. }
  418. if (cls == null)
  419. cls = Class.forName(m_driver);
  420. // We have also had problems with drivers unloading
  421. // load an instance that will get freed with the class.
  422. m_Driver = cls.newInstance();
  423. }
  424. catch(ClassNotFoundException e)
  425. {
  426. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
  427. // "Invalid Driver Name Specified!");
  428. }
  429. catch(Exception e)
  430. {
  431. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_DRIVER_NAME, null));
  432. }
  433. // IF we are not active, don't actuall build a pool yet
  434. // Just set up the driver and periphal items.
  435. if ( !m_IsActive) return;
  436. // Create Connections based on the size member
  437. do
  438. {
  439. Connection con = createConnection();
  440. if ( con != null )
  441. {
  442. // Create a PooledConnection to encapsulate the
  443. // real JDBC Connection
  444. PooledConnection pcon = new PooledConnection(con);
  445. // Add the Connection the pool.
  446. addConnection(pcon);
  447. if (DEBUG) System.out.println("Adding DB Connection to the Pool");
  448. }
  449. }
  450. while (m_pool.size() < m_PoolMinSize);
  451. }
  452. // Adds the PooledConnection to the pool
  453. /**
  454. * @param value
  455. * @return
  456. */
  457. private void addConnection( PooledConnection value )
  458. {
  459. // Add the PooledConnection Object to the vector
  460. m_pool.addElement(value);
  461. }
  462. /**
  463. * @return
  464. * @throws Throwable
  465. */
  466. protected void finalize( )throws Throwable
  467. {
  468. if (DEBUG)
  469. {
  470. System.out.println("In Default Connection Pool, Finalize");
  471. }
  472. // Iterate over the entire pool closing the
  473. // JDBC Connections.
  474. for ( int x = 0; x < m_pool.size(); x++ )
  475. {
  476. if (DEBUG)
  477. {
  478. System.out.println("Closing JDBC Connection " + x);
  479. }
  480. PooledConnection pcon =
  481. (PooledConnection) m_pool.elementAt(x);
  482. // If the PooledConnection is not in use, close it
  483. if ( pcon.inUse() == false ) { pcon.close(); }
  484. else
  485. {
  486. if (DEBUG)
  487. {
  488. System.out.println("--> Force close");
  489. }
  490. // If it still in use, sleep for 30 seconds and
  491. // force close.
  492. try
  493. {
  494. java.lang.Thread.sleep(30000);
  495. pcon.close();
  496. }
  497. catch (InterruptedException ie)
  498. {
  499. if (DEBUG) System.err.println(ie.getMessage());
  500. }
  501. }
  502. }
  503. if (DEBUG)
  504. {
  505. System.out.println("Exit Default Connection Pool, Finalize");
  506. }
  507. super.finalize();
  508. }
  509. /**
  510. * The Pool can be Enabled and Disabled. Disabling the pool
  511. * closes all the outstanding Unused connections and any new
  512. * connections will be closed upon release.
  513. * @param flag Control the Connection Pool. If it is enabled then Connections will actuall be held
  514. * around. If disabled then all unused connections will be instantly closed and as
  515. * connections are released they are closed and removed from the pool.
  516. * @return
  517. */
  518. public void setPoolEnabled( final boolean flag )
  519. {
  520. }
  521. }