001: /*
002: * Licensed under the X license (see http://www.x.org/terms.htm)
003: */
004: package org.ofbiz.minerva.pool.jdbc.xa;
005:
006: import java.io.InvalidObjectException;
007: import java.io.ObjectStreamException;
008: import java.io.PrintWriter;
009: import java.io.Serializable;
010: import java.sql.Connection;
011: import java.util.Collection;
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Hashtable;
015:
016: import javax.naming.Context;
017: import javax.naming.InitialContext;
018: import javax.naming.Name;
019: import javax.naming.NamingException;
020: import javax.naming.RefAddr;
021: import javax.naming.Reference;
022: import javax.naming.Referenceable;
023: import javax.naming.StringRefAddr;
024: import javax.naming.spi.ObjectFactory;
025: import javax.sql.DataSource;
026: import javax.sql.XAConnection;
027: import javax.sql.XADataSource;
028: import javax.transaction.TransactionManager;
029:
030: import org.apache.log4j.Logger;
031: import org.ofbiz.base.util.Log4jLoggerWriter;
032: import org.ofbiz.minerva.pool.ObjectPool;
033:
034: /**
035: * DataSource for transactional JDBC pools. This handles configuration
036: * parameters for both the pool and the JDBC connection. It is important that
037: * you set all the configuration parameters before you initialize the DataSource,
038: * and you must initialize it before you use it. All the configuration
039: * parameters are not documented here; you are instead referred to ObjectPool
040: * and XAConnectionFactory.
041: * @see org.ofbiz.minerva.pool.ObjectPool
042: * @see org.ofbiz.minerva.pool.jdbc.xa.XAConnectionFactory
043: *
044: * @author Aaron Mulder (ammulder@alumni.princeton.edu)
045: * @author <a href="mailto:danch@nvisia.com">danch (Dan Christopherson)</a>
046: * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
047: *
048: * Revision:
049: * 20010701 danch added code for timeout in blocking.
050: * 20010703 bill added code for transaction isolation and setting the ps cache size
051: */
052: public class XAPoolDataSource implements DataSource, Referenceable,
053: ObjectFactory, Serializable
054: //just including Serializable to strongly indicate serialization support
055: {
056:
057: private transient static Logger log = Logger
058: .getLogger(XAPoolDataSource.class);
059: private transient static HashMap sources = new HashMap();
060:
061: /**
062: * Gets all the current JDBC pool data sources.
063: */
064: public static Collection getDataSources() {
065: return new HashSet(sources.values());
066: }
067:
068: /**
069: * Gets a specific JDBC pool data source by pool name.
070: */
071: public static XAPoolDataSource getDataSource(String poolName) {
072: return (XAPoolDataSource) sources.get(poolName);
073: }
074:
075: private transient ObjectPool pool;
076: private transient XAConnectionFactory factory;
077: private transient PrintWriter logWriter;
078: private transient int timeout;
079: private transient boolean initialized = false;
080: //Unused cruft.
081: private transient String jndiName;
082: //what is actually used to bind in jndi by XADataSourceLoader.
083: private String name;
084:
085: /**
086: * Creates a new XA pool data source. Be sure to configure it and then
087: * call initialize before you try to use it.
088: */
089: public XAPoolDataSource() {
090: log.debug("Creating XA Pool");
091: pool = new ObjectPool();
092: try {
093: factory = new XAConnectionFactory();
094: log.debug("Created factory");
095: } catch (NamingException e) {
096: log.error("Can't get XAConnectionFactory", e);
097: }
098: XAPoolDriver.instance();
099: log.debug("got driver instance");
100: }
101:
102: // Unique properties
103: /**
104: * If you use this to set a JNDI name, this pool will be bound to that name
105: * using the default InitialContext. You can also do this manually if you
106: * have additional requirements.
107: */
108: public void setJNDIName(String name) throws NamingException {
109: if (log.isDebugEnabled())
110: log.debug("Binding to JNDI name " + name);
111:
112: InitialContext ctx = new InitialContext();
113: if (jndiName != null && !jndiName.equals(name))
114: ctx.unbind(jndiName);
115: if (name != null)
116: ctx.bind(name, this );
117: jndiName = name;
118: }
119:
120: /**
121: * Gets the JNDI name this pool is bound to. Only valid if you used
122: * setJNDIName to bind it.
123: * @see #setJNDIName
124: */
125: public String getJNDIName() {
126: return jndiName;
127: }
128:
129: // XA properties
130: public void setDataSource(XADataSource ds) {
131: factory.setDataSource(ds);
132: }
133:
134: public XADataSource getDataSource() {
135: return factory.getDataSource();
136: }
137:
138: public void setTransactionManager(TransactionManager tm) {
139: factory.setTransactionManager(tm);
140: }
141:
142: //public String getTransactionManagerJNDIName() {return factory.getTransactionManagerJNDIName();}
143: public void setJDBCUser(String user) {
144: factory.setUser(user);
145: }
146:
147: public String getJDBCUser() {
148: return factory.getUser();
149: }
150:
151: public void setJDBCPassword(String password) {
152: factory.setPassword(password);
153: }
154:
155: public String getJDBCPassword() {
156: return factory.getPassword();
157: }
158:
159: public int getTransactionIsolation() {
160: return factory.getTransactionIsolation();
161: }
162:
163: public void setTransactionIsolation(int iso) {
164: factory.setTransactionIsolation(iso);
165: }
166:
167: public void setTransactionIsolation(String iso) {
168: factory.setTransactionIsolation(iso);
169: }
170:
171: public int getPSCacheSize() {
172: return factory.getPSCacheSize();
173: }
174:
175: public void setPSCacheSize(int size) {
176: factory.setPSCacheSize(size);
177: }
178:
179: public boolean getReleaseOnCommit() {
180: return factory.getReleaseOnCommit();
181: }
182:
183: public void setReleaseOnCommit(boolean rel) {
184: factory.setReleaseOnCommit(rel);
185: }
186:
187: /**
188: * Have XAClientConnections save a stack trace on creation
189: * This is useful for debugging non-closed connections.
190: * It must be used with ReleaseOnCommit option
191: */
192: public boolean getSaveStackTrace() {
193: return factory.getSaveStackTrace();
194: }
195:
196: public void setSaveStackTrace(boolean save) {
197: factory.setSaveStackTrace(save);
198: }
199:
200: // Pool properties
201: public void setPoolName(String name) {
202: //remember the name so we can look ourselves up on deserialization.
203: this .name = name;
204: pool.setName(name);
205: sources.put(pool.getName(), this );
206: }
207:
208: public String getPoolName() {
209: return name;
210: }
211:
212: public void setMinSize(int size) {
213: pool.setMinSize(size);
214: }
215:
216: public int getMinSize() {
217: return pool.getMinSize();
218: }
219:
220: public void setMaxSize(int size) {
221: pool.setMaxSize(size);
222: }
223:
224: public int getMaxSize() {
225: return pool.getMaxSize();
226: }
227:
228: public void setBlocking(boolean blocking) {
229: pool.setBlocking(blocking);
230: }
231:
232: public boolean isBlocking() {
233: return pool.isBlocking();
234: }
235:
236: public void setBlockingTimeout(int blockingTimeout) {
237: pool.setBlockingTimeout(blockingTimeout);
238: }
239:
240: public int getBlockingTimeout() {
241: return pool.getBlockingTimeout();
242: }
243:
244: public void setIdleTimeoutEnabled(boolean allowShrinking) {
245: pool.setIdleTimeoutEnabled(allowShrinking);
246: }
247:
248: public boolean isIdleTimeoutEnabled() {
249: return pool.isIdleTimeoutEnabled();
250: }
251:
252: public void setGCEnabled(boolean allowGC) {
253: pool.setGCEnabled(allowGC);
254: }
255:
256: public boolean isGCEnabled() {
257: return pool.isGCEnabled();
258: }
259:
260: public void setMaxIdleTimeoutPercent(float percent) {
261: pool.setMaxIdleTimeoutPercent(percent);
262: }
263:
264: public float getMaxIdleTimeoutPercent() {
265: return pool.getMaxIdleTimeoutPercent();
266: }
267:
268: public void setIdleTimeout(long millis) {
269: pool.setIdleTimeout(millis);
270: }
271:
272: public long getIdleTimeout() {
273: return pool.getIdleTimeout();
274: }
275:
276: public void setGCMinIdleTime(long millis) {
277: pool.setGCMinIdleTime(millis);
278: }
279:
280: public long getGCMinIdleTime() {
281: return pool.getGCMinIdleTime();
282: }
283:
284: public void setGCInterval(long millis) {
285: pool.setGCInterval(millis);
286: }
287:
288: public long getGCInterval() {
289: return pool.getGCInterval();
290: }
291:
292: public void setInvalidateOnError(boolean invalidate) {
293: pool.setInvalidateOnError(invalidate);
294: }
295:
296: public boolean isInvalidateOnError() {
297: return pool.isInvalidateOnError();
298: }
299:
300: public void setTimestampUsed(boolean timestamp) {
301: pool.setTimestampUsed(timestamp);
302: }
303:
304: public boolean isTimestampUsed() {
305: return pool.isTimestampUsed();
306: }
307:
308: // Other methods
309:
310: /**
311: * Initializes the pool. You need to have configured all the pool and
312: * XA properties first.
313: */
314: public void initialize() {
315: initialized = true;
316: pool.setObjectFactory(factory);
317: pool.initialize();
318: }
319:
320: /**
321: * Returns a string describing the pool status (number of connections
322: * created, used, and maximum).
323: */
324: public String getPoolStatus() {
325: return pool.toString();
326: }
327:
328: /**
329: * Shuts down this data source and the underlying pool. If you used
330: * setJNDI name to bind it in JNDI, it is unbound.
331: */
332: public void close() {
333: if (log.isDebugEnabled())
334: log.debug("Closing DataSource");
335:
336: try {
337: setJNDIName(null);
338: } catch (NamingException e) {
339: log.warn("Can't unbind from JNDI", e);
340: }
341: sources.remove(pool.getName());
342: pool.shutDown();
343: pool = null;
344: factory = null;
345: }
346:
347: /**
348: * Gets a connection from the pool.
349: * Since no userid or password is specified, we get a connection using the default
350: * userid and password, specified when the factory was created.
351: */
352: public Connection getConnection() throws java.sql.SQLException {
353: if (!initialized)
354: initialize();
355:
356: log.debug("Getting a Connection");
357: String user = factory.getUser();
358: String password = factory.getPassword();
359: String[] params = { user, password };
360: XAConnection xaConn = (XAConnection) pool.getObject(params);
361: return xaConn.getConnection();
362: }
363:
364: /**
365: * Gets a new connection from the pool. If a new connection must be
366: * created, it will use the specified user name and password. If there is
367: * a connection available in the pool, it will be used, regardless of the
368: * user name and password use to created it initially.
369: */
370: public Connection getConnection(String user, String password)
371: throws java.sql.SQLException {
372: if (!initialized)
373: initialize();
374:
375: log.debug("Getting a connection for user " + user
376: + " with password " + password);
377: String[] params = { user, password };
378: XAConnection xaConn = (XAConnection) pool.getObject(params);
379: return xaConn.getConnection();
380: }
381:
382: /**
383: * Gets a log writer used to record pool events.
384: */
385: public PrintWriter getLogWriter() throws java.sql.SQLException {
386: return logWriter;
387: }
388:
389: /**
390: * Sets a log writer used to record pool events.
391: */
392: public void setLogWriter(PrintWriter writer)
393: throws java.sql.SQLException {
394: if (writer == null) {
395: logWriter = null;
396: } else {
397: if (logWriter == null) {
398: logWriter = new Log4jLoggerWriter(log);
399: }
400: }
401: }
402:
403: /**
404: * This property is not used by this implementation.
405: */
406: public int getLoginTimeout() throws java.sql.SQLException {
407: return timeout;
408: }
409:
410: /**
411: * This property is not used by this implementation.
412: */
413: public void setLoginTimeout(int timeout)
414: throws java.sql.SQLException {
415: this .timeout = timeout;
416: }
417:
418: // Referenceable implementation ----------------------------------
419: /**
420: * Gets a reference to this data source.
421: */
422: public Reference getReference() {
423: return new Reference(getClass().getName(), new StringRefAddr(
424: "XAPool", pool.getName()), getClass().getName(), null);
425: }
426:
427: // ObjectFactory implementation ----------------------------------
428: /**
429: * Decodes a reference to a specific pool data source.
430: */
431: public Object getObjectInstance(Object obj, Name name,
432: Context nameCtx, Hashtable environment) {
433: if (obj instanceof Reference) {
434: Reference ref = (Reference) obj;
435: if (ref.getClassName().equals(getClass().getName())) {
436: RefAddr addr = ref.get("XAPool");
437: return sources.get(addr.getContent());
438: }
439: }
440: return null;
441: }
442:
443: //deserialization to correct instance support
444:
445: private Object readResolve() throws ObjectStreamException {
446: try {
447: InitialContext ctx = new InitialContext();
448: return ctx.lookup("java:/" + name);
449: } catch (NamingException e) {
450: throw new InvalidObjectException(
451: "problem finding correct datasource instance" + e);
452: } // end of try-catch
453:
454: }
455: }
456:
457: /*
458: vim:tabstop=3:et:shiftwidth=3
459: */
|