db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject 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...
Date Fri, 15 Jun 2007 13:18:58 GMT
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 -



Mime
View raw message