1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.dbcp.cpdsadapter;
  17. import java.util.Hashtable;
  18. import java.io.PrintWriter;
  19. import java.io.Serializable;
  20. import java.sql.DriverManager;
  21. import java.sql.SQLException;
  22. import javax.sql.PooledConnection;
  23. import javax.sql.ConnectionPoolDataSource;
  24. import javax.naming.Name;
  25. import javax.naming.Context;
  26. import javax.naming.Referenceable;
  27. import javax.naming.spi.ObjectFactory;
  28. import javax.naming.Reference;
  29. import javax.naming.RefAddr;
  30. import javax.naming.StringRefAddr;
  31. import javax.naming.NamingException;
  32. import org.apache.commons.pool.KeyedObjectPool;
  33. import org.apache.commons.pool.impl.GenericKeyedObjectPool;
  34. /**
  35. * <p>
  36. * An adapter for jdbc drivers that do not include an implementation
  37. * of {@link javax.sql.ConnectionPoolDataSource}, but still include a
  38. * {@link java.sql.DriverManager} implementation.
  39. * <code>ConnectionPoolDataSource</code>s are not used within general
  40. * applications. They are used by <code>DataSource</code> implementations
  41. * that pool <code>Connection</code>s, such as
  42. * {@link org.apache.commons.dbcp.datasources.SharedPoolDataSource}. A J2EE
  43. * container will normally provide some method of initializing the
  44. * <code>ConnectionPoolDataSource</code> whose attributes are presented
  45. * as bean getters/setters and then deploying it via JNDI. It is then
  46. * available as a source of physical connections to the database, when
  47. * the pooling <code>DataSource</code> needs to create a new
  48. * physical connection.
  49. * </p>
  50. *
  51. * <p>
  52. * Although normally used within a JNDI environment, the DriverAdapterCPDS
  53. * can be instantiated and initialized as any bean and then attached
  54. * directly to a pooling <code>DataSource</code>.
  55. * <code>Jdbc2PoolDataSource</code> can use the
  56. * <code>ConnectionPoolDataSource</code> with or without the use of JNDI.
  57. * </p>
  58. *
  59. * <p>
  60. * The DriverAdapterCPDS also provides <code>PreparedStatement</code> pooling
  61. * which is not generally available in jbdc2
  62. * <code>ConnectionPoolDataSource</code> implementation, but is
  63. * addressed within the jdbc3 specification. The <code>PreparedStatement</code>
  64. * pool in DriverAdapterCPDS has been in the dbcp package for some time, but
  65. * it has not undergone extensive testing in the configuration used here.
  66. * It should be considered experimental and can be toggled with the
  67. * poolPreparedStatements attribute.
  68. * </p>
  69. *
  70. * <p>
  71. * The <a href="package-summary.html">package documentation</a> contains an
  72. * example using catalina and JNDI. The <a
  73. * href="../datasources/package-summary.html">datasources package documentation</a>
  74. * shows how to use <code>DriverAdapterCPDS</code> as a source for
  75. * <code>Jdbc2PoolDataSource</code> without the use of JNDI.
  76. * </p>
  77. *
  78. * @author John D. McNally
  79. * @version $Revision: 1.8 $ $Date: 2004/02/28 12:18:17 $
  80. */
  81. public class DriverAdapterCPDS
  82. implements ConnectionPoolDataSource, Referenceable, Serializable,
  83. ObjectFactory {
  84. private static final String GET_CONNECTION_CALLED
  85. = "A PooledConnection was already requested from this source, "
  86. + "further initialization is not allowed.";
  87. /** Description */
  88. private String description;
  89. /** Password */
  90. private String password;
  91. /** Url name */
  92. private String url;
  93. /** User name */
  94. private String user;
  95. /** Driver class name */
  96. private String driver;
  97. /** Login TimeOut in seconds */
  98. private int loginTimeout;
  99. /** Log stream */
  100. private PrintWriter logWriter = null;
  101. // PreparedStatement pool properties
  102. private boolean poolPreparedStatements;
  103. private int maxActive = 10;
  104. private int maxIdle = 10;
  105. private int _timeBetweenEvictionRunsMillis = -1;
  106. private int _numTestsPerEvictionRun = -1;
  107. private int _minEvictableIdleTimeMillis = -1;
  108. private boolean getConnectionCalled = false;
  109. /**
  110. * Default no-arg constructor for Serialization
  111. */
  112. public DriverAdapterCPDS() {
  113. }
  114. /**
  115. * Attempt to establish a database connection using the default
  116. * user and password.
  117. */
  118. public PooledConnection getPooledConnection() throws SQLException {
  119. return getPooledConnection(getUser(), getPassword());
  120. }
  121. /**
  122. * Attempt to establish a database connection.
  123. */
  124. public PooledConnection getPooledConnection(String username,
  125. String password)
  126. throws SQLException {
  127. getConnectionCalled = true;
  128. /*
  129. public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory,
  130. int maxActive, byte whenExhaustedAction, long maxWait,
  131. int maxIdle, boolean testOnBorrow, boolean testOnReturn,
  132. long timeBetweenEvictionRunsMillis,
  133. int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
  134. boolean testWhileIdle) {
  135. */
  136. KeyedObjectPool stmtPool = null;
  137. if (isPoolPreparedStatements()) {
  138. stmtPool = new GenericKeyedObjectPool(null,
  139. getMaxActive(), GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 0,
  140. getMaxIdle(), false, false, getTimeBetweenEvictionRunsMillis(),
  141. getNumTestsPerEvictionRun(),
  142. getMinEvictableIdleTimeMillis(), false);
  143. }
  144. // Workaround for buggy WebLogic 5.1 classloader - ignore the
  145. // exception upon first invocation.
  146. try {
  147. return new PooledConnectionImpl(
  148. DriverManager.getConnection(getUrl(), username, password),
  149. stmtPool );
  150. }
  151. catch (ClassCircularityError e)
  152. {
  153. return new PooledConnectionImpl(
  154. DriverManager.getConnection(getUrl(), username, password),
  155. stmtPool );
  156. }
  157. }
  158. // ----------------------------------------------------------------------
  159. // Referenceable implementation
  160. /**
  161. * <CODE>Referenceable</CODE> implementation.
  162. */
  163. public Reference getReference() throws NamingException {
  164. // this class implements its own factory
  165. String factory = getClass().getName();
  166. Reference ref = new Reference(getClass().getName(), factory, null);
  167. ref.add(new StringRefAddr("description", getDescription()));
  168. ref.add(new StringRefAddr("driver", getDriver()));
  169. ref.add(new StringRefAddr("loginTimeout",
  170. String.valueOf(getLoginTimeout())));
  171. ref.add(new StringRefAddr("password", getPassword()));
  172. ref.add(new StringRefAddr("user", getUser()));
  173. ref.add(new StringRefAddr("url", getUrl()));
  174. ref.add(new StringRefAddr("poolPreparedStatements",
  175. String.valueOf(isPoolPreparedStatements())));
  176. ref.add(new StringRefAddr("maxActive",
  177. String.valueOf(getMaxActive())));
  178. ref.add(new StringRefAddr("maxIdle",
  179. String.valueOf(getMaxIdle())));
  180. ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis",
  181. String.valueOf(getTimeBetweenEvictionRunsMillis())));
  182. ref.add(new StringRefAddr("numTestsPerEvictionRun",
  183. String.valueOf(getNumTestsPerEvictionRun())));
  184. ref.add(new StringRefAddr("minEvictableIdleTimeMillis",
  185. String.valueOf(getMinEvictableIdleTimeMillis())));
  186. return ref;
  187. }
  188. // ----------------------------------------------------------------------
  189. // ObjectFactory implementation
  190. /**
  191. * implements ObjectFactory to create an instance of this class
  192. */
  193. public Object getObjectInstance(Object refObj, Name name,
  194. Context context, Hashtable env)
  195. throws Exception {
  196. // The spec says to return null if we can't create an instance
  197. // of the reference
  198. DriverAdapterCPDS cpds = null;
  199. if (refObj instanceof Reference) {
  200. Reference ref = (Reference)refObj;
  201. if (ref.getClassName().equals(getClass().getName())) {
  202. RefAddr ra = ref.get("description");
  203. if (ra != null && ra.getContent() != null) {
  204. setDescription(ra.getContent().toString());
  205. }
  206. ra = ref.get("driver");
  207. if (ra != null && ra.getContent() != null) {
  208. setDriver(ra.getContent().toString());
  209. }
  210. ra = ref.get("url");
  211. if (ra != null && ra.getContent() != null) {
  212. setUrl(ra.getContent().toString());
  213. }
  214. ra = ref.get("user");
  215. if (ra != null && ra.getContent() != null) {
  216. setUser(ra.getContent().toString());
  217. }
  218. ra = ref.get("password");
  219. if (ra != null && ra.getContent() != null) {
  220. setPassword(ra.getContent().toString());
  221. }
  222. ra = ref.get("poolPreparedStatements");
  223. if (ra != null && ra.getContent() != null) {
  224. setPoolPreparedStatements(
  225. Boolean.getBoolean(ra.getContent().toString()));
  226. }
  227. ra = ref.get("maxActive");
  228. if (ra != null && ra.getContent() != null) {
  229. setMaxActive(Integer.parseInt(ra.getContent().toString()));
  230. }
  231. ra = ref.get("maxIdle");
  232. if (ra != null && ra.getContent() != null) {
  233. setMaxIdle(Integer.parseInt(ra.getContent().toString()));
  234. }
  235. ra = ref.get("timeBetweenEvictionRunsMillis");
  236. if (ra != null && ra.getContent() != null) {
  237. setTimeBetweenEvictionRunsMillis(
  238. Integer.parseInt(ra.getContent().toString()));
  239. }
  240. ra = ref.get("numTestsPerEvictionRun");
  241. if (ra != null && ra.getContent() != null) {
  242. setNumTestsPerEvictionRun(
  243. Integer.parseInt(ra.getContent().toString()));
  244. }
  245. ra = ref.get("minEvictableIdleTimeMillis");
  246. if (ra != null && ra.getContent() != null) {
  247. setMinEvictableIdleTimeMillis(
  248. Integer.parseInt(ra.getContent().toString()));
  249. }
  250. cpds = this;
  251. }
  252. }
  253. return cpds;
  254. }
  255. /**
  256. * Throws an IllegalStateException, if a PooledConnection has already
  257. * been requested.
  258. */
  259. private void assertInitializationAllowed() throws IllegalStateException {
  260. if (getConnectionCalled) {
  261. throw new IllegalStateException(GET_CONNECTION_CALLED);
  262. }
  263. }
  264. // ----------------------------------------------------------------------
  265. // Properties
  266. /**
  267. * Get the value of description. This property is here for use by
  268. * the code which will deploy this datasource. It is not used
  269. * internally.
  270. *
  271. * @return value of description.
  272. */
  273. public String getDescription() {
  274. return description;
  275. }
  276. /**
  277. * Set the value of description. This property is here for use by
  278. * the code which will deploy this datasource. It is not used
  279. * internally.
  280. *
  281. * @param v Value to assign to description.
  282. */
  283. public void setDescription(String v) {
  284. this.description = v;
  285. }
  286. /**
  287. * Get the value of password for the default user.
  288. * @return value of password.
  289. */
  290. public String getPassword() {
  291. return password;
  292. }
  293. /**
  294. * Set the value of password for the default user.
  295. * @param v Value to assign to password.
  296. */
  297. public void setPassword(String v) {
  298. assertInitializationAllowed();
  299. this.password = v;
  300. }
  301. /**
  302. * Get the value of url used to locate the database for this datasource.
  303. * @return value of url.
  304. */
  305. public String getUrl() {
  306. return url;
  307. }
  308. /**
  309. * Set the value of url used to locate the database for this datasource.
  310. * @param v Value to assign to url.
  311. */
  312. public void setUrl(String v) {
  313. assertInitializationAllowed();
  314. this.url = v;
  315. }
  316. /**
  317. * Get the value of default user (login or username).
  318. * @return value of user.
  319. */
  320. public String getUser() {
  321. return user;
  322. }
  323. /**
  324. * Set the value of default user (login or username).
  325. * @param v Value to assign to user.
  326. */
  327. public void setUser(String v) {
  328. assertInitializationAllowed();
  329. this.user = v;
  330. }
  331. /**
  332. * Get the driver classname.
  333. * @return value of driver.
  334. */
  335. public String getDriver() {
  336. return driver;
  337. }
  338. /**
  339. * Set the driver classname. Setting the driver classname cause the
  340. * driver to be registered with the DriverManager.
  341. * @param v Value to assign to driver.
  342. */
  343. public void setDriver(String v) throws ClassNotFoundException {
  344. assertInitializationAllowed();
  345. this.driver = v;
  346. // make sure driver is registered
  347. Class.forName(v);
  348. }
  349. /**
  350. * Gets the maximum time in seconds that this data source can wait
  351. * while attempting to connect to a database. NOT USED.
  352. */
  353. public int getLoginTimeout() {
  354. return loginTimeout;
  355. }
  356. /**
  357. * Get the log writer for this data source. NOT USED.
  358. */
  359. public PrintWriter getLogWriter() {
  360. return logWriter;
  361. }
  362. /**
  363. * Sets the maximum time in seconds that this data source will wait
  364. * while attempting to connect to a database. NOT USED.
  365. */
  366. public void setLoginTimeout(int seconds) {
  367. loginTimeout = seconds;
  368. }
  369. /**
  370. * Set the log writer for this data source. NOT USED.
  371. */
  372. public void setLogWriter(java.io.PrintWriter out) {
  373. logWriter = out;
  374. }
  375. // ------------------------------------------------------------------
  376. // PreparedStatement pool properties
  377. /**
  378. * Flag to toggle the pooling of <code>PreparedStatement</code>s
  379. * @return value of poolPreparedStatements.
  380. */
  381. public boolean isPoolPreparedStatements() {
  382. return poolPreparedStatements;
  383. }
  384. /**
  385. * Flag to toggle the pooling of <code>PreparedStatement</code>s
  386. * @param v true to pool statements.
  387. */
  388. public void setPoolPreparedStatements(boolean v) {
  389. assertInitializationAllowed();
  390. this.poolPreparedStatements = v;
  391. }
  392. /**
  393. * The maximum number of active statements that can be allocated from
  394. * this pool at the same time, or zero for no limit.
  395. */
  396. public int getMaxActive() {
  397. return (this.maxActive);
  398. }
  399. /**
  400. * The maximum number of active statements that can be allocated from
  401. * this pool at the same time, or zero for no limit.
  402. */
  403. public void setMaxActive(int maxActive) {
  404. assertInitializationAllowed();
  405. this.maxActive = maxActive;
  406. }
  407. /**
  408. * The maximum number of statements that can remain idle in the
  409. * pool, without extra ones being released, or zero for no limit.
  410. */
  411. public int getMaxIdle() {
  412. return (this.maxIdle);
  413. }
  414. /**
  415. * The maximum number of statements that can remain idle in the
  416. * pool, without extra ones being released, or zero for no limit.
  417. */
  418. public void setMaxIdle(int maxIdle) {
  419. assertInitializationAllowed();
  420. this.maxIdle = maxIdle;
  421. }
  422. /**
  423. * Returns the number of milliseconds to sleep between runs of the
  424. * idle object evictor thread.
  425. * When non-positive, no idle object evictor thread will be
  426. * run.
  427. *
  428. * *see #setTimeBetweenEvictionRunsMillis
  429. */
  430. public int getTimeBetweenEvictionRunsMillis() {
  431. return _timeBetweenEvictionRunsMillis;
  432. }
  433. /**
  434. * Sets the number of milliseconds to sleep between runs of the
  435. * idle object evictor thread.
  436. * When non-positive, no idle object evictor thread will be
  437. * run.
  438. *
  439. * *see #getTimeBetweenEvictionRunsMillis
  440. */
  441. public void setTimeBetweenEvictionRunsMillis(
  442. int timeBetweenEvictionRunsMillis) {
  443. assertInitializationAllowed();
  444. _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
  445. }
  446. /**
  447. * Returns the number of statements to examine during each run of the
  448. * idle object evictor thread (if any).
  449. *
  450. * *see #setNumTestsPerEvictionRun
  451. * *see #setTimeBetweenEvictionRunsMillis
  452. */
  453. public int getNumTestsPerEvictionRun() {
  454. return _numTestsPerEvictionRun;
  455. }
  456. /**
  457. * Sets the number of statements to examine during each run of the
  458. * idle object evictor thread (if any).
  459. * <p>
  460. * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
  461. * tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
  462. * idle objects will be tested per run.
  463. *
  464. * *see #getNumTestsPerEvictionRun
  465. * *see #setTimeBetweenEvictionRunsMillis
  466. */
  467. public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
  468. assertInitializationAllowed();
  469. _numTestsPerEvictionRun = numTestsPerEvictionRun;
  470. }
  471. /**
  472. * Returns the minimum amount of time a statement may sit idle in the pool
  473. * before it is eligible for eviction by the idle object evictor
  474. * (if any).
  475. *
  476. * *see #setMinEvictableIdleTimeMillis
  477. * *see #setTimeBetweenEvictionRunsMillis
  478. */
  479. public int getMinEvictableIdleTimeMillis() {
  480. return _minEvictableIdleTimeMillis;
  481. }
  482. /**
  483. * Sets the minimum amount of time a statement may sit idle in the pool
  484. * before it is eligable for eviction by the idle object evictor
  485. * (if any).
  486. * When non-positive, no objects will be evicted from the pool
  487. * due to idle time alone.
  488. *
  489. * *see #getMinEvictableIdleTimeMillis
  490. * *see #setTimeBetweenEvictionRunsMillis
  491. */
  492. public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
  493. assertInitializationAllowed();
  494. _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
  495. }
  496. }