001: /*
002: * XAPool: Open Source XA JDBC Pool
003: * Copyright (C) 2003 Objectweb.org
004: * Initial Developer: Lutris Technologies Inc.
005: * Contact: xapool-public@lists.debian-sf.objectweb.org
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020: * USA
021: */
022: package org.enhydra.jdbc.pool;
023:
024: import java.io.PrintWriter;
025: import java.sql.Connection;
026: import java.sql.SQLException;
027: import java.sql.Statement;
028: import java.util.Hashtable;
029:
030: import javax.naming.Context;
031: import javax.naming.InitialContext;
032: import javax.naming.Name;
033: import javax.naming.NamingException;
034: import javax.naming.Reference;
035: import javax.naming.StringRefAddr;
036: import javax.sql.ConnectionEvent;
037: import javax.sql.ConnectionEventListener;
038: import javax.sql.ConnectionPoolDataSource;
039: import javax.sql.DataSource;
040: import javax.sql.PooledConnection;
041: import org.enhydra.jdbc.core.CoreDataSource;
042: import org.enhydra.jdbc.core.JdbcThreadFactory;
043: import org.enhydra.jdbc.util.Logger;
044: import org.apache.commons.logging.Log;
045: import org.apache.commons.logging.LogFactory;
046:
047: /**
048: * StandardPoolDataSource class allows to make some operations on
049: * PooledConnection. It implements PoolHelper for the 3 methods :<p>
050: * create : create a PooledConnection<p>
051: * create(user,password) : create a PooledConnection with an other user/password<p>
052: * testThisObject : check if the object is still valid<p>
053: * checkThisObject : check if the object is closed<p>
054: * expire : kill the object<p>
055: */
056: public class StandardPoolDataSource extends CoreDataSource implements
057: DataSource, PoolHelper, ConnectionEventListener {
058:
059: public ConnectionPoolDataSource cpds; // object to build PooledConnection
060: public GenericPool pool; // pool to store StandardDataSource object
061: public String dataSourceName; // jndi name for DataSource Factory
062: public String jdbcTestStmt;
063: // JDBC test statement for checkLevelObject=1 or 2
064: public boolean onOff; // If the pool is started or not
065: public Context ictx; // the initial context
066: public Log glog = LogFactory.getLog("org.enhydra.jdbc.xapool");
067:
068: /**
069: * Constructor
070: */
071: public StandardPoolDataSource() {
072: log = new Logger(glog);
073: super .setLogWriter(log);
074:
075: pool = new GenericPool(this );
076: // create the pool with StandardPoolDataSource object
077: pool.setLogger(log);
078: onOff = false;
079: dataSourceName = null;
080: }
081:
082: /**
083: * Constructor
084: */
085: public StandardPoolDataSource(int initSize) { // with an init Max size
086: log = new Logger(glog);
087: super .setLogWriter(log);
088:
089: pool = new GenericPool(this , initSize);
090: // create the pool with StandardPoolDataSource object
091: pool.setLogger(log);
092: onOff = false;
093: dataSourceName = null;
094: }
095:
096: /**
097: * Constructor
098: */
099: public StandardPoolDataSource(ConnectionPoolDataSource cc) {
100: cpds = cc;
101: log = new Logger(glog);
102:
103: super .setLogWriter(log);
104: pool = new GenericPool(this );
105: // create the pool with StandardPoolDataSource object
106: pool.setLogger(log);
107: try {
108: cpds.setLogWriter(log);
109: } catch (SQLException sqle) {
110:
111: }
112:
113: onOff = false;
114: dataSourceName = null;
115: }
116:
117: /**
118: * Constructor
119: */
120: public StandardPoolDataSource(ConnectionPoolDataSource cc,
121: int initSize) { // with an init Max size
122: cpds = cc;
123: log = new Logger(glog);
124: super .setLogWriter(log);
125:
126: pool = new GenericPool(this , initSize);
127: // create the pool with StandardPoolDataSource object
128: pool.setLogger(log);
129:
130: onOff = false;
131: dataSourceName = null;
132: }
133:
134: /**
135: * Set up the data source name, get the initial context,
136: * and lookup in JNDI to obtain a reference of the DataSourceName
137: * this method must be called before a getConnection (in this case
138: * an exception is returned
139: */
140: public void setDataSourceName(String dataSourceName) {
141: log.debug("StandardPoolDataSource:setDataSourceName");
142: this .dataSourceName = dataSourceName; // set up the data source name
143: /*
144: synchronized(this) {
145: if (onOff) {
146: pool.stop();
147: onOff = false;
148: }
149: }
150: */
151: }
152:
153: public String getDataSourceName() {
154: return dataSourceName; // return the dataSourceName (jndi mechanism)
155: }
156:
157: /**
158: * getConnection allows to get an object from the pool and returns it
159: * to the user. In this case, we return an PooledConnection
160: */
161: public Connection getConnection() throws SQLException {
162: return getConnection(getUser(), getPassword());
163: }
164:
165: /**
166: * getConnection allows to get an object from the pool and returns it
167: * to the user. In this case, we return an PooledConnection
168: */
169: public Connection getConnection(String _user, String _password)
170: throws SQLException {
171: log.debug("StandardPoolDataSource:getConnection");
172: Connection ret = null;
173: PooledConnection con = null;
174:
175: synchronized (this ) {
176: if (!onOff) {
177: log
178: .debug("StandardPoolDataSource:getConnection must configure the pool...");
179: pool.start(); // the pool starts now
180: onOff = true; // and is initialized
181: log
182: .debug("StandardPoolDataSource:getConnection pool config : \n"
183: + pool.toString());
184: }
185: }
186:
187: try {
188: try {
189: log
190: .debug("StandardPoolDataSource:getConnection Try to give a "
191: + "connection (checkOut)");
192: con = (PooledConnection) pool
193: .checkOut(_user, _password);
194: // get a connection from the pool
195: log
196: .debug("StandardPoolDataSource:getConnection checkOut return"
197: + "a new connection");
198: } catch (Exception e) {
199: e.printStackTrace();
200: log
201: .debug("StandardPoolDataSource:getConnection SQLException in StandardPoolDataSource:getConnection"
202: + e);
203: throw new SQLException(
204: "SQLException in StandardPoolDataSource:getConnection no connection available "
205: + e);
206: }
207:
208: ret = con.getConnection();
209: } catch (Exception e) {
210: log.debug("StandardPoolDataSource:getConnection exception"
211: + e);
212: e.printStackTrace();
213: SQLException sqle = new SQLException(
214: "SQLException in StandardPoolDataSource:getConnection exception: "
215: + e);
216: if (e instanceof SQLException)
217: sqle.setNextException((SQLException) e);
218: if (con != null) {
219: pool.checkIn(con);
220: }
221: throw sqle;
222: }
223: log
224: .debug("StandardPoolDataSource:getConnection return a connection");
225: return ret;
226: }
227:
228: /**
229: * connectionErrorOccurred and connectionClosed are methods
230: * from ConnectionEventListener interface
231: *
232: * Invoked when a fatal connection error occurs,
233: * just before an SQLException is thrown to the application
234: */
235: public void connectionErrorOccurred(ConnectionEvent event) {
236: Object obj = event.getSource();
237: PooledConnection pc = (PooledConnection) obj;
238: pool.nextGeneration(pc);
239: pool.removeLockedObject(pc); // remove the object from the locked pool
240: expire(pc); // kill the connection (from super)
241: log
242: .debug("StandardXAPoolDataSource:connectionErrorOccurred remove the object from the pool");
243: }
244:
245: /**
246: * Invoked when the application calls close()
247: * on its representation of the connection
248: */
249: public void connectionClosed(ConnectionEvent event) {
250: log
251: .debug("StandardPoolDataSource:connectionClosed close the connection");
252: Object obj = event.getSource();
253: pool.checkIn(obj);
254: }
255:
256: /**
257: * object specific work to kill the object
258: */
259: public void expire(Object o) {
260: log
261: .debug("StandardPoolDataSource:expire expire a connection, remove from the pool");
262: if (o == null)
263: return;
264: try {
265: PooledConnection pooledCon = (PooledConnection) o;
266: pooledCon.close(); // call close() of PooledConnection
267: pooledCon.removeConnectionEventListener(this );
268: log
269: .debug("StandardPoolDataSource:expire close the connection");
270: } catch (java.sql.SQLException e) {
271: log
272: .error("StandardPoolDataSource:expire Error java.sql.SQLException in StandardPoolDataSource:expire");
273: }
274: }
275:
276: /**
277: * This method tests if a connection is closed or not
278: */
279: public boolean checkThisObject(Object o) {
280:
281: PooledConnection con;
282: Connection ret;
283: log
284: .debug("StandardPoolDataSource:checkThisObject verify the current object");
285: try {
286: con = (PooledConnection) o;
287: ret = con.getConnection(); // get the connection from the pool
288: if (ret.isClosed()) {
289: return false;
290: }
291: try {
292: ret.close();
293: } catch (Exception e) {
294: log
295: .error("StandardPoolDataSource:checkThisObject can't closed the connection: "
296: + e);
297: }
298:
299: return true;
300: } catch (java.sql.SQLException e) {
301: log
302: .error("StandardPoolDataSource:checkThisObject Error java.sql.SQLException in StandardPoolDataSource:checkThisObject");
303: return false;
304: }
305: }
306:
307: /**
308: * This method tests if a connection is valid or not
309: */
310: public boolean testThisObject(Object o) {
311: Connection ret = null;
312: log
313: .debug("StandardPoolDataSource:testThisObject verify the current object");
314: try {
315: PooledConnection con = (PooledConnection) o;
316: ret = con.getConnection();
317: Statement s = ret.createStatement();
318: s.execute(jdbcTestStmt);
319: s.close();
320: try {
321: ret.close();
322: } catch (Exception e) {
323: log
324: .error("StandardPoolDataSource:checkThisObject can't closed the connection: "
325: + e);
326: }
327: return true;
328: } catch (java.sql.SQLException e) {
329: log
330: .error("StandardPoolDataSource:checkThisObject Error java.sql.SQLException in StandardPoolDataSource:testThisObject");
331: return false;
332: }
333: }
334:
335: public GenerationObject create() throws SQLException {
336: return create(getUser(), getPassword());
337: }
338:
339: public GenerationObject create(String _user, String _password)
340: throws SQLException {
341: log
342: .debug("StandardPoolDataSource:create create a connection for the pool");
343: GenerationObject genObject;
344: PooledConnection pooledCon = cpds.getPooledConnection(_user,
345: _password);
346: // get the pooled connection
347:
348: pooledCon.addConnectionEventListener(this );
349: // add it to the event listener
350: log
351: .debug("StandardPoolDataSource:create create a object for the pool");
352: genObject = new GenerationObject(pooledCon, pool
353: .getGeneration(), _user, _password);
354: return genObject; // return a connection
355: }
356:
357: /**
358: * stop method to switch off the pool
359: */
360: public void stopPool() {
361: pool.stop();
362: onOff = false;
363: log.debug("StandardPoolDataSource:stopPool stop now the pool");
364: }
365:
366: public void shutdown(boolean force) {
367: stopPool();
368: }
369:
370: /**
371: * set the logwriter for the current object, the logwriter will be use by
372: * the current object and by the generic pool
373: * @param logWriter a PrintWriter object
374: */
375: public void setLogWriter(PrintWriter logWriter) {
376: pool.setLogger(log);
377: super .setLogger(log);
378: }
379:
380: /**
381: * set the debug flag
382: * @param debug a boolean flag
383: */
384: public void setDebug(boolean debug) {
385: super .setDebug(debug);
386: pool.setDebug(debug);
387: }
388:
389: /**
390: * set the minimum size of the pool
391: * @param minSize minimum size of the pool
392: * @throws Exception
393: */
394: public void setMinSize(int minSize) throws Exception {
395: pool.setMinSize(minSize);
396: }
397:
398: /**
399: * set the maximum size of the pool
400: * @param maxSize maximum size of the pool
401: * @throws Exception
402: */
403: public void setMaxSize(int maxSize) throws Exception {
404: pool.setMaxSize(maxSize);
405: }
406:
407: /**
408: * set the life time of the pooled objects
409: * @param lifeTime life time of the pooled objects (in milliseconds)
410: */
411: public void setLifeTime(long lifeTime) {
412: pool.setLifeTime(lifeTime);
413: }
414:
415: /**
416: * set the sleep time of pooled objects
417: * @param sleepTime sleep time of the pooled objects (in milliseconds)
418: */
419: public void setSleepTime(long sleepTime) {
420: pool.setSleepTime(sleepTime);
421: }
422:
423: /**
424: * set the garbage collection option
425: * @param gc true: the garbage collector will be launched when clean up of the
426: * pool, else false
427: */
428: public void setGC(boolean gc) {
429: pool.setGC(gc);
430: }
431:
432: /**
433: * set the check level of the pooled object before using them
434: * @param checkLevelObject (<br>
435: * 0 = no special checking
436: * 1 = just a check on an object
437: * 2 = test the object
438: * 3 = just a check on an object (for all the objects)
439: * 4 = test the object (for all the objects)
440: */
441: public void setCheckLevelObject(int checkLevelObject) {
442: pool.setCheckLevelObject(checkLevelObject);
443: }
444:
445: /**
446: * set the String to test the jdbc connection before using it
447: * @param jdbcTestStmt an sql statement
448: */
449: public void setJdbcTestStmt(String jdbcTestStmt) {
450: this .jdbcTestStmt = jdbcTestStmt;
451: }
452:
453: /**
454: * set the generation number for future connection, the generation number
455: * is used to identify a group a created objects
456: * @param generation an integer value which represents a generation
457: */
458: public void setGeneration(int generation) {
459: pool.setGeneration(generation);
460: }
461:
462: /**
463: * set the global time the pool can wait for a free object
464: * @param deadLock in milliseconds
465: */
466: public void setDeadLockMaxWait(long deadLock) {
467: pool.setDeadLockMaxWait(deadLock);
468: }
469:
470: /**
471: * set the time before 2 tries when trying to obtain an object from the pool
472: * @param loopWait in milliseconds
473: */
474: public void setDeadLockRetryWait(long loopWait) {
475: pool.setDeadLockRetryWait(loopWait);
476: }
477:
478: public PrintWriter getLogWriter() {
479: return log;
480: }
481:
482: public int getMinSize() {
483: return pool.getMinSize();
484: }
485:
486: public int getMaxSize() {
487: return pool.getMaxSize();
488: }
489:
490: public long getLifeTime() {
491: return pool.getLifeTime();
492: }
493:
494: public long getSleepTime() {
495: return pool.getSleepTime();
496: }
497:
498: public int getGeneration() {
499: return pool.generation;
500: }
501:
502: public boolean isGC() {
503: return pool.isGC();
504: }
505:
506: public int getLockedObjectCount() {
507: return pool.getLockedObjectCount();
508: }
509:
510: public int getUnlockedObjectCount() {
511: return pool.getUnlockedObjectCount();
512: }
513:
514: public int getCheckLevelObject() {
515: return pool.getCheckLevelObject();
516: }
517:
518: public String getJdbcTestStmt() {
519: return jdbcTestStmt;
520: }
521:
522: public long getDeadLockMaxWait() {
523: return pool.getDeadLockMaxWait();
524: }
525:
526: public long getDeadLockRetryWait() {
527: return pool.getDeadLockRetryWait();
528: }
529:
530: public String toString() {
531: StringBuffer sb = new StringBuffer();
532: sb.append("StandardPoolDataSource:\n");
533: sb.append(" data source name=<" + this .dataSourceName
534: + ">\n");
535: sb.append(" jdbc test stmt=<" + this .jdbcTestStmt + ">\n");
536: sb.append(" user=<" + this .user + ">\n");
537: if (this .cpds != null)
538: sb.append(this .cpds.toString());
539: sb.append(pool.toString());
540:
541: return sb.toString();
542: }
543:
544: /**
545: * Retrieves the Reference of this object. Used at binding time by JNDI
546: * to build a reference on this object.
547: *
548: * @return The non-null Reference of this object.
549: * @exception NamingException If a naming exception was encountered while
550: * retrieving the reference.
551: */
552: public Reference getReference() throws NamingException {
553: log
554: .debug("StandardPoolDataSource:getReference return a reference of the object");
555: Reference ref = super .getReference();
556: ref.add(new StringRefAddr("checkLevelObject", Integer
557: .toString(getCheckLevelObject())));
558: ref.add(new StringRefAddr("lifeTime", Long
559: .toString(getLifeTime())));
560: ref.add(new StringRefAddr("jdbcTestStmt", getJdbcTestStmt()));
561: ref.add(new StringRefAddr("maxSize", Integer
562: .toString(getMaxSize())));
563: ref.add(new StringRefAddr("minSize", Integer
564: .toString(getMinSize())));
565: ref
566: .add(new StringRefAddr("dataSourceName",
567: getDataSourceName()));
568: return ref;
569: }
570:
571: /* (non-Javadoc)
572: * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
573: */
574: public Object getObjectInstance(Object refObj, Name name,
575: Context nameCtx, Hashtable env) throws Exception {
576:
577: super .getObjectInstance(refObj, name, nameCtx, env);
578: Reference ref = (Reference) refObj;
579: this .setLifeTime(Long.parseLong((String) ref.get("lifeTime")
580: .getContent()));
581: this .setJdbcTestStmt((String) ref.get("jdbcTestStmt")
582: .getContent());
583: this .setMaxSize(Integer.parseInt((String) ref.get("maxSize")
584: .getContent()));
585: this .setMinSize(Integer.parseInt((String) ref.get("minSize")
586: .getContent()));
587: this .setDataSourceName((String) ref.get("dataSourceName")
588: .getContent());
589: InitialContext ictx = new InitialContext(env);
590: cpds = (ConnectionPoolDataSource) ictx
591: .lookup(this .dataSourceName);
592: return this ;
593: }
594:
595: /**
596: * Override this so that the pool's tf gets set as well
597: */
598: public void setThreadFactory(JdbcThreadFactory tf) {
599: super .setThreadFactory(tf);
600: pool.setThreadFactory(tf);
601: }
602:
603: public GenericPool getPool() {
604: return pool;
605: }
606:
607: public void removeUnlockedObject(GenerationObject obj) {
608: pool.removeUnlockedObject(obj);
609: }
610:
611: public GenericPool getUsedPool() {
612: return pool;
613: }
614:
615: public void fullRemoveLockedObject(GenerationObject obj) {
616: pool.fullRemoveLockedObject(obj);
617: }
618:
619: public void setMaxLifeTime(long maxLifeTime) {
620: pool.setMaxLifeTime(maxLifeTime);
621: }
622:
623: public long getMaxLifeTime() {
624: return pool.getMaxLifeTime();
625: }
626: }
|