001: /* ====================================================================
002: * The LateralNZ Software License, Version 1.0
003: *
004: * Copyright (c) 2003 LateralNZ. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by
021: * LateralNZ (http://www.lateralnz.org/) and other third parties."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. The names "LateralNZ" must not be used to endorse or promote
026: * products derived from this software without prior written
027: * permission. For written permission, please
028: * contact oss@lateralnz.org.
029: *
030: * 5. Products derived from this software may not be called "Panther",
031: * or "Lateral" or "LateralNZ", nor may "PANTHER" or "LATERAL" or
032: * "LATERALNZ" appear in their name, without prior written
033: * permission of LateralNZ.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of LateralNZ. For more
051: * information on Lateral, please see http://www.lateralnz.com/ or
052: * http://www.lateralnz.org
053: *
054: */
055: package org.lateralnz.simpletrans;
056:
057: import java.io.PrintWriter;
058: import java.sql.Connection;
059: import java.sql.SQLException;
060: import java.util.Map;
061: import java.util.HashMap;
062: import javax.sql.DataSource;
063: import javax.naming.Reference;
064: import javax.naming.Referenceable;
065: import javax.naming.StringRefAddr;
066:
067: import org.apache.log4j.Logger;
068:
069: import org.lateralnz.common.util.Constants;
070: import org.lateralnz.common.util.JNDIUtils;
071: import org.lateralnz.common.util.StackThreadLocal;
072:
073: /**
074: * a transaction aware data source, wrapping a 'real' datasource (ensuring that
075: * all connections returned are wrapped by a TransConnection)
076: *
077: * @author J R Briggs
078: */
079: public class TransDataSource implements Constants, DataSource,
080: Referenceable {
081: private static final Logger log = Logger
082: .getLogger(TransDataSource.class.getName());
083: private static Map dataSources = new HashMap();
084: private static SimpleTransactionManager stm = null;
085:
086: private String name;
087: private DataSource source;
088:
089: public TransDataSource(String name, DataSource source)
090: throws Exception {
091: this .name = name;
092: this .source = source;
093: dataSources.put(name, this );
094:
095: if (stm == null) {
096: synchronized (TransDataSource.class) {
097: if (stm == null) {
098: try {
099: Object tmp = JNDIUtils
100: .get("java:/TransactionManager");
101: if (tmp instanceof SimpleTransactionManager) {
102: stm = (SimpleTransactionManager) tmp;
103: } else {
104: stm = (SimpleTransactionManager) tmp;
105: }
106: } catch (Exception e) {
107: log.error(e);
108: throw e;
109: }
110: }
111: }
112: }
113: }
114:
115: /**
116: * helper method for the object factory associated with this class
117: */
118: protected static final TransDataSource getTransDataSource(
119: String name) {
120: return (TransDataSource) dataSources.get(name);
121: }
122:
123: /**
124: * get a connection (see the next getConnection method for more detail)
125: */
126: public Connection getConnection() throws SQLException {
127: return getConnection(null, null);
128: }
129:
130: /**
131: * get a jdbc connection -- if the calling thread is in a transaction, then
132: * this connection will be wrapped by a TransConnection object, otherwise
133: * the base connection is returned
134: *
135: * NOTE: RequiresNew is -NOT- handled properly at the moment
136: */
137: public Connection getConnection(String str, String str1)
138: throws SQLException {
139: String methodTrans = (String) StackThreadLocal.peek();
140:
141: if (stm.isInTransaction()
142: && (methodTrans != null && (methodTrans
143: .equals(REQUIRED)
144: || methodTrans.equals(SUPPORTS) || methodTrans
145: .equals(REQUIRES_NEW)))) {
146: SimpleTransaction trans = (SimpleTransaction) stm
147: .getTransaction();
148: Object tmp = trans.getResource(name);
149: TransConnection tc;
150: if (tmp != null) {
151: tc = (TransConnection) tmp;
152: return tc;
153: } else if (methodTrans.equals(REQUIRED)
154: || methodTrans.equals(REQUIRES_NEW)) {
155: tc = new TransConnection(name, getConn(str, str1), true);
156: trans.enlistResource(tc);
157: return tc;
158: }
159: }
160:
161: Connection conn = getConn(str, str1);
162: conn.setAutoCommit(true);
163: return conn;
164: }
165:
166: /**
167: * call getConnection on the underlying datasource
168: */
169: private final Connection getConn(String str, String str1)
170: throws SQLException {
171: long time = System.currentTimeMillis();
172: try {
173: if (str == null && str1 == null) {
174: return source.getConnection();
175: } else {
176: return source.getConnection(str, str1);
177: }
178: } catch (SQLException e) {
179: time = System.currentTimeMillis() - time;
180: if (log.isDebugEnabled()) {
181: log.debug("time taken at SQL error point was " + time
182: + "ms");
183: }
184: e.printStackTrace();
185: throw e;
186: }
187: }
188:
189: /**
190: * @see javax.sql.DataSource#setLogWriter
191: */
192: public void setLogWriter(PrintWriter pw) throws SQLException {
193: source.setLogWriter(pw);
194: }
195:
196: /**
197: * @see javax.sql.DataSource#getLogWriter
198: */
199: public PrintWriter getLogWriter() throws SQLException {
200: return source.getLogWriter();
201: }
202:
203: /**
204: * @see javax.sql.DataSource#setLoginTimeout
205: */
206: public void setLoginTimeout(int timeout) throws SQLException {
207: source.setLoginTimeout(timeout);
208: }
209:
210: /**
211: * @see javax.sql.DataSource#getLoginTimeout
212: */
213: public int getLoginTimeout() throws SQLException {
214: return source.getLoginTimeout();
215: }
216:
217: /**
218: * reference method required so this object can be loaded into a registry
219: */
220: public Reference getReference() {
221: Reference ref = new Reference(getClass().getName(),
222: TransDataSourceObjectFactory.class.getName(), null);
223: ref.add(new StringRefAddr("name", name));
224: return ref;
225: }
226: }
|