| /* |
| |
| Derby - Class org.apache.derby.jdbc.EmbedXAResource |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.jdbc; |
| |
| import java.security.PrivilegedExceptionAction; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedAction; |
| import java.security.AccessController; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| |
| |
| import javax.transaction.xa.XAResource; |
| import javax.transaction.xa.Xid; |
| import javax.transaction.xa.XAException; |
| |
| import org.apache.derby.iapi.error.ExceptionSeverity; |
| import org.apache.derby.iapi.error.ExceptionUtil; |
| import org.apache.derby.iapi.error.StandardException; |
| import org.apache.derby.iapi.jdbc.BrokeredConnection; |
| import org.apache.derby.iapi.jdbc.ResourceAdapter; |
| import org.apache.derby.iapi.reference.SQLState; |
| import org.apache.derby.iapi.services.context.ContextManager; |
| import org.apache.derby.iapi.services.context.ContextService; |
| import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
| import org.apache.derby.iapi.store.access.TransactionController; |
| import org.apache.derby.iapi.store.access.XATransactionController; |
| import org.apache.derby.iapi.store.access.xa.XAResourceManager; |
| import org.apache.derby.iapi.store.access.xa.XAXactId; |
| import org.apache.derby.impl.jdbc.EmbedConnection; |
| import org.apache.derby.impl.jdbc.TransactionResourceImpl; |
| import org.apache.derby.impl.jdbc.Util; |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.services.property.PropertyUtil; |
| import org.apache.derby.iapi.reference.Property; |
| |
| /** |
| * Implements XAResource |
| */ |
| class EmbedXAResource implements XAResource { |
| |
| private EmbedPooledConnection con; |
| private ResourceAdapter ra; |
| private XAXactId currentXid; |
| /** The value of the transaction timeout on this resource. */ |
| private int timeoutSeconds; |
| |
| private LanguageConnectionContext lcc; |
| |
| EmbedXAResource (EmbedPooledConnection con, ResourceAdapter ra) { |
| this.con = con; |
| this.ra = ra; |
| // Setup the default value for the transaction timeout. |
| this.timeoutSeconds = 0; |
| } |
| |
| /** |
| * Commit the global transaction specified by xid. |
| * @param xid A global transaction identifier |
| * @param onePhase If true, the resource manager should use a one-phase |
| * commit protocol to commit the work done on behalf of xid. |
| * |
| * @exception XAException An error has occurred. Possible XAExceptions are |
| * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, |
| * XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO. |
| * <P>If the resource manager did not commit the transaction and |
| * the paramether onePhase is set to true, the resource manager |
| * may throw one of the XA_RB* exceptions. Upon return, the |
| * resource manager has rolled back the branch's work and has |
| * released all held resources. |
| */ |
| public final synchronized void commit(Xid xid, boolean onePhase) |
| throws XAException { |
| checkXAActive(); |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| XATransactionState tranState = getTransactionState(xid_im); |
| |
| if (tranState == null) { |
| XAResourceManager rm = ra.getXAResourceManager(); |
| ContextManager inDoubtCM = rm.find(xid); |
| // RM also does not know about this xid. |
| if (inDoubtCM == null) |
| throw new XAException(XAException.XAER_NOTA); |
| ContextService csf = getContextService(); |
| csf.setCurrentContextManager(inDoubtCM); |
| try { |
| rm.commit(inDoubtCM, xid_im, onePhase); |
| |
| // close the connection/transaction since it can never |
| // be used again. DERBY-4856 No extended diagnostic information needed. |
| inDoubtCM.cleanupOnError(StandardException.closeException(), |
| false); |
| return; |
| } catch (StandardException se) { |
| // The rm threw an exception, clean it up in the approprate |
| // context. There is no transactionResource to handle the |
| // exception for us. |
| inDoubtCM.cleanupOnError(se, con.isActive()); |
| throw wrapInXAException(se); |
| } finally { |
| csf.resetCurrentContextManager(inDoubtCM); |
| } |
| |
| } |
| |
| synchronized (tranState) { |
| checkUserCredentials(tranState.creatingResource); |
| |
| // Check the transaction is no associated with |
| // any XAResource. |
| switch (tranState.associationState) { |
| case XATransactionState.T0_NOT_ASSOCIATED: |
| break; |
| |
| case XATransactionState.TRO_FAIL: |
| throw new XAException(tranState.rollbackOnlyCode); |
| |
| default: |
| throw new XAException(XAException.XAER_PROTO); |
| } |
| |
| if (tranState.suspendedList != null && tranState.suspendedList.size() != 0) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| if (tranState.isPrepared == onePhase) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| try { |
| tranState.xa_commit(onePhase); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } finally { |
| returnConnectionToResource(tranState, xid_im); |
| } |
| } |
| } |
| |
| /** |
| * Ends the work performed on behalf of a transaction branch. The resource |
| * manager disassociates the XA resource from the transaction branch |
| * specified and let the transaction be completed. |
| * |
| * <p> If TMSUSPEND is specified in flags, the transaction branch is |
| * temporarily suspended in incomplete state. The transaction context |
| * is in suspened state and must be resumed via start with TMRESUME |
| * specified. |
| * |
| * <p> If TMFAIL is specified, the portion of work has failed. The |
| * resource manager may mark the transaction as rollback-only |
| * |
| * <p> If TMSUCCESS is specified, the portion of work has completed |
| * successfully. |
| * |
| * @param xid A global transaction identifier that is the same as what was |
| * used previously in the start method. |
| * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND |
| * |
| * @exception XAException An error has occurred. |
| * Possible XAException values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA, |
| * XAER_INVAL, XAER_PROTO, or XA_RB*. |
| */ |
| public final synchronized void end(Xid xid, int flags) throws XAException { |
| checkXAActive(); |
| |
| try { |
| // It is possible that the isolation level state in connection |
| // handle has gotten out of sync with the real isolation level. |
| // This can happen if SLQ instead of JDBC api has been used to |
| // set the isolation level. The code below will check if isolation |
| // was set using JDBC or SQL and if yes, then it will update the |
| // isolation state in BrokeredConnection with EmbedConnection's |
| // isolation level. |
| if (con.currentConnectionHandle != null) |
| con.currentConnectionHandle.getIsolationUptoDate(); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } |
| |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| |
| boolean endingCurrentXid = false; |
| |
| // must match the Xid from start() |
| if (currentXid != null) { |
| if (!currentXid.equals(xid_im)) |
| throw new XAException(XAException.XAER_PROTO); |
| endingCurrentXid = true; |
| } |
| |
| XATransactionState tranState = getTransactionState(xid_im); |
| if (tranState == null) |
| throw new XAException(XAException.XAER_NOTA); |
| |
| boolean rollbackOnly = tranState.end(this, flags, endingCurrentXid); |
| |
| // RESOLVE - what happens to the connection on a fail |
| // where we are not ending the current XID. |
| if (endingCurrentXid) { |
| currentXid = null; |
| con.realConnection = null; |
| } |
| |
| if (rollbackOnly) |
| throw new XAException(tranState.rollbackOnlyCode); |
| } |
| |
| /** |
| * Ask the resource manager to prepare for a transaction commit of the |
| * transaction specified in xid. |
| * |
| * @param xid A global transaction identifier |
| * |
| * @return A value indicating the resource manager's vote on the outcome |
| * of the transaction. The possible values are: XA_RDONLY or XA_OK. If the |
| * resource manager wants to roll back the transaction, it should do so by |
| * raising an appropriate XAException in the prepare method. |
| * |
| * @exception XAException An error has occurred. Possible exception values |
| * are: XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or |
| * XAER_PROTO. |
| * |
| */ |
| public final synchronized int prepare(Xid xid) throws XAException { |
| checkXAActive(); |
| |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| |
| XATransactionState tranState = getTransactionState(xid_im); |
| |
| if (tranState == null) { |
| XAResourceManager rm = ra.getXAResourceManager(); |
| |
| ContextManager inDoubtCM = rm.find(xid); |
| |
| // RM also does not know about this xid. |
| if (inDoubtCM == null) |
| throw new XAException(XAException.XAER_NOTA); |
| |
| // cannot prepare in doubt transactions |
| throw new XAException(XAException.XAER_PROTO); |
| |
| } |
| |
| synchronized (tranState) { |
| |
| checkUserCredentials(tranState.creatingResource); |
| |
| // Check the transaction is no associated with |
| // any XAResource. |
| switch (tranState.associationState) { |
| case XATransactionState.T0_NOT_ASSOCIATED: |
| break; |
| |
| case XATransactionState.TRO_FAIL: |
| throw new XAException(tranState.rollbackOnlyCode); |
| |
| default: |
| throw new XAException(XAException.XAER_PROTO); |
| } |
| |
| if (tranState.suspendedList != null |
| && tranState.suspendedList.size() != 0) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| if (tranState.isPrepared) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| try { |
| |
| int ret = tranState.xa_prepare(); |
| |
| if (ret == XATransactionController.XA_OK) { |
| tranState.isPrepared = true; |
| |
| return XAResource.XA_OK; |
| } else { |
| |
| returnConnectionToResource(tranState, xid_im); |
| |
| if (SanityManager.DEBUG) { |
| if (con.realConnection != null) { |
| SanityManager.ASSERT( |
| con.realConnection.transactionIsIdle(), |
| "real connection should have been idle." + |
| "tranState = " + tranState + |
| "ret = " + ret + |
| "con.realConnection = " + con.realConnection); |
| } |
| } |
| return XAResource.XA_RDONLY; |
| } |
| } catch (SQLException sqle) { |
| XAException xe = wrapInXAException(sqle); |
| |
| if (ExceptionUtil. |
| isDeferredConstraintViolation(sqle.getSQLState())) { |
| // We are rolling back |
| returnConnectionToResource(tranState, xid_im); |
| } |
| |
| throw xe; |
| } |
| } |
| |
| } |
| |
| /** |
| * Obtain the current transaction timeout value set for this XAResource |
| * instance. If XAResource.setTransactionTimeout was not use prior to |
| * invoking this method, the return value is 0; otherwise, the value |
| * used in the previous setTransactionTimeout call is returned. |
| * |
| * @return the transaction timeout value in seconds. If the returned value |
| * is equal to Integer.MAX_VALUE it means no timeout. |
| */ |
| public synchronized int getTransactionTimeout() { |
| return timeoutSeconds; |
| } |
| |
| /** |
| * This method is called to determine if the resource manager instance |
| * represented by the target object is the same as the resouce manager |
| * instance represented by the parameter xares. |
| * |
| * @param xares An XAResource object whose resource manager instance is to |
| * be compared with the resource manager instance of the target object. |
| * |
| * @return true if it's the same RM instance; otherwise false. |
| * @exception XAException An error has occurred. Possible exception values |
| * are XAER_RMERR, XAER_RMFAIL. |
| */ |
| public final synchronized boolean isSameRM(XAResource xares) |
| throws XAException { |
| checkXAActive(); |
| if (xares instanceof EmbedXAResource) { |
| return ra == ((EmbedXAResource) xares).ra; |
| } |
| return false; |
| } |
| |
| /** |
| * Obtain a list of prepared transaction branches from a resource |
| * manager. The transaction manager calls this method during recovery to |
| * obtain the list of transaction branches that are currently in prepared |
| * or heuristically completed states. |
| * |
| * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must |
| * be used when no other flags are set in flags. |
| * |
| * @return The resource manager returns zero or more XIDs for the |
| * transaction branches that are currently in a prepared or heuristically |
| * completed state. If an error occurs during the operation, the resource |
| * manager should throw the appropriate XAException. |
| * |
| * @exception XAException An error has occurred. Possible values are |
| * XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO. |
| * |
| */ |
| public final synchronized Xid[] recover(int flag) throws XAException { |
| checkXAActive(); |
| |
| try { |
| return ra.getXAResourceManager().recover(flag); |
| } catch (StandardException se) { |
| throw wrapInXAException(se); |
| } |
| } |
| |
| /** |
| * Tell the resource manager to forget about a heuristically completed |
| * transaction branch. |
| * |
| * @param xid A global transaction identifier |
| * @exception XAException An error has occurred. Possible exception values |
| * are XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO. |
| */ |
| public final synchronized void forget(Xid xid) throws XAException { |
| |
| checkXAActive(); |
| |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| |
| XATransactionState tranState = getTransactionState(xid_im); |
| if (tranState == null) { |
| XAResourceManager rm = ra.getXAResourceManager(); |
| |
| ContextManager inDoubtCM = rm.find(xid); |
| |
| // RM also does not know about this xid. |
| if (inDoubtCM == null) |
| throw new XAException(XAException.XAER_NOTA); |
| |
| ContextService csf = getContextService(); |
| |
| csf.setCurrentContextManager(inDoubtCM); |
| try { |
| rm.forget(inDoubtCM, xid_im); |
| |
| // close the connection/transaction since it can never be used again. |
| inDoubtCM.cleanupOnError(StandardException.closeException(), |
| false); |
| return; |
| } catch (StandardException se) { |
| // The rm threw an exception, clean it up in the approprate |
| // context. There is no transactionResource to handle the |
| // exception for us. |
| inDoubtCM.cleanupOnError(se, con.isActive()); |
| throw wrapInXAException(se); |
| } finally { |
| csf.resetCurrentContextManager(inDoubtCM); |
| } |
| |
| } |
| |
| // DERBY-1016; if the transaction exists throw XAER_PROTO on forget |
| throw new XAException(XAException.XAER_PROTO); |
| } |
| |
| /** |
| * Inform the resource manager to roll back work done on behalf of a |
| * transaction branch |
| * |
| * @param xid A global transaction identifier |
| * @exception XAException - An error has occurred |
| */ |
| public final synchronized void rollback(Xid xid) throws XAException { |
| checkXAActive(); |
| |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| |
| XATransactionState tranState = getTransactionState(xid_im); |
| |
| if (tranState == null) { |
| XAResourceManager rm = ra.getXAResourceManager(); |
| |
| ContextManager inDoubtCM = rm.find(xid); |
| |
| // RM also does not know about this xid. |
| if (inDoubtCM == null) |
| throw new XAException(XAException.XAER_NOTA); |
| |
| ContextService csf = getContextService(); |
| |
| csf.setCurrentContextManager(inDoubtCM); |
| try { |
| rm.rollback(inDoubtCM, xid_im); |
| |
| // close the connection/transaction since it can never be used again. |
| inDoubtCM.cleanupOnError(StandardException.closeException(), |
| false); |
| return; |
| } catch (StandardException se) { |
| // The rm threw an exception, clean it up in the approprate |
| // context. There is no transactionResource to handle the |
| // exception for us. |
| inDoubtCM.cleanupOnError(se, con.isActive()); |
| throw wrapInXAException(se); |
| } finally { |
| csf.resetCurrentContextManager(inDoubtCM); |
| } |
| |
| } |
| |
| synchronized (tranState) { |
| |
| // Check the transaction is no associated with |
| // any XAResource. |
| switch (tranState.associationState) { |
| case XATransactionState.T0_NOT_ASSOCIATED: |
| case XATransactionState.TRO_FAIL: |
| break; |
| |
| default: |
| throw new XAException(XAException.XAER_PROTO); |
| } |
| |
| if (tranState.suspendedList != null |
| && tranState.suspendedList.size() != 0) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| checkUserCredentials(tranState.creatingResource); |
| |
| try { |
| |
| tranState.xa_rollback(); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } finally { |
| returnConnectionToResource(tranState, xid_im); |
| } |
| } |
| } |
| |
| |
| /** |
| * Set the current transaction timeout value for this XAResource |
| * instance. Once set, this timeout value is effective until |
| * setTransactionTimeout is invoked again with a different value. To reset |
| * the timeout value to the default value used by the resource manager, |
| * set the value to zero. If the timeout operation is performed |
| * successfully, the method returns true; otherwise false. If a resource |
| * manager does not support transaction timeout value to be set |
| * explicitly, this method returns false. |
| * |
| * @param seconds the transaction timeout value in seconds. |
| * Value of 0 means the reasource manager's default value. |
| * Value of Integer.MAX_VALUE means no timeout. |
| * @return true if transaction timeout value is set successfully; |
| * otherwise false. |
| * |
| * @exception XAException - An error has occurred. Possible exception |
| * values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL. |
| */ |
| public synchronized boolean setTransactionTimeout(int seconds) |
| throws XAException { |
| if (seconds < 0) { |
| // throw an exception if invalid value was specified |
| throw new XAException(XAException.XAER_INVAL); |
| } |
| timeoutSeconds = seconds; |
| return true; |
| } |
| |
| /** Returns the default value for the transaction timeout in milliseconds |
| * setted up by the system properties. |
| */ |
| private long getDefaultXATransactionTimeout() throws XAException { |
| try { |
| LanguageConnectionContext lcc = getLanguageConnectionContext( con ); |
| TransactionController tc = lcc.getTransactionExecute(); |
| |
| long timeoutMillis = 1000 * (long) PropertyUtil.getServiceInt( |
| tc, |
| Property.PROP_XA_TRANSACTION_TIMEOUT, |
| 0, |
| Integer.MAX_VALUE, |
| Property.DEFAULT_XA_TRANSACTION_TIMEOUT |
| ); |
| |
| return timeoutMillis; |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } catch (StandardException se) { |
| throw wrapInXAException(se); |
| } |
| } |
| |
| /** |
| * Start work on behalf of a transaction branch specified in xid If TMJOIN |
| * is specified, the start is for joining a transaction previously seen by |
| * the resource manager. If TMRESUME is specified, the start is to resume |
| * a suspended transaction specified in the parameter xid. If neither |
| * TMJOIN nor TMRESUME is specified and the transaction specified by xid |
| * has previously been seen by the resource manager, the resource manager |
| * throws the XAException exception with XAER_DUPID error code. |
| * |
| * @param xid A global transaction identifier to be associated with the |
| * resource |
| * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME |
| * |
| * @exception XAException An error has occurred. Possible exceptions are |
| * XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, |
| * XAER_INVAL, or XAER_PROTO. |
| */ |
| public final synchronized void start(Xid xid, |
| int flags) throws XAException { |
| checkXAActive(); |
| |
| // JDBC 3.0 section 12.3 - One transaction associated with a XAConnection |
| if (currentXid != null) |
| throw new XAException(XAException.XAER_PROTO); |
| |
| // ensure immtable and correct equals method. |
| XAXactId xid_im = new XAXactId(xid); |
| |
| XATransactionState tranState = getTransactionState(xid_im); |
| |
| switch (flags) { |
| case XAResource.TMNOFLAGS: |
| if (tranState != null) |
| throw new XAException(XAException.XAER_DUPID); |
| |
| try { |
| |
| if (con.realConnection == null) { |
| con.openRealConnection(); |
| |
| if (con.currentConnectionHandle != null) { |
| |
| // since this is a new connection, set its complete |
| // state according to the application's Connection |
| // handle view of the world. |
| con.currentConnectionHandle.setState(true); |
| con.realConnection.setApplicationConnection |
| (con.currentConnectionHandle); |
| } |
| |
| } else { |
| |
| // XAResource.start() auto commits in DB2 when in |
| // auto commit mode. |
| if (con.currentConnectionHandle != null) { |
| if (con.currentConnectionHandle.getAutoCommit()) |
| con.currentConnectionHandle.rollback(); |
| } |
| if (!con.realConnection.transactionIsIdle()) |
| throw new XAException(XAException.XAER_OUTSIDE); |
| |
| if (con.currentConnectionHandle != null) { |
| // It is possible that the isolation level state |
| // in connection handle has gotten out of sync |
| // with the real isolation level. This can happen |
| // if SLQ instead of JDBC api has been used to set |
| // the isolation level. The code below will check |
| // if isolation was set using JDBC or SQL and if |
| // yes, then it will update the isolation state |
| // in BrokeredConnection with EmbedConnection's |
| // isolation level. |
| con.currentConnectionHandle.getIsolationUptoDate(); |
| // we have a current handle so we need to keep |
| // the connection state of the current connection. |
| con.currentConnectionHandle.setState(true); |
| |
| // At the local to global transition we need |
| // to discard and close any open held result |
| // sets, a rollback will do this. |
| con.realConnection.rollback(); |
| } else { |
| con.resetRealConnection(); |
| } |
| |
| } |
| |
| // Global connections are always in auto commit false mode. |
| con.realConnection.setAutoCommit(false); |
| |
| // and holdability false (cannot hold cursors across |
| // XA transactions. |
| con.realConnection.setHoldability( |
| ResultSet.CLOSE_CURSORS_AT_COMMIT); |
| |
| getLanguageConnectionContext( con.realConnection ). |
| getTransactionExecute(). |
| createXATransactionFromLocalTransaction( |
| xid_im.getFormatId(), |
| xid_im.getGlobalTransactionId(), |
| xid_im.getBranchQualifier()); |
| |
| |
| } catch (StandardException se) { |
| throw wrapInXAException(se); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } |
| |
| tranState = new XATransactionState |
| ( |
| getContextManager( con.realConnection ), |
| con.realConnection, this, xid_im |
| ); |
| if (!ra.addConnection(xid_im, tranState)) |
| throw new XAException(XAException.XAER_DUPID); |
| |
| currentXid = xid_im; |
| |
| // If the the timeout specified is equal to Integer.MAX_VALUE |
| // it means that transaction timeout is disabled. |
| if (timeoutSeconds != Integer.MAX_VALUE) { |
| // Find out the value of the transaction timeout |
| long timeoutMillis; |
| if (timeoutSeconds > 0) { |
| timeoutMillis = 1000*timeoutSeconds; |
| } else { |
| timeoutMillis = getDefaultXATransactionTimeout(); |
| } |
| // If we have non-zero transaction timeout schedule a timeout task. |
| // The only way how timeoutMillis might be equeal to 0 is that |
| // it was specified as a default transaction timeout |
| if (timeoutMillis > 0) { |
| tranState.scheduleTimeoutTask(timeoutMillis); |
| } |
| } |
| |
| break; |
| |
| case XAResource.TMRESUME: |
| case XAResource.TMJOIN: |
| if (tranState == null) |
| throw new XAException(XAException.XAER_NOTA); |
| |
| tranState.start(this, flags); |
| |
| if (tranState.conn != con.realConnection) { |
| |
| if (con.realConnection != null) { |
| |
| if (!con.realConnection.transactionIsIdle()) |
| throw new XAException(XAException.XAER_OUTSIDE); |
| |
| // We need to get the isolation level up to date same |
| // way as it is done at start of a transaction. Before |
| // joining the transaction, it is possible that the |
| // isolation level was updated using SQL. We need to |
| // get this state and store in the connection handle so |
| // that we can restore the isolation when we are in the |
| // local mode. |
| try { |
| if (con.currentConnectionHandle != null) { |
| con.currentConnectionHandle.getIsolationUptoDate(); |
| } |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } |
| |
| closeUnusedConnection(con.realConnection); |
| } |
| con.realConnection = tranState.conn; |
| |
| if (con.currentConnectionHandle != null) { |
| |
| try { |
| // only reset the non-transaction specific |
| // Connection state. |
| con.currentConnectionHandle.setState(false); |
| con.realConnection.setApplicationConnection( |
| con.currentConnectionHandle); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } |
| } |
| |
| } |
| |
| |
| break; |
| |
| default: |
| throw new XAException(XAException.XAER_INVAL); |
| } |
| |
| currentXid = xid_im; |
| } |
| |
| /** |
| * Resturns currently active xid |
| * @return Xid |
| */ |
| Xid getCurrentXid () { |
| return currentXid; |
| } |
| |
| /** |
| * Returns the XATransactionState of the the transaction |
| * @param xid_im |
| * @return XATransactionState |
| */ |
| private XATransactionState getTransactionState(XAXactId xid_im) { |
| return (XATransactionState) ra.findConnection(xid_im); |
| } |
| |
| /** |
| * Compares the user name and password of the XAResource with |
| * user name and password of this and throws XAException if there is |
| * a mismatch |
| * @param original EmbedXAResource |
| */ |
| private void checkUserCredentials(EmbedXAResource original) |
| throws XAException { |
| if (original == this) |
| return; |
| if (original.con.getPassword().equals(con.getPassword()) && |
| (original.con.getUsername().equals(con.getUsername()))) |
| return; |
| throw new XAException(XAException.XA_RBINTEGRITY); |
| } |
| |
| /** |
| * Checks if currently associated connection is active |
| * throws exception if not |
| */ |
| private void checkXAActive() throws XAException { |
| try { |
| con.checkActive(); |
| } catch (SQLException sqle) { |
| throw wrapInXAException(sqle); |
| } |
| } |
| |
| /** |
| * Map a SQL exception to appropriate XAException. |
| * Return the mapped XAException. |
| */ |
| private static XAException wrapInXAException(SQLException se) { |
| // Map interesting exceptions to XAException |
| String sqlstate = se.getSQLState(); |
| String message = se.getMessage(); |
| int seErrorCode = se.getErrorCode(); |
| int xaErrorCode; |
| |
| XAException xae; |
| |
| // Determine the XAException.errorCode. This is known for |
| // some specific exceptions. For other exceptions, we will |
| // return XAER_RMFAIL for SESSION_SEVERITY or greater and |
| // XAER_RMERR for less severe errors. DERBY-4141. |
| if (sqlstate.equals(StandardException.getSQLStateFromIdentifier( |
| SQLState.STORE_XA_XAER_DUPID))) |
| xaErrorCode = XAException.XAER_DUPID; |
| else if (sqlstate.equals(StandardException.getSQLStateFromIdentifier( |
| SQLState.STORE_XA_PROTOCOL_VIOLATION))) |
| xaErrorCode = XAException.XA_RBPROTO; |
| else if (sqlstate.equals(SQLState.DEADLOCK)) |
| xaErrorCode = XAException.XA_RBDEADLOCK; |
| else if (sqlstate.equals(SQLState.LOCK_TIMEOUT)) |
| xaErrorCode = XAException.XA_RBTIMEOUT; |
| else if (seErrorCode >= ExceptionSeverity.SESSION_SEVERITY) |
| xaErrorCode = XAException.XAER_RMFAIL; |
| else if (ExceptionUtil.isDeferredConstraintViolation(sqlstate)) { |
| xaErrorCode = XAException.XA_RBINTEGRITY; |
| } else { |
| xaErrorCode = XAException.XAER_RMERR; |
| } |
| |
| xae = new XAException(message); |
| xae.errorCode = xaErrorCode; |
| xae.initCause(se); |
| return xae; |
| } |
| |
| /** |
| * Map a Standard exception to appropriate XAException. |
| * Return the mapped XAException. |
| */ |
| private static XAException wrapInXAException(StandardException se) { |
| return wrapInXAException( |
| TransactionResourceImpl.wrapInSQLException(se)); |
| } |
| |
| /** |
| * Return an underlying connection object back to its XAResource |
| * if possible. If not close the connection. |
| * @param tranState |
| * @param xid_im |
| */ |
| void returnConnectionToResource(XATransactionState tranState, |
| XAXactId xid_im) { |
| |
| removeXATransaction(xid_im); |
| synchronized (tranState) { |
| // global transaction is over. |
| tranState.associationState = XATransactionState.TC_COMPLETED; |
| tranState.notifyAll(); |
| |
| EmbedConnection conn = tranState.conn; |
| |
| // already set in its own resource |
| // or can it be returned to its original resource? |
| if ((tranState.creatingResource.con.realConnection == conn) || |
| (tranState.creatingResource.con.realConnection == null)) { |
| |
| tranState.creatingResource.con.realConnection = conn; |
| |
| BrokeredConnection handle = |
| tranState.creatingResource.con.currentConnectionHandle; |
| |
| conn.setApplicationConnection(handle); |
| |
| if (handle != null) { |
| try { |
| handle.setState(true); |
| } catch (SQLException sqle) { |
| |
| // couldn't reset the connection |
| closeUnusedConnection(tranState.conn); |
| tranState.creatingResource.con.realConnection = null; |
| } |
| } |
| return; |
| } |
| } |
| |
| // nowhere to place it, close it. |
| closeUnusedConnection(tranState.conn); |
| } |
| |
| |
| /** |
| * Close an underlying connection object when there is |
| * no active XAResource to hand it to. |
| * @param conn |
| */ |
| private static void closeUnusedConnection(EmbedConnection conn) { |
| if (conn != null) { |
| try { |
| conn.close(); |
| } catch (SQLException sqle) { |
| |
| } |
| } |
| } |
| |
| /** |
| * Removes the xid from currently active transactions |
| * @param xid_im |
| */ |
| void removeXATransaction(XAXactId xid_im) { |
| XATransactionState tranState = |
| (XATransactionState) ra.removeConnection(xid_im); |
| if (tranState != null) |
| tranState.popMe(); |
| } |
| |
| void setCurrentXid(XAXactId aCurrentXid) { |
| currentXid = aCurrentXid; |
| } |
| |
| |
| /** |
| * Privileged lookup of the ContextService. Must be private so that user code |
| * can't call this entry point. |
| */ |
| private static ContextService getContextService() |
| { |
| return AccessController.doPrivileged |
| ( |
| new PrivilegedAction<ContextService>() |
| { |
| public ContextService run() |
| { |
| return ContextService.getFactory(); |
| } |
| } |
| ); |
| } |
| |
| /** |
| * Privileged lookup of the ContextManager. Must be private so that user code |
| * can't call this entry point. |
| */ |
| private static ContextManager getContextManager( final EmbedConnection conn ) |
| { |
| return AccessController.doPrivileged |
| ( |
| new PrivilegedAction<ContextManager>() |
| { |
| public ContextManager run() |
| { |
| return conn.getContextManager(); |
| } |
| } |
| ); |
| } |
| |
| /** |
| * Gets the LanguageConnectionContext for this connection. |
| */ |
| private LanguageConnectionContext getLanguageConnectionContext( final EmbedConnection conn ) |
| { |
| return AccessController.doPrivileged |
| ( |
| new PrivilegedAction<LanguageConnectionContext>() |
| { |
| public LanguageConnectionContext run() |
| { |
| return conn.getLanguageConnection(); |
| } |
| } |
| ); |
| } |
| |
| /** |
| * Privileged LCC lookup. Must be private so that user code |
| * can't call this entry point. |
| */ |
| private LanguageConnectionContext getLanguageConnectionContext( final EmbedPooledConnection conn ) |
| throws SQLException |
| { |
| if ( lcc == null ) |
| { |
| try { |
| lcc = AccessController.doPrivileged |
| ( |
| new PrivilegedExceptionAction<LanguageConnectionContext>() |
| { |
| public LanguageConnectionContext run() |
| throws SQLException |
| { |
| return conn.getLanguageConnection(); |
| } |
| } |
| ); |
| } catch (PrivilegedActionException pae) |
| { |
| throw Util.javaException( pae ); |
| } |
| } |
| |
| return lcc; |
| } |
| |
| } |