You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2007/06/15 15:18:58 UTC
svn commit: r547674 - in /db/derby/code/trunk/java:
client/org/apache/derby/client/net/ drda/org/apache/derby/impl/drda/
engine/org/apache/derby/iapi/reference/
engine/org/apache/derby/iapi/services/monitor/
engine/org/apache/derby/impl/jdbc/ engine/or...
Author: kahatlen
Date: Fri Jun 15 06:18:56 2007
New Revision: 547674
URL: http://svn.apache.org/viewvc?view=rev&rev=547674
Log:
DERBY-2432: Implement transaction timeout for XA transactions
Contributed by Julius Stroffek.
Modified:
db/derby/code/trunk/java/client/org/apache/derby/client/net/CodePoint.java
db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnectionRequest.java
db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXACallInfo.java
db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAConnectionRequest.java
db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAResource.java
db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAXAProtocol.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/Monitor.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java
db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedXAResource.java
db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/CodePoint.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/CodePoint.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/net/CodePoint.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/net/CodePoint.java Fri Jun 15 06:18:56 2007
@@ -673,6 +673,9 @@
// XA Return Value
public static final int XARETVAL = 0x1904;
+ // XA Timeout Value;
+ public static final int TIMEOUT = 0x1907;
+
// new unit of work for XA
public static final int SYNCTYPE_NEW_UOW = 0x09;
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnectionRequest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnectionRequest.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnectionRequest.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnectionRequest.java Fri Jun 15 06:18:56 2007
@@ -161,8 +161,10 @@
void writeXID(int codepoint, Xid xid) throws SqlException {
}
-
void writeXAFlags(int codepoint, int xaFlags) {
+ }
+
+ void writeXATimeout(int codepoint, long xaTimeout) {
}
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXACallInfo.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXACallInfo.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXACallInfo.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXACallInfo.java Fri Jun 15 06:18:56 2007
@@ -45,6 +45,9 @@
public class NetXACallInfo {
Xid xid_; // current xid
int xaFlags_; // current xaFlags
+ /** XA transaction timeout in milliseconds. The value less than 0 means
+ * that the time out is not specified. The value 0 means infinite timeout. */
+ long xaTimeoutMillis_;
// may not be needed!!!~~~
int xaFunction_; // queued XA function being performed
int xaRetVal_; // xaretval from server
@@ -74,6 +77,7 @@
public NetXACallInfo() {
xid_ = null;
xaFlags_ = XAResource.TMNOFLAGS;
+ xaTimeoutMillis_ = -1;
xaInProgress_ = false;
currConnection_ = false;
freeEntry_ = true;
@@ -88,6 +92,7 @@
public NetXACallInfo(Xid xid, int flags, NetXAResource xares, NetXAConnection actualConn) {
xid_ = xid;
xaFlags_ = flags;
+ xaTimeoutMillis_ = -1;
xaInProgress_ = false;
currConnection_ = false;
freeEntry_ = true;
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAConnectionRequest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAConnectionRequest.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAConnectionRequest.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAConnectionRequest.java Fri Jun 15 06:18:56 2007
@@ -53,6 +53,7 @@
NetXACallInfo callInfo = conn.xares_.callInfoArray_[conn.currXACallInfoOffset_];
Xid xid = callInfo.xid_;
int xaFlags = callInfo.xaFlags_;
+ long xaTimeout = callInfo.xaTimeoutMillis_;
// create DSS command with reply.
createCommand();
@@ -72,6 +73,11 @@
}
writeXAFlags(CodePoint.XAFLAGS, xaFlags);
+ // Check whether the timeout value was specified.
+ // Value less than 0 means no timeout is specified.
+ if (xaTimeout >= 0) {
+ writeXATimeout(CodePoint.TIMEOUT, xaTimeout);
+ }
updateLengthBytes();
}
@@ -254,6 +260,11 @@
void writeXAFlags(int codepoint, int xaFlags) {
writeScalar4Bytes(codepoint, xaFlags);
+ }
+
+
+ void writeXATimeout(int codepoint, long xaTimeout) {
+ writeScalar8Bytes(codepoint, xaTimeout);
}
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAResource.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAResource.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAResource.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetXAResource.java Fri Jun 15 06:18:56 2007
@@ -113,6 +113,9 @@
private List specialRegisters_ = Collections.synchronizedList(new LinkedList());
+ /** The value of the transaction timeout in seconds. */
+ private int timeoutSeconds = 0;
+
public NetXAResource(XAConnection xaconn, int rmId,
String userId, String password,
org.apache.derby.client.net.NetXAConnection conn) {
@@ -312,14 +315,13 @@
}
/**
- * Obtain the current transaction timeout value set for this XAResource instance. If
- * <CODE>XAResource.setTransactionTimeout</CODE> was not use prior to invoking this method, the return value is the
- * default timeout set for the resource manager; otherwise, the value used in the previous
- * <CODE>setTransactionTimeout</CODE> call is returned.
- *
- * @return the transaction timeout value in seconds.
+ * 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.
*
- * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL.
+ * @return the transaction timeout value in seconds. If the returned value
+ * is equal to Integer.MAX_VALUE it means no timeout.
*/
public int getTransactionTimeout() throws XAException {
if (conn_.agent_.loggingEnabled()) {
@@ -331,9 +333,9 @@
}
if (conn_.agent_.loggingEnabled()) {
- conn_.agent_.logWriter_.traceExit(this, "getTransactionTimeout", 0);
+ conn_.agent_.logWriter_.traceExit(this, "getTransactionTimeout", timeoutSeconds);
}
- return 0; // we don't support transaction timeout
+ return timeoutSeconds;
}
/**
@@ -521,23 +523,38 @@
}
/**
- * <P>Set the current transaction timeout value for this <CODE>XAResource</CODE> instance. This value overwrites the
- * default transaction timeout value in the resource manager. The newly assigned timeout value is effective for the
- * life of this <CODE>XAResource</CODE> instance unless a new value is set.<P>
+ * 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.
*
- * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
+ * @exception XAException - An error has occurred. Possible exception
+ * values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
*/
public boolean setTransactionTimeout(int seconds) throws XAException {
if (conn_.agent_.loggingEnabled()) {
- conn_.agent_.logWriter_.traceExit(this, "setTransactionTimeout", false);
+ conn_.agent_.logWriter_.traceEntry(this, "setTransactionTimeout");
+ }
+ if (seconds < 0) {
+ // throw an exception if invalid value was specified
+ throw new XAException(XAException.XAER_INVAL);
}
exceptionsOnXA = null;
- return false; // we don't support transaction timeout in our layer.
- /* int rc = xaSetTransTimeOut(seconds);
- if (rc != XAResource.XA_OK)
- throwXAException(rc); */
+ timeoutSeconds = seconds;
+ if (conn_.agent_.loggingEnabled()) {
+ conn_.agent_.logWriter_.traceExit(this, "setTransactionTimeout", true);
+ }
+ return true;
}
/**
@@ -579,6 +596,26 @@
callInfo.xid_ = xid;
callInfo.xaResource_ = this;
callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
+
+ // check and setup the transaction timeout settings
+ if (flags == TMNOFLAGS) {
+ if (timeoutSeconds == Integer.MAX_VALUE) {
+ // Disable the transaction timeout.
+ callInfo.xaTimeoutMillis_ = 0;
+ } else if (timeoutSeconds > 0) {
+ // Use the timeout value specified.
+ callInfo.xaTimeoutMillis_ = 1000*timeoutSeconds;
+ } else if (timeoutSeconds == 0) {
+ // The -1 value means that the timeout codepoint
+ // will not be sent in the request and thus the server
+ // will use the default value.
+ callInfo.xaTimeoutMillis_ = -1;
+ } else {
+ // This should not ever happen due that setTransactionTimeout
+ // does not allow a negative value
+ throwXAException(XAException.XAER_RMERR);
+ }
+ }
try {
netAgent.beginWriteChainOutsideUOW();
netAgent.netConnectionRequest_.writeXaStartUnitOfWork(conn_);
Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAXAProtocol.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAXAProtocol.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAXAProtocol.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAXAProtocol.java Fri Jun 15 06:18:56 2007
@@ -67,6 +67,9 @@
int xaflags = 0;
boolean readXAFlags = false;
Xid xid = null;
+ // The value -1 means no value of timeout received
+ long xaTimeout = -1;
+ boolean readXATimeout = false;
codePoint = reader.getCodePoint();
while (codePoint != -1)
@@ -81,8 +84,8 @@
readXAFlags =true;
break;
case CodePoint.TIMEOUT:
- // optional/ignorable.
- reader.skipBytes();
+ xaTimeout = parseXATimeout();
+ readXATimeout = true;
break;
case CodePoint.RLSCONV:
connThread.codePointNotSupported(codePoint);
@@ -117,7 +120,7 @@
case CodePoint.SYNCTYPE_NEW_UOW:
// new unit of work for XA
// formatId -1 is just a local connection
- startXATransaction(xid,xaflags);
+ startXATransaction(xid, xaflags, xaTimeout);
break;
case CodePoint.SYNCTYPE_END_UOW:
// End unit of work
@@ -226,23 +229,63 @@
return reader.readNetworkInt();
}
+ /**
+ * Parses a XA transaction timout value.
+ *
+ * @return A timeout value.
+ * @throws DRDAProtocolException
+ */
+ private long parseXATimeout() throws DRDAProtocolException
+ {
+ return reader.readNetworkLong();
+ }
+
/**
* Start the xa transaction. Send SYNCRRD response
*
* @param xid - XID (formatId = -1 for local transaction)
* @param xaflags - xaflags
+ * @param xaTimeout - The timeout for the global transaction in millis
+ * (or -1 if not specified)
* @throws DRDAProtocolException
*/
- private void startXATransaction(Xid xid, int xaflags) throws DRDAProtocolException
+ private void startXATransaction(Xid xid, int xaflags, long xaTimeout)
+ throws DRDAProtocolException
{
XAResource xaResource = getXAResource();
int xaRetVal = xaResource.XA_OK;
try {
- if (xid.getFormatId() != -1)
- xaResource.start(xid,xaflags);
- this.xid = xid;
+ if (xid.getFormatId() == -1 && xaTimeout != -1) {
+ // The value of timeout might be specified only for global transactions
+ throw new XAException(XAException.XAER_PROTO);
+ } else if (xaTimeout != -1 && xaflags != XAResource.TMNOFLAGS) {
+ // According the DRDA spec if the value of timeout was specified
+ // a TMNOFLAGS have to be used
+ throw new XAException(XAException.XAER_PROTO);
+ } else {
+ if (xaTimeout == 0) {
+ // According the DRDA specification
+ // value 0 means the unlimited timeout
+ // Integer.MAX_VALUE is used in derby
+ // to set up the infinite timeout.
+ // In JDBC spec the value 0 means the resource
+ // manager's default value.
+ xaResource.setTransactionTimeout(Integer.MAX_VALUE);
+ } else if (xaTimeout == -1) {
+ // The timeout value was not specified, so use the default
+ // timeout - see javadoc for XAResource.setTransactionTimeout
+ xaResource.setTransactionTimeout(0);
+ } else {
+ // The value of timeout was specified
+ xaResource.setTransactionTimeout((int) (xaTimeout/1000));
+ }
+ if (xid.getFormatId() != -1) {
+ xaResource.start(xid,xaflags);
+ }
+ this.xid = xid;
+ }
} catch (XAException xe)
{
xaRetVal = processXAException(xe);
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java Fri Jun 15 06:18:56 2007
@@ -625,6 +625,21 @@
String STATEMENT_CACHE_SIZE = "derby.language.statementCacheSize";
int STATEMENT_CACHE_SIZE_DEFAULT = 100;
+ /*
+ ** Transactions
+ */
+
+ /** The property name used to get the default value for XA transaction
+ * timeout in seconds. Zero means no timout.
+ */
+ String PROP_XA_TRANSACTION_TIMEOUT = "derby.jdbc.xaTransactionTimeout";
+
+ /** The default value for XA transaction timeout if the corresponding
+ * property is not found in system properties. Zero means no timeout.
+ */
+ int DEFAULT_XA_TRANSACTION_TIMEOUT = 0;
+
+
/* some static fields */
public static final String DEFAULT_USER_NAME = "APP";
public static final String DATABASE_MODULE = "org.apache.derby.database.Database";
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/Monitor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/Monitor.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/Monitor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/Monitor.java Fri Jun 15 06:18:56 2007
@@ -788,4 +788,11 @@
// database must already exist
return isDesiredType(p, type);
}
+
+ /**
+ * Logs the stack trace of the specified throwable object.
+ */
+ public static void logThrowable(Throwable t) {
+ t.printStackTrace(getStream().getPrintWriter());
+ }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Fri Jun 15 06:18:56 2007
@@ -2052,6 +2052,11 @@
** XA support
*/
+ /**
+ * Do not use this method directly use XATransactionState.xa_prepare
+ * instead because it also maintains/cancels the timout task which is
+ * scheduled to cancel/rollback the global transaction.
+ */
public final int xa_prepare() throws SQLException {
synchronized (getConnectionSynchronization())
@@ -2090,6 +2095,11 @@
}
+ /**
+ * Do not use this method directly use XATransactionState.xa_commit
+ * instead because it also maintains/cancels the timout task which is
+ * scheduled to cancel/rollback the global transaction.
+ */
public final void xa_commit(boolean onePhase) throws SQLException {
synchronized (getConnectionSynchronization())
@@ -2108,8 +2118,14 @@
}
}
}
- public final void xa_rollback() throws SQLException {
+
+ /**
+ * Do not use this method directly use XATransactionState.xa_rollback
+ * instead because it also maintains/cancels the timout task which is
+ * scheduled to cancel/rollback the global transaction.
+ */
+ public final void xa_rollback() throws SQLException {
synchronized (getConnectionSynchronization())
{
setupContextStack();
@@ -2126,6 +2142,8 @@
}
}
}
+
+
/**
* returns false if there is an underlying transaction and that transaction
* has done work. True if there is no underlying transaction or that
@@ -2374,4 +2392,9 @@
}
return rootConnection.lobHashMap;
}
+
+ /** Cancels the current running statement. */
+ public void cancelRunningStatement() {
+ getLanguageConnection().getStatementContext().cancel();
+ }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java Fri Jun 15 06:18:56 2007
@@ -25,6 +25,7 @@
import org.apache.derby.iapi.reference.Property;
import org.apache.derby.iapi.error.ExceptionSeverity;
import org.apache.derby.iapi.reference.JDBC30Translation;
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
/* import impl class */
import org.apache.derby.impl.jdbc.Util;
@@ -298,6 +299,17 @@
checkActive();
return realConnection;
+ }
+
+ /**
+ * @return The underlying language connection.
+ */
+ public synchronized LanguageConnectionContext getLanguageConnection()
+ throws SQLException
+ {
+ checkActive();
+
+ return realConnection.getLanguageConnection();
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedXAResource.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedXAResource.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedXAResource.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/jdbc/EmbedXAResource.java Fri Jun 15 06:18:56 2007
@@ -22,6 +22,8 @@
package org.apache.derby.jdbc;
import java.sql.SQLException;
+
+
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import javax.transaction.xa.XAException;
@@ -34,12 +36,16 @@
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.context.ContextService;
import org.apache.derby.iapi.services.info.JVMInfo;
+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.shared.common.sanity.SanityManager;
+import org.apache.derby.iapi.services.property.PropertyUtil;
+import org.apache.derby.iapi.reference.Property;
/**
* Implements XAResource
@@ -49,10 +55,14 @@
private EmbedPooledConnection con;
private ResourceAdapter ra;
private XAXactId currentXid;
+ /** The value of the transaction timeout on this resource. */
+ private int timeoutSeconds;
EmbedXAResource (EmbedPooledConnection con, ResourceAdapter ra) {
this.con = con;
this.ra = ra;
+ // Setup the default value for the transaction timeout.
+ this.timeoutSeconds = 0;
}
/**
@@ -60,7 +70,7 @@
* @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.
@@ -126,10 +136,8 @@
if (tranState.isPrepared == onePhase)
throw new XAException(XAException.XAER_PROTO);
- EmbedConnection conn = tranState.conn;
-
try {
- conn.xa_commit(onePhase);
+ tranState.xa_commit(onePhase);
} catch (SQLException sqle) {
throw wrapInXAException(sqle);
} finally {
@@ -270,11 +278,9 @@
if (tranState.isPrepared)
throw new XAException(XAException.XAER_PROTO);
- EmbedConnection conn = tranState.conn;
-
try {
- int ret = conn.xa_prepare();
+ int ret = tranState.xa_prepare();
if (ret == XATransactionController.XA_OK) {
tranState.isPrepared = true;
@@ -283,8 +289,7 @@
} else {
returnConnectionToResource(tranState, xid_im);
- if (SanityManager.DEBUG)
- {
+ if (SanityManager.DEBUG) {
if (con.realConnection != null)
SanityManager.ASSERT(con.realConnection.transactionIsIdle(),
"real connection should have been idle at this point");
@@ -301,14 +306,14 @@
/**
* 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 the default timeout set for
- * the resource manager; otherwise, the value used in the previous
- * setTransactionTimeout call is returned.
+ * 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.
+ * @return the transaction timeout value in seconds. If the returned value
+ * is equal to Integer.MAX_VALUE it means no timeout.
*/
- public int getTransactionTimeout() {
- return 0;
+ public synchronized int getTransactionTimeout() {
+ return timeoutSeconds;
}
/**
@@ -477,7 +482,7 @@
try {
- tranState.conn.xa_rollback();
+ tranState.xa_rollback();
} catch (SQLException sqle) {
throw wrapInXAException(sqle);
} finally {
@@ -498,14 +503,49 @@
* 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 boolean setTransactionTimeout(int seconds) {
- return false;
+ 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.
+ *
+ * @see Property.PROP_XA_TRANSACTION_TIMEOUT
+ * @see Property.DEFAULT_XA_TRANSACTION_TIMEOUT
+ */
+ private long getDefaultXATransactionTimeout() throws XAException {
+ try {
+ LanguageConnectionContext lcc = con.getLanguageConnection();
+ 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);
+ }
}
/**
@@ -616,13 +656,32 @@
throw wrapInXAException(sqle);
}
-
- if (!ra.addConnection(xid_im,
- new XATransactionState(
- con.realConnection.getContextManager(),
- con.realConnection, this, xid_im)))
+ tranState = new XATransactionState(
+ con.realConnection.getContextManager(),
+ 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:
@@ -780,7 +839,7 @@
* @param tranState
* @param xid_im
*/
- private void returnConnectionToResource(XATransactionState tranState,
+ void returnConnectionToResource(XATransactionState tranState,
XAXactId xid_im) {
removeXATransaction(xid_im);
@@ -841,11 +900,15 @@
* Removes the xid from currently active transactions
* @param xid_im
*/
- private void removeXATransaction(XAXactId 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;
+ }
+
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/jdbc/XATransactionState.java Fri Jun 15 06:18:56 2007
@@ -22,6 +22,11 @@
package org.apache.derby.jdbc;
+import java.sql.SQLException;
+import java.util.Timer;
+import java.util.TimerTask;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.services.timer.TimerFactory;
import org.apache.derby.impl.jdbc.EmbedConnection;
import javax.transaction.xa.XAResource;
import org.apache.derby.iapi.services.context.ContextImpl;
@@ -37,6 +42,8 @@
*/
final class XATransactionState extends ContextImpl {
+ /** Rollback-only due to timeout */
+ final static int TRO_TIMEOUT = -3;
/** Rollback-only due to deadlock */
final static int TRO_DEADLOCK = -2;
/** Rollback-only due to end(TMFAIL) */
@@ -74,6 +81,34 @@
*/
boolean isPrepared;
+ /** Has this transaction been finished (committed
+ * or rolled back)? */
+ boolean isFinished;
+
+ /** A timer task scheduled for the time when the transaction will timeout. */
+ CancelXATransactionTask timeoutTask = null;
+
+
+ /** The implementation of TimerTask to cancel a global transaction. */
+ private class CancelXATransactionTask extends TimerTask {
+
+ /** Creates the cancelation object to be passed to a timer. */
+ public CancelXATransactionTask() {
+ XATransactionState.this.timeoutTask = this;
+ }
+
+ /** Runs the cancel task of the global transaction */
+ public void run() {
+ try {
+ XATransactionState.this.cancel();
+ } catch (XAException ex) {
+ Monitor.logThrowable(ex);
+ }
+ }
+ }
+
+
+
XATransactionState(ContextManager cm, EmbedConnection conn,
EmbedXAResource resource, XAXactId xid) {
@@ -83,6 +118,7 @@
this.creatingResource = resource;
this.associationState = XATransactionState.T1_ASSOCIATED;
this.xid = xid;
+ this.isFinished = false;
}
@@ -147,6 +183,8 @@
case XATransactionState.T0_NOT_ASSOCIATED:
break;
+ case XATransactionState.TRO_DEADLOCK:
+ case XATransactionState.TRO_TIMEOUT:
case XATransactionState.TRO_FAIL:
throw new XAException(rollbackOnlyCode);
@@ -163,6 +201,7 @@
associationState = XATransactionState.T1_ASSOCIATED;
associatedResource = resource;
+
}
}
@@ -262,4 +301,93 @@
}
}
+ /**
+ * Schedule a timeout task wich will rollback the global transaction
+ * after the specified time will elapse.
+ *
+ * @param timeoutMillis The number of milliseconds to be elapsed before
+ * the transaction will be rolled back.
+ */
+ synchronized void scheduleTimeoutTask(long timeoutMillis) {
+ // schedule a time out task if the timeout was specified
+ if (timeoutMillis > 0) {
+ // take care of the transaction timeout
+ TimerTask cancelTask = new CancelXATransactionTask();
+ TimerFactory timerFactory = Monitor.getMonitor().getTimerFactory();
+ Timer timer = timerFactory.getCancellationTimer();
+ timer.schedule(cancelTask, timeoutMillis);
+ } else {
+ timeoutTask = null;
+ }
+ }
+
+ /**
+ * Rollback the global transaction and cancel the timeout task.
+ */
+ synchronized void xa_rollback() throws SQLException {
+ conn.xa_rollback();
+ xa_finalize();
+ }
+
+ /**
+ * Commit the global transaction and cancel the timeout task.
+ * @param onPhase Indicates whether to use one phase commit protocol.
+ * Otherwise two phase commit protocol will be used.
+ */
+ synchronized void xa_commit(boolean onePhase) throws SQLException {
+ conn.xa_commit(onePhase);
+ xa_finalize();
+ }
+
+ /**
+ * Prepare the global transaction for commit.
+ */
+ synchronized int xa_prepare() throws SQLException {
+ int retVal = conn.xa_prepare();
+ return retVal;
+ }
+
+ /** This method cancels timeoutTask and marks the transaction
+ * as finished by assigning 'isFinished = true'.
+ */
+ synchronized void xa_finalize() {
+ if (timeoutTask != null) {
+ timeoutTask.cancel();
+ }
+ isFinished = true;
+ }
+
+ /**
+ * This function is called from the timer task when the transaction
+ * times out.
+ *
+ * @see CancelXATransactionTask
+ */
+ private synchronized void cancel() throws XAException {
+ // Check isFinished just to be sure that
+ // the cancellation task was not started
+ // just before the xa_commit/rollback
+ // obtained this object's monitor.
+ if (!isFinished) {
+ // Check whether the transaction is associated
+ // with any EmbedXAResource instance.
+ if (associationState == XATransactionState.T1_ASSOCIATED) {
+ conn.cancelRunningStatement();
+ EmbedXAResource assocRes = associatedResource;
+ end(assocRes, XAResource.TMFAIL, true);
+ }
+
+ // Rollback the global transaction
+ try {
+ conn.xa_rollback();
+ } catch (SQLException sqle) {
+ XAException ex = new XAException(XAException.XAER_RMERR);
+ ex.initCause(sqle);
+ throw ex;
+ }
+
+ // Do the cleanup on the resource
+ creatingResource.returnConnectionToResource(this, xid);
+ }
+ }
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java?view=diff&rev=547674&r1=547673&r2=547674
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATransactionTest.java Fri Jun 15 06:18:56 2007
@@ -23,9 +23,11 @@
import java.sql.Connection;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
+import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
@@ -34,6 +36,9 @@
import junit.framework.TestSuite;
import org.apache.derby.client.ClientXid;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.shared.common.reference.SQLState;
+
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.J2EEDataSource;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
@@ -132,7 +137,157 @@
xaConn.close();
}
+
+ /** Tests the functionality of the XA transaction timeout.
+ * <p>
+ * It executes 1000 global transactions during the test. Everyone
+ * of them just inserts a row into XATT table. The rows inserted
+ * by the transactions are different. Some of these transactions
+ * are committed and some of them are left in different stages.
+ * The stage of the transaction in which it is left is chosed
+ * depending on division remainders.
+ * </p>
+ * <p>
+ * After finishing these 1000 transactions a select statement is executed
+ * on that table. However, if there are still some unfinished transactions
+ * that were not aborted they will hold a lock on a XATT table until they
+ * will get rolled back by the transaction timeout. The number of rows
+ * in the XATT table is calculated. It is then compared with the excepted
+ * number of rows (the transaction we know we have committed).
+ * </p>
+ * <p>
+ * The call to xaRes.setTransactionTimeout(5) before the call
+ * to xaRes.start() makes the transactions to be rolled back
+ * due to timeout.
+ * </p>
+ */
+ public void testXATransactionTimeout() throws Exception {
+
+ /* The number of statements to execute in timeout related test. */
+ int timeoutStatementsToExecute = 1000;
+
+ /* Specifies the number of total executed statements per one
+ commited statement in timout related test. */
+ int timeoutCommitEveryStatement = 3;
+
+ /* Specifies the number of statements that should be commited
+ during a timout related test. */
+ int timeoutStatementsCommitted
+ = (timeoutStatementsToExecute + timeoutCommitEveryStatement - 1)
+ / timeoutCommitEveryStatement;
+
+ Statement stm = getConnection().createStatement();
+ stm.execute("create table XATT (i int, text char(10))");
+
+ XADataSource xaDataSource = J2EEDataSource.getXADataSource();
+ XAConnection xaConn = null;
+ XAResource xaRes = null;
+ Connection conn = null;
+
+ for (int i=0; i < timeoutStatementsToExecute; i++) {
+ xaConn = xaDataSource.getXAConnection();
+ xaRes = xaConn.getXAResource();
+ conn = xaConn.getConnection();
+
+ Xid xid = createXid(123, i);
+ xaRes.setTransactionTimeout(5);
+ xaRes.start(xid, XAResource.TMNOFLAGS);
+
+ stm = conn.createStatement();
+ stm.execute("insert into XATT values (" + i + ", 'Test_Entry')");
+
+ if (i % timeoutCommitEveryStatement == 0) {
+ stm.close();
+ xaRes.end(xid, XAResource.TMSUCCESS);
+ xaRes.prepare(xid);
+ xaRes.commit(xid, false);
+ xaConn.close();
+ } else if (i % 11 != 0) {
+ // check the timout for transactions disassociated
+ // with failure.
+ try {
+ xaRes.end(xid, XAResource.TMFAIL);
+ Assert.fail();
+ } catch (XAException ex) {
+ if (ex.errorCode < XAException.XA_RBBASE
+ || ex.errorCode > XAException.XA_RBEND)
+ {
+ throw ex;
+ }
+ }
+ stm.close();
+ xaConn.close();
+ } else if (i % 2 == 0) {
+ // check the timout for transactions disassociated
+ // with success.
+ xaRes.end(xid, XAResource.TMSUCCESS);
+ stm.close();
+ xaConn.close();
+ } else {
+ // check the timout for associated transactions
+ ;
+ }
+ }
+
+ stm = getConnection().createStatement();
+ ResultSet rs = stm.executeQuery("select count(*) from XATT");
+ rs.next();
+
+ // Check whether the correct number of transactions
+ // was rolled back
+ Assert.assertTrue(rs.getInt(1) == timeoutStatementsCommitted);
+
+ // test the timout during the statement is run
+ xaConn = xaDataSource.getXAConnection();
+ xaRes = xaConn.getXAResource();
+ conn = xaConn.getConnection();
+
+ Xid xid = createXid(124, 100);
+ xaRes.setTransactionTimeout(10);
+ xaRes.start(xid, XAResource.TMNOFLAGS);
+
+ stm = conn.createStatement();
+
+ // Check whether the statement was correctly timed out
+ // and the appropriate exception was thrown
+ boolean exceptionThrown = false;
+ try {
+ rs = stm.executeQuery(
+ "select * from XATT a, XATT b, XATT c, XATT d, XATT e "
+ + "order by a.i");
+ } catch (SQLException ex) {
+ // Check the sql state of the thrown exception
+ assertSQLState(
+ SQLState.LANG_STATEMENT_CANCELLED_OR_TIMED_OUT.substring(0,5),
+ ex
+ );
+ exceptionThrown = true;
+ }
+ Assert.assertTrue(exceptionThrown);
+
+ stm = getConnection().createStatement();
+ rs = stm.executeQuery("select count(*) from XATT");
+ rs.next();
+
+ // Again, check whether the correct number of transactions
+ // was rolled back
+ Assert.assertTrue(rs.getInt(1) == timeoutStatementsCommitted);
+ }
+
+
/* ------------------- end helper methods -------------------------- */
+
+ /** Create the Xid object for global transaction identification
+ * with the specified identification values.
+ * @param gtrid Global Transaction ID
+ * @param bqual Branch Qualifier
+ */
+ static Xid createXid(int gtrid, int bqual) throws XAException {
+ byte[] gid = new byte[2]; gid[0]= (byte) (gtrid % 256); gid[1]= (byte) (gtrid / 256);
+ byte[] bid = new byte[2]; bid[0]= (byte) (bqual % 256); bid[1]= (byte) (bqual / 256);
+ Xid xid = new ClientXid(0x1234, gid, bid);
+ return xid;
+ }
/** Parses the xid value from the string. The format of the input string is
* the same as the global_xid column in syscs_diag.transaction_table table -