001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.enterprise.spi.inprocessimpl;
016:
017: import java.sql.Connection;
018: import java.sql.SQLException;
019: import java.util.HashSet;
020: import java.util.Set;
021:
022: import javax.naming.Context;
023: import javax.naming.NamingException;
024: import javax.transaction.RollbackException;
025: import javax.transaction.Status;
026: import javax.transaction.Synchronization;
027: import javax.transaction.SystemException;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031:
032: import tyrex.tm.RuntimeContext;
033:
034: import com.metaboss.enterprise.spi.JDBCConnectionProvider;
035:
036: /** Interface to the JDBC connection provider */
037: public class JDBCConnectionProviderImpl implements
038: JDBCConnectionProvider {
039: private static final Log sLogger = LogFactory
040: .getLog(JDBCConnectionProviderImpl.class);
041: private static final String sDataSourceEnvKeyPrefix = "TransactionDataSource_";
042: private static final String sOpenedConnectionsSetEnvKey = "OpenedConnectionsSet";
043:
044: // Transaction synchronisation object used to close the conection
045: private class ConnectionCloser implements Synchronization {
046: private RuntimeContext mRuntimeContext = null;
047: private String mDatasourceName = null;
048:
049: public ConnectionCloser(RuntimeContext pRuntimeContext,
050: String pDatasourceName) {
051: mRuntimeContext = pRuntimeContext;
052: mDatasourceName = pDatasourceName;
053: }
054:
055: // javax.transaction.Synchronization method
056: public void afterCompletion(int status) {
057: try {
058: String lConnectionUrl = sDataSourceEnvKeyPrefix
059: + mDatasourceName;
060: Context lEnvContext = mRuntimeContext.getEnvContext();
061: Connection lConnection = findConnectionInContext(
062: lEnvContext, lConnectionUrl);
063: if (lConnection != null) {
064: sLogger
065: .debug("Closing datasource connection in transaction. Connection : "
066: + lConnection);
067: unregisterConnectionInContext(lEnvContext,
068: lConnectionUrl, lConnection);
069: lConnection.close();
070: }
071: } catch (SQLException e) {
072: // Ignore
073: e.printStackTrace();
074: } catch (NamingException e) {
075: // Ignore
076: e.printStackTrace();
077: }
078: }
079:
080: // javax.transaction.Synchronization method
081: public void beforeCompletion() {
082: // Not interested
083: }
084: }
085:
086: /** Returns JDBC connection to the named datasource */
087: public Connection getConnection(String pDatasourceName)
088: throws SQLException {
089: try {
090: Connection lConnection = null;
091: // We need to check if we are insinde an active transaction.
092: // If we are - our context may already hold this connection
093: RuntimeContext lRuntimeContext = RuntimeContext
094: .getRuntimeContext();
095: javax.transaction.Transaction lTransaction = lRuntimeContext
096: .getTransaction();
097: if (lTransaction == null
098: || lTransaction.getStatus() != Status.STATUS_ACTIVE) {
099: String lDatasourceName = pDatasourceName + "_NonXA";
100: lConnection = newConnection(lDatasourceName); // No transaction - just return new connection
101: sLogger
102: .debug("Opened datasource connection out of transaction. DataSource : "
103: + lDatasourceName
104: + ". Connection : "
105: + lConnection);
106: } else {
107: String lDatasourceName = pDatasourceName + "_XA";
108: // We do have transaction. See if we have already had a context for it
109: String lConnectionUrl = sDataSourceEnvKeyPrefix
110: + lDatasourceName;
111: Context lEnvContext = lRuntimeContext.getEnvContext();
112: lConnection = findConnectionInContext(lEnvContext,
113: lConnectionUrl);
114: if (lConnection == null) {
115: // Create new connection. Enlist it in the context and create
116: // connection closer, which will make sure that the connection is closed after transaction is gone
117: lConnection = newConnection(lDatasourceName);
118: registerConnectionInContext(lEnvContext,
119: lConnectionUrl, lConnection);
120: lTransaction
121: .registerSynchronization(new ConnectionCloser(
122: lRuntimeContext, lDatasourceName));
123: sLogger
124: .debug("Opened datasource connection in transaction. DataSource : "
125: + lDatasourceName
126: + ". Connection : " + lConnection);
127: }
128: }
129: return lConnection;
130: } catch (RollbackException e) {
131: throw new java.sql.SQLException(
132: "Unable to obtain "
133: + pDatasourceName
134: + " Connection. Caught javax.transaction.RollbackException : "
135: + e.toString());
136: } catch (SystemException e) {
137: throw new java.sql.SQLException(
138: "Unable to obtain "
139: + pDatasourceName
140: + " Connection. Caught javax.transaction.SystemException : "
141: + e.toString());
142: } catch (NamingException e) {
143: throw new java.sql.SQLException(
144: "Unable to obtain "
145: + pDatasourceName
146: + " Connection. Caught javax.naming.NamingException : "
147: + e.toString());
148: }
149: }
150:
151: /* Releases JDBC connection to the named datasource */
152: public void closeConnection(Connection pConnection)
153: throws SQLException {
154: try {
155: // We need to check if this connection is stored on the active transaction connections list
156: // If it is listed there it should not be closed explicitly. Such connection will be closed
157: // at the time when transaction is destroyed
158: RuntimeContext lRuntimeContext = RuntimeContext
159: .getRuntimeContext();
160: Context lEnvContext = lRuntimeContext.getEnvContext();
161: if (hasConnectionInContext(lEnvContext, pConnection))
162: return; // Do not close - it will happen latter via transaction synchronisation mechanism
163: sLogger
164: .debug("Closing datasource connection out of transaction. Connection : "
165: + pConnection);
166: // Just do the usual close
167: pConnection.close();
168: } catch (NamingException e) {
169: throw new java.sql.SQLException(
170: "Unable to close connection. Caught "
171: + e.getClass().getName() + " exception. "
172: + e.toString());
173: }
174: }
175:
176: // Helper. Creates new connection and returns it
177: private Connection newConnection(String pDatasourceName)
178: throws SQLException {
179: tyrex.resource.Resources lRes = TyrexUtil
180: .getTransactionDomain().getResources();
181: javax.sql.DataSource lDataSource = null;
182: try {
183: lDataSource = (javax.sql.DataSource) lRes
184: .getResource(pDatasourceName);
185: } catch (tyrex.resource.ResourceException e) {
186: java.lang.Exception lNestedException = e.getException();
187: throw new java.sql.SQLException(
188: "Unable to obtain "
189: + pDatasourceName
190: + " Datasource. Exception : "
191: + e.getMessage()
192: + ((lNestedException != null) ? (" Nested Exception : " + e
193: .getMessage())
194: : ""));
195: }
196: if (lDataSource == null)
197: throw new java.sql.SQLException(
198: "Unable to obtain "
199: + pDatasourceName
200: + " DataSource. It appears that resource is not configured in Tyrex");
201: Connection lConnection = lDataSource.getConnection();
202: if (lConnection == null)
203: throw new java.sql.SQLException(
204: "Unable to obtain "
205: + pDatasourceName
206: + " Connection. It appears that resource is not configured in Tyrex");
207: if (lConnection.getAutoCommit())
208: lConnection.setAutoCommit(false);
209: return lConnection;
210: }
211:
212: // Registers newly created connection in the context
213: private void registerConnectionInContext(Context pContext,
214: String pConnectionUrl, Connection pConnection)
215: throws NamingException {
216: synchronized (pContext) {
217: // Bind connection under its own data source name
218: pContext.bind(pConnectionUrl, pConnection);
219:
220: // Register this connection in the connections list
221: Set lNewOpenedConnectionSet = new HashSet();
222: try {
223: Set lExistingOpenedConnectionSet = (Set) pContext
224: .lookup(sOpenedConnectionsSetEnvKey);
225: if (lExistingOpenedConnectionSet != null)
226: lNewOpenedConnectionSet
227: .addAll(lExistingOpenedConnectionSet);
228: } catch (javax.naming.NameNotFoundException e) {
229: // Ignore as this is expected
230: }
231: if (lNewOpenedConnectionSet.add(pConnection) == true)
232: pContext.rebind(sOpenedConnectionsSetEnvKey,
233: lNewOpenedConnectionSet);
234: }
235: }
236:
237: // Unregisters newly created connection in the context
238: private void unregisterConnectionInContext(Context pContext,
239: String pConnectionUrl, Connection pConnection)
240: throws NamingException {
241: synchronized (pContext) {
242: // Bind connection under its own data source name
243: pContext.unbind(pConnectionUrl);
244:
245: // Un register this connection from the connections list
246: try {
247: Set lExistingOpenedConnectionSet = (Set) pContext
248: .lookup(sOpenedConnectionsSetEnvKey);
249: if (lExistingOpenedConnectionSet != null) {
250: Set lNewOpenedConnectionSet = new HashSet();
251: lNewOpenedConnectionSet
252: .addAll(lExistingOpenedConnectionSet);
253: if (lNewOpenedConnectionSet.remove(pConnection) == true)
254: pContext.rebind(sOpenedConnectionsSetEnvKey,
255: lNewOpenedConnectionSet);
256: }
257: } catch (javax.naming.NameNotFoundException e) {
258: // Ignore as this is expected
259: }
260: }
261: }
262:
263: // Registers newly created connection in the context
264: private Connection findConnectionInContext(Context pContext,
265: String pConnectionUrl) throws NamingException {
266: synchronized (pContext) {
267: try {
268: return (Connection) pContext.lookup(pConnectionUrl);
269: } catch (javax.naming.NameNotFoundException e) {
270: // Ignore as this is expected
271: return null;
272: }
273: }
274: }
275:
276: // Registers newly created connection in the context
277: private boolean hasConnectionInContext(Context pContext,
278: Connection pConnection) throws NamingException {
279: synchronized (pContext) {
280: try {
281: Set lOpenendConnectionSet = (Set) pContext
282: .lookup(sOpenedConnectionsSetEnvKey);
283: return lOpenendConnectionSet != null
284: && lOpenendConnectionSet.contains(pConnection);
285: } catch (javax.naming.NameNotFoundException e) {
286: // Ignore as this is expected
287: return false;
288: }
289: }
290: }
291: }
|