/**
 *  Derby - Class org.apache.derbyTesting.functionTests.tests.jdbapi.XATest
 *  
 * 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.derbyTesting.functionTests.tests.jdbcapi;


import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import static junit.framework.Assert.fail;
import junit.framework.Test;
import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derbyTesting.functionTests.util.DeadlockWatchdog;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import static org.apache.derbyTesting.junit.BaseJDBCTestCase.assertSQLState;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.J2EEDataSource;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
import org.apache.derbyTesting.junit.Utilities;
import org.apache.derbyTesting.junit.XATestUtil;

public class XATest extends BaseJDBCTestCase {
    //create own policy file
    private static final String POLICY_FILE_NAME =
            "org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.policy";

    public static final String LOCKTIMEOUT="40XL1";
    
    public XATest(String name) {
        super(name);

    }

    /**
     * A single connection and 1 phase commit.
     * 
     * 
     * Original "SQL" from xaSimplePositive.sql <code>
     xa_connect ;
     xa_start xa_noflags 0;
     xa_getconnection;
     drop table foo;
     create table foo (a int);
     insert into foo values (0);
     select * from foo;
     run resource '/org/apache/derbyTesting/functionTests/tests/store/global_xactTable.view';
     select * from global_xactTable where gxid is not null order by gxid;
     xa_end xa_success 0;
     xa_commit xa_1phase 0;
     
     xa_datasource 'wombat' shutdown;
     </code>
     * 
     * @throws SQLException
     * @throws XAException
     * @throws XAException
     */
    public void testSingleConnectionOnePhaseCommit() throws SQLException,
            XAException {

        XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");

        XAConnection xac = xads.getXAConnection();

        XAResource xar = xac.getXAResource();

        Xid xid = XATestUtil.getXid(0, 32, 46);

        xar.start(xid, XAResource.TMNOFLAGS);

        Connection conn = xac.getConnection();
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, conn.getHoldability());

        Statement s = conn.createStatement();
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, s
                .getResultSetHoldability());

        s.execute("create table foo (a int)");
        s.executeUpdate("insert into foo values (0)");

        ResultSet rs = s.executeQuery("select * from foo");
        JDBC.assertDrainResults(rs, 1);

        String[][] expectedRows = { { "(0", "ACTIVE", "false", "APP",
                "UserTransaction" } };

        XATestUtil.checkXATransactionView(conn, expectedRows);

        s.close();
        xar.end(xid, XAResource.TMSUCCESS);

        // 1 phase commit
        xar.commit(xid, true);

        conn.close();
        xac.close();

    }

    /*
     * Two interleaving transaction and prepare/commit prepare/rollback.
     * 
     * (original test said two connections but only one connection was opened)
     * 
     * <code> xa_datasource 'wombat'; xa_connect user 'sku' password 'testxa' ;
     * 
     * xa_start xa_noflags 1; xa_getconnection; insert into APP.foo values (1);
     * xa_end xa_suspend 1;
     * 
     * xa_start xa_noflags 2; insert into APP.foo values (2); xa_end xa_suspend
     * 2;
     * 
     * xa_start xa_resume 1; insert into APP.foo values (3); xa_end xa_suspend
     * 1;
     * 
     * xa_start xa_resume 2; insert into APP.foo values (4); select * from
     * APP.global_xactTable where gxid is not null order by gxid; -- this
     * prepare won't work since transaction 1 has been suspended - XA_PROTO
     * xa_prepare 1;
     * 
     * select * from APP.global_xactTable where gxid is not null order by gxid;
     * xa_end xa_success 2; -- this assumes a resume xa_end xa_success 1;
     * xa_prepare 1; xa_prepare 2; -- both transactions should be prepared
     * select * from APP.global_xactTable where gxid is not null order by gxid; --
     * NOTE: The following call to "xa_recover xa_startrscan" is apt to --
     * return the result set rows in reverse order when changes to -- the Derby
     * engine affect the number of transactions that it takes -- to create a
     * database. The transactions are stored in a hash table -- based on a
     * global and local id, and when the number of transactions -- changes, the
     * (internal) local id can change, which may lead to a -- change in the
     * result set order. This order is determined by the -- JVM's hashing
     * algorithm. Examples of changes to the engine that -- can affect this
     * include ones that cause more commits or that -- change the amount of data
     * being stored, such as changes to the -- metadata statements (which is
     * what prompted this explanation in -- the first place). Ultimately, the
     * problem is that there is no -- way to order the return values from
     * "xa_recover" since it is an -- ij internal statement, not SQL...
     * xa_recover xa_startrscan; xa_recover xa_noflags;
     * 
     * xa_commit xa_2Phase 1; xa_rollback 2; -- check results xa_start
     * xa_noflags 3; select * from APP.global_xactTable where gxid is not null
     * order by gxid; select * from APP.foo; xa_end xa_success 3;
     * 
     * xa_prepare 3; -- should fail with XA_NOTA because we prepared a read only
     * transaction xa_commit xa_1Phase 3; disconnect; </code>
     */
    public void testInterleavingTransactions() throws SQLException, XAException {
        Statement preStatement = getConnection().createStatement();
        preStatement.execute("create table fooInterleaving (a int)");
        preStatement.execute("insert into fooInterleaving values (0)");
        preStatement.close();
        
        XADataSource xads = J2EEDataSource.getXADataSource();

        XAConnection xac = xads.getXAConnection("sku", "testxa");
        XAResource xar = xac.getXAResource();

        Xid xid1 = XATestUtil.getXid(1, 93, 18);
        Xid xid2 = XATestUtil.getXid(2, 45, 77);

        xar.start(xid1, XAResource.TMNOFLAGS);

        Connection conn = xac.getConnection();

        Statement s = conn.createStatement();
        s.executeUpdate("insert into APP.fooInterleaving values (1)");
        xar.end(xid1, XAResource.TMSUSPEND);

        xar.start(xid2, XAResource.TMNOFLAGS);
        s.executeUpdate("insert into APP.fooInterleaving values (2)");
        xar.end(xid2, XAResource.TMSUSPEND);

        xar.start(xid1, XAResource.TMRESUME);
        s.executeUpdate("insert into APP.fooInterleaving values (3)");
        xar.end(xid1, XAResource.TMSUSPEND);

        xar.start(xid2, XAResource.TMRESUME);
        s.executeUpdate("insert into APP.fooInterleaving values (4)");

        String[][] expectedRows = {
                { "(1", "ACTIVE", "false", "SKU", "UserTransaction" },
                { "(2", "ACTIVE", "false", "SKU", "UserTransaction" } };

        XATestUtil.checkXATransactionView(conn, expectedRows);

        // this prepare won't work since
        // transaction 1 has been suspended - XA_PROTO
        try {
            xar.prepare(xid1);
            fail("FAIL - prepare on suspended transaction");
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_PROTO)
                XATestUtil.dumpXAException(
                        "FAIL - prepare on suspended transaction", e);

        }

        // check it was not prepared

        XATestUtil.checkXATransactionView(conn, expectedRows);

        xar.end(xid2, XAResource.TMSUCCESS);

        xar.end(xid1, XAResource.TMSUCCESS);

        xar.prepare(xid1);
        xar.prepare(xid2);

        // both should be prepared.
        expectedRows = new String[][] {
                { "(1", "PREPARED", "false", "SKU", "UserTransaction" },
                { "(2", "PREPARED", "false", "SKU", "UserTransaction" } };

        XATestUtil.checkXATransactionView(conn, expectedRows);

        Xid[] recoveredStart = xar.recover(XAResource.TMSTARTRSCAN);
        assertEquals(2, recoveredStart.length);
        Xid[] recovered = xar.recover(XAResource.TMNOFLAGS);
        assertEquals(0, recovered.length);
        Xid[] recoveredEnd = xar.recover(XAResource.TMENDRSCAN);
        assertEquals(0, recoveredEnd.length);

        for (int i = 0; i < recoveredStart.length; i++) {
            Xid xid = recoveredStart[i];
            if (xid.getFormatId() == 1) {
                // commit 1 with 2pc
                xar.commit(xid, false);
            } else if (xid.getFormatId() == 2) {
                xar.rollback(xid);
            } else {
                fail("FAIL: unknown xact");
            }
        }

        // check the results
        Xid xid3 = XATestUtil.getXid(3, 2, 101);
        xar.start(xid3, XAResource.TMNOFLAGS);
        expectedRows = new String[][] { { "(3", "IDLE", "NULL", "SKU",
                "UserTransaction" } };
        XATestUtil.checkXATransactionView(conn, expectedRows);
        ResultSet rs = s.executeQuery("select * from APP.fooInterleaving");
        expectedRows = new String[][] { { "0" }, { "1" }, { "3" } };
        JDBC.assertFullResultSet(rs, expectedRows);

        rs.close();
        xar.end(xid3, XAResource.TMSUCCESS);

        int pr = xar.prepare(xid3);
        if (pr != XAResource.XA_RDONLY)
            fail("FAIL - prepare on read only xact returned " + pr);

        try {
            xar.commit(xid3, true);
            fail("FAIL - 2pc commit on read-only xact");
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }

        s.close();
        conn.close();
        xac.close();

    }

    /**
     * Tests on INIT STATE (no tr Original SQL from xaStateTran.sql. <code>

     -- the following should error XAER_NOTA
     xa_start xa_join 11;
     -- the following should error XAER_NOTA
     xa_start xa_resume 11;
     -- the following should error XAER_NOTA
     xa_end xa_success 11;
     -- the following should error XAER_NOTA
     xa_end xa_fail 11;
     -- the following should error XAER_NOTA
     xa_end xa_suspend 11;
     -- the following should error XAER_NOTA
     xa_prepare 11;
     -- the following should error XAER_NOTA
     xa_commit xa_1phase 11;
     -- the following should error XAER_NOTA
     xa_commit xa_2phase 11;
     -- the following should error XAER_NOTA
     xa_rollback 11;
     -- the following should error XAER_NOTA
     xa_forget 11;
     </code>
     */
    public void testNoTransaction() throws SQLException, XAException {
        XADataSource xads = J2EEDataSource.getXADataSource();
        XAConnection xac = xads.getXAConnection();
        XAResource xar = xac.getXAResource();

        Xid xid11 = XATestUtil.getXid(11, 3, 128);

        try {
            xar.start(xid11, XAResource.TMJOIN);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }

        try {
            xar.start(xid11, XAResource.TMRESUME);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }

        try {
            xar.end(xid11, XAResource.TMSUCCESS);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
        try {
            xar.end(xid11, XAResource.TMFAIL);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }

        try {
            xar.end(xid11, XAResource.TMSUSPEND);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }

        try {
            xar.prepare(xid11);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
        try {
            xar.commit(xid11, false);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
        try {
            xar.commit(xid11, true);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
        try {
            xar.rollback(xid11);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
        try {
            xar.forget(xid11);
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_NOTA)
                throw e;
        }
    }

    /**
     * Morph a connection between local anf global transactions.
     */
    public void testMorph() throws SQLException, XAException {
        Statement preStatement = getConnection().createStatement();
        preStatement.execute("create table fooMorph (a int)");
        preStatement.executeUpdate("insert into APP.fooMorph values (0)");
        preStatement.executeUpdate("insert into APP.fooMorph values (1)");
        preStatement.executeUpdate("insert into APP.fooMorph values (2)");
        preStatement.executeUpdate("insert into APP.fooMorph values (3)");
        preStatement.executeUpdate("insert into APP.fooMorph values (4)");
        preStatement.close();
        
        XADataSource xads = J2EEDataSource.getXADataSource();
        XAConnection xac = xads.getXAConnection();

        XAResource xar = xac.getXAResource();

        Connection conn = xac.getConnection();

        /*
         * autocommit off; insert into foo values (1); select * from
         * global_xactTable where gxid is not null order by gxid,username;
         * commit;
         */
        conn.setAutoCommit(false);
        Statement s = conn.createStatement();
        s.executeUpdate("insert into APP.fooMorph values (2001)");
        // no rows expected
        XATestUtil.checkXATransactionView(conn, null);
        conn.commit();

        /*
         * autocommit on; insert into foo values (2); select * from
         * global_xactTable where gxid is not null order by gxid,username;
         * 
         */

        conn.setAutoCommit(true);
        s.executeUpdate("insert into APP.fooMorph values (2002)");
        XATestUtil.checkXATransactionView(conn, null);

        /*
         * -- morph the connection to a global transaction xa_start xa_noflags
         * 1; select * from global_xactTable where gxid is not null order by
         * gxid,username; insert into foo values (3);
         */

        Xid xid = XATestUtil.getXid(1001, 66, 13);
        xar.start(xid, XAResource.TMNOFLAGS);
        String[][] expectedRows = { { "(1", "IDLE", "NULL", "APP",
                "UserTransaction" } };
        XATestUtil.checkXATransactionView(conn, expectedRows);
        s.executeUpdate("insert into APP.fooMorph values (2003)");

        /*
         * -- disallowed commit; -- disallowed rollback; -- disallowed
         * autocommit on; -- OK autocommit off;
         */
        try {
            conn.commit();
            fail("FAIL: commit allowed in global xact");
        } catch (SQLException e) {
        }

        try {
            conn.rollback();
            fail("FAIL: roll back allowed in global xact");
        } catch (SQLException e) {
        }
        try {
            conn.setAutoCommit(true);
            fail("FAIL: setAutoCommit(true) allowed " + "in global xact");
        } catch (SQLException e) {
        }
        try {
            conn.setSavepoint();
            fail("FAIL: setSavepoint() allowed in global xact");
        } catch (SQLException e) {
        }
        try {
            conn.setSavepoint("badsavepoint");
            fail("FAIL: setSavepoint(String) allowed in " + "global xact");
        } catch (SQLException e) {
        }

        conn.setAutoCommit(false);

        // s was created in local mode so it has holdibilty
        // set, will execute but ResultSet will have close on commit

        // DERBY-1158 query with holdable statement
        s.executeQuery("select * from APP.fooMorph where A >= 2000").close();
        s.close();

        // statement created in global xact is CLOSE_CURSORS_AT_COMMIT
        s = conn.createStatement();
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, s
                .getResultSetHoldability());

        /*
         * select * from foo; xa_end xa_success 1; xa_prepare 1;
         */
        ResultSet rs = s.executeQuery("select * from APP.fooMorph where A >= 2000");
        expectedRows = new String[][] { { "2001" }, { "2002" }, { "2003" } };

        rs.close();

        xar.end(xid, XAResource.TMSUCCESS);
        xar.prepare(xid);

        /*
         * -- dup id xa_start xa_noflags 1;
         */
        try {
            xar.start(xid, XAResource.TMNOFLAGS);
            fail("FAIL - start with duplicate XID");
        } catch (XAException e) {
            if (e.errorCode != XAException.XAER_DUPID)
                throw e;
        }

        /*
         * xa_start xa_noflags 2; -- still should disallow autommit; autocommit
         * on; -- still should disallow commit and rollback commit; rollback;
         * select * from global_xactTable where gxid is not null order by
         * gxid,username; xa_end xa_suspend 2;
         */

        Xid xid2 = XATestUtil.getXid(1002, 23, 3);
        xar.start(xid2, XAResource.TMNOFLAGS);
        try {
            conn.commit();
            fail("FAIL: commit allowed in global xact");
        } catch (SQLException e) {
        }
        try {
            conn.rollback();
            fail("FAIL: roll back allowed in global xact");
        } catch (SQLException e) {
        }
        try {
            conn.setAutoCommit(true);
            fail("FAIL: setAutoCommit(true) allowed in global xact");
        } catch (SQLException e) {
        }
        conn.setAutoCommit(false);

        xar.end(xid2, XAResource.TMSUSPEND);

        /*
         * -- get local connection again xa_getconnection;
         * 
         * insert into foo values (5); -- autocommit should be on by default;
         * commit;
         * 
         * autocommit off; insert into foo values (6); -- commit and rollback is
         * allowed on local connection rollback;
         * 
         * insert into foo values (6); commit;
         */
        conn = xac.getConnection();
        s = conn.createStatement();
        s.executeUpdate("insert into APP.fooMorph values (2005)");
        conn.commit();
        conn.setAutoCommit(false);
        s.executeUpdate("insert into APP.fooMorph values (2006)");
        conn.rollback();
        s.executeUpdate("insert into APP.fooMorph values (2007)");
        conn.commit();

        expectedRows = new String[][] {
                { "(1", "PREPARED", "false", "APP", "UserTransaction" },
                { "(1", "IDLE", "NULL", "APP", "UserTransaction" } };
        XATestUtil.checkXATransactionView(conn, expectedRows);
        /*
         * -- I am still able to commit other global transactions while I am
         * attached to a -- local transaction. xa_commit xa_2phase 1; xa_end
         * xa_success 2; xa_rollback 2;
         */
        xar.commit(xid, false);
        xar.end(xid2, XAResource.TMSUCCESS);
        xar.rollback(xid2);

        XATestUtil.checkXATransactionView(conn, null);
        rs = s.executeQuery("select * from APP.fooMorph where A >= 2000");
        expectedRows = new String[][] { { "2001" }, { "2002" }, { "2003" },
                { "2005" }, { "2007" } };
        JDBC.assertFullResultSet(rs, expectedRows);
        rs.close();
        conn.rollback();
        conn.close();

        /*
         * xa_getconnection; select * from global_xactTable where gxid is not
         * null order by gxid,username; select * from foo; autocommit off;
         * delete from foo;
         */
        conn = xac.getConnection();
        conn.setAutoCommit(false);
        s = conn.createStatement();
        s.executeUpdate("delete from app.fooMorph");
        rs = s.executeQuery("select * from APP.fooMorph");
        JDBC.assertEmpty(rs);
        rs.close();

        /*
         * -- yanking a local connection away should rollback the changes
         */
        conn = xac.getConnection();
        conn.setAutoCommit(false);
        s = conn.createStatement();
        rs = s.executeQuery("select * from APP.fooMorph where A >= 2000");
        expectedRows = new String[][] { { "2001" }, { "2002" }, { "2003" },
                { "2005" }, { "2007" } };
        JDBC.assertFullResultSet(rs, expectedRows);

        /*
         * -- cannot morph it if the local transaction is not idle xa_start
         * xa_noflags 3; commit; -- now morph it to a global transaction
         * xa_start xa_noflags 3;
         */
        Xid xid3 = XATestUtil.getXid(1003, 27, 9);
        try {
            xar.start(xid3, XAResource.TMNOFLAGS);
            fail("FAIL XAResource.start on a global transaction with an active local transaction (autocommit false)");
        } catch (XAException xae) {
            if (xae.errorCode != XAException.XAER_OUTSIDE)
                throw xae;
        }
        conn.commit();
        xar.start(xid3, XAResource.TMNOFLAGS);

        /*
         * -- now I shouldn't be able to yank it xa_getconnection;
         */
        // DERBY-341 - client skip XAConnection with active local xact
        if (usingEmbedded()) {
            try {
                xac.getConnection();
                fail("FAIL: getConnection with active global xact");
            } catch (SQLException sqle) {
                assertSQLState("XJ059", sqle);
            }
        }
        /*
         * select * from foo; delete from foo;
         * 
         * xa_end xa_fail 3; xa_rollback 3; -- local connection again
         * xa_getconnection; select * from global_xactTable where gxid is not
         * null order by gxid,username; select * from foo;
         */
        s = conn.createStatement();
        s.executeUpdate("delete from APP.fooMorph");
        rs = s.executeQuery("select * from APP.fooMorph where A >= 2000");
        JDBC.assertEmpty(rs);

        rs.close();
        try {
            xar.end(xid3, XAResource.TMFAIL);
        } catch (XAException e) {
            if (e.errorCode != XAException.XA_RBROLLBACK)
                throw e;
        }
        xar.rollback(xid3);

        conn = xac.getConnection();
        s = conn.createStatement();
        rs = s.executeQuery("select * from APP.fooMorph where A >= 2000");
        expectedRows = new String[][] { { "2001" }, { "2002" }, { "2003" },
                { "2005" }, { "2007" } };
        JDBC.assertFullResultSet(rs, expectedRows);
        rs.close();

        s.close();
        conn.close();

    }

    /**
     * This test checks the fix on DERBY-4310, for not repreparing PreparedStatements
     * upon calling close() on them.
     */
    public void testDerby4310PreparedStatement() throws SQLException, XAException {
        XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");

        XAConnection xaconn = xads.getXAConnection();
       
        XAResource xar = xaconn.getXAResource();
        Xid xid = XATestUtil.getXid(1,93,18);
        
        /* Create the table and insert some records into it. */
        Connection conn = xaconn.getConnection();
        Statement s = conn.createStatement();
        s.executeUpdate("CREATE TABLE foo4310_PS (I INT)");

        conn.createStatement().executeUpdate("insert into APP.foo4310_PS values (0)");
        conn.createStatement().executeUpdate("insert into APP.foo4310_PS values (1)");
        conn.createStatement().executeUpdate("insert into APP.foo4310_PS values (2)");
        conn.commit();
        
        /* Prepare and execute the statement to be tested */
        PreparedStatement ps = conn.prepareStatement("SELECT * FROM APP.foo4310_PS");
        ps.executeQuery().close();

        /* Start and end a transaction on the XAResource object */
        xar.start(xid, XAResource.TMNOFLAGS);
        xar.end(xid, XAResource.TMSUCCESS);
        
        /* Drop the table on a parallel, regular connection */
        Connection conn2 = getConnection();
        Statement s2 = conn2.createStatement();
        s2.execute("DROP TABLE foo4310_PS");
        conn2.commit();
        conn2.close();
        
        try {
            /* Try to close the prepared statement. This would throw an exception
             * before the fix, claiming that the table was not found. */
            ps.close();
        } finally {
            /* Rollback the transaction and close the connections */
            xar.rollback(xid);
            conn.close();
            xaconn.close();
        }
        
    }
    
    /**
     * This test checks the fix on DERBY-4310, for not repreparing CallableStatements
     * upon calling close() on them.
     */
    public void testDerby4310CallableStatement() throws SQLException, XAException {
        XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");

        XAConnection xaconn = xads.getXAConnection();
       
        XAResource xar = xaconn.getXAResource();
        Xid xid = XATestUtil.getXid(1,93,18);
        
        /* Create the procedure bazed on XATest.zeroArg() */
        Connection conn = xaconn.getConnection();
        Statement s = conn.createStatement();
        s.executeUpdate("CREATE PROCEDURE ZA() LANGUAGE JAVA "+
                        "EXTERNAL NAME 'org.apache.derbyTesting.functionTests.tests.jdbcapi.XATest.zeroArg' "+
                        "PARAMETER STYLE JAVA");
        conn.commit();
        
        /* Prepare and execute CallableStatement based on the procedure above */
        CallableStatement cs = conn.prepareCall("CALL ZA()");
        cs.execute();

        /* Start and end a transaction on the XAResource object */
        xar.start(xid, XAResource.TMNOFLAGS);
        xar.end(xid, XAResource.TMSUCCESS);
        
        /* Drop the procedure on a parallel, regular connection */
        Connection conn2 = getConnection();
        Statement s2 = conn2.createStatement();
        s2.execute("DROP PROCEDURE ZA");
        conn2.commit();
        conn2.close();
        
        try {
            /* Try to close the prepared statement. This would throw an exception
             * before the fix, claiming that the table was not found. */
            cs.close();
        } finally {
            /* Rollback the transaction and close the connections */
            xar.rollback(xid);
            conn.close();
            xaconn.close();
        }
        
    }
    
    /**
     * Derby-966 holdability and global/location transactions. (work in
     * progress)
     */
    public void testDerby966() throws SQLException, XAException {
        Statement preStatement = getConnection().createStatement();
        preStatement.execute("create table foo966 (a int)");
        preStatement.executeUpdate("insert into APP.foo966 values (0)");
        preStatement.executeUpdate("insert into APP.foo966 values (1)");
        preStatement.executeUpdate("insert into APP.foo966 values (2)");
        preStatement.executeUpdate("insert into APP.foo966 values (3)");
        preStatement.executeUpdate("insert into APP.foo966 values (4)");
        preStatement.executeUpdate("insert into APP.foo966 values (2001)");
        preStatement.executeUpdate("insert into APP.foo966 values (2002)");
        preStatement.executeUpdate("insert into APP.foo966 values (2003)");
        preStatement.executeUpdate("insert into APP.foo966 values (2005)");
        preStatement.executeUpdate("insert into APP.foo966 values (2007)");
        preStatement.close();
        
        XADataSource xads = J2EEDataSource.getXADataSource();
        XAConnection xac = xads.getXAConnection();
        XAResource xar = xac.getXAResource();

        Xid xid = XATestUtil.getXid(996, 9, 48);

        Connection conn = xac.getConnection();

        // Obtain Statements and PreparedStatements
        // with all the holdability options.
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability());

        Statement sdh = conn.createStatement();
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, sdh
                .getResultSetHoldability());

        checkHeldRS(conn, sdh, sdh.executeQuery("select * from app.foo966"));
        PreparedStatement psdh = conn.prepareStatement("SELECT * FROM APP.foo966");
        PreparedStatement psdh_d = conn
                .prepareStatement("DELETE FROM APP.foo966 WHERE A < -99");
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, psdh
                .getResultSetHoldability());
        checkHeldRS(conn, psdh, psdh.executeQuery());

        Statement shh = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, shh
                .getResultSetHoldability());
        checkHeldRS(conn, shh, shh.executeQuery("select * from app.foo966"));
        PreparedStatement pshh = conn.prepareStatement("SELECT * FROM APP.foo966",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        PreparedStatement pshh_d = conn.prepareStatement(
                "DELETE FROM APP.foo966 WHERE A < -99",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, shh
                .getResultSetHoldability());
        checkHeldRS(conn, pshh, pshh.executeQuery());

        Statement sch = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sch
                .getResultSetHoldability());

        checkHeldRS(conn, sch, sch.executeQuery("select * from app.foo966"));
        PreparedStatement psch = conn.prepareStatement("SELECT * FROM APP.foo966",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
        PreparedStatement psch_d = conn.prepareStatement(
                "DELETE FROM APP.foo966 WHERE A < -99",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psch_d
                .getResultSetHoldability());
        checkHeldRS(conn, psch, psch.executeQuery());

        // set the connection's holdabilty to false
        conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT);

        Statement sdc = conn.createStatement();
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sdc
                .getResultSetHoldability());
        checkHeldRS(conn, sdc, sdc.executeQuery("select * from app.foo966"));
        PreparedStatement psdc = conn.prepareStatement("SELECT * FROM APP.foo966");
        PreparedStatement psdc_d = conn
                .prepareStatement("DELETE FROM APP.foo966 WHERE A < -99");
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psdc
                .getResultSetHoldability());
        checkHeldRS(conn, psdc, psdc.executeQuery());

        Statement shc = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psdc
                .getResultSetHoldability());
        checkHeldRS(conn, shc, shc.executeQuery("select * from app.foo966"));
        PreparedStatement pshc = conn.prepareStatement("SELECT * FROM APP.foo966",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        PreparedStatement pshc_d = conn.prepareStatement(
                "DELETE FROM APP.foo966 WHERE A < -99",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, pshc
                .getResultSetHoldability());

        checkHeldRS(conn, pshc, pshc.executeQuery());

        Statement scc = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, scc
                .getResultSetHoldability());
        checkHeldRS(conn, scc, scc.executeQuery("select * from app.foo966"));
        PreparedStatement pscc = conn.prepareStatement("SELECT * FROM APP.foo966",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
        PreparedStatement pscc_d = conn.prepareStatement(
                "DELETE FROM APP.foo966 WHERE A < -99",
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, pscc
                .getResultSetHoldability());

        checkHeldRS(conn, pscc, pscc.executeQuery());

        // Revert back to holdable
        conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);

        ResultSet rs = sdh.executeQuery("SELECT * FROM APP.foo966");
        rs.next();
        // before commit
        assertEquals(0, +rs.getInt(1));
        conn.commit();
        // aftercommit
        rs.next();
        assertEquals(1, rs.getInt(1));
        rs.close();

        // ensure a transaction is active to test DERBY-1025
        rs = sdh.executeQuery("SELECT * FROM APP.foo966");

        // This switch to global is ok because conn
        // is in auto-commit mode, thus the start performs
        // an implicit commit to complete the local transaction.

        // start a global xact and test those statements.
        xar.start(xid, XAResource.TMNOFLAGS);

        // Statements not returning ResultSet's should be ok
        sdh.executeUpdate("DELETE FROM APP.foo966 where A < -99");
        shh.executeUpdate("DELETE FROM APP.foo966 where A < -99");
        sch.executeUpdate("DELETE FROM APP.foo966 where A < -99");

        ArrayList<ResultSet> openRS = new ArrayList<ResultSet>();

        // Statements obtained while default was hold.
        // All should work, holability will be downgraded
        // to close on commit for those Statements with hold set.
        openRS.add(sdh.executeQuery("SELECT * FROM APP.foo966"));
        openRS.add(shh.executeQuery("SELECT * FROM APP.foo966"));
        openRS.add(sch.executeQuery("SELECT * FROM APP.foo966"));

        // PreparedStatements obtained while default was hold.
        // Holdability should be downgraded.
        openRS.add(psdh.executeQuery());
        openRS.add(pshh.executeQuery());
        openRS.add(psch.executeQuery());

        // Statements not returning ResultSet's should be ok
        psdh_d.executeUpdate();
        pshh_d.executeUpdate();
        psch_d.executeUpdate();

        // Statements not returning ResultSet's should be ok
        sdc.executeUpdate("DELETE FROM APP.foo966 where A < -99");
        shc.executeUpdate("DELETE FROM APP.foo966 where A < -99");
        scc.executeUpdate("DELETE FROM APP.foo966 where A < -99");

        // Statements obtained while default was close.
        // all should return close on commit ResultSets
        openRS.add(sdc.executeQuery("SELECT * FROM APP.foo966"));
        openRS.add(shc.executeQuery("SELECT * FROM APP.foo966"));
        openRS.add(scc.executeQuery("SELECT * FROM APP.foo966"));

        // PreparedStatements obtained while default was close.
        openRS.add(psdc.executeQuery());
        openRS.add(pshc.executeQuery());
        openRS.add(pscc.executeQuery());

        // Statements not returning ResultSet's should be ok
        psdc_d.executeUpdate();
        pshc_d.executeUpdate();
        pscc_d.executeUpdate();

        // All the ResultSets should be open. Run a simple
        // test, clearWarnings throws an error if the ResultSet
        // is closed. Also would be nice here to use the new
        // JDBC 4.0 method getHoldabilty to ensure the
        // holdability is reported correctly.
        int orsCount = 0;
        for (ResultSet ors : openRS) {
            ors.clearWarnings();
            orsCount++;
        }
        assertEquals("Incorrect number of open result sets", 12, orsCount);

        // Test we cannot switch the connection to holdable
        try {
            conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
            fail("FAIL - set holdability in global xact.");
        } catch (SQLException sqle) {
            assertSQLState("XJ05C", sqle);
        }

        // JDBC 4.0 (proposed final draft) section allows
        // drivers to change the holdability when creating
        // a Statement object and attach a warning to the Connection.
        Statement sglobalhold = conn.createStatement(
                ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sglobalhold
                .getResultSetHoldability());

        sglobalhold.close();

        // DERBY2481 Client does not downgrade PreparedStatement holdability
        if (!usingDerbyNetClient()) {
            PreparedStatement psglobalhold = conn.prepareStatement(
                    "SELECT * FROM APP.foo966", ResultSet.TYPE_FORWARD_ONLY,
                    ResultSet.CONCUR_READ_ONLY,
                    ResultSet.HOLD_CURSORS_OVER_COMMIT);
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psglobalhold
                    .getResultSetHoldability());

            psglobalhold.close();

            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sdh
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sch
                    .getResultSetHoldability());

            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psdh
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, pshh
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psch
                    .getResultSetHoldability());

            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sdc
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, shc
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, scc
                    .getResultSetHoldability());

            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psdh_d
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, pshh_d
                    .getResultSetHoldability());
            assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psch_d
                    .getResultSetHoldability());
        }

        xar.end(xid, XAResource.TMSUCCESS);
        if (xar.prepare(xid) != XAResource.XA_RDONLY)
            fail("FAIL prepare didn't indicate r/o");

        // All the ResultSets should be closed. Run a simple
        // test, clearWarnings throws an error if the ResultSet
        // is closed.
        int crsCount = 0;
        for (ResultSet crs : openRS) {
            try {
                crs.clearWarnings();
            } catch (SQLException sqle) {
            }
            crsCount++;
        }
        assertEquals("After global transaction closed ResultSets ", 12,
                crsCount);

        // Check the statements revert to holdable as required.
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, sdh
                .getResultSetHoldability());
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, shh
                .getResultSetHoldability());
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sch
                .getResultSetHoldability());

        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, psdh
                .getResultSetHoldability());
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, pshh
                .getResultSetHoldability());
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psch
                .getResultSetHoldability());

        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, sdc
                .getResultSetHoldability());
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, shc
                .getResultSetHoldability());
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, scc
                .getResultSetHoldability());

        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, psdh_d
                .getResultSetHoldability());
        assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, pshh_d
                .getResultSetHoldability());
        assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, psch_d
                .getResultSetHoldability());

        // intermittently we get a failure in this test if we do not
        // rollback, see DERBY-6248.
        conn.rollback();
        conn.close();

    }

    /**
     * DERBY-4731
     * Test using a GLOBAL TEMPORARY TABLE  table in an
     * XA transaction and leaving it active during two phase commit.
     * Before the fix this test would throw the following at commit
     * time:
     * ERROR 40XT0: An internal error was identified by RawStore module. 
     *
     *
     * @throws XAException 
     * @throws SQLException 
     * 
     */
    public void testXATempTableD4731_RawStore() 
        throws SQLException, XAException {
        doXATempTableD4731Work(true, false, XATestUtil.getXid(997, 9, 49));
    }
    

    /**
     * DERBY-4735 Temp tables with XA transactions
     * an Assert will occur on prepare if only
     * temp table work is done in the xact.
     *
     * @throws XAException 
     * @throws SQLException 
     * 
     */
    public void xtestXATempTableD4735_Assert() 
        throws SQLException, XAException {

          doXATempTableD4731Work(false, false, XATestUtil.getXid(999,  11, 51));
          doXATempTableD4731Work(false, true,  XATestUtil.getXid(1000, 12, 52));
    }

    /**
     * DERBY-4743 Temp tables with XA transactions
     *
     * Verifies that global temporary table can not be accessed after the
     * commit of the XA transaction.
     *
     * @throws XAException 
     * @throws SQLException 
     * 
     */
    public void testXATempTableD4743() 
        throws SQLException, XAException {

          doXATempTableD4731Work(true, true, XATestUtil.getXid(998, 10, 50));
    }
 
    /**
     * DERBY-5552 Check that lock timeout does not destroy connection
     * during an XA Transaction.
     * 
     * @throws SQLException
     * @throws XAException
     */
    public void testXALockTimeout() throws SQLException, XAException {
        XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");
       
        // Get the first connection and lock table in 
        // xa transaction
        XAConnection xaconn = xads.getXAConnection();
        XAResource xar = xaconn.getXAResource();
        Xid xid = XATestUtil.getXid(998,10,50);
   
        Connection conn = xaconn.getConnection();
        Statement s = conn.createStatement();
        xar.start(xid, XAResource.TMNOFLAGS);
        s.executeUpdate("INSERT INTO TABLT VALUES(2)");
        
        // Get a second connection and global xact
        // and try to select causing lock timeout
        XAConnection xaconn2 = xads.getXAConnection();
        XAResource xar2 = xaconn2.getXAResource();
        Xid xid2 = XATestUtil.getXid(999,11,51);
        Connection conn2 = xaconn2.getConnection();
        // Set to serializable so we get lock timeout
        conn2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
        xar2.start(xid2, XAResource.TMNOFLAGS);
        Statement s2 = conn2.createStatement();
        s2.executeUpdate("INSERT INTO TABLT VALUES(3)");
        assertGlobalXactCount(2);
        try {
            ResultSet rs = s2.executeQuery("SELECT * FROM TABLT");
            fail("Should have gotten lock timeout error: " + LOCKTIMEOUT);
        } catch (SQLException se) {
            assertSQLState(LOCKTIMEOUT,se);
            }
        // After the lock timeout we just have one global transaction.
        // lock timeout implicitly rolled back xid2
        assertGlobalXactCount(1);
        assertConnOK(conn);
        // DERBY-5552 Make sure connection is ok after lock timeout
        assertConnOK(conn2);
        //Should be able to end and commit xid1
        xar.end(xid, XAResource.TMSUCCESS);
        xar.prepare(xid);
        xar.commit(xid, false);
        
        // xid2 should have already been rolled back so end should fail
        try {    
            xar2.end(xid2, XAResource.TMSUCCESS);
            fail("Should have gotten exception ending xid2");
        } catch (XAException xae) {
            //xae.printStackTrace();
            assertEquals(XAException.XA_RBTIMEOUT, xae.errorCode);
            
        } 
     
        // Should have no locks on TABLT
        Statement drops = createStatement();
        drops.executeUpdate("DROP TABLE TABLT");
        // verify there are no more global transactions
        assertGlobalXactCount(0);
        
        // Need to explicitly rollback xid2 as it ended with
        // an implicit rollback XA_RBTIMEOUT
        xar2.rollback(xid2);
        
        // Make sure both connections can be used to make a new global xact
        xar.start(xid, XAResource.TMNOFLAGS);
        s.executeUpdate("CREATE TABLE TABLT (I INT)");
        s.executeUpdate("INSERT INTO TABLT VALUES(1)");
        xar.end(xid, XAResource.TMSUCCESS);
        xar.prepare(xid);
        xar.commit(xid, false);
        
        // now the other connection ..
        xar2.start(xid2, XAResource.TMNOFLAGS);
        s2.executeUpdate("INSERT INTO TABLT VALUES(2)");
        xar2.end(xid2, XAResource.TMSUCCESS);
        xar.prepare(xid2);
        xar.commit(xid2, false);
        assertGlobalXactCount(0);
        conn.close();
        xaconn.close();
        conn2.close();
        xaconn2.close();
        
    }
    
    /**
     * DERBY-6879 Check that a XA transaction timeout while a cleanupOnError is
     * being performed does not cause a Java level deadlock.
     *
     * The strategy is to cause a XA statement to wait for a period that is
     * longer than the XA transaction timeout which will allow the timeout to
     * cancel the XA transaction. That is accomplished by locking a table using
     * a standard connection and then issuing a select on the same table using a
     * XA connection. The select will wait for the required locks to be
     * available up to the lock timeout. For the test to work correctly the XA
     * transaction timeout must be less than the lock timeout.
     *
     * A dealock watchdog is used to detect the deadlock until the DERBY-6879
     * issue is fixed.
     *
     * @throws SQLException
     * @throws XAException
     */
    public void testDerby6879() throws SQLException, XAException {
        XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");

        Connection conn = J2EEDataSource.getConnectionPoolDataSource().getPooledConnection().getConnection();
        conn.setAutoCommit(false);
        Statement s = conn.createStatement();
        s.execute("LOCK TABLE TABLT IN EXCLUSIVE MODE");

        // Get a second connection and global xact
        // and try to select causing lock timeout
        XAConnection xaconn2 = xads.getXAConnection();
        XAResource xar2 = xaconn2.getXAResource();
        xar2.setTransactionTimeout(2);
        Xid xid2 = XATestUtil.getXid(6879, 11, 51);
        Connection conn2 = xaconn2.getConnection();
        // Set to serializable so we get lock timeout
        conn2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
        xar2.start(xid2, XAResource.TMNOFLAGS);
        Statement s2 = conn2.createStatement();
        assertGlobalXactCount(1);
        DeadlockWatchdog wd = new DeadlockWatchdog(30 * 1000);
        wd.start();
        try {
            ResultSet rs = s2.executeQuery("SELECT * FROM TABLT");
            fail("Should have gotten lock timeout error: " + LOCKTIMEOUT);
        } catch (SQLException se) {
            assertSQLState(LOCKTIMEOUT, se);
        }
        wd.stop();

        // xid2 should have already been rolled back so end should fail
        try {
            xar2.end(xid2, XAResource.TMSUCCESS);
            fail("Should have gotten exception ending xid2");
        } catch (XAException xae) {
            assertTrue(xae.errorCode >= XAException.XAER_OUTSIDE || xae.errorCode <= XAException.XAER_ASYNC);
        }

        conn.commit();

        conn.close();
        conn2.close();
        xaconn2.close();
    }
    
    
    /**
     * The two cases for DERBY-4371 do essentially the same thing. Except doing
     * logged work causes the RawStore error and doing only temp table 
     * operations causes the assert.
     *  
     * @param doLoggedWorkInXact
     * @throws SQLException
     * @throws XAException
     */
    private void doXATempTableD4731Work(
    boolean doLoggedWorkInXact,
    boolean access_temp_table_after_xaendandcommit,
    Xid     xid)
        throws SQLException, XAException{

        XADataSource xads = J2EEDataSource.getXADataSource();
        XAConnection xaconn = xads.getXAConnection();
        XAResource xar = xaconn.getXAResource();

        xar.start(xid, XAResource.TMNOFLAGS);
        Connection conn = xaconn.getConnection();
        Statement s = conn.createStatement(); 
        if (doLoggedWorkInXact){
            // need to do some real work in our transaction
            // so make a table
            makeARealTable(s);
        }
        
        // make the temp table
        s.executeUpdate("DECLARE GLOBAL TEMPORARY TABLE SESSION.T1 ( XWSID INT, XCTID INT, XIID CHAR(26), XVID SMALLINT, XLID CHAR(8) FOR BIT DATA) ON COMMIT DELETE ROWS NOT LOGGED ON ROLLBACK DELETE ROWS");

        // insert a row
        PreparedStatement ps = 
            conn.prepareStatement("INSERT INTO SESSION.T1 VALUES (?,?,?,?,?)");
        ps.setInt(1,1);
        ps.setInt(2,1);
        ps.setString(3,"hello");
        ps.setShort(4, (short) 1);
        ps.setBytes(5, new byte[] {0x0,0x1});
        ps.executeUpdate();
        ResultSet rs = s.executeQuery("SELECT count(*) FROM SESSION.t1");
        JDBC.assertFullResultSet(rs, new String[][] {{"1"}});
        rs.close();
        // You could work around the issue by dropping the TEMP table
        //s.executeUpdate("DROP TABLE SESSION.T1");
        xar.end(xid, XAResource.TMSUCCESS);

        assertEquals(
            (doLoggedWorkInXact ? XAResource.XA_OK : XAResource.XA_RDONLY),
            xar.prepare(xid));

        if (doLoggedWorkInXact)
        {
            // if you don't do logged work in the transaction, then the 
            // prepare with be done with XAResource.XA_RDONLY.  If you
            // try to commit a prepared read only transaction you will get 
            // an error, so only commit prepared transactions here that did
            // some "real" logged work.
            xar.commit(xid,false); 
        }

        if (access_temp_table_after_xaendandcommit)
        {
            // temp table should not be available after commit of an XA
            // transaction.
            assertStatementError(
                "42X05", s, "SELECT count(*) FROM SESSION.t1");
            conn.commit();
        }


        s.close();
        conn.close();
        xaconn.close();
    }


    private void makeARealTable(Statement s) throws SQLException {
        try {
            s.executeUpdate("DROP TABLE REALTABLE1");
        } catch (SQLException se) {
            {
            s.executeUpdate("CREATE TABLE REALTABLE1 (i int)");
            }
        }
    }
    
    /**
     * Check the held state of a ResultSet by fetching one row, executing a
     * commit and then fetching the next. Checks the held state matches the
     * behaviour.
     */
    private static void checkHeldRS(Connection conn, Statement s, ResultSet rs)
            throws SQLException {
        // DERBY-1008 - can't run with client
        if (!usingDerbyNetClient()) {
            if (s.getConnection() != conn)
                fail("FAIL - mismatched statement & Connection");
        }
        if (rs.getStatement() != s) {
            // DERBY-1009
            fail("FAIL - mismatched statement & ResultSet "
                    + " Statement class " + s.getClass()
                    + " ResultSet' Statements class "
                    + rs.getStatement().getClass());
        }

        boolean held = (ResultSet.HOLD_CURSORS_OVER_COMMIT == s
                .getResultSetHoldability());
        rs.next();
        assertEquals(0, rs.getInt(1));
        conn.commit();

        try {
            rs.next();
        } catch (SQLException sqle) {
            boolean ok = !held;

            if (ok) {
                assertSQLState("XCL16", sqle);
            } else {
                fail("Held cursor closed on commit");
            }
        }

        rs.close();
        conn.commit();
    }
    
    /** 
     * Dummy method for testDerby4310* fixtures
     */
    public static void zeroArg() {  }

    public static Test baseSuite(String name) {
        BaseTestSuite suite = new BaseTestSuite(name);
        suite.addTestSuite(XATest.class);

        return new CleanDatabaseTestSetup(suite) {
            /**
             * Creates the table used in the test cases.
             * 
             */
            protected void decorateSQL(Statement s) throws SQLException {
                XATestUtil.createXATransactionView(s);
                // Table for lock timeout test
                s.executeUpdate("CREATE TABLE TABLT (I INT)");
                s.executeUpdate("INSERT INTO TABLT VALUES(1)");
            }

        };
    }
    
    /**
     * Excecute a simple SQL statement to assert that the connection is valid
     * @param conn Connection to check
     * @throws SQLException on error
     */
    private static void assertConnOK(Connection conn) throws SQLException{
        Statement s = conn.createStatement();
        ResultSet rs = s.executeQuery("VALUES(1)");
        JDBC.assertSingleValueResultSet(rs, "1");
      }
    
    
    /**
     * Verify the expected number of global transactions.
     * Run test with -Dderby.tests.debug to see the full transaction table on the
     * console
     * 
     * @param expectedCount expected number of global transaction
     * @throws SQLException on error
     */
    private void assertGlobalXactCount(int expectedCount ) throws SQLException {
        Connection conn = openDefaultConnection();
        Statement s = conn.createStatement();
        ResultSet rs = s.executeQuery(
              "SELECT COUNT(*) FROM syscs_diag.transaction_table WHERE GLOBAL_XID IS NOT NULL");
        rs.next();
        int count = rs.getInt(1);
        if (TestConfiguration.getCurrent().isVerbose()) {
            System.out.println("assertGlobalXactCount(" + expectedCount +
                    "): Full Transaction Table ...");
            Utilities.showResultSet(s.executeQuery(
                    "SELECT * FROM syscs_diag.transaction_table"));
        }
        assertTrue("Expected " + expectedCount + "global transactions but saw " + count, expectedCount == count);
    }
    
    /**
     * Runs the test fixtures in embedded and client.
     * 
     * @return test suite
     */
    public static Test suite() {
        BaseTestSuite suite = new BaseTestSuite("XATest");
        // no XA for JSR169
        if (JDBC.vmSupportsJSR169())
            return suite;

        suite.addTest(baseSuite("XATest:embedded"));

        suite.addTest(TestConfiguration
                .clientServerDecorator(baseSuite("XATest:client")));
        
        Test test = DatabasePropertyTestSetup.setLockTimeouts(suite, 3, 5);
        test = decorateWithPolicy(test);
        return test;
    }   
    
    // grant ALL FILES execute, and getPolicy permissions,
    // as well as write for the trace files.
    private static Test decorateWithPolicy(Test test) {
        //
        // Install a security manager using the initial policy file. This is 
        // needed foro the DeadlockWatchdog to allow it to access the ThreadMXBean
        // to check for a deadlock
        //
        return new SecurityManagerSetup(test, POLICY_FILE_NAME);
    }
    
    
}

