Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 11859 invoked from network); 18 Feb 2006 18:36:28 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 18 Feb 2006 18:36:28 -0000 Received: (qmail 11367 invoked by uid 500); 18 Feb 2006 18:36:27 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 11341 invoked by uid 500); 18 Feb 2006 18:36:27 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 11330 invoked by uid 99); 18 Feb 2006 18:36:27 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 18 Feb 2006 10:36:27 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Sat, 18 Feb 2006 10:36:25 -0800 Received: (qmail 11751 invoked by uid 65534); 18 Feb 2006 18:36:04 -0000 Message-ID: <20060218183604.11750.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r378755 - in /db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests: master/XATest.out suites/xa.runall tests/jdbcapi/XATest.java tests/jdbcapi/build.xml util/XATestUtil.java Date: Sat, 18 Feb 2006 18:36:03 -0000 To: derby-commits@db.apache.org From: djd@apache.org X-Mailer: svnmailer-1.0.6 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: djd Date: Sat Feb 18 10:36:02 2006 New Revision: 378755 URL: http://svn.apache.org/viewcvs?rev=378755&view=rev Log: DERBY-434 Add a new initial version XATest.java that will work with the embedded and client XA data sources. Contains test cases taken from the xaSimplePositve,sql, xaStateTran.sql and all of xaMorph.sql. Future work includes getting it to run with client (currently some XA client bugs hold this up) and moving the remaining test cases from the XA SQL tests into it. Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out (with props) db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java (with props) db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java (with props) Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/xa.runall db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/build.xml Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out?rev=378755&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out (added) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out Sat Feb 18 10:36:02 2006 @@ -0,0 +1,172 @@ +singleConnectionOnePhaseCommit +A +----------- +0 +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(0 |ACTIVE |false|APP |UserTransaction +interleavingTransactions +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(1 |ACTIVE |false|SKU |UserTransaction +(2 |ACTIVE |false|SKU |UserTransaction +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(1 |ACTIVE |false|SKU |UserTransaction +(2 |ACTIVE |false|SKU |UserTransaction +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(1 |PREPARED|false|SKU |UserTransaction +(2 |PREPARED|false|SKU |UserTransaction +recovered start 2 +recovered 0 +recovered end 0 +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(3 |IDLE |NULL |SKU |UserTransaction +A +----------- +0 +1 +3 +noTransaction +morph +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(1 |IDLE |NULL |APP |UserTransaction +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +Statement created in global has holdabilty: false +A +----------- +2001 +2002 +2003 +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +(1 |PREPARED|false|APP |UserTransaction +(1 |IDLE |NULL |APP |UserTransaction +GXID|STATUS |READ&|USERNAME |TYPE +------------------------------------------------------------- +A +----------- +2001 +2002 +2003 +2005 +2007 +A +----------- +A +----------- +2001 +2002 +2003 +2005 +2007 +EXPECTED SQLSTATE(XJ059): Cannot close a connection while a global transaction is still active. +A +----------- +A +----------- +2001 +2002 +2003 +2005 +2007 +derby966 +Local connection holdable true +Local(held) default Statement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held true + BC A=0 + AC A=1 +Local(held) default PreparedStatement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held true + BC A=0 + AC A=1 +Local(held) held Statement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held true + BC A=0 + AC A=1 +Local(held) held PreparedStatement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held true + BC A=0 + AC A=1 +Local(held) close Statement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +Local(held) close PreparedStatement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +Local(close) default Statement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +Local(close) default PreparedStatement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +Local(close) held Statement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held true + BC A=0 + AC A=1 +Local(close) held PreparedStatement holdable true +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held true + BC A=0 + AC A=1 +Local(close) close Statement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredStatement +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedStatement +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +Local(close) close PreparedStatement holdable false +FAIL - mismatched statement & ResultSet +Statement class class org.apache.derby.iapi.jdbc.BrokeredPreparedStatement30 +ResultSet' Statements class class org.apache.derby.impl.jdbc.EmbedPreparedStatement30 +held false + BC A=0 +EXPECTED SQLSTATE(XCL16): ResultSet not open. Operation 'next' not permitted. Verify that autocommit is OFF. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +EXPECTED SQLSTATE(XJ05C): Cannot set holdability ResultSet.HOLD_CURSORS_OVER_COMMIT for a global transaction. +XATest complete Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/XATest.out ------------------------------------------------------------------------------ svn:eol-style = native Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/xa.runall URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/xa.runall?rev=378755&r1=378754&r2=378755&view=diff ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/xa.runall (original) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/xa.runall Sat Feb 18 10:36:02 2006 @@ -4,6 +4,7 @@ jdbcapi/xaSimplePositive.sql jdbcapi/xaSimpleNegative.sql jdbcapi/xaStateTran.sql +jdbcapi/XATest.java jdbcapi/xaMorph.sql store/xaOffline1.sql store/xab2354.sql Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java?rev=378755&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java (added) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java Sat Feb 18 10:36:02 2006 @@ -0,0 +1,983 @@ +/* + + Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk + + Copyright 2006 The Apache Software Foundation or its licensors, as applicable. + + Licensed 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.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import javax.sql.XAConnection; +import javax.sql.XADataSource; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.apache.derby.tools.JDBCDisplayUtil; +import org.apache.derby.tools.ij; +import org.apache.derbyTesting.functionTests.util.TestUtil; +import org.apache.derbyTesting.functionTests.util.XATestUtil; + +/** + * XATests harvested from SQL XA tests. + * Modified so that they can be run with NetworkServer. + */ +public class XATest { + + /** + * Run all the tests. + */ + public static void main(String[] args) throws Exception { + ij.getPropertyArg(args); + Connection dmc = ij.startJBMS(); + + XATestUtil.createXATransactionView(dmc); + dmc.close(); + + XADataSource dsx = TestUtil.getXADataSource(cleanProperties()); + + // tests originally from xaSimplePositive.sql + singleConnectionOnePhaseCommit(dsx); + xaShutdown(); + interleavingTransactions(dsx); + + xaShutdown(); + + // tests originally from xaStateTran.sql + noTransaction(dsx); + + // test originally from xaMorph.sql + morph(dsx); + + // DERBY-966 holdability testing + derby966(dsx); + + System.out.println("XATest complete"); + } + + /** + * Get the basic set of properties for an XADataSource. + * Only sets databaseName to wombat. + * @return + */ + private static Properties cleanProperties() { + Properties dsAttrs = new Properties(); + dsAttrs.setProperty("databaseName", "wombat"); + return dsAttrs; + } + + /** + * Shutdown the database through an XADataSource. + */ + private static void xaShutdown() { + + Properties dsAttrs = cleanProperties(); + + if (TestUtil.isEmbeddedFramework()) + dsAttrs.put("shutdownDatabase", "shutdown"); + else + dsAttrs.put("connectionAttributes", "shutdown=true"); + + XADataSource dsx = TestUtil.getXADataSource(dsAttrs); + + try { + dsx.getXAConnection().getConnection(); + } catch (SQLException sqle) { + if ("08006".equals(sqle.getSQLState())) + return; + TestUtil.dumpSQLExceptions(sqle); + } + System.out.println("FAIL: no exception on shutdown"); + } + + /* + ** Test cases + */ + + /** + * A single connection and 1 phase commit. + * + + Original "SQL" from xaSimplePositive.sql + + 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; + + * @throws SQLException + * @throws XAException + */ + private static void singleConnectionOnePhaseCommit(XADataSource xads) { + System.out.println("singleConnectionOnePhaseCommit"); + try { + XAConnection xac = xads.getXAConnection(); + + XAResource xar = xac.getXAResource(); + + Xid xid = XATestUtil.getXid(0, 32, 46); + + xar.start(xid, XAResource.TMNOFLAGS); + + Connection conn = xac.getConnection(); + + Statement s = conn.createStatement(); + + s.execute("create table foo (a int)"); + s.executeUpdate("insert into foo values (0)"); + + ResultSet rs = s.executeQuery("select * from foo"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + XATestUtil.showXATransactionView(conn); + + s.close(); + xar.end(xid, XAResource.TMSUCCESS); + + // 1 phase commit + xar.commit(xid, true); + + conn.close(); + xac.close(); + + } catch (SQLException sqle) { + TestUtil.dumpSQLExceptions(sqle); + } catch (XAException e) { + XATestUtil.dumpXAException("singleConnectionOnePhaseCommit", e); + } + } + + /* + * Two interleaving transaction and prepare/commit prepare/rollback. + * + * (original test said two connections but only one connection was opened) + + + 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; + + */ + private static void interleavingTransactions(XADataSource xads) { + System.out.println("interleavingTransactions"); + try { + 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.foo values (1)"); + xar.end(xid1, XAResource.TMSUSPEND); + + xar.start(xid2, XAResource.TMNOFLAGS); + s.executeUpdate("insert into APP.foo values (2)"); + xar.end(xid2, XAResource.TMSUSPEND); + + xar.start(xid1, XAResource.TMRESUME); + s.executeUpdate("insert into APP.foo values (3)"); + xar.end(xid1, XAResource.TMSUSPEND); + + xar.start(xid2, XAResource.TMRESUME); + s.executeUpdate("insert into APP.foo values (4)"); + + XATestUtil.showXATransactionView(conn); + + // this prepare won't work since + // transaction 1 has been suspended - XA_PROTO + try { + xar.prepare(xid1); + System.out.println("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.showXATransactionView(conn); + + xar.end(xid2, XAResource.TMSUCCESS); + + xar.end(xid1, XAResource.TMSUCCESS); + + xar.prepare(xid1); + xar.prepare(xid2); + + // both should be prepared. + XATestUtil.showXATransactionView(conn); + + Xid[] recoveredStart = xar.recover(XAResource.TMSTARTRSCAN); + System.out.println("recovered start " + recoveredStart.length); + Xid[] recovered = xar.recover(XAResource.TMNOFLAGS); + System.out.println("recovered " + recovered.length); + Xid[] recoveredEnd = xar.recover(XAResource.TMENDRSCAN); + System.out.println("recovered end " + 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 { + System.out.println("FAIL: unknown xact"); + } + } + + // check the results + Xid xid3 = XATestUtil.getXid(3, 2, 101); + xar.start(xid3, XAResource.TMNOFLAGS); + XATestUtil.showXATransactionView(conn); + ResultSet rs = s.executeQuery("select * from APP.foo"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + xar.end(xid3, XAResource.TMSUCCESS); + + int pr = xar.prepare(xid3); + if (pr != XAResource.XA_RDONLY) + System.out.println("FAIL - prepare on read only xact returned " + + pr); + + try { + xar.commit(xid3, true); + System.out.println("FAIL - 2pc commit on read-only xact"); + } catch (XAException e) { + if (e.errorCode != XAException.XAER_NOTA) + throw e; + } + + s.close(); + conn.close(); + xac.close(); + } catch (SQLException sqle) { + TestUtil.dumpSQLExceptions(sqle); + } catch (XAException e) { + XATestUtil.dumpXAException("interleavingTransactions", e); + } + } + + /** + Tests on INIT STATE (no tr + Original SQL from xaStateTran.sql. + + + -- 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; + + */ + private static void noTransaction(XADataSource xads) { + System.out.println("noTransaction"); + try { + 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; + } + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e); + } catch (XAException e) { + XATestUtil.dumpXAException("noTransaction", e); + } + } + + /** + * Morph a connection between local anf global transactions. + */ + private static void morph(XADataSource xads) { + System.out.println("morph"); + + try { + 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.foo values (2001)"); + XATestUtil.showXATransactionView(conn); + 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.foo values (2002)"); + XATestUtil.showXATransactionView(conn); + + /* + -- 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); + XATestUtil.showXATransactionView(conn); + s.executeUpdate("insert into APP.foo values (2003)"); + + /* + -- disallowed + commit; + -- disallowed + rollback; + -- disallowed + autocommit on; + -- OK + autocommit off; + */ + try { + conn.commit(); + System.out.println("FAIL: commit allowed in global xact"); + } catch (SQLException e) { + } + + try { + conn.rollback(); + System.out.println("FAIL: roll back allowed in global xact"); + } catch (SQLException e) { + } + try { + conn.setAutoCommit(true); + System.out + .println("FAIL: setAutoCommit(true) allowed in global xact"); + } catch (SQLException e) { + } + conn.setAutoCommit(false); + + // s was created in local mode so it has holdibilty + // set, + try { + s.executeQuery("select * from APP.foo where A >= 2000"); + System.out.println("FAIL: query with holdable statement"); + } catch (SQLException sqle) { + TestUtil.dumpSQLExceptions(sqle, true); + } + s.close(); + + s = conn.createStatement(); + boolean holdable = s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT; + System.out.println("Statement created in global has holdabilty: " + + holdable); + + /* + select * from foo; + xa_end xa_success 1; + xa_prepare 1; + */ + ResultSet rs = s + .executeQuery("select * from APP.foo where A >= 2000"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + xar.end(xid, XAResource.TMSUCCESS); + xar.prepare(xid); + + /* + -- dup id + xa_start xa_noflags 1; + */ + try { + xar.start(xid, XAResource.TMNOFLAGS); + System.out.println("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(); + System.out.println("FAIL: commit allowed in global xact"); + } catch (SQLException e) { + } + try { + conn.rollback(); + System.out.println("FAIL: roll back allowed in global xact"); + } catch (SQLException e) { + } + try { + conn.setAutoCommit(true); + System.out + .println("FAIL: setAutoCommit(true) allowed in global xact"); + } catch (SQLException e) { + } + conn.setAutoCommit(false); + + xar.end(xid2, XAResource.TMSUSPEND); + + // DERBY-1004 + if (TestUtil.isDerbyNetClientFramework()) + conn.close(); + + /* + -- 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.foo values (2005)"); + conn.commit(); + conn.setAutoCommit(false); + s.executeUpdate("insert into APP.foo values (2006)"); + conn.rollback(); + s.executeUpdate("insert into APP.foo values (2007)"); + conn.commit(); + + XATestUtil.showXATransactionView(conn); + /* + -- 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.showXATransactionView(conn); + rs = s.executeQuery("select * from APP.foo where A >= 2000"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + 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.foo"); + rs = s.executeQuery("select * from APP.foo"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + // DERBY-1004 + if (TestUtil.isDerbyNetClientFramework()) + conn.rollback(); + /* + -- yanking a local connection away should rollback the changes + */ + conn = xac.getConnection(); + conn.setAutoCommit(false); + s = conn.createStatement(); + rs = s.executeQuery("select * from APP.foo where A >= 2000"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + /* + -- 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); + } 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; + */ + try { + xac.getConnection(); + System.out + .println("FAIL: getConnection with active global xact"); + } catch (SQLException sqle) { + TestUtil.dumpSQLExceptions(sqle, true); + } + /* + 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.foo"); + rs = s.executeQuery("select * from APP.foo where A >= 2000"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + 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.foo where A >= 2000"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + + s.close(); + conn.close(); + + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e); + e.printStackTrace(System.out); + } catch (XAException e) { + XATestUtil.dumpXAException("morph", e); + } + + } + + /** + * Derby-966 holdability and global/location transactions. + * (work in progress) + */ + private static void derby966(XADataSource xads) + { + System.out.println("derby966"); + + try { + 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. + + boolean hold = + conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT; + + System.out.println("Local connection holdable " + hold); + + Statement sdh = conn.createStatement(); + showHoldStatus("Local(held) default ", sdh); + checkHeldRS(conn, sdh, sdh.executeQuery("select * from app.foo")); + PreparedStatement psdh = conn.prepareStatement("SELECT * FROM APP.FOO"); + showHoldStatus("Local(held) default Prepared", psdh); + checkHeldRS(conn, psdh, psdh.executeQuery()); + + Statement shh = conn.createStatement( + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT); + showHoldStatus("Local(held) held ", shh); + checkHeldRS(conn, shh, shh.executeQuery("select * from app.foo")); + PreparedStatement pshh = + conn.prepareStatement("SELECT * FROM APP.FOO", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT); + showHoldStatus("Local(held) held Prepared", pshh); + checkHeldRS(conn, pshh, pshh.executeQuery()); + + Statement sch = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + showHoldStatus("Local(held) close ", sch); + checkHeldRS(conn, sch, sch.executeQuery("select * from app.foo")); + PreparedStatement psch = + conn.prepareStatement("SELECT * FROM APP.FOO", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + showHoldStatus("Local(held) close Prepared", psch); + checkHeldRS(conn, psch, psch.executeQuery()); + + // set the connection's holdabilty to false + conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + + Statement sdc = conn.createStatement(); + showHoldStatus("Local(close) default ", sdc); + checkHeldRS(conn, sdc, sdc.executeQuery("select * from app.foo")); + PreparedStatement psdc = conn.prepareStatement("SELECT * FROM APP.FOO"); + showHoldStatus("Local(close) default Prepared", psdc); + checkHeldRS(conn, psdc, psdc.executeQuery()); + + Statement shc = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT); + showHoldStatus("Local(close) held ", shc); + checkHeldRS(conn, shc, shc.executeQuery("select * from app.foo")); + PreparedStatement pshc = + conn.prepareStatement("SELECT * FROM APP.FOO", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.HOLD_CURSORS_OVER_COMMIT); + showHoldStatus("Local(close) held Prepared", pshc); + checkHeldRS(conn, pshc, pshc.executeQuery()); + + Statement scc = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + showHoldStatus("Local(close) close ", scc); + checkHeldRS(conn, scc, scc.executeQuery("select * from app.foo")); + PreparedStatement pscc = + conn.prepareStatement("SELECT * FROM APP.FOO", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, + ResultSet.CLOSE_CURSORS_AT_COMMIT); + showHoldStatus("Local(close) close Prepared", pscc); + checkHeldRS(conn, pscc, pscc.executeQuery()); + + // Revert back to holdable + conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT); + + ResultSet rs = sdh.executeQuery("SELECT * FROM APP.FOO"); + conn.commit(); + rs.next(); + + // start a global xact and test those statements. + xar.start(xid, XAResource.TMNOFLAGS); + + // Statements obtained while default was hold. + // Only sch should work as held cursors not supported in XA + try { + sdh.executeQuery("SELECT * FROM APP.FOO").close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + try { + shh.executeQuery("SELECT * FROM APP.FOO").close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + sch.executeQuery("SELECT * FROM APP.FOO").close(); + + // PreparedStatements obtained while default was hold. + // Only sch should work as held cursors not supported in XA + try { + psdh.executeQuery().close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + try { + pshh.executeQuery().close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + psch.executeQuery().close(); + + // Statements obtained while default was close. + // Only sch should work as held cursors not supported in XA + sdc.executeQuery("SELECT * FROM APP.FOO").close(); + try { + shc.executeQuery("SELECT * FROM APP.FOO").close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + scc.executeQuery("SELECT * FROM APP.FOO").close(); + + // PreparedStatements obtained while default was close. + psdc.executeQuery().close(); + try { + pshc.executeQuery().close(); + System.out.println("FAIL - held Statement in global"); + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e, true); + } + pscc.executeQuery().close(); + + } catch (SQLException e) { + TestUtil.dumpSQLExceptions(e); + e.printStackTrace(System.out); + } catch (XAException e) { + XATestUtil.dumpXAException("derby966", e); + } + } + + /** + * 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 + { + if (s.getConnection() != conn) + System.out.println("FAIL - mismatched statement & Connection"); + if (rs.getStatement() != s) + { + // DERBY-1009 + System.out.println("FAIL - mismatched statement & ResultSet"); + System.out.println("Statement class " + s.getClass()); + System.out.println("ResultSet' Statements class " + rs.getStatement().getClass()); + } + + boolean held = s.getResultSetHoldability() == + ResultSet.HOLD_CURSORS_OVER_COMMIT; + + + + System.out.println("held " + held); + + rs.next(); + System.out.println(" BC A=" + rs.getInt(1)); + conn.commit(); + + try { + rs.next(); + rs.getInt(1); + System.out.println(" AC A=" + rs.getInt(1)); + if (!held) + System.out.println("FAIL: non-held cursor not closed by commit"); + } catch (SQLException sqle) + { + TestUtil.dumpSQLExceptions(sqle, !held); + } + + rs.close(); + conn.commit(); + } + + /** + * Show the held status of the Statement. + */ + private static void showHoldStatus(String tag, Statement s) throws SQLException + { + boolean hold = + s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT; + + System.out.println(tag + "Statement holdable " + hold); + } +} Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/XATest.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/build.xml URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/build.xml?rev=378755&r1=378754&r2=378755&view=diff ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/build.xml (original) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/build.xml Sat Feb 18 10:36:02 2006 @@ -79,6 +79,7 @@ + @@ -108,6 +109,7 @@ + Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java?rev=378755&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java (added) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java Sat Feb 18 10:36:02 2006 @@ -0,0 +1,180 @@ +/* + + Derby - Class org.apache.derby.impl.services.bytecode.CodeChunk + + Copyright 2006 The Apache Software Foundation or its licensors, as applicable. + + Licensed 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.util; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.Xid; + +import org.apache.derby.tools.JDBCDisplayUtil; + +public class XATestUtil { + + /** + * Return a new Xid for testing. + */ + public static Xid getXid(int xid, int b1, int b2) { + return new utilXid(xid, b1, b2); + } + + /** + * Dump an unexpected XAException. + * @param tag Useful info to print + * @param xae The exception + */ + public static void dumpXAException(String tag, XAException xae) { + + System.out.println(tag + " : XAException - " + xae.getMessage() + + " errorCode " + errorCode(xae)); + xae.printStackTrace(System.out); + } + + /** + * Create a view that allows useful inspection of the active + * global transactions. + */ + public static void createXATransactionView(Connection conn) throws SQLException + { + Statement s = conn.createStatement(); + s.execute( + "create view XATESTUTIL.global_xactTable as " + + "select cast(global_xid as char(2)) as gxid," + + " status, " + + " case when first_instant is NULL then 'NULL' else 'false' end as readOnly, " + + " cast (username as char(10)) as username, type " + + " from syscs_diag.transaction_table"); + s.close(); + } + + /** + * Display the active global transactions. + * @param conn + * @throws SQLException + */ + public static void showXATransactionView(Connection conn) throws SQLException + { + Statement s = conn.createStatement(); + ResultSet rs = s.executeQuery( + "select * from XATESTUTIL.global_xactTable where gxid is not null order by gxid"); + JDBCDisplayUtil.DisplayResults(System.out, rs, conn); + rs.close(); + } + + /** + * Return a string for the error code of the XAException. + */ + public static String errorCode(XAException e) + { + String error; + switch(e.errorCode) + { + case XAException.XA_HEURCOM : error = "XA_HEURCOM "; break; + case XAException.XA_HEURHAZ : error = "XA_HEURHAZ"; break; + case XAException.XA_HEURMIX : error = "XA_HEURMIX"; break; + case XAException.XA_HEURRB : error = "XA_HEURRB "; break; + case XAException.XA_NOMIGRATE : error = "XA_NOMIGRATE "; break; + case XAException.XA_RBCOMMFAIL : error = "XA_RBCOMMFAIL "; break; + case XAException.XA_RBDEADLOCK : error = "XA_RBDEADLOCK "; break; + case XAException.XA_RBINTEGRITY : error = "XA_RBINTEGRITY "; break; + case XAException.XA_RBOTHER : error = "XA_RBOTHER "; break; + case XAException.XA_RBPROTO : error = "XA_RBPROTO "; break; + case XAException.XA_RBROLLBACK : error = "XA_RBROLLBACK "; break; + case XAException.XA_RBTIMEOUT : error = "XA_RBTIMEOUT "; break; + case XAException.XA_RBTRANSIENT : error = "XA_RBTRANSIENT "; break; + case XAException.XA_RDONLY : error = "XA_RDONLY "; break; + case XAException.XA_RETRY : error = "XA_RETRY "; break; + case XAException.XAER_ASYNC : error = "XAER_ASYNC "; break; + case XAException.XAER_DUPID : error = "XAER_DUPID "; break; + case XAException.XAER_INVAL : error = "XAER_INVAL "; break; + case XAException.XAER_NOTA : error = "XAER_NOTA "; break; + case XAException.XAER_OUTSIDE : error = "XAER_OUTSIDE "; break; + case XAException.XAER_PROTO : error = "XAER_PROTO "; break; + case XAException.XAER_RMERR : error = "XAER_RMERR "; break; + case XAException.XAER_RMFAIL : error = "XAER_RMFAIL "; break; + default: error = Integer.toString(e.errorCode); break; + } + return error; + } + +} +/** + * Simple utility class implementation of Xid for tests. + * + */ +class utilXid implements Xid, Serializable { + private static final long serialVersionUID = 64467338100036L; + + private final int format_id; + + private byte[] global_id; + + private byte[] branch_id; + + utilXid(int xid, int b1, int b2) { + format_id = xid; + global_id = new byte[Xid.MAXGTRIDSIZE]; + branch_id = new byte[Xid.MAXBQUALSIZE]; + + for (int i = 0; i < global_id.length; i++) { + global_id[i] = (byte) (b1 + i); + } + + for (int i = 0; i < branch_id.length; i++) { + branch_id[i] = (byte) (b2 + i); + } + } + + /** + * Obtain the format id part of the Xid. + *

+ * + * @return Format identifier. O means the OSI CCR format. + **/ + public int getFormatId() { + return (format_id); + } + + /** + * Obtain the global transaction identifier part of XID as an array of + * bytes. + *

+ * + * @return A byte array containing the global transaction identifier. + **/ + public byte[] getGlobalTransactionId() { + return (global_id); + } + + /** + * Obtain the transaction branch qualifier part of the Xid in a byte array. + *

+ * + * @return A byte array containing the branch qualifier of the transaction. + **/ + public byte[] getBranchQualifier() { + return (branch_id); + } +} \ No newline at end of file Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/XATestUtil.java ------------------------------------------------------------------------------ svn:eol-style = native