001: /*
002:
003: Derby - Class org.apache.derby.impl.jdbc.TransactionResourceImpl
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.jdbc;
023:
024: import org.apache.derby.jdbc.InternalDriver;
025:
026: import org.apache.derby.iapi.services.context.Context;
027: import org.apache.derby.iapi.services.context.ContextService;
028: import org.apache.derby.iapi.services.context.ContextManager;
029: import org.apache.derby.iapi.services.monitor.Monitor;
030: import org.apache.derby.iapi.services.sanity.SanityManager;
031:
032: import org.apache.derby.iapi.db.Database;
033: import org.apache.derby.iapi.error.StandardException;
034: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
035: import org.apache.derby.iapi.error.ExceptionSeverity;
036:
037: import org.apache.derby.iapi.reference.Attribute;
038: import org.apache.derby.iapi.reference.SQLState;
039: import org.apache.derby.iapi.reference.Property;
040: import org.apache.derby.iapi.util.StringUtil;
041: import org.apache.derby.iapi.util.IdUtil;
042:
043: import java.util.Properties;
044: import java.sql.SQLException;
045:
046: /**
047: * An instance of a TransactionResourceImpl is a bundle of things that
048: * connects a connection to the database - it is the transaction "context" in
049: * a generic sense. It is also the object of synchronization used by the
050: * connection object to make sure only one thread is accessing the underlying
051: * transaction and context.
052: *
053: * <P>TransactionResourceImpl not only serves as a transaction "context", it
054: * also takes care of: <OL>
055: * <LI>context management: the pushing and popping of the context manager in
056: * and out of the global context service</LI>
057: * <LI>transaction demarcation: all calls to commit/abort/prepare/close a
058: * transaction must route thru the transaction resource.
059: * <LI>error handling</LI>
060: * </OL>
061: *
062: * <P>The only connection that have access to the TransactionResource is the
063: * root connection, all other nested connections (called proxyConnection)
064: * accesses the TransactionResource via the root connection. The root
065: * connection may be a plain EmbedConnection, or a DetachableConnection (in
066: * case of a XATransaction). A nested connection must be a ProxyConnection.
067: * A proxyConnection is not detachable and can itself be a XA connection -
068: * although an XATransaction may start nested local (proxy) connections.
069: *
070: * <P> this is an example of how all the objects in this package relate to each
071: * other. In this example, the connection is nested 3 deep.
072: * DetachableConnection.
073: * <P><PRE>
074: *
075: * lcc cm database jdbcDriver
076: * ^ ^ ^ ^
077: * | | | |
078: * |======================|
079: * | TransactionResource |
080: * |======================|
081: * ^ |
082: * | |
083: * | | |---------------rootConnection----------|
084: * | | | |
085: * | | |- rootConnection-| |
086: * | | | | |
087: * | V V | |
088: *|========================| |=================| |=================|
089: *| EmbedConnection | | EmbedConnection | | EmbedConnection |
090: *| |<-----| |<-----| |
091: *| (DetachableConnection) | | ProxyConnection | | ProxyConnection |
092: *|========================| |=================| |=================|
093: * ^ | ^ ^ ^
094: * | | | | |
095: * ---rootConnection-- | | |
096: * | | |
097: * | | |
098: * |======================| |======================| |======================|
099: * | ConnectionChild | | ConnectionChild | | ConnectionChild |
100: * | | | | | |
101: * | (EmbedStatement) | | (EmbedResultSet) | | (...) |
102: * |======================| |======================| |======================|
103: *
104: * <P>A plain local connection <B>must</B> be attached (doubly linked with) to a
105: * TransactionResource at all times. A detachable connection can be without a
106: * TransactionResource, and a TransactionResource for an XATransaction
107: * (called XATransactionResource) can be without a connection.
108: *
109: *
110: */
111: public final class TransactionResourceImpl {
112: /*
113: ** instance variables set up in the constructor.
114: */
115: // conn is only present if TR is attached to a connection
116: protected ContextManager cm;
117: protected ContextService csf;
118: protected String username;
119:
120: private String dbname;
121: private InternalDriver driver;
122: private String url;
123: private String drdaID;
124:
125: // set these up after constructor, called by EmbedConnection
126: protected Database database;
127: protected LanguageConnectionContext lcc;
128:
129: /*
130: * create a brand new connection for a brand new transaction
131: */
132: TransactionResourceImpl(InternalDriver driver, String url,
133: Properties info) throws SQLException {
134: this .driver = driver;
135: csf = driver.getContextServiceFactory();
136: dbname = InternalDriver.getDatabaseName(url, info);
137: this .url = url;
138:
139: // the driver manager will push a user name
140: // into the properties if its getConnection(url, string, string)
141: // interface is used. Thus, we look there first.
142: // Default to APP.
143: username = IdUtil.getUserNameFromURLProps(info);
144:
145: drdaID = info.getProperty(Attribute.DRDAID_ATTR, null);
146:
147: // make a new context manager for this TransactionResource
148:
149: // note that the Database API requires that the
150: // getCurrentContextManager call return the context manager
151: // associated with this session. The JDBC driver assumes
152: // responsibility (for now) for tracking and installing
153: // this context manager for the thread, each time a database
154: // call is made.
155: cm = csf.newContextManager();
156: }
157:
158: /*
159: * Called only in EmbedConnection construtor.
160: * The Local Connection sets up the database in its constructor and sets it
161: * here.
162: */
163: void setDatabase(Database db) {
164: if (SanityManager.DEBUG)
165: SanityManager.ASSERT(database == null,
166: "setting database when it is not null");
167:
168: database = db;
169: }
170:
171: /*
172: * Called only in EmbedConnection constructor. Create a new transaction
173: * by creating a lcc.
174: *
175: * The arguments are not used by this object, it is used by
176: * XATransactionResoruceImpl. Put them here so that there is only one
177: * routine to start a local connection.
178: */
179: void startTransaction() throws StandardException, SQLException {
180: // setting up local connection
181: lcc = database.setupConnection(cm, username, drdaID, dbname);
182: }
183:
184: /*
185: * Return instance variables to EmbedConnection. RESOLVE: given time, we
186: * should perhaps stop giving out reference to these things but instead use
187: * the transaction resource itself.
188: */
189: InternalDriver getDriver() {
190: return driver;
191: }
192:
193: ContextService getCsf() {
194: return csf;
195: }
196:
197: /*
198: * need to be public because it is in the XATransactionResource interface
199: */
200: ContextManager getContextManager() {
201: return cm;
202: }
203:
204: LanguageConnectionContext getLcc() {
205: return lcc;
206: }
207:
208: String getDBName() {
209: return dbname;
210: }
211:
212: String getUrl() {
213: return url;
214: }
215:
216: Database getDatabase() {
217: return database;
218: }
219:
220: StandardException shutdownDatabaseException() {
221: StandardException se = StandardException.newException(
222: SQLState.SHUTDOWN_DATABASE, getDBName());
223: se.setReport(StandardException.REPORT_NEVER);
224: return se;
225: }
226:
227: /*
228: * local transaction demarcation - note that global or xa transaction
229: * cannot commit thru the connection, they can only commit thru the
230: * XAResource, which uses the xa_commit or xa_rollback interface as a
231: * safeguard.
232: */
233: void commit() throws StandardException {
234: lcc.userCommit();
235: }
236:
237: void rollback() throws StandardException {
238: // lcc may be null if this is called in close.
239: if (lcc != null)
240: lcc.userRollback();
241: }
242:
243: /*
244: * context management
245: */
246:
247: /**
248: * An error happens in the constructor, pop the context.
249: */
250: void clearContextInError() {
251: csf.resetCurrentContextManager(cm);
252: cm = null;
253: }
254:
255: /**
256: * Resolve: probably superfluous
257: */
258: void clearLcc() {
259: lcc = null;
260: }
261:
262: final void setupContextStack() {
263: if (SanityManager.DEBUG) {
264: SanityManager.ASSERT(cm != null,
265: "setting up null context manager stack");
266: }
267:
268: csf.setCurrentContextManager(cm);
269: }
270:
271: final void restoreContextStack() {
272:
273: if ((csf == null) || (cm == null))
274: return;
275: csf.resetCurrentContextManager(cm);
276: }
277:
278: /*
279: * exception handling
280: */
281:
282: /*
283: * clean up the error and wrap the real exception in some SQLException.
284: */
285: final SQLException handleException(Throwable thrownException,
286: boolean autoCommit, boolean rollbackOnAutoCommit)
287: throws SQLException {
288: try {
289: if (SanityManager.DEBUG)
290: SanityManager.ASSERT(thrownException != null);
291:
292: /*
293: just pass SQL exceptions right back. We assume that JDBC driver
294: code has cleaned up sufficiently. Not passing them through would mean
295: that all cleanupOnError methods would require knowledge of Utils.
296: */
297: if (thrownException instanceof SQLException) {
298:
299: return (SQLException) thrownException;
300:
301: }
302:
303: boolean checkForShutdown = false;
304: if (thrownException instanceof StandardException) {
305: StandardException se = (StandardException) thrownException;
306: int severity = se.getSeverity();
307: if (severity <= ExceptionSeverity.STATEMENT_SEVERITY) {
308: /*
309: ** If autocommit is on, then do a rollback
310: ** to release locks if requested. We did a stmt
311: ** rollback in the cleanupOnError above, but we still
312: ** may hold locks from the stmt.
313: */
314: if (autoCommit && rollbackOnAutoCommit) {
315: se
316: .setSeverity(ExceptionSeverity.TRANSACTION_SEVERITY);
317: }
318: } else if (SQLState.CONN_INTERRUPT.equals(se
319: .getMessageId())) {
320: // an interrupt closed the connection.
321: checkForShutdown = true;
322: }
323: }
324: // if cm is null, we don't have a connection context left,
325: // it was already removed. all that's left to cleanup is
326: // JDBC objects.
327: if (cm != null) {
328: boolean isShutdown = cleanupOnError(thrownException);
329: if (checkForShutdown && isShutdown) {
330: // Change the error message to be a known shutdown.
331: thrownException = shutdownDatabaseException();
332: }
333: }
334:
335: return wrapInSQLException((SQLException) null,
336: thrownException);
337:
338: } catch (Throwable t) {
339:
340: if (cm != null) { // something to let us cleanup?
341: cm.cleanupOnError(t);
342: }
343: /*
344: We'd rather throw the Throwable,
345: but then javac complains...
346: We assume if we are in this degenerate
347: case that it is actually a java exception
348: */
349: throw wrapInSQLException((SQLException) null, t);
350: //throw t;
351: }
352:
353: }
354:
355: public static final SQLException wrapInSQLException(
356: SQLException sqlException, Throwable thrownException) {
357:
358: if (thrownException == null)
359: return sqlException;
360:
361: SQLException nextSQLException;
362:
363: if (thrownException instanceof SQLException) {
364:
365: // server side JDBC can end up with a SQLException in the nested stack
366: nextSQLException = (SQLException) thrownException;
367: } else if (thrownException instanceof StandardException) {
368:
369: StandardException se = (StandardException) thrownException;
370:
371: nextSQLException = Util.generateCsSQLException(se);
372:
373: wrapInSQLException(nextSQLException, se
374: .getNestedException());
375:
376: } else {
377:
378: nextSQLException = Util.javaException(thrownException);
379:
380: // special case some java exceptions that have nested exceptions themselves
381: Throwable nestedByJVM = null;
382: if (thrownException instanceof ExceptionInInitializerError) {
383: nestedByJVM = ((ExceptionInInitializerError) thrownException)
384: .getException();
385: } else if (thrownException instanceof java.lang.reflect.InvocationTargetException) {
386: nestedByJVM = ((java.lang.reflect.InvocationTargetException) thrownException)
387: .getTargetException();
388: }
389:
390: if (nestedByJVM != null) {
391: wrapInSQLException(nextSQLException, nestedByJVM);
392: }
393:
394: }
395:
396: if (sqlException != null) {
397: sqlException.setNextException(nextSQLException);
398: }
399:
400: return nextSQLException;
401: }
402:
403: /*
404: * TransactionResource methods
405: */
406:
407: String getUserName() {
408: return username;
409: }
410:
411: boolean cleanupOnError(Throwable e) {
412: if (SanityManager.DEBUG)
413: SanityManager
414: .ASSERT(cm != null,
415: "cannot cleanup on error with null context manager");
416:
417: return cm.cleanupOnError(e);
418: }
419:
420: boolean isIdle() {
421: // If no lcc, there is no transaction.
422: return (lcc == null || lcc.getTransactionExecute().isIdle());
423: }
424:
425: /*
426: * class specific methods
427: */
428:
429: /*
430: * is the underlaying database still active?
431: */
432: boolean isActive() {
433: // database is null at connection open time
434: return (driver.isActive() && ((database == null) || database
435: .isActive()));
436: }
437:
438: }
|