Skip to Content.
Sympa Menu

notify-dpml - svn commit: r1771 - in development/laboratory/planet/components/tx/impl/src: main/net main/net/dpml main/net/dpml/tx main/net/dpml/tx/context main/net/dpml/tx/log main/net/dpml/tx/manager test/net test/net/dpml test/net/dpml/tx test/net/dpml/tx/tests test/net/dpml/tx/tests/context test/net/dpml/tx/tests/log test/net/dpml/tx/tests/manager

notify-dpml AT lists.ibiblio.org

Subject: DPML Notify

List archive

Chronological Thread  
  • From: niclas AT netcompartner.com
  • To: notify-dpml AT lists.ibiblio.org
  • Subject: svn commit: r1771 - in development/laboratory/planet/components/tx/impl/src: main/net main/net/dpml main/net/dpml/tx main/net/dpml/tx/context main/net/dpml/tx/log main/net/dpml/tx/manager test/net test/net/dpml test/net/dpml/tx test/net/dpml/tx/tests test/net/dpml/tx/tests/context test/net/dpml/tx/tests/log test/net/dpml/tx/tests/manager
  • Date: Sun, 13 Feb 2005 09:38:17 +0100

Author: niclas
Date: Sun Feb 13 09:38:17 2005
New Revision: 1771

Added:
development/laboratory/planet/components/tx/impl/src/main/net/
development/laboratory/planet/components/tx/impl/src/main/net/dpml/
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ConnectionReleaser.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DefaultInstanceContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DoubleKeyedHashMap.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ExtendedTransactionManager.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ImportedTransactionActiveException.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/InstanceContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/OnlineUserTransaction.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/TrackedConnectionAssociator.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/UserTransactionImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/XAWork.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/BeanTransactionContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/ContainerTransactionContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/InheritableTransactionContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContext.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContextManager.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/UnspecifiedTransactionContext.java
(contents, props changed)
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/UnrecoverableLog.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/XidImpl2.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/LogException.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/NamedXAResource.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/Recovery.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/RecoveryImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/ResourceManager.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfo.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfoImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionLog.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionManagerImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionTimer.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/WrapperNamedXAResource.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactory.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactoryImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImpl.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImporter.java
(contents, props changed)
development/laboratory/planet/components/tx/impl/src/test/net/
development/laboratory/planet/components/tx/impl/src/test/net/dpml/
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/context/

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/context/TransactionContextManagerTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/log/

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/log/AbstractLogTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/AbstractRecoveryTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLog.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLogRecoveryTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResource.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResourceManager.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/ProtocolTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/RecoveryTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TestTransactionManager.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TransactionManagerImplTest.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XATransactionTester.java
(contents, props changed)

development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XidImporterTest.java
(contents, props changed)
Log:
First cut at an JTA implementation, to be crafted to fit the Metro platform.

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ConnectionReleaser.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ConnectionReleaser.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface ConnectionReleaser
+{
+ /** Called after the completion of a transaction.
+ *
+ * @param managedConnectionInfo
+ */
+ void afterCompletion(Object managedConnectionInfo);
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DefaultInstanceContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DefaultInstanceContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,93 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Simple implementation of ComponentContext satisfying invariant.
+ *
+ * @version $Rev: 54714 $ $Date: 2004-10-13 08:38:55 +0800 (Wed, 13 Oct
2004) $
+ *
+ * */
+public class DefaultInstanceContext
+ implements InstanceContext
+{
+
+ private final Map m_connectionManagerMap = new HashMap();
+ private final Set m_unshareableResources;
+ private final Set m_applicationManagedSecurityResources;
+
+ public DefaultInstanceContext( Set unshareableResources, Set
applicationManagedSecurityResources )
+ {
+ this.m_unshareableResources = unshareableResources;
+ this.m_applicationManagedSecurityResources =
applicationManagedSecurityResources;
+ }
+
+ public Object getId()
+ {
+ return null;
+ }
+
+ public void setId( Object id )
+ {
+ }
+
+ public Object getContainerId() {
+ return null;
+ }
+
+ public void associate()
+ throws Exception
+ {
+ }
+
+ public void flush()
+ throws Exception
+ {
+ }
+
+ public void beforeCommit()
+ throws Exception
+ {
+ }
+
+ public void afterCommit( boolean status )
+ throws Exception
+ {
+ }
+
+ public Map getConnectionManagerMap()
+ {
+ return m_connectionManagerMap;
+ }
+
+ public Set getUnshareableResources()
+ {
+ return m_unshareableResources;
+ }
+
+ public Set getApplicationManagedSecurityResources()
+ {
+ return m_applicationManagedSecurityResources;
+ }
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DoubleKeyedHashMap.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/DoubleKeyedHashMap.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collection;
+
+/**
+ * <b>Really</b> stupid implementation of a double keyed map.
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public final class DoubleKeyedHashMap
+{
+ private final Map m_map = new HashMap();
+
+ public Object put( Object key1, Object key2, Object value )
+ {
+ return m_map.put( new Key( key1, key2 ), value );
+ }
+
+ public Object get( Object key1, Object key2 )
+ {
+ return m_map.get (new Key( key1, key2 ) );
+ }
+
+ public Object remove( Object key1, Object key2 )
+ {
+ return m_map.remove( new Key( key1, key2 ) );
+ }
+
+ public Collection values()
+ {
+ return m_map.values();
+ }
+
+ public void clear()
+ {
+ m_map.clear();
+ }
+
+ public boolean isEmpty()
+ {
+ return m_map.isEmpty();
+ }
+
+ private final static class Key
+ {
+ private final Object m_part1;
+ private final Object m_part2;
+
+ public Key( Object part1, Object part2 )
+ {
+ this.m_part1 = part1;
+ this.m_part2 = part2;
+ }
+
+ public int hashCode()
+ {
+ return m_part1.hashCode() ^ m_part2.hashCode();
+ }
+
+ public boolean equals( Object obj )
+ {
+ if( obj instanceof Key == false )
+ {
+ return false;
+ }
+ Key other = (Key) obj;
+ if( ! this.m_part1.equals(other.m_part1) )
+ return false;
+ return this.m_part2.equals( other.m_part2 );
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ExtendedTransactionManager.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ExtendedTransactionManager.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,19 @@
+package net.dpml.tx;
+
+import java.util.Map;
+import javax.transaction.TransactionManager;
+import javax.transaction.NotSupportedException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+/**
+ */
+public interface ExtendedTransactionManager extends TransactionManager
+{
+
+ Transaction begin( long transactionTimeoutMilliseconds )
+ throws NotSupportedException, SystemException;
+
+ Map getExternalXids();
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ImportedTransactionActiveException.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/ImportedTransactionActiveException.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,22 @@
+package net.dpml.tx;
+
+import javax.transaction.xa.Xid;
+
+/**
+ */
+public class ImportedTransactionActiveException extends Exception
+{
+
+ private final Xid m_xid;
+
+ public ImportedTransactionActiveException(Xid xid)
+ {
+ this.m_xid = xid;
+ }
+
+ public Xid getXid()
+ {
+ return m_xid;
+ }
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/InstanceContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/InstanceContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,59 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface InstanceContext
+{
+ Object getId();
+
+ void setId( Object id );
+
+ Object getContainerId();
+
+ void associate()
+ throws Throwable;
+
+ void flush()
+ throws Throwable;
+
+ void beforeCommit()
+ throws Exception;
+
+ void afterCommit(boolean status)
+ throws Exception;
+
+ /**
+ * IMPORTANT INVARIANT: this should always return a map, never null.
+ * @return map of ConnectionManager to (list of ) managed connection
info objects.
+ */
+ Map getConnectionManagerMap();
+
+ Set getUnshareableResources();
+
+ Set getApplicationManagedSecurityResources();
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/OnlineUserTransaction.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/OnlineUserTransaction.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,124 @@
+package net.dpml.tx;
+
+import java.io.Serializable;
+import javax.transaction.UserTransaction;
+import javax.transaction.SystemException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.xa.XAException;
+
+// kill j2ee deps; import javax.resource.ResourceException;
+
+import net.dpml.lang.NullArgumentException;
+import net.dpml.tx.context.BeanTransactionContext;
+import net.dpml.tx.context.TransactionContext;
+import net.dpml.tx.context.TransactionContextManager;
+import net.dpml.tx.context.UnspecifiedTransactionContext;
+
+
+/**
+ */
+public final class OnlineUserTransaction
+ implements UserTransaction, Serializable
+{
+ private transient TransactionContextManager m_transactionContextManager;
+ private transient TrackedConnectionAssociator
m_trackedConnectionAssociator;
+
+ boolean isActive()
+ {
+ return m_transactionContextManager != null;
+ }
+
+ public void setUp( TransactionContextManager transactionContextManager,
+ TrackedConnectionAssociator
trackedConnectionAssociator )
+ {
+ this.m_transactionContextManager = transactionContextManager;
+ this.m_trackedConnectionAssociator = trackedConnectionAssociator;
+ }
+
+
+ public int getStatus() throws SystemException
+ {
+ return m_transactionContextManager.getStatus();
+ }
+
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ m_transactionContextManager.setRollbackOnly();
+ }
+
+ public void setTransactionTimeout( int seconds )
+ throws SystemException
+ {
+ if (seconds < 0)
+ {
+ throw new SystemException( "transaction timeout must be positive
or 0, not " + seconds );
+ }
+ m_transactionContextManager.setTransactionTimeout(seconds);
+ }
+
+ public void begin()
+ throws NotSupportedException, SystemException
+ {
+ m_transactionContextManager.newBeanTransactionContext( 0L );
+
+ if( m_trackedConnectionAssociator != null )
+ {
+ try
+ {
+ m_trackedConnectionAssociator.newTransaction();
+ }
+ catch( XAException e )
+ {
+ throw (SystemException) new SystemException().initCause(e);
+ }
+ }
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
+ IllegalStateException, RollbackException, SecurityException,
+ SystemException
+ {
+ TransactionContext ctx = m_transactionContextManager.getContext();
+ if( ctx instanceof BeanTransactionContext == false )
+ {
+ throw new IllegalStateException( "Transaction has not been
started" );
+ }
+ BeanTransactionContext beanContext = (BeanTransactionContext) ctx;
+ try
+ {
+ beanContext.commit();
+ }
+ finally
+ {
+ UnspecifiedTransactionContext oldContext =
beanContext.getOldContext();
+ m_transactionContextManager.setContext( oldContext );
+ oldContext.resume();
+ }
+ }
+
+ public void rollback()
+ throws IllegalStateException, SecurityException, SystemException
+ {
+ TransactionContext ctx = m_transactionContextManager.getContext();
+ if( ctx instanceof BeanTransactionContext == false )
+ {
+ throw new IllegalStateException("Transaction has not been
started");
+ }
+ BeanTransactionContext beanContext = (BeanTransactionContext) ctx;
+ try
+ {
+ beanContext.rollback();
+ }
+ finally
+ {
+ UnspecifiedTransactionContext oldContext =
beanContext.getOldContext();
+ m_transactionContextManager.setContext(oldContext);
+ oldContext.resume();
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/TrackedConnectionAssociator.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/TrackedConnectionAssociator.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+// kill J2EE deps; import javax.resource.ResourceException;
+import javax.transaction.xa.XAException;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface TrackedConnectionAssociator
+{
+
+ InstanceContext enter( InstanceContext newInstanceContext )
+ throws XAException;
+
+ void newTransaction()
+ throws XAException;
+
+ void exit( InstanceContext instanceContext )
+ throws XAException;
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/UserTransactionImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/UserTransactionImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,164 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+import java.io.Serializable;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+import net.dpml.tx.context.TransactionContextManager;
+
+/**
+ * Implementation of UserTransaction for use in an EJB.
+ * This adds the ability to enable or disable the operations depending on
+ * the lifecycle of the EJB instance.
+ *
+ * @version $Rev: 71492 $ $Date: 2004-11-15 13:31:50 +0800 (Mon, 15 Nov
2004) $
+ */
+public class UserTransactionImpl
+ implements UserTransaction, Serializable
+{
+ private final ThreadLocal m_state = new StateThreadLocal();
+
+ private static class StateThreadLocal extends ThreadLocal
+ implements Serializable
+ {
+ protected Object initialValue()
+ {
+ return OFFLINE;
+ }
+ }
+
+ public UserTransactionImpl()
+ {
+ m_state.set(OFFLINE);
+ }
+
+ public void setUp( TransactionContextManager transactionContextManager,
TrackedConnectionAssociator trackedConnectionAssociator )
+ {
+ if( isOnline() )
+ throw new InternalError( "Only set the tx manager when
UserTransaction is stop" );
+ this.ONLINE.setUp(transactionContextManager,
trackedConnectionAssociator);
+ }
+
+ public boolean isOnline()
+ {
+ return m_state.get() == ONLINE;
+ }
+
+ public void setOnline( boolean online )
+ {
+ //too bad there's no implies operation
+ // online implies transactionContextManager != null
+ if( online && !ONLINE.isActive() )
+ throw new InternalError( "online requires a tx manager" );
+ m_state.set( online ? ONLINE : OFFLINE );
+ }
+
+ private UserTransaction getUserTransaction()
+ {
+ return (UserTransaction) m_state.get();
+ }
+
+ public void begin()
+ throws NotSupportedException, SystemException
+ {
+ getUserTransaction().begin();
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
IllegalStateException,
+ RollbackException, SecurityException, SystemException
+ {
+ getUserTransaction().commit();
+ }
+
+ public int getStatus()
+ throws SystemException
+ {
+ return getUserTransaction().getStatus();
+ }
+
+ public void rollback()
+ throws IllegalStateException, SecurityException, SystemException
+ {
+ getUserTransaction().rollback();
+ }
+
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ getUserTransaction().setRollbackOnly();
+ }
+
+ public void setTransactionTimeout(int timeout)
+ throws SystemException
+ {
+ getUserTransaction().setTransactionTimeout(timeout);
+ }
+
+ private final OnlineUserTransaction ONLINE = new OnlineUserTransaction();
+
+ private static final UserTransaction OFFLINE = new
OfflineUserTransaction();
+
+ private static final class OfflineUserTransaction
+ implements UserTransaction, Serializable
+ {
+ public void begin()
+ throws NotSupportedException, SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
IllegalStateException,
+ RollbackException, SecurityException, SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+
+ public int getStatus()
+ throws SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+
+ public void rollback()
+ throws IllegalStateException, SecurityException, SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+
+ public void setTransactionTimeout( int seconds )
+ throws SystemException
+ {
+ throw new IllegalStateException("Cannot use UserTransaction
methods in this state");
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/XAWork.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/XAWork.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx;
+
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAException;
+import javax.transaction.SystemException;
+import javax.transaction.InvalidTransactionException;
+
+/**
+ * primarily an interface between the WorkManager/ExecutionContext and the
tm.
+ *
+ * @version $Rev: 54013 $ $Date: 2004-10-08 04:49:55 +0800 (Fri, 08 Oct
2004) $
+ *
+ * */
+public interface XAWork
+{
+ void begin( Xid xid, long txTimeout )
+ throws XAException, InvalidTransactionException, SystemException,
ImportedTransactionActiveException;
+
+ void end(Xid xid)
+ throws XAException, SystemException;
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/BeanTransactionContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/BeanTransactionContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,138 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import net.dpml.tx.ExtendedTransactionManager;
+
+/**
+ *
+ *
+ * @version $Rev: 55410 $ $Date: 2004-10-24 10:14:58 +0800 (Sun, 24 Oct
2004) $
+ */
+public class BeanTransactionContext extends InheritableTransactionContext
+{
+ private final ExtendedTransactionManager m_txnManager;
+ private final UnspecifiedTransactionContext m_oldContext;
+ private Transaction transaction;
+
+ public BeanTransactionContext( ExtendedTransactionManager txnManager,
UnspecifiedTransactionContext oldContext )
+ {
+ this.m_txnManager = txnManager;
+ this.m_oldContext = oldContext;
+ }
+
+ public UnspecifiedTransactionContext getOldContext()
+ {
+ return m_oldContext;
+ }
+
+ public void begin( long transactionTimeoutMilliseconds )
+ throws SystemException, NotSupportedException
+ {
+ transaction = m_txnManager.begin(transactionTimeoutMilliseconds);
+ }
+
+ public void suspend()
+ throws SystemException
+ {
+ Transaction suspendedTransaction = m_txnManager.suspend();
+// assert (transaction == suspendedTransaction) : "suspend did not
return our transaction";
+ if( transaction != suspendedTransaction )
+ {
+ throw new InternalError( "suspend did not return our
transaction. ours: " + transaction + ", suspended returned: " +
suspendedTransaction );
+ }
+ }
+
+ public void resume()
+ throws SystemException, InvalidTransactionException
+ {
+ m_txnManager.resume( transaction );
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
+ RollbackException, SystemException
+ {
+ try
+ {
+ try
+ {
+ flushState();
+ }
+ catch( Throwable t )
+ {
+ try
+ {
+ m_txnManager.rollback();
+ }
+ catch( Throwable t1 )
+ {
+ System.err.println( "Unable to roll back transaction" );
+ t1.printStackTrace();
+ }
+ throw (RollbackException) new RollbackException( "Could not
flush state before commit" ).initCause( t );
+ }
+ m_txnManager.commit();
+ }
+ finally
+ {
+ connectorAfterCommit();
+ transaction = null;
+ }
+ }
+
+ public void rollback()
+ throws SystemException
+ {
+ try
+ {
+ m_txnManager.rollback();
+ }
+ finally
+ {
+ connectorAfterCommit();
+ transaction = null;
+ }
+ }
+
+ //Geronimo connector framework support
+
+ public boolean isActive()
+ {
+ try
+ {
+ return m_txnManager.getStatus() == Status.STATUS_ACTIVE;
+ } catch (SystemException e) {
+ return false;
+ }
+ }
+
+ public Transaction getTransaction()
+ {
+ return transaction;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/ContainerTransactionContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/ContainerTransactionContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,304 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+
+/**
+ * @version $Rev: 54719 $ $Date: 2004-10-13 09:28:03 +0800 (Wed, 13 Oct
2004) $
+ */
+public class ContainerTransactionContext extends
InheritableTransactionContext
+{
+ private final TransactionManager m_txnManager;
+ private Transaction m_transaction;
+
+ private boolean threadAssociated = false;
+
+ public ContainerTransactionContext( TransactionManager txnManager )
+ throws SystemException, NotSupportedException
+ {
+ this.m_txnManager = txnManager;
+ txnManager.begin();
+ m_transaction = txnManager.getTransaction();
+ threadAssociated = true;
+ }
+
+ public ContainerTransactionContext( TransactionManager txnManager,
Transaction transaction )
+ {
+ this.m_txnManager = txnManager;
+ this.m_transaction = transaction;
+ }
+
+ public void suspend()
+ throws SystemException
+ {
+ Transaction suspendedTransaction = m_txnManager.suspend();
+// assert (transaction == suspendedTransaction) : "suspend did not
return our transaction. ours: " + transaction + ", suspended returned: " +
suspendedTransaction;
+ if( m_transaction != suspendedTransaction )
+ {
+ throw new InternalError( "suspend did not return our
transaction. ours: " + m_transaction + ", suspended returned: " +
suspendedTransaction );
+ }
+ threadAssociated = false;
+ }
+
+ public void resume()
+ throws SystemException, InvalidTransactionException
+ {
+ m_txnManager.resume( m_transaction );
+ threadAssociated = true;
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
+ SystemException, RollbackException
+ {
+ boolean wasCommitted = false;
+ try
+ {
+ if( checkRolledback() )
+ {
+ return;
+ }
+ flushState();
+ if( checkRolledback() )
+ {
+ return;
+ }
+
+ // todo we need to flush anyone enrolled during before and then
call before on any flushed...
+ beforeCommit();
+
+ if( checkRolledback() )
+ {
+ return;
+ }
+
+ m_txnManager.commit();
+ wasCommitted = true;
+ }
+ catch( Throwable t )
+ {
+ rollbackAndThrow( "Unable to commit container transaction", t );
+ }
+ finally
+ {
+ try
+ {
+ afterCommit(wasCommitted);
+ }
+ catch (Exception e)
+ {
+ rollbackAndThrow( "After commit of container transaction
failed", e );
+ }
+ finally
+ {
+ connectorAfterCommit();
+ m_transaction = null;
+ }
+ }
+ }
+
+ private boolean checkRolledback()
+ throws SystemException
+ {
+ int status;
+ try
+ {
+ status = m_transaction.getStatus();
+ }
+ catch( SystemException e )
+ {
+ m_txnManager.rollback();
+ throw e;
+ }
+
+ if( status == Status.STATUS_MARKED_ROLLBACK )
+ {
+ // we need to rollback
+ m_txnManager.rollback();
+ return true;
+ }
+ else if( status == Status.STATUS_ROLLEDBACK ||
+ status == Status.STATUS_ROLLING_BACK)
+ {
+ // already rolled back
+ return true;
+ }
+ return false;
+ }
+
+ private void rollbackAndThrow( String message, Throwable throwable )
+ throws HeuristicMixedException, HeuristicRollbackException,
+ SystemException, RollbackException
+ {
+ try
+ {
+ // just incase there is a junk transaction on the thread
+ if( m_txnManager.getStatus() != Status.STATUS_NO_TRANSACTION )
+ {
+ m_txnManager.rollback();
+ }
+ }
+ catch( Throwable t )
+ {
+ System.err.println( "Unable to roll back transaction" );
+ t.printStackTrace();
+ }
+
+ if( throwable instanceof HeuristicMixedException )
+ {
+ throw (HeuristicMixedException) throwable;
+ }
+ else if( throwable instanceof HeuristicRollbackException )
+ {
+ throw (HeuristicRollbackException) throwable;
+ }
+ else if( throwable instanceof RollbackException )
+ {
+ throw (RollbackException) throwable;
+ }
+ else if( throwable instanceof SystemException )
+ {
+ throw (SystemException) throwable;
+ }
+ else if( throwable instanceof Error )
+ {
+ throw (Error) throwable;
+ }
+ else if( throwable instanceof RuntimeException )
+ {
+ throw (RuntimeException) throwable;
+ }
+ else
+ {
+ throw (SystemException) new SystemException( message
).initCause( throwable );
+ }
+ }
+
+ public void rollback()
+ throws SystemException
+ {
+ try
+ {
+ try
+ {
+ if( m_txnManager.getStatus() != Status.STATUS_NO_TRANSACTION
)
+ {
+ m_txnManager.rollback();
+ }
+ }
+ finally
+ {
+ try
+ {
+ afterCommit(false);
+ }
+ catch( Throwable e )
+ {
+ try
+ {
+ // just incase there is a junk transaction on the
thread
+ if( m_txnManager.getStatus() !=
Status.STATUS_NO_TRANSACTION )
+ {
+ m_txnManager.rollback();
+ }
+ }
+ catch( Throwable t1 )
+ {
+ System.err.println( "Unable to roll back
transaction" );
+ t1.printStackTrace();
+ }
+
+ if( e instanceof SystemException )
+ {
+ throw (SystemException) e;
+ }
+ else if( e instanceof Error )
+ {
+ throw (Error) e;
+ }
+ else if( e instanceof RuntimeException )
+ {
+ throw (RuntimeException) e;
+ }
+ throw (SystemException) new SystemException( "After
commit of container transaction failed" ).initCause( e );
+ }
+ }
+ }
+ finally
+ {
+ connectorAfterCommit();
+ m_transaction = null;
+ }
+ }
+
+ public boolean isThreadAssociated()
+ {
+ return threadAssociated;
+ }
+
+ //Geronimo connector framework support
+ public boolean isActive()
+ {
+ try
+ {
+ return m_txnManager.getStatus() == Status.STATUS_ACTIVE;
+ }
+ catch (SystemException e)
+ {
+ return false;
+ }
+ }
+
+ public Transaction getTransaction()
+ {
+ return m_transaction;
+ }
+
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ if( m_transaction == null )
+ {
+ throw new IllegalStateException( "There is no transaction in
progress." );
+ }
+ m_transaction.setRollbackOnly();
+ }
+
+ public boolean getRollbackOnly()
+ throws SystemException
+ {
+ if( m_transaction == null )
+ {
+ throw new IllegalStateException( "There is no transaction in
progress." );
+ }
+
+ int status = m_transaction.getStatus();
+ return status == Status.STATUS_MARKED_ROLLBACK ||
+ status == Status.STATUS_ROLLEDBACK ||
+ status == Status.STATUS_ROLLING_BACK;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/InheritableTransactionContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/InheritableTransactionContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import net.dpml.tx.context.TransactionContext;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public abstract class InheritableTransactionContext extends
TransactionContext
+{
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,236 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import net.dpml.tx.ConnectionReleaser;
+import net.dpml.tx.DoubleKeyedHashMap;
+import net.dpml.tx.InstanceContext;
+//import org.tranql.cache.InTxCache;
+
+
+/**
+ *
+ *
+ * @version $Rev: 56821 $ $Date: 2004-11-07 15:59:36 +0800 (Sun, 07 Nov
2004) $
+ */
+public abstract class TransactionContext
+{
+ private static ThreadLocal CONTEXT = new ThreadLocal();
+
+ public static TransactionContext getContext()
+ {
+ return (TransactionContext) CONTEXT.get();
+ }
+
+ public static void setContext( TransactionContext context )
+ {
+ CONTEXT.set( context );
+ }
+
+ private InstanceContext currentContext;
+ private final DoubleKeyedHashMap associatedContexts = new
DoubleKeyedHashMap();
+ private final DoubleKeyedHashMap dirtyContexts = new
DoubleKeyedHashMap();
+ private Map managedConnections;
+// private InTxCache inTxCache;
+
+// public abstract void begin() throws SystemException,
NotSupportedException;
+
+ public abstract void suspend()
+ throws SystemException;
+
+ public abstract void resume()
+ throws SystemException, InvalidTransactionException;
+
+ public abstract void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
RollbackException, SystemException;
+
+ public abstract void rollback()
+ throws SystemException;
+
+ public final void associate(InstanceContext context)
+ throws Throwable
+ {
+ if( associatedContexts.put( context.getContainerId(),
context.getId(), context ) == null )
+ {
+ context.associate();
+ }
+ }
+
+ public final void unassociate( Object containerId, Object id )
+ throws Exception
+ {
+ associatedContexts.remove( containerId, id );
+ dirtyContexts.remove( containerId, id );
+ }
+
+ public final InstanceContext beginInvocation( InstanceContext context )
+ {
+ if (context.getId() != null)
+ {
+ dirtyContexts.put( context.getContainerId(), context.getId(),
context );
+ }
+ InstanceContext caller = currentContext;
+ currentContext = context;
+ return caller;
+ }
+
+ public final void endInvocation(InstanceContext caller)
+ {
+ currentContext = caller;
+ }
+
+ public final void flushState()
+ throws Throwable
+ {
+ while( dirtyContexts.isEmpty() == false )
+ {
+ ArrayList toFlush = new ArrayList( dirtyContexts.values() );
+ dirtyContexts.clear();
+ Iterator iterator = toFlush.iterator();
+ while( iterator.hasNext() )
+ {
+ InstanceContext context = (InstanceContext) iterator.next();
+ context.flush();
+ }
+ }
+ if( currentContext != null && currentContext.getId() != null )
+ {
+ dirtyContexts.put( currentContext.getContainerId(),
currentContext.getId(), currentContext );
+ }
+/*
+ if(inTxCache != null) {
+ inTxCache.flush();
+ }
+*/
+ }
+
+ protected void beforeCommit()
+ throws Exception
+ {
+ // @todo allow for enrollment during pre-commit
+ ArrayList toFlush = new ArrayList(associatedContexts.values());
+ Iterator iterator = toFlush.iterator();
+ while( iterator.hasNext() )
+ {
+ InstanceContext context = (InstanceContext) iterator.next();
+ context.beforeCommit();
+ }
+ }
+
+ protected void afterCommit( boolean status )
+ throws Exception
+ {
+ Throwable firstThrowable = null;
+ ArrayList toFlush = new ArrayList(associatedContexts.values());
+ Iterator iterator = toFlush.iterator();
+ while( iterator.hasNext() )
+ {
+ InstanceContext context = (InstanceContext) iterator.next();
+ try
+ {
+ context.afterCommit(status);
+ }
+ catch( Throwable e )
+ {
+ if( firstThrowable == null )
+ {
+ firstThrowable = e;
+ }
+ }
+ }
+
+ if( firstThrowable instanceof Error )
+ {
+ throw (Error) firstThrowable;
+ }
+ else if( firstThrowable instanceof Exception )
+ {
+ throw (Exception) firstThrowable;
+ }
+ else if( firstThrowable != null )
+ {
+ throw (SystemException) new SystemException().initCause(
firstThrowable );
+ }
+ }
+
+ public final InstanceContext getContext( Object containerId, Object id )
+ {
+ return (InstanceContext) associatedContexts.get( containerId, id );
+ }
+/*
+ public final void setInTxCache(InTxCache inTxCache) {
+ this.inTxCache = inTxCache;
+ }
+
+ public final InTxCache getInTxCache() {
+ return inTxCache;
+ }
+*/
+
+ //Geronimo connector framework support
+ public void setManagedConnectionInfo( ConnectionReleaser key, Object
info )
+ {
+ if( managedConnections == null )
+ {
+ managedConnections = new HashMap();
+ }
+ managedConnections.put( key, info );
+ }
+
+ public Object getManagedConnectionInfo( ConnectionReleaser key )
+ {
+ if( managedConnections == null )
+ {
+ return null;
+ }
+ return managedConnections.get( key );
+ }
+
+ public abstract boolean isActive();
+
+ public abstract Transaction getTransaction();
+
+ protected void connectorAfterCommit()
+ {
+ if (managedConnections != null)
+ {
+ Iterator entries = managedConnections.entrySet().iterator();
+ while( entries.hasNext() )
+ {
+ Map.Entry entry = (Map.Entry) entries.next();
+ ConnectionReleaser key = (ConnectionReleaser) entry.getKey();
+ key.afterCompletion( entry.getValue() );
+ }
+ //If BeanTransactionContext never reuses the same instance for
sequential BMT, this
+ //clearing is unnecessary.
+ managedConnections.clear();
+ }
+ }
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContextManager.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/TransactionContextManager.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,393 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+// kill j2ee deps; import javax.resource.spi.XATerminator;
+
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import net.dpml.tx.ExtendedTransactionManager;
+import net.dpml.tx.ImportedTransactionActiveException;
+import net.dpml.tx.XAWork;
+import net.dpml.tx.manager.Recovery;
+import net.dpml.tx.manager.XidImporter;
+
+/**
+ * @version $Rev: 128441 $ $Date: 2005-01-28 06:56:21 +0800 (Fri, 28 Jan
2005) $
+ */
+public class TransactionContextManager
+// remove j2ee deps; implements XATerminator, XAWork
+ implements XAWork
+{
+
+ private static final boolean NOT_IN_RECOVERY = false;
+ private static final boolean IN_RECOVERY = true;
+
+
+ private final ExtendedTransactionManager m_transactionManager;
+ private final XidImporter m_importer;
+ private final Map m_importedTransactions = new HashMap();
+
+ private boolean recoveryState = NOT_IN_RECOVERY;
+
+ //use as reference endpoint.
+ public TransactionContextManager()
+ {
+ this( null, null );
+ }
+
+ public TransactionContextManager( ExtendedTransactionManager
transactionManager, XidImporter importer )
+ {
+ this.m_transactionManager = transactionManager;
+ this.m_importer = importer;
+ }
+
+ public TransactionManager getTransactionManager()
+ {
+ return m_transactionManager;
+ }
+
+ public void setTransactionTimeout(int timeoutSeconds)
+ throws SystemException
+ {
+ m_transactionManager.setTransactionTimeout(timeoutSeconds);
+ }
+
+ public TransactionContext getContext()
+ {
+ return TransactionContext.getContext();
+ }
+
+ public void setContext(TransactionContext transactionContext)
+ {
+ TransactionContext.setContext( transactionContext );
+ }
+
+ public ContainerTransactionContext newContainerTransactionContext()
+ throws NotSupportedException, SystemException
+ {
+ ContainerTransactionContext transactionContext = new
ContainerTransactionContext( m_transactionManager );
+ TransactionContext.setContext( transactionContext );
+ return transactionContext;
+ }
+
+ public BeanTransactionContext newBeanTransactionContext( long
transactionTimeoutMilliseconds )
+ throws NotSupportedException, SystemException
+ {
+ TransactionContext ctx = TransactionContext.getContext();
+ if( ctx instanceof UnspecifiedTransactionContext == false )
+ {
+ throw new NotSupportedException("Previous Transaction has not
been committed");
+ }
+ UnspecifiedTransactionContext oldContext =
(UnspecifiedTransactionContext) ctx;
+ BeanTransactionContext transactionContext = new
BeanTransactionContext( m_transactionManager, oldContext );
+ oldContext.suspend();
+ try
+ {
+ transactionContext.begin( transactionTimeoutMilliseconds );
+ }
+ catch( SystemException e )
+ {
+ oldContext.resume();
+ throw e;
+ }
+ catch( NotSupportedException e )
+ {
+ oldContext.resume();
+ throw e;
+ }
+ TransactionContext.setContext( transactionContext );
+ return transactionContext;
+ }
+
+ public UnspecifiedTransactionContext newUnspecifiedTransactionContext()
+ {
+ UnspecifiedTransactionContext transactionContext = new
UnspecifiedTransactionContext();
+ TransactionContext.setContext( transactionContext );
+ return transactionContext;
+ }
+
+ public int getStatus()
+ throws SystemException
+ {
+ return m_transactionManager.getStatus();
+ }
+
+ public void setRollbackOnly()
+ throws SystemException
+ {
+ m_transactionManager.setRollbackOnly();
+ }
+
+
+ /**
+ * @see javax.resource.spi.XATerminator#commit(javax.transaction.xa.Xid,
boolean)
+ */
+ public void commit( Xid xid, boolean onePhase )
+ throws XAException
+ {
+ ContainerTransactionContext containerTransactionContext;
+ synchronized (m_importedTransactions)
+ {
+ containerTransactionContext = (ContainerTransactionContext)
m_importedTransactions.remove(xid);
+ }
+ if( containerTransactionContext == null )
+ {
+ throw new XAException("No imported transaction for xid: " + xid);
+ }
+
+ try
+ {
+ int status =
containerTransactionContext.getTransaction().getStatus();
+// assert status == Status.STATUS_ACTIVE || status ==
Status.STATUS_PREPARED;
+ if( status != Status.STATUS_ACTIVE && status !=
Status.STATUS_PREPARED )
+ throw new InternalError( "Status is in an inconsistent
state." );
+ }
+ catch( SystemException e )
+ {
+ throw new XAException();
+ }
+ m_importer.commit( containerTransactionContext.getTransaction(),
onePhase );
+ }
+
+ /**
+ * @see javax.resource.spi.XATerminator#forget(javax.transaction.xa.Xid)
+ */
+ public void forget( Xid xid )
+ throws XAException
+ {
+ ContainerTransactionContext containerTransactionContext;
+ synchronized( m_importedTransactions )
+ {
+ containerTransactionContext = (ContainerTransactionContext)
m_importedTransactions.remove(xid);
+ }
+ if (containerTransactionContext == null)
+ {
+ throw new XAException( "No imported transaction for xid: " + xid
);
+ }
+ //todo is there a correct status test here?
+// try {
+// int status = tx.getStatus();
+// assert status == Status.STATUS_ACTIVE || status ==
Status.STATUS_PREPARED;
+// } catch (SystemException e) {
+// throw new XAException();
+// }
+ m_importer.forget(containerTransactionContext.getTransaction());
+ }
+
+ /**
+ * @see javax.resource.spi.XATerminator#prepare(javax.transaction.xa.Xid)
+ */
+ public int prepare( Xid xid )
+ throws XAException
+ {
+ ContainerTransactionContext containerTransactionContext;
+ synchronized( m_importedTransactions )
+ {
+ containerTransactionContext = (ContainerTransactionContext)
m_importedTransactions.get(xid);
+ }
+ if( containerTransactionContext == null )
+ {
+ throw new XAException("No imported transaction for xid: " + xid);
+ }
+ Transaction tx = containerTransactionContext.getTransaction();
+ try
+ {
+ int status = tx.getStatus();
+// assert status == Status.STATUS_ACTIVE;
+ if( status != Status.STATUS_ACTIVE )
+ throw new InternalError( "Incorrect Status." );
+ }
+ catch (SystemException e)
+ {
+ throw new XAException();
+ }
+ return m_importer.prepare(tx);
+ }
+
+ /**
+ * @see javax.resource.spi.XATerminator#recover(int)
+ */
+ public Xid[] recover( int flag )
+ throws XAException
+ {
+ if( recoveryState == NOT_IN_RECOVERY )
+ {
+ if( ( flag & XAResource.TMSTARTRSCAN ) == 0 )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ recoveryState = IN_RECOVERY;
+ }
+ if( ( flag & XAResource.TMENDRSCAN ) != 0 )
+ {
+ recoveryState = NOT_IN_RECOVERY;
+ }
+
+ //we always return all xids in first call.
+ //calling "startrscan" repeatedly starts at beginning of list again.
+ if( ( flag & XAResource.TMSTARTRSCAN ) != 0 )
+ {
+ Map recoveredXidMap = m_transactionManager.getExternalXids();
+ Xid[] recoveredXids = new Xid[ recoveredXidMap.size() ];
+ int i = 0;
+ synchronized( m_importedTransactions )
+ {
+ Iterator iterator = recoveredXidMap.entrySet().iterator();
+ while( iterator.hasNext() )
+ {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ Xid xid = (Xid) entry.getKey();
+ recoveredXids[ i++ ] = xid;
+ Transaction transaction = (Transaction) entry.getValue();
+ ContainerTransactionContext containerTransactionContext
= new ContainerTransactionContext( m_transactionManager, transaction );
+ m_importedTransactions.put( xid,
containerTransactionContext );
+ }
+ }
+ return recoveredXids;
+ }
+ else
+ {
+ return new Xid[0];
+ }
+ }
+
+ /**
+ * @see
javax.resource.spi.XATerminator#rollback(javax.transaction.xa.Xid)
+ */
+ public void rollback( Xid xid )
+ throws XAException
+ {
+ ContainerTransactionContext containerTransactionContext;
+ synchronized( m_importedTransactions )
+ {
+ containerTransactionContext = (ContainerTransactionContext)
m_importedTransactions.remove( xid );
+ }
+ if( containerTransactionContext == null )
+ {
+ throw new XAException( "No imported transaction for xid: " + xid
);
+ }
+ Transaction tx = containerTransactionContext.getTransaction();
+
+ try
+ {
+ int status = tx.getStatus();
+// assert status == Status.STATUS_ACTIVE || status ==
Status.STATUS_PREPARED;
+ if( status != Status.STATUS_ACTIVE && status !=
Status.STATUS_PREPARED )
+ {
+ throw new InternalError( "Inconsistent Status." );
+ }
+ }
+ catch( SystemException e )
+ {
+ throw new XAException();
+ }
+ m_importer.rollback(tx);
+ }
+
+
+ //XAWork implementation
+ public void begin( Xid xid, long txTimeoutMillis )
+ throws XAException, InvalidTransactionException, SystemException,
+ ImportedTransactionActiveException
+ {
+ ContainerTransactionContext containerTransactionContext;
+ synchronized( m_importedTransactions )
+ {
+ containerTransactionContext = (ContainerTransactionContext)
m_importedTransactions.get( xid );
+ if (containerTransactionContext == null)
+ {
+ //this does not associate tx with current thread.
+ Transaction transaction = m_importer.importXid( xid,
txTimeoutMillis );
+ containerTransactionContext = new
ContainerTransactionContext( m_transactionManager, transaction );
+ m_importedTransactions.put( xid, containerTransactionContext
);
+ }
+ else
+ {
+ if( containerTransactionContext.isThreadAssociated() )
+ {
+ throw new ImportedTransactionActiveException( xid );
+ }
+ }
+ containerTransactionContext.resume();
+ }
+ TransactionContext.setContext(containerTransactionContext);
+ }
+
+ public void end( Xid xid )
+ throws XAException, SystemException
+ {
+ TransactionContext.setContext(null);
+ synchronized( m_importedTransactions )
+ {
+ ContainerTransactionContext containerTransactionContext =
(ContainerTransactionContext) m_importedTransactions.get( xid );
+ if( containerTransactionContext == null )
+ {
+ throw new XAException( "No imported transaction for xid: " +
xid );
+ }
+ if( !containerTransactionContext.isThreadAssociated() )
+ {
+ throw new XAException( "tx not active for
containerTransactionContext: " + containerTransactionContext + ", xid: " +
xid );
+ }
+ containerTransactionContext.suspend();
+ }
+ }
+
+/*
+ public static final GBeanInfo GBEAN_INFO;
+
+ static {
+ GBeanInfoBuilder infoFactory = new
GBeanInfoBuilder(TransactionContextManager.class, NameFactory.JTA_RESOURCE);
+
+ infoFactory.addOperation("getTransactionManager");
+ infoFactory.addOperation("getContext");
+ infoFactory.addOperation("setContext", new
Class[]{TransactionContext.class});
+ infoFactory.addOperation("newContainerTransactionContext");
+ infoFactory.addOperation("newBeanTransactionContext", new Class[]
{long.class});
+ infoFactory.addOperation("newUnspecifiedTransactionContext");
+ infoFactory.addOperation("getStatus");
+ infoFactory.addOperation("setRollbackOnly");
+ infoFactory.addOperation("setTransactionTimeout", new Class[]
{int.class});
+
+ infoFactory.addReference("TransactionManager",
ExtendedTransactionManager.class);
+ infoFactory.addReference("XidImporter", XidImporter.class);
+
+ infoFactory.addInterface(XATerminator.class);
+ infoFactory.addInterface(XAWork.class);
+
+ infoFactory.setConstructor(new String[]{"TransactionManager",
"XidImporter"});
+ GBEAN_INFO = infoFactory.getBeanInfo();
+ }
+
+ public static GBeanInfo getGBeanInfo() {
+ return GBEAN_INFO;
+ }
+*/
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/UnspecifiedTransactionContext.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/context/UnspecifiedTransactionContext.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,89 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.context;
+
+import javax.transaction.Transaction;
+
+import net.dpml.tx.ConnectionReleaser;
+import net.dpml.tx.context.TransactionContext;
+
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public class UnspecifiedTransactionContext extends TransactionContext
+{
+ public void begin()
+ {
+ }
+
+ public void suspend()
+ {
+ }
+
+ public void resume()
+ {
+ }
+
+ public void commit()
+ {
+ try
+ {
+ flushState();
+ }
+ catch( Error e )
+ {
+ throw e;
+ }
+ catch( RuntimeException re )
+ {
+ throw re;
+ }
+ catch( Throwable e )
+ {
+ System.err.println( "Unable to flush state, continuing" );
+ e.printStackTrace();
+ }
+ }
+
+ public void rollback()
+ {
+ }
+
+ //Geronimo connector framework support
+ public void setManagedConnectionInfo( ConnectionReleaser key, Object
info )
+ {
+ }
+
+ public Object getManagedConnectionInfo( ConnectionReleaser key )
+ {
+ return null;
+ }
+
+ public boolean isActive()
+ {
+ return false;
+ }
+
+ public Transaction getTransaction()
+ {
+ return null;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/UnrecoverableLog.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/UnrecoverableLog.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.transaction.xa.Xid;
+
+import net.dpml.tx.manager.LogException;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.XidFactory;
+
+/**
+ * A log sink that doesn't actually do anything.
+ * Not recommended for production use as heuristic recovery will be needed if
+ * the transaction coordinator dies.
+ *
+ * @version $Rev: 54973 $ $Date: 2004-10-18 04:06:11 +0800 (Mon, 18 Oct
2004) $
+ */
+public class UnrecoverableLog
+ implements TransactionLog
+{
+
+ public void begin(Xid xid)
+ throws LogException
+ {
+ }
+
+ public Object prepare( Xid xid, List branches )
+ throws LogException
+ {
+ return null;
+ }
+
+ public void commit( Xid xid, Object logMark )
+ throws LogException
+ {
+ }
+
+ public void rollback( Xid xid, Object logMark )
+ throws LogException
+ {
+ }
+
+ public Collection recover( XidFactory xidFactory )
+ throws LogException
+ {
+ return new ArrayList();
+ }
+
+ public String getXMLStats()
+ {
+ return null;
+ }
+
+ public int getAverageForceTime()
+ {
+ return 0;
+ }
+
+ public int getAverageBytesPerForce()
+ {
+ return 0;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/XidImpl2.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/log/XidImpl2.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,173 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.log;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import javax.transaction.xa.Xid;
+
+/**
+ * Unique id for a transaction. This implementation is backed by a single
byte buffer
+ * so can do less copying than one backed by several byte buffers for the
different components.
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public class XidImpl2
+ implements Xid, Serializable
+{
+ private static int HEADER_SIZE = 4;
+ private static int ACTION_POS = 0;
+ private static int GLOBALID_SIZE_POS = 1;
+ private static int BRANCHID_SIZE_POS = 2;
+ //3 unused
+ private static int FORMAT_ID = 0x4765526f; // Gero
+ private static int FORMAT_SIZE = 4;
+
+ private static byte[] FORMAT_ID_BYTES = "Gero".getBytes();
+
+ private final byte[] m_buffer = new byte[HEADER_SIZE + FORMAT_SIZE +
Xid.MAXGTRIDSIZE + Xid.MAXBQUALSIZE];
+ private int m_hash;
+ private Object m_key;
+
+ /**
+ * Constructor taking a global id (for the main transaction)
+ * @param globalId the global transaction id
+ */
+ public XidImpl2(byte[] globalId)
+ {
+ System.arraycopy( FORMAT_ID_BYTES, 0, m_buffer, HEADER_SIZE,
FORMAT_SIZE );
+ m_buffer[ GLOBALID_SIZE_POS ] = (byte) globalId.length;
+ System.arraycopy( globalId, 0, m_buffer, HEADER_SIZE + FORMAT_SIZE,
Xid.MAXGTRIDSIZE );
+
+ //this.hash = hash(buffer);
+ }
+
+ /**
+ * Constructor for a branch id
+ * @param global the xid of the global transaction this branch belongs to
+ * @param branch the branch id
+ */
+ public XidImpl2( Xid global, byte[] branch )
+ {
+ if( global instanceof XidImpl2 )
+ {
+ System.arraycopy( ( (XidImpl2) global).m_buffer, 0, m_buffer, 0,
HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE );
+ }
+ else
+ {
+ System.arraycopy( FORMAT_ID_BYTES, 0, m_buffer, HEADER_SIZE,
FORMAT_SIZE );
+ byte[] globalId = global.getGlobalTransactionId();
+ System.arraycopy( globalId, 0, m_buffer, HEADER_SIZE +
FORMAT_SIZE, globalId.length );
+ }
+ m_buffer[ BRANCHID_SIZE_POS ] = (byte) branch.length;
+ System.arraycopy( branch, 0, m_buffer, HEADER_SIZE + FORMAT_SIZE +
Xid.MAXGTRIDSIZE, Xid.MAXBQUALSIZE );
+ //hash = hash(buffer);
+ }
+
+ public XidImpl2( int formatId, byte[] globalId, byte[] branch )
+ {
+ //todo this is wrong, it ignores formatId supplied. Maybe this is
ok?
+ System.arraycopy( FORMAT_ID_BYTES, 0, m_buffer, HEADER_SIZE,
FORMAT_SIZE );
+ System.arraycopy( globalId, 0, m_buffer, HEADER_SIZE + FORMAT_SIZE,
globalId.length );
+ m_buffer[ BRANCHID_SIZE_POS ] = (byte) branch.length;
+ System.arraycopy( branch, 0, m_buffer, HEADER_SIZE + FORMAT_SIZE +
Xid.MAXGTRIDSIZE, Xid.MAXBQUALSIZE );
+ //hash = hash(buffer);
+ }
+
+ private int hash( byte[] id )
+ {
+ int hash = 0;
+ for( int i = 0; i < id.length; i++ )
+ {
+ hash = hash * 37 + id[ i ];
+ }
+ return hash;
+ }
+
+ public int getFormatId()
+ {
+ return FORMAT_ID;
+ }
+
+ public byte[] getGlobalTransactionId()
+ {
+ byte[] globalId = new byte[ m_buffer[ GLOBALID_SIZE_POS ] ];
+ System.arraycopy( m_buffer, HEADER_SIZE + FORMAT_SIZE, globalId, 0,
m_buffer[ GLOBALID_SIZE_POS ] );
+ return globalId;
+ }
+
+ public byte[] getBranchQualifier()
+ {
+ byte[] branchId = new byte[ m_buffer[ BRANCHID_SIZE_POS ] ];
+ System.arraycopy( m_buffer, HEADER_SIZE + FORMAT_SIZE +
Xid.MAXGTRIDSIZE, branchId, 0, m_buffer[ BRANCHID_SIZE_POS ] );
+ return branchId;
+ }
+
+ public boolean equals( Object obj )
+ {
+ if( obj instanceof XidImpl2 == false )
+ {
+ return false;
+ }
+ XidImpl2 other = (XidImpl2) obj;
+ return Arrays.equals( m_buffer, other.m_buffer );
+ }
+
+ public int hashCode()
+ {
+ if (m_hash == 0)
+ {
+ m_hash = hash(m_buffer);
+ }
+ return m_hash;
+ }
+
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer( "[formatId=Gero," );
+ s.append( "globalId=" );
+ for( int i = FORMAT_SIZE; i < FORMAT_SIZE + Xid.MAXGTRIDSIZE; i++ )
+ {
+ s.append( Integer.toHexString( m_buffer[ i ] ) );
+ }
+ s.append( ",branchId=" );
+ for( int i = FORMAT_SIZE + Xid.MAXGTRIDSIZE; i < m_buffer.length;
i++ )
+ {
+ s.append( Integer.toHexString( m_buffer[ i ] ) );
+ }
+ s.append( "]" );
+ return s.toString();
+ }
+
+ byte[] getBuffer( byte action )
+ {
+ m_buffer[ ACTION_POS ] = action;
+ return m_buffer;
+ }
+
+ public void setKey( Object key )
+ {
+ this.m_key = key;
+ }
+
+ public Object getKey()
+ {
+ return m_key;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/LogException.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/LogException.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,48 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public class LogException extends Exception
+{
+
+ public LogException()
+ {
+ super();
+ }
+
+ public LogException( String message )
+ {
+ super(message);
+ }
+
+ public LogException( String message, Throwable cause )
+ {
+ super(message, cause);
+ }
+
+ public LogException( Throwable cause )
+ {
+ super(cause);
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/NamedXAResource.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/NamedXAResource.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,14 @@
+package net.dpml.tx.manager;
+
+import javax.transaction.xa.XAResource;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface NamedXAResource extends XAResource
+{
+ String getName();
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/Recovery.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/Recovery.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+/**
+ *
+ *
+ * @version $Rev: 111396 $ $Date: 2004-12-09 23:41:29 +0800 (Thu, 09 Dec
2004) $
+ *
+ * */
+public interface Recovery
+{
+
+ void recoverLog()
+ throws XAException;
+
+ void recoverResourceManager(NamedXAResource xaResource)
+ throws XAException;
+
+ boolean hasRecoveryErrors();
+
+ List getRecoveryErrors();
+
+ boolean localRecoveryComplete();
+
+ int localUnrecoveredCount();
+
+ //hard to implement.. needs ExternalTransaction to have a reference to
externalXids.
+// boolean remoteRecoveryComplete();
+
+ Map getExternalXids();
+
+ public static class XidBranchesPair
+ {
+ private final Xid m_xid;
+
+ //set of TransactionBranchInfo
+ private final Set m_branches = new HashSet();
+
+ private final Object m_mark;
+
+ public XidBranchesPair( Xid xid, Object mark )
+ {
+ this.m_xid = xid;
+ this.m_mark = mark;
+ }
+
+ public Xid getXid()
+ {
+ return m_xid;
+ }
+
+ public Set getBranches()
+ {
+ return m_branches;
+ }
+
+ public Object getMark()
+ {
+ return m_mark;
+ }
+
+ public void addBranch( TransactionBranchInfo branchInfo )
+ {
+ m_branches.add( branchInfo );
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/RecoveryImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/RecoveryImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,323 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Collection;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import net.dpml.lang.NullArgumentException;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public class RecoveryImpl
+ implements Recovery
+{
+
+ private final TransactionLog m_txLog;
+ private final XidFactory m_xidFactory;
+
+ private final Map m_externalXids = new HashMap();
+ private final Map m_ourXids = new HashMap();
+ private final Map m_nameToOurTxMap = new HashMap();
+ private final Map m_externalGlobalIdMap = new HashMap();
+
+ private final List m_recoveryErrors = new ArrayList();
+
+ public RecoveryImpl( TransactionLog txLog, XidFactory xidFactory )
+ {
+ this.m_txLog = txLog;
+ this.m_xidFactory = xidFactory;
+ }
+
+ public synchronized void recoverLog()
+ throws XAException
+ {
+ Collection preparedXids = null;
+ try
+ {
+ preparedXids = m_txLog.recover( m_xidFactory );
+ }
+ catch( LogException e )
+ {
+ throw (XAException) new XAException( XAException.XAER_RMERR
).initCause( e );
+ }
+ Iterator iterator = preparedXids.iterator();
+ while( iterator.hasNext() )
+ {
+ XidBranchesPair xidBranchesPair = (Recovery.XidBranchesPair)
iterator.next();
+ Xid xid = xidBranchesPair.getXid();
+ if( m_xidFactory.matchesGlobalId( xid.getGlobalTransactionId() )
)
+ {
+ ByteArrayWrapper wrapper = new ByteArrayWrapper(
xid.getGlobalTransactionId() );
+ m_ourXids.put( wrapper, xidBranchesPair);
+ Iterator branches = xidBranchesPair.getBranches().iterator();
+ while( branches.hasNext() )
+ {
+ TransactionBranchInfo tbi = (TransactionBranchInfo)
branches.next();
+ String name = tbi.getResourceName();
+ Set transactionsForName = (Set) m_nameToOurTxMap.get(
name );
+ if( transactionsForName == null )
+ {
+ transactionsForName = new HashSet();
+ m_nameToOurTxMap.put( name, transactionsForName );
+ }
+ transactionsForName.add( xidBranchesPair );
+ }
+ }
+ else
+ {
+ TransactionImpl externalTx = new ExternalTransaction( xid,
m_txLog, xidBranchesPair.getBranches() );
+ m_externalXids.put( xid, externalTx );
+ m_externalGlobalIdMap.put( xid.getGlobalTransactionId(),
externalTx );
+ }
+ }
+ }
+
+
+ public synchronized void recoverResourceManager( NamedXAResource
xaResource )
+ throws XAException
+ {
+ String name = xaResource.getName();
+ Xid[] prepared = xaResource.recover( XAResource.TMSTARTRSCAN +
XAResource.TMENDRSCAN );
+ for( int i = 0; i < prepared.length; i++ )
+ {
+ Xid xid = prepared[ i ];
+ ByteArrayWrapper globalIdWrapper = new ByteArrayWrapper(
xid.getGlobalTransactionId() );
+ XidBranchesPair xidNamesPair = (XidBranchesPair) m_ourXids.get(
globalIdWrapper );
+ if( xidNamesPair != null )
+ {
+ try
+ {
+ xaResource.commit( xid, false );
+ }
+ catch( XAException e )
+ {
+ m_recoveryErrors.add( e );
+ e.printStackTrace();
+ }
+ removeNameFromTransaction( xidNamesPair, name, true );
+ }
+ else if( m_xidFactory.matchesGlobalId(
xid.getGlobalTransactionId() ) )
+ {
+ //ours, but prepare not logged
+ try
+ {
+ xaResource.rollback( xid );
+ }
+ catch( XAException e )
+ {
+ m_recoveryErrors.add( e );
+ e.printStackTrace();
+ }
+ }
+ else if( m_xidFactory.matchesBranchId( xid.getBranchQualifier()
) )
+ {
+ //our branch, but we did not start this tx.
+ TransactionImpl externalTx = (TransactionImpl)
m_externalGlobalIdMap.get( xid.getGlobalTransactionId() );
+ if( externalTx == null )
+ {
+ //we did not prepare this branch, rollback.
+ try
+ {
+ xaResource.rollback( xid );
+ }
+ catch( XAException e )
+ {
+ m_recoveryErrors.add( e );
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ //we prepared this branch, must wait for commit/rollback
command.
+ externalTx.addBranchXid( xaResource, xid );
+ }
+ }
+ //else we had nothing to do with this xid.
+ }
+ Set transactionsForName = (Set) m_nameToOurTxMap.get( name );
+ if( transactionsForName != null )
+ {
+ Iterator transactions = transactionsForName.iterator();
+ while( transactions.hasNext() )
+ {
+ XidBranchesPair xidBranchesPair = (XidBranchesPair)
transactions.next();
+ removeNameFromTransaction( xidBranchesPair, name, false );
+ }
+ }
+ }
+
+ private void removeNameFromTransaction( XidBranchesPair xidBranchesPair,
String name, boolean warn )
+ {
+ int removed = 0;
+ Iterator branches = xidBranchesPair.getBranches().iterator();
+ while( branches.hasNext() )
+ {
+ TransactionBranchInfo transactionBranchInfo =
(TransactionBranchInfo) branches.next();
+ if( name.equals( transactionBranchInfo.getResourceName() ) )
+ {
+ branches.remove();
+ removed++;
+ }
+ }
+ if (warn && removed == 0)
+ {
+ System.err.println( "XAResource named: " + name + " returned
branch xid for xid: " + xidBranchesPair.getXid() + " but was not registered
with that transaction!" );
+ }
+ if( xidBranchesPair.getBranches().isEmpty() && 0 != removed )
+ {
+ try
+ {
+ Xid xid = xidBranchesPair.getXid();
+ byte[] txId = xid.getGlobalTransactionId();
+ ByteArrayWrapper wrapper = new ByteArrayWrapper( txId );
+ m_ourXids.remove( wrapper );
+ m_txLog.commit( xidBranchesPair.getXid(),
xidBranchesPair.getMark() );
+ }
+ catch( LogException e )
+ {
+ m_recoveryErrors.add( e );
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public synchronized boolean hasRecoveryErrors()
+ {
+ return !m_recoveryErrors.isEmpty();
+ }
+
+ public synchronized List getRecoveryErrors()
+ {
+ return Collections.unmodifiableList( m_recoveryErrors );
+ }
+
+ public synchronized boolean localRecoveryComplete()
+ {
+ return m_ourXids.isEmpty();
+ }
+
+ public synchronized int localUnrecoveredCount()
+ {
+ return m_ourXids.size();
+ }
+
+ //hard to implement.. needs ExternalTransaction to have a reference to
externalXids.
+// public boolean remoteRecoveryComplete() {
+// }
+
+ public synchronized Map getExternalXids()
+ {
+ return new HashMap( m_externalXids );
+ }
+
+ private static class ByteArrayWrapper
+ {
+ private final byte[] m_bytes;
+ private final int m_hashCode;
+
+ public ByteArrayWrapper(final byte[] bytes)
+ throws NullArgumentException
+ {
+// assert bytes != null;
+ if( bytes == null )
+ {
+ throw new NullArgumentException( "bytes" );
+ }
+ this.m_bytes = bytes;
+ int hash = 0;
+ for (int i = 0; i < bytes.length; i++)
+ {
+ hash += 37 * bytes[ i ];
+ }
+ m_hashCode = hash;
+ }
+
+ public boolean equals( Object other )
+ {
+ if( other instanceof ByteArrayWrapper )
+ {
+ return Arrays.equals( m_bytes, ( (ByteArrayWrapper)
other).m_bytes);
+ }
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return m_hashCode;
+ }
+ }
+
+ private static class ExternalTransaction extends TransactionImpl
+ {
+ private Set m_resourceNames;
+
+ public ExternalTransaction( Xid xid, TransactionLog txLog, Set
resourceNames )
+ {
+ super( xid, txLog );
+ this.m_resourceNames = resourceNames;
+ }
+
+ public boolean hasName( String name )
+ {
+ return m_resourceNames.contains( name );
+ }
+
+ public void removeName( String name )
+ {
+ m_resourceNames.remove( name );
+ }
+
+ public void preparedCommit()
+ throws SystemException
+ {
+ if( m_resourceNames.isEmpty() == false )
+ {
+ throw new SystemException( "This tx does not have all
resource managers online, commit not allowed yet" );
+ }
+ super.preparedCommit();
+ }
+
+ public void rollback()
+ throws SystemException
+ {
+ if( m_resourceNames.isEmpty() == false )
+ {
+ throw new SystemException( "This tx does not have all
resource managers online, rollback not allowed yet" );
+ }
+ super.rollback();
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/ResourceManager.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/ResourceManager.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,19 @@
+package net.dpml.tx.manager;
+
+import javax.transaction.SystemException;
+
+/**
+ *
+ *
+ * @version $Rev: 54805 $ $Date: 2004-10-15 05:51:13 +0800 (Fri, 15 Oct
2004) $
+ *
+ * */
+public interface ResourceManager
+{
+
+ NamedXAResource getRecoveryXAResources()
+ throws SystemException;
+
+ void returnResource( NamedXAResource xaResource );
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfo.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfo.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,18 @@
+package net.dpml.tx.manager;
+
+import javax.transaction.xa.Xid;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface TransactionBranchInfo
+{
+
+ String getResourceName();
+
+ Xid getBranchXid();
+
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfoImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionBranchInfoImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+
+import javax.transaction.xa.Xid;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public class TransactionBranchInfoImpl
+ implements TransactionBranchInfo
+{
+
+ private final Xid m_branchXid;
+ private final String m_resourceName;
+
+ public TransactionBranchInfoImpl( Xid branchXid, String resourceName )
+ {
+ this.m_branchXid = branchXid;
+ this.m_resourceName = resourceName;
+ }
+
+ public Xid getBranchXid()
+ {
+ return m_branchXid;
+ }
+
+ public String getResourceName()
+ {
+ return m_resourceName;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,873 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import net.dpml.lang.NullArgumentException;
+
+/**
+ * Basic local transaction with support for multiple resources.
+ *
+ * @version $Rev: 128441 $ $Date: 2005-01-28 06:56:21 +0800 (Fri, 28 Jan
2005) $
+ */
+public class TransactionImpl
+ implements Transaction
+{
+ private final XidFactory m_xidFactory;
+ private final Xid m_xid;
+ private final TransactionLog m_txnLog;
+ private final long m_timeout;
+ private final List m_syncList = new ArrayList(5);
+ private final LinkedList m_resourceManagers = new LinkedList();
+ private final IdentityHashMap m_activeXaResources = new
IdentityHashMap(3);
+ private final IdentityHashMap m_suspendedXaResources = new
IdentityHashMap(3);
+ private int m_status = Status.STATUS_NO_TRANSACTION;
+ private Object m_logMark;
+
+ TransactionImpl( XidFactory xidFactory, TransactionLog txnLog, long
transactionTimeoutMilliseconds )
+ throws SystemException
+ {
+ this( xidFactory.createXid(), xidFactory, txnLog,
transactionTimeoutMilliseconds );
+ }
+
+ TransactionImpl( Xid xid, XidFactory xidFactory, TransactionLog txnLog,
long transactionTimeoutMilliseconds )
+ throws SystemException
+ {
+ this.m_xidFactory = xidFactory;
+ this.m_txnLog = txnLog;
+ this.m_xid = xid;
+ this.m_timeout = transactionTimeoutMilliseconds +
TransactionTimer.getCurrentTime();
+ try
+ {
+ txnLog.begin(xid);
+ }
+ catch( LogException e )
+ {
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ SystemException ex = new SystemException( "Error logging begin;
transaction marked for roll back)" );
+ ex.initCause(e);
+ throw ex;
+ }
+ m_status = Status.STATUS_ACTIVE;
+ }
+
+ //reconstruct a tx for an external tx found in recovery
+ public TransactionImpl( Xid xid, TransactionLog txLog )
+ {
+ this.m_xidFactory = null;
+ this.m_txnLog = txLog;
+ this.m_xid = xid;
+ m_status = Status.STATUS_PREPARED;
+ //TODO is this a good idea?
+ this.m_timeout = Long.MAX_VALUE;
+ }
+
+ public synchronized int getStatus()
+ throws SystemException
+ {
+ return m_status;
+ }
+
+ public synchronized void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ switch( m_status )
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_PREPARING:
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ break;
+ case Status.STATUS_MARKED_ROLLBACK:
+ case Status.STATUS_ROLLING_BACK:
+ // nothing to do
+ break;
+ default:
+ throw new IllegalStateException("Cannot set rollback only,
status is " + getStateString(m_status));
+ }
+ }
+
+ public synchronized void registerSynchronization( Synchronization synch )
+ throws IllegalStateException, RollbackException, SystemException
+ {
+ if( synch == null )
+ {
+ throw new NullArgumentException( "synch" );
+ }
+ switch( m_status )
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_PREPARING:
+ break;
+ case Status.STATUS_MARKED_ROLLBACK:
+ throw new RollbackException( "Transaction is marked for
rollback" );
+ default:
+ throw new IllegalStateException( "Status is " +
getStateString( m_status ) );
+ }
+ m_syncList.add(synch);
+ }
+
+ public synchronized boolean enlistResource( XAResource xaRes )
+ throws IllegalStateException, RollbackException, SystemException
+ {
+ if( xaRes == null )
+ {
+ throw new NullArgumentException("xaRes");
+ }
+ switch( m_status )
+ {
+ case Status.STATUS_ACTIVE:
+ break;
+ case Status.STATUS_MARKED_ROLLBACK:
+ throw new RollbackException( "Transaction is marked for
rollback" );
+ default:
+ throw new IllegalStateException( "Status is " +
getStateString( m_status ) );
+ }
+
+ if( m_activeXaResources.containsKey( xaRes ) )
+ {
+ throw new IllegalStateException( "xaresource: " + xaRes + " is
already enlisted!" );
+ }
+
+ try
+ {
+ TransactionBranch manager = (TransactionBranch)
m_suspendedXaResources.remove( xaRes );
+ if( manager != null )
+ {
+ //we know about this one, it was suspended
+ xaRes.start( manager.getBranchId(), XAResource.TMRESUME );
+ m_activeXaResources.put( xaRes, manager );
+ return true;
+ }
+ //it is not suspended.
+ Iterator iterator = m_resourceManagers.iterator();
+ while( iterator.hasNext() )
+ {
+ manager = (TransactionBranch) iterator.next();
+ boolean sameRM;
+ //if the xares is already known, we must be resuming after a
suspend.
+ if( xaRes == manager.getCommitter() )
+ {
+ throw new IllegalStateException( "xaRes " + xaRes + " is
a committer but is not active or suspended" );
+ }
+ //Otherwise, see if this is a new xares for the same
resource manager
+ try
+ {
+ sameRM = xaRes.isSameRM( manager.getCommitter() );
+ }
+ catch( XAException e )
+ {
+ System.err.println( "WARN: Unexpected error checking for
same RM" );
+ e.printStackTrace();
+ continue;
+ }
+ if( sameRM )
+ {
+ xaRes.start( manager.getBranchId(), XAResource.TMJOIN );
+ m_activeXaResources.put( xaRes, manager );
+ return true;
+ }
+ }
+ //we know nothing about this XAResource or resource manager
+ Xid branchId = m_xidFactory.createBranch( m_xid,
m_resourceManagers.size() + 1 );
+ xaRes.start( branchId, XAResource.TMNOFLAGS );
+ m_activeXaResources.put( xaRes, addBranchXid( xaRes, branchId )
);
+ return true;
+ }
+ catch( XAException e )
+ {
+ System.err.println( "WARN: Unable to enlist XAResource " + xaRes
);
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public synchronized boolean delistResource( XAResource xaRes, int flag )
+ throws IllegalStateException, SystemException
+ {
+ if( ! ( flag == XAResource.TMFAIL
+ || flag == XAResource.TMSUCCESS
+ || flag == XAResource.TMSUSPEND ) )
+ {
+ throw new IllegalStateException("invalid flag for
delistResource: " + flag);
+ }
+ if( xaRes == null )
+ {
+ throw new IllegalArgumentException( "xaRes" );
+ }
+ switch( m_status )
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_MARKED_ROLLBACK:
+ break;
+ default:
+ throw new IllegalStateException( "Status is " + getStateString(
m_status ) );
+ }
+
+ TransactionBranch manager = (TransactionBranch)
m_activeXaResources.remove( xaRes );
+ if( manager == null )
+ {
+ if( flag == XAResource.TMSUSPEND )
+ {
+ throw new IllegalStateException( "trying to suspend an
inactive xaresource: " + xaRes );
+ }
+ //not active, and we are not trying to suspend. We must be
ending tx.
+ manager = (TransactionBranch) m_suspendedXaResources.remove(
xaRes );
+ if( manager == null )
+ {
+ throw new IllegalStateException( "Resource not known to
transaction: " + xaRes );
+ }
+ }
+
+ try
+ {
+ xaRes.end( manager.getBranchId(), flag );
+ if (flag == XAResource.TMSUSPEND)
+ {
+ m_suspendedXaResources.put( xaRes, manager );
+ }
+ return true;
+ }
+ catch( XAException e )
+ {
+ System.err.println( "WARN: Unable to delist XAResource " + xaRes
);
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ //Transaction method, does 2pc
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
+ RollbackException, SecurityException, SystemException
+ {
+ beforePrepare();
+ try
+ {
+ boolean timedout = false;
+ if( TransactionTimer.getCurrentTime() > m_timeout )
+ {
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ timedout = true;
+ }
+
+ if( m_status == Status.STATUS_MARKED_ROLLBACK )
+ {
+ rollbackResources( m_resourceManagers );
+ if( timedout )
+ {
+ throw new RollbackException( "Transaction timout" );
+ }
+ else
+ {
+ throw new RollbackException( "Unable to commit" );
+ }
+ }
+ synchronized( this )
+ {
+ if (m_status == Status.STATUS_ACTIVE)
+ {
+ if( this.m_resourceManagers.size() == 0 )
+ {
+ // nothing to commit
+ m_status = Status.STATUS_COMMITTED;
+ }
+ else if( this.m_resourceManagers.size() == 1 )
+ {
+ // one-phase commit decision
+ m_status = Status.STATUS_COMMITTING;
+ }
+ else
+ {
+ // start prepare part of two-phase
+ m_status = Status.STATUS_PREPARING;
+ }
+ }
+ // resourceManagers is now immutable
+ }
+
+ // no-phase
+ if (m_resourceManagers.size() == 0) {
+ synchronized (this) {
+ m_status = Status.STATUS_COMMITTED;
+ }
+ return;
+ }
+
+ // one-phase
+ if (m_resourceManagers.size() == 1)
+ {
+ TransactionBranch manager = (TransactionBranch)
m_resourceManagers.getFirst();
+ try
+ {
+ manager.getCommitter().commit( manager.getBranchId(),
true );
+ synchronized( this )
+ {
+ m_status = Status.STATUS_COMMITTED;
+ }
+ return;
+ }
+ catch( XAException e )
+ {
+ synchronized( this )
+ {
+ m_status = Status.STATUS_ROLLEDBACK;
+ }
+ throw (RollbackException) new RollbackException( "Error
during one-phase commit" ).initCause( e );
+ }
+ }
+
+ // two-phase
+ boolean willCommit = internalPrepare();
+
+ // notify the RMs
+ if( willCommit )
+ {
+ commitResources( m_resourceManagers );
+ }
+ else
+ {
+ rollbackResources( m_resourceManagers );
+ throw new RollbackException( "Unable to commit" );
+ }
+ } finally
+ {
+ afterCompletion();
+ synchronized( this )
+ {
+ m_status = Status.STATUS_NO_TRANSACTION;
+ }
+ }
+ }
+
+ //Used from XATerminator for first phase in a remotely controlled tx.
+ int prepare()
+ throws SystemException, RollbackException
+ {
+ beforePrepare();
+ int result = XAResource.XA_RDONLY;
+ try
+ {
+ LinkedList rms;
+ synchronized( this )
+ {
+ if( m_status == Status.STATUS_ACTIVE )
+ {
+ if( m_resourceManagers.size() == 0 )
+ {
+ // nothing to commit
+ m_status = Status.STATUS_COMMITTED;
+ return result;
+ }
+ else
+ {
+ // start prepare part of two-phase
+ m_status = Status.STATUS_PREPARING;
+ }
+ }
+ // resourceManagers is now immutable
+ rms = m_resourceManagers;
+ }
+
+ boolean willCommit = internalPrepare();
+
+ // notify the RMs
+ if( willCommit )
+ {
+ if( !rms.isEmpty() )
+ {
+ result = XAResource.XA_OK;
+ }
+ }
+ else
+ {
+ rollbackResources( rms );
+ throw new RollbackException( "Unable to commit" );
+ }
+ }
+ finally
+ {
+ if( result == XAResource.XA_RDONLY )
+ {
+ afterCompletion();
+ synchronized( this )
+ {
+ m_status = Status.STATUS_NO_TRANSACTION;
+ }
+ }
+ }
+ return result;
+ }
+
+ //used from XATerminator for commit phase of non-readonly remotely
controlled tx.
+ void preparedCommit()
+ throws SystemException
+ {
+ try
+ {
+ commitResources( m_resourceManagers );
+ }
+ finally
+ {
+ afterCompletion();
+ synchronized (this)
+ {
+ m_status = Status.STATUS_NO_TRANSACTION;
+ }
+ }
+ }
+
+ //helper method used by Transaction.commit and XATerminator prepare.
+ private void beforePrepare()
+ {
+ synchronized (this)
+ {
+ switch (m_status)
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_MARKED_ROLLBACK:
+ break;
+ default:
+ throw new IllegalStateException( "Status is " +
getStateString( m_status ) );
+ }
+ }
+ beforeCompletion();
+ endResources();
+ }
+
+
+ //helper method used by Transaction.commit and XATerminator prepare.
+ private boolean internalPrepare()
+ throws SystemException
+ {
+ Iterator rms = m_resourceManagers.iterator();
+ while( rms.hasNext() )
+ {
+ synchronized( this )
+ {
+ if( m_status != Status.STATUS_PREPARING )
+ {
+ // we were marked for rollback
+ break;
+ }
+ }
+ TransactionBranch manager = (TransactionBranch) rms.next();
+ try
+ {
+ int vote = manager.getCommitter().prepare(
manager.getBranchId() );
+ if( vote == XAResource.XA_RDONLY )
+ {
+ // we don't need to consider this RM any more
+ rms.remove();
+ }
+ }
+ catch( XAException e )
+ {
+ synchronized( this )
+ {
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ //TODO document why this is true from the spec.
+ //XAException during prepare means we can assume
resource is rolled back.
+ rms.remove();
+ break;
+ }
+ }
+ }
+
+ // decision time...
+ boolean willCommit;
+ synchronized( this )
+ {
+ willCommit = ( m_status != Status.STATUS_MARKED_ROLLBACK );
+ if( willCommit )
+ {
+ m_status = Status.STATUS_PREPARED;
+ }
+ }
+ // log our decision
+ if( willCommit && !m_resourceManagers.isEmpty() )
+ {
+ try
+ {
+ m_logMark = m_txnLog.prepare(m_xid, m_resourceManagers);
+ }
+ catch( LogException e )
+ {
+ try
+ {
+ rollbackResources( m_resourceManagers );
+ }
+ catch( Exception se )
+ {
+ System.err.println( "ERROR: Unable to rollback after
failure to log prepare" );
+ Throwable cause = se.getCause();
+ if( cause != null )
+ {
+ cause.printStackTrace();
+ }
+ }
+ throw (SystemException) new SystemException( "Error logging
prepare; transaction was rolled back)" ).initCause( e );
+ }
+ }
+ return willCommit;
+ }
+
+ public void rollback()
+ throws IllegalStateException, SystemException
+ {
+ List rms;
+ synchronized( this )
+ {
+ switch( m_status )
+ {
+ case Status.STATUS_ACTIVE:
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ break;
+ case Status.STATUS_MARKED_ROLLBACK:
+ break;
+ default:
+ throw new IllegalStateException( "Status is " +
getStateString( m_status ) );
+ }
+ rms = m_resourceManagers;
+ }
+
+ beforeCompletion();
+ endResources();
+ try
+ {
+ rollbackResources( rms );
+ //only write rollback record if we have already written prepare
record.
+ if( m_logMark != null )
+ {
+ try
+ {
+ m_txnLog.rollback( m_xid, m_logMark );
+ } catch( LogException e )
+ {
+ try
+ {
+ rollbackResources( rms );
+ } catch( Exception se )
+ {
+ System.err.println( "ERROR: Unable to rollback after
failure to log decision" );
+ Throwable cause = se.getCause();
+ if( cause != null )
+ {
+ cause.printStackTrace();
+ }
+ }
+ throw (SystemException) new SystemException( "Error
logging rollback" ).initCause( e );
+ }
+ }
+ }
+ finally
+ {
+ afterCompletion();
+ synchronized( this )
+ {
+ m_status = Status.STATUS_NO_TRANSACTION;
+ }
+ }
+ }
+
+ private void beforeCompletion()
+ {
+ int i = 0;
+ while( true )
+ {
+ Synchronization synch;
+ synchronized( this )
+ {
+ if( i == m_syncList.size() )
+ {
+ return;
+ }
+ synch = (Synchronization) m_syncList.get(i++);
+ }
+ try
+ {
+ synch.beforeCompletion();
+ }
+ catch( Exception e )
+ {
+ System.err.println( "WARN: Unexpected exception from
beforeCompletion; transaction will roll back" );
+ e.printStackTrace();
+
+ synchronized( this )
+ {
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ }
+ }
+ }
+ }
+
+ private void afterCompletion()
+ {
+ // this does not synchronize because nothing can modify our state at
this time
+ Iterator iterator = m_syncList.iterator();
+ while( iterator.hasNext() )
+ {
+ Synchronization synch = (Synchronization) iterator.next();
+ try
+ {
+ synch.afterCompletion( m_status );
+ }
+ catch( Exception e )
+ {
+ System.err.println( "WARN: Unexpected exception from
afterCompletion; continuing" );
+ e.printStackTrace();
+ continue;
+ }
+ }
+ }
+
+ private void endResources()
+ {
+ endResources( m_activeXaResources );
+ endResources( m_suspendedXaResources );
+ }
+
+ private void endResources( IdentityHashMap resourceMap )
+ {
+ while( true )
+ {
+ XAResource xaRes;
+ TransactionBranch manager;
+ int flags;
+ synchronized( this )
+ {
+ Set entrySet = resourceMap.entrySet();
+ if( entrySet.isEmpty() )
+ {
+ return;
+ }
+ Map.Entry entry = (Map.Entry) entrySet.iterator().next();
+ xaRes = (XAResource) entry.getKey();
+ manager = (TransactionBranch) entry.getValue();
+ if( m_status == Status.STATUS_MARKED_ROLLBACK )
+ {
+ flags = XAResource.TMFAIL;
+ }
+ else
+ {
+ flags = XAResource.TMSUCCESS;
+ }
+ resourceMap.remove(xaRes);
+ }
+ try
+ {
+ xaRes.end( manager.getBranchId(), flags );
+ } catch( XAException e )
+ {
+ System.err.println( "WARN: Error ending association for
XAResource " + xaRes + "; transaction will roll back" );
+ e.printStackTrace();
+
+ synchronized (this)
+ {
+ m_status = Status.STATUS_MARKED_ROLLBACK;
+ }
+ }
+ }
+ }
+
+ private void rollbackResources( List rms )
+ throws SystemException
+ {
+ SystemException cause = null;
+ synchronized( this )
+ {
+ m_status = Status.STATUS_ROLLING_BACK;
+ }
+ Iterator iterator = rms.iterator();
+ while( iterator.hasNext() )
+ {
+ TransactionBranch manager = (TransactionBranch) iterator.next();
+ try
+ {
+ manager.getCommitter().rollback(manager.getBranchId());
+ }
+ catch( XAException e )
+ {
+ System.err.println( "ERROR: Unexpected exception rolling
back " + manager.getCommitter() + "; continuing with rollback" );
+ e.printStackTrace();
+ if( cause == null )
+ {
+ cause = new SystemException( e.errorCode );
+ }
+ continue;
+ }
+ }
+ synchronized( this )
+ {
+ m_status = Status.STATUS_ROLLEDBACK;
+ }
+ if( cause != null )
+ {
+ throw cause;
+ }
+ }
+
+ private void commitResources( List rms )
+ throws SystemException
+ {
+ SystemException cause = null;
+ synchronized( this )
+ {
+ m_status = Status.STATUS_COMMITTING;
+ }
+ Iterator i = rms.iterator();
+ while( i.hasNext() )
+ {
+ TransactionBranch manager = (TransactionBranch) i.next();
+ try
+ {
+ manager.getCommitter().commit( manager.getBranchId(), false
);
+ }
+ catch( XAException e )
+ {
+ System.err.println( "ERROR: Unexpected exception committing"
+ manager.getCommitter() + "; continuing to commit other RMs" );
+ e.printStackTrace();
+ if (cause == null)
+ {
+ cause = new SystemException( e.errorCode );
+ }
+ continue;
+ }
+ }
+ try
+ {
+ m_txnLog.commit( m_xid, m_logMark );
+ }
+ catch( LogException e )
+ {
+ System.err.println( "ERROR: Unexpected exception logging commit
completion for xid " + m_xid );
+ e.printStackTrace();
+ throw (SystemException) new SystemException( "Unexpected error
logging commit completion for xid " + m_xid ).initCause( e );
+ }
+ synchronized( this )
+ {
+ m_status = Status.STATUS_COMMITTED;
+ }
+ if( cause != null )
+ {
+ throw cause;
+ }
+ }
+
+ private static String getStateString( int status )
+ {
+ switch( status )
+ {
+ case Status.STATUS_ACTIVE:
+ return "STATUS_ACTIVE";
+ case Status.STATUS_PREPARING:
+ return "STATUS_PREPARING";
+ case Status.STATUS_PREPARED:
+ return "STATUS_PREPARED";
+ case Status.STATUS_MARKED_ROLLBACK:
+ return "STATUS_MARKED_ROLLBACK";
+ case Status.STATUS_ROLLING_BACK:
+ return "STATUS_ROLLING_BACK";
+ case Status.STATUS_COMMITTING:
+ return "STATUS_COMMITTING";
+ case Status.STATUS_COMMITTED:
+ return "STATUS_COMMITTED";
+ case Status.STATUS_ROLLEDBACK:
+ return "STATUS_ROLLEDBACK";
+ case Status.STATUS_NO_TRANSACTION:
+ return "STATUS_NO_TRANSACTION";
+ case Status.STATUS_UNKNOWN:
+ return "STATUS_UNKNOWN";
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ public boolean equals( Object obj )
+ {
+ if( obj instanceof TransactionImpl )
+ {
+ TransactionImpl other = (TransactionImpl) obj;
+ return m_xid.equals( other.m_xid );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //when used from recovery, do not add manager to active or suspended
resource maps.
+ // The xaresources have already been ended with TMSUCCESS.
+ public TransactionBranch addBranchXid( XAResource xaRes, Xid branchId )
+ {
+ TransactionBranch manager = new TransactionBranch( xaRes, branchId );
+ m_resourceManagers.add( manager );
+ return manager;
+ }
+
+ private static class TransactionBranch
+ implements TransactionBranchInfo
+ {
+ private final NamedXAResource m_committer;
+ private final Xid m_branchId;
+
+ public TransactionBranch( XAResource xaRes, Xid branchId )
+ {
+ m_committer = (NamedXAResource) xaRes;
+ this.m_branchId = branchId;
+ }
+
+ public NamedXAResource getCommitter()
+ {
+ return m_committer;
+ }
+
+ public Xid getBranchId()
+ {
+ return m_branchId;
+ }
+
+ public String getResourceName()
+ {
+ return m_committer.getName();
+ }
+
+ public Xid getBranchXid()
+ {
+ return m_branchId;
+ }
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionLog.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionLog.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.transaction.xa.Xid;
+
+
+/**
+ * Interface used to notify a logging subsystem of transaction events.
+ *
+ * @version $Rev: 54973 $ $Date: 2004-10-18 04:06:11 +0800 (Mon, 18 Oct
2004) $
+ */
+public interface TransactionLog
+{
+
+ void begin( Xid xid )
+ throws LogException;
+
+ /**
+ * log prepare for the global xid xid and the list of
TransactionBranchInfo branches
+ * @param xid global xid for the transactions
+ * @param branches List of TransactionBranchInfo
+ * @throws LogException
+ */
+ Object prepare( Xid xid, List branches )
+ throws LogException;
+
+ void commit( Xid xid, Object logMark )
+ throws LogException;
+
+ void rollback( Xid xid, Object logMark )
+ throws LogException;
+
+ /**
+ * Recovers the log, returning a map of (top level) xid to List of
TransactionBranchInfo for the branches.
+ * Uses the XidFactory to reconstruct the xids.
+ *
+ * @param xidFactory
+ * @return Map of recovered xid to List of TransactionBranchInfo
representing the branches.
+ * @throws LogException
+ */
+ Collection recover( XidFactory xidFactory )
+ throws LogException;
+
+ String getXMLStats();
+
+ int getAverageForceTime();
+
+ int getAverageBytesPerForce();
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionManagerImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionManagerImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,432 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+/*
+import org.apache.geronimo.gbean.GBeanInfo;
+import org.apache.geronimo.gbean.GBeanInfoBuilder;
+import org.apache.geronimo.gbean.ReferenceCollection;
+import org.apache.geronimo.gbean.ReferenceCollectionEvent;
+import org.apache.geronimo.gbean.ReferenceCollectionListener;
+import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
+*/
+
+import net.dpml.tx.ExtendedTransactionManager;
+import net.dpml.tx.log.UnrecoverableLog;
+
+/**
+ * Simple implementation of a transaction manager.
+ *
+ * @version $Rev: 128441 $ $Date: 2005-01-28 06:56:21 +0800 (Fri, 28 Jan
2005) $
+ */
+public class TransactionManagerImpl
+ implements ExtendedTransactionManager, XidImporter
+{
+ final TransactionLog m_transactionLog;
+ final XidFactory m_xidFactory;
+ private final int m_defaultTransactionTimeoutMilliseconds;
+ private final ThreadLocal m_transactionTimeoutMilliseconds = new
ThreadLocal();
+ private final ThreadLocal m_threadTx = new ThreadLocal();
+
+ final Recovery m_recovery;
+ final Collection m_resourceManagers;
+ private List m_recoveryErrors = new ArrayList();
+
+ /**
+ * TODO NOTE!!! this should be called in an unspecified transaction
context, but we cannot enforce this restriction!
+ */
+ public TransactionManagerImpl( int defaultTransactionTimeoutSeconds,
TransactionLog transactionLog, Collection resourceManagers )
+ throws XAException
+ {
+ if( defaultTransactionTimeoutSeconds <= 0 )
+ {
+ throw new IllegalArgumentException(
"defaultTransactionTimeoutSeconds must be positive: attempted value: " +
defaultTransactionTimeoutSeconds );
+ }
+
+ this.m_defaultTransactionTimeoutMilliseconds =
defaultTransactionTimeoutSeconds * 1000;
+ this.m_transactionLog = transactionLog == null ? new
UnrecoverableLog() : transactionLog;
+ this.m_xidFactory = new XidFactoryImpl( "WHAT DO WE CALL
IT?".getBytes() );
+ this.m_resourceManagers = resourceManagers;
+ m_recovery = new RecoveryImpl( this.m_transactionLog,
this.m_xidFactory );
+
+ if( resourceManagers != null )
+ {
+ m_recovery.recoverLog();
+ List copy = null;
+ synchronized( resourceManagers )
+ {
+ copy = new ArrayList( resourceManagers );
+/* Niclas: TODO; What is this needed for??
+ this.resourceManagers.addReferenceCollectionListener(
+ new ReferenceCollectionListener()
+ {
+ public void memberAdded( ReferenceCollectionEvent
event )
+ {
+ ResourceManager resourceManager =
(ResourceManager) event.getMember();
+ recoverResourceManager( resourceManager );
+ }
+
+ public void memberRemoved( ReferenceCollectionEvent
event )
+ {
+ }
+
+ }
+ );
+*/
+ }
+ Iterator iterator = copy.iterator();
+ while( iterator.hasNext() )
+ {
+ ResourceManager resourceManager = (ResourceManager)
iterator.next();
+ recoverResourceManager( resourceManager );
+ }
+ //what to do if there are recovery errors? or not all resource
managers are online?
+ }
+ }
+
+ public Transaction getTransaction()
+ throws SystemException
+ {
+ return (Transaction) m_threadTx.get();
+ }
+
+ public void setTransactionTimeout( int seconds )
+ throws SystemException
+ {
+ if( seconds < 0 )
+ {
+ throw new SystemException( "transaction timeout must be positive
or 0 to reset to default" );
+ }
+
+ if (seconds == 0)
+ {
+ m_transactionTimeoutMilliseconds.set(null);
+ }
+ else
+ {
+ m_transactionTimeoutMilliseconds.set( new Long( seconds * 1000 )
);
+ }
+ }
+
+ public int getStatus()
+ throws SystemException
+ {
+ Transaction tx = getTransaction();
+ if( tx != null )
+ {
+ return tx.getStatus();
+ }
+ else
+ {
+ return Status.STATUS_NO_TRANSACTION;
+ }
+ }
+
+ public void begin()
+ throws NotSupportedException, SystemException
+ {
+ begin( getTransactionTimeoutMilliseconds( 0L ) );
+ }
+
+ public Transaction begin( long transactionTimeoutMilliseconds )
+ throws NotSupportedException, SystemException
+ {
+ if( getStatus() != Status.STATUS_NO_TRANSACTION )
+ {
+ throw new NotSupportedException("Nested Transactions are not
supported");
+ }
+ long timeout = getTransactionTimeoutMilliseconds(
transactionTimeoutMilliseconds );
+ TransactionImpl tx = new TransactionImpl( m_xidFactory,
m_transactionLog, timeout );
+// timeoutTimer.schedule(tx,
getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds));
+ m_threadTx.set( tx );
+ // Todo: Verify if this is correct thing to do. Use default timeout
for next transaction.
+ this.m_transactionTimeoutMilliseconds.set( null );
+ return tx;
+ }
+
+ public Transaction suspend()
+ throws SystemException
+ {
+ Transaction tx = getTransaction();
+ if( tx != null )
+ {
+ }
+ m_threadTx.set( null );
+ return tx;
+ }
+
+ public void resume( Transaction tx )
+ throws IllegalStateException, InvalidTransactionException,
SystemException
+ {
+ if( m_threadTx.get() != null )
+ {
+ throw new IllegalStateException( "Transaction already associated
with current thread" );
+ }
+ if( tx instanceof TransactionImpl == false )
+ {
+ throw new InvalidTransactionException( "Cannot resume foreign
transaction: " + tx );
+ }
+ m_threadTx.set( tx );
+ }
+
+ public void setRollbackOnly()
+ throws IllegalStateException, SystemException
+ {
+ Transaction tx = getTransaction();
+ if( tx == null )
+ {
+ throw new IllegalStateException( "No transaction associated with
current thread" );
+ }
+ tx.setRollbackOnly();
+ }
+
+ public void commit()
+ throws HeuristicMixedException, HeuristicRollbackException,
IllegalStateException,
+ RollbackException, SecurityException, SystemException
+ {
+ Transaction tx = getTransaction();
+ if( tx == null )
+ {
+ throw new IllegalStateException( "No transaction associated with
current thread" );
+ }
+ try
+ {
+ tx.commit();
+ }
+ finally
+ {
+ m_threadTx.set( null );
+ }
+ }
+
+ public void rollback()
+ throws IllegalStateException, SecurityException, SystemException
+ {
+ Transaction tx = getTransaction();
+ if( tx == null )
+ {
+ throw new IllegalStateException("No transaction associated with
current thread");
+ }
+ try
+ {
+ tx.rollback();
+ }
+ finally
+ {
+ m_threadTx.set( null );
+ }
+ }
+
+ //XidImporter implementation
+ public Transaction importXid( Xid xid, long
transactionTimeoutMilliseconds )
+ throws XAException, SystemException
+ {
+ if( transactionTimeoutMilliseconds < 0 )
+ {
+ throw new SystemException("transaction timeout must be positive
or 0 to reset to default");
+ }
+ long timeout = getTransactionTimeoutMilliseconds(
transactionTimeoutMilliseconds );
+ TransactionImpl tx = new TransactionImpl( xid, m_xidFactory,
m_transactionLog, timeout );
+ return tx;
+ }
+
+ public void commit( Transaction tx, boolean onePhase )
+ throws XAException
+ {
+ if( onePhase )
+ {
+ try
+ {
+ tx.commit();
+ }
+ catch( HeuristicMixedException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch(HeuristicRollbackException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch( RollbackException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch( SecurityException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch( SystemException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ }
+ else
+ {
+ try
+ {
+ TransactionImpl impl = (TransactionImpl) tx;
+ impl.preparedCommit();
+ }
+ catch( SystemException e )
+ {
+ throw (XAException) new XAException().initCause(e);
+ }
+ }
+ }
+
+ public void forget( Transaction tx )
+ throws XAException
+ {
+ //TODO implement this!
+ }
+
+ public int prepare( Transaction tx )
+ throws XAException
+ {
+ try
+ {
+ TransactionImpl impl = (TransactionImpl) tx;
+ return impl.prepare();
+ }
+ catch( SystemException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch( RollbackException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ }
+
+ public void rollback( Transaction tx )
+ throws XAException
+ {
+ try
+ {
+ tx.rollback();
+ }
+ catch( IllegalStateException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ catch( SystemException e )
+ {
+ throw (XAException) new XAException().initCause( e );
+ }
+ }
+
+ public long getTransactionTimeoutMilliseconds( long
transactionTimeoutMilliseconds )
+ {
+ if( transactionTimeoutMilliseconds != 0 )
+ {
+ return transactionTimeoutMilliseconds;
+ }
+ Long timeout = (Long) this.m_transactionTimeoutMilliseconds.get();
+ if( timeout != null )
+ {
+ return timeout.longValue();
+ }
+ return m_defaultTransactionTimeoutMilliseconds;
+ }
+
+ protected void recoverResourceManager( ResourceManager resourceManager )
+ {
+ NamedXAResource namedXAResource = null;
+ try
+ {
+ namedXAResource = resourceManager.getRecoveryXAResources();
+ }
+ catch( SystemException e )
+ {
+ e.printStackTrace();
+ m_recoveryErrors.add( e );
+ return;
+ }
+ if( namedXAResource != null )
+ {
+ try
+ {
+ m_recovery.recoverResourceManager( namedXAResource );
+ }
+ catch( XAException e )
+ {
+ e.printStackTrace();
+ m_recoveryErrors.add( e );
+ } finally
+ {
+ resourceManager.returnResource( namedXAResource );
+ }
+ }
+ }
+
+ public Map getExternalXids()
+ {
+ return new HashMap( m_recovery.getExternalXids() );
+ }
+
+ public XidFactory getXidFactory()
+ {
+ return m_xidFactory;
+ }
+
+ public Recovery getRecovery()
+ {
+ return m_recovery;
+ }
+
+/*
+ public static final GBeanInfo GBEAN_INFO;
+
+ static {
+ GBeanInfoBuilder infoBuilder = new
GBeanInfoBuilder(TransactionManagerImpl.class, NameFactory.JTA_RESOURCE);
+
+ infoBuilder.addAttribute("defaultTransactionTimeoutSeconds",
int.class, true);
+ infoBuilder.addReference("TransactionLog", TransactionLog.class);
+ infoBuilder.addReference("ResourceManagers", ResourceManager.class);
+
+ infoBuilder.addInterface(ExtendedTransactionManager.class);
+ infoBuilder.addInterface(XidImporter.class);
+
+ infoBuilder.setConstructor(new
String[]{"defaultTransactionTimeoutSeconds", "TransactionLog",
"ResourceManagers"});
+
+ GBEAN_INFO = infoBuilder.getBeanInfo();
+ }
+
+
+ public static GBeanInfo getGBeanInfo() {
+ return GBEAN_INFO;
+ }
+*/
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionTimer.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/TransactionTimer.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+/**
+ * TODO improve shutdown
+ *
+ * @version $Revision: $ $Date: $
+ */
+public class TransactionTimer
+{
+ private static volatile long currentTime;
+
+ private static class CurrentTime extends Thread
+ {
+ protected CurrentTime()
+ {
+ currentTime = System.currentTimeMillis();
+ }
+
+ public void run()
+ {
+ while( true )
+ {
+ currentTime = System.currentTimeMillis();
+ try
+ {
+ Thread.sleep(1000);
+ } catch( InterruptedException e )
+ {
+ // Ignore exception
+ }
+ }
+ }
+ }
+
+ static
+ {
+ CurrentTime tm = new CurrentTime();
+ tm.setDaemon( true );
+ tm.start();
+ }
+
+ public static long getCurrentTime()
+ {
+ return currentTime;
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/WrapperNamedXAResource.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/WrapperNamedXAResource.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,116 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAException;
+
+import net.dpml.tx.manager.NamedXAResource;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public class WrapperNamedXAResource
+ implements NamedXAResource
+{
+
+ private final XAResource m_xaResource;
+ private final String m_name;
+
+ public WrapperNamedXAResource( XAResource xaResource, String name )
+ {
+ this.m_xaResource = xaResource;
+ this.m_name = name;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public void commit( Xid xid, boolean onePhase )
+ throws XAException
+ {
+ m_xaResource.commit( xid, onePhase );
+ }
+
+ public void end( Xid xid, int flags )
+ throws XAException
+ {
+ m_xaResource.end( xid, flags );
+ }
+
+ public void forget( Xid xid )
+ throws XAException
+ {
+ m_xaResource.forget( xid );
+ }
+
+ public int getTransactionTimeout()
+ throws XAException
+ {
+ return m_xaResource.getTransactionTimeout();
+ }
+
+ public boolean isSameRM( XAResource other )
+ throws XAException
+ {
+ if( other instanceof WrapperNamedXAResource )
+ {
+ WrapperNamedXAResource resource = (WrapperNamedXAResource) other;
+ return m_xaResource.isSameRM( resource.m_xaResource);
+ }
+ return false;
+ }
+
+ public int prepare( Xid xid )
+ throws XAException
+ {
+ return m_xaResource.prepare( xid );
+ }
+
+ public Xid[] recover( int flag )
+ throws XAException
+ {
+ return m_xaResource.recover( flag );
+ }
+
+ public void rollback( Xid xid )
+ throws XAException
+ {
+ m_xaResource.rollback(xid);
+ }
+
+ public boolean setTransactionTimeout( int seconds )
+ throws XAException
+ {
+ return m_xaResource.setTransactionTimeout(seconds);
+ }
+
+ public void start( Xid xid, int flags )
+ throws XAException
+ {
+ m_xaResource.start( xid, flags );
+ }
+}
+
+

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactory.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactory.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,22 @@
+package net.dpml.tx.manager;
+
+import javax.transaction.xa.Xid;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ * */
+public interface XidFactory
+{
+ Xid createXid();
+
+ Xid createBranch( Xid globalId, int branch );
+
+ boolean matchesGlobalId( byte[] globalTransactionId );
+
+ boolean matchesBranchId( byte[] branchQualifier );
+
+ Xid recover( int formatId, byte[] globalTransactionid, byte[]
branchQualifier );
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactoryImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidFactoryImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,151 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.transaction.xa.Xid;
+
+/*
+import org.apache.geronimo.gbean.GBeanInfo;
+import org.apache.geronimo.gbean.GBeanInfoBuilder;
+import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
+*/
+
+/**
+ * Factory for transaction ids.
+ * The Xid is constructed of three parts:
+ * <ol><li>8 byte count (LSB first)</li>
+ * <li>4 byte system id</li>
+ * <li>4 or 16 byte IP address of host</li>
+ * <ol>
+ * @version $Rev: 126313 $ $Date: 2005-01-25 05:03:52 +0800 (Tue, 25 Jan
2005) $
+ * todo Should have a way of setting baseId
+ */
+public class XidFactoryImpl
+ implements XidFactory
+{
+ private final byte[] m_baseId = new byte[ Xid.MAXGTRIDSIZE ];
+ private long m_count = 1;
+
+ public XidFactoryImpl( byte[] tmId )
+ {
+ System.arraycopy( tmId, 0, m_baseId, 8, tmId.length );
+ }
+
+ public XidFactoryImpl()
+ {
+ byte[] hostid;
+ try
+ {
+ hostid = InetAddress.getLocalHost().getAddress();
+ }
+ catch( UnknownHostException e )
+ {
+ hostid = new byte[] { 127, 0, 0, 1 };
+ }
+ int uid = System.identityHashCode( this );
+ m_baseId[ 8 ] = (byte) uid;
+ m_baseId[ 9 ] = (byte) ( uid >>> 8 );
+ m_baseId[ 10 ] = (byte) ( uid >>> 16 );
+ m_baseId[ 11 ] = (byte) ( uid >>> 24 );
+ System.arraycopy( hostid, 0, m_baseId, 12, hostid.length );
+ }
+
+ public Xid createXid()
+ {
+ byte[] globalId = (byte[]) m_baseId.clone();
+ long id;
+
+ synchronized( this )
+ {
+ id = m_count++;
+ }
+ globalId[0] = (byte) id;
+ globalId[1] = (byte) (id >>> 8);
+ globalId[2] = (byte) (id >>> 16);
+ globalId[3] = (byte) (id >>> 24);
+ globalId[4] = (byte) (id >>> 32);
+ globalId[5] = (byte) (id >>> 40);
+ globalId[6] = (byte) (id >>> 48);
+ globalId[7] = (byte) (id >>> 56);
+ return new XidImpl( globalId );
+ }
+
+ public Xid createBranch( Xid globalId, int branch )
+ {
+ byte[] branchId = (byte[]) m_baseId.clone();
+ branchId[0] = (byte) branch;
+ branchId[1] = (byte) (branch >>> 8);
+ branchId[2] = (byte) (branch >>> 16);
+ branchId[3] = (byte) (branch >>> 24);
+ return new XidImpl( globalId, branchId );
+ }
+
+ public boolean matchesGlobalId( byte[] globalTransactionId )
+ {
+ if( globalTransactionId.length != Xid.MAXGTRIDSIZE )
+ {
+ return false;
+ }
+ for( int i = 8; i < globalTransactionId.length; i++ )
+ {
+ if( globalTransactionId[ i ] != m_baseId[ i ] )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean matchesBranchId( byte[] branchQualifier )
+ {
+ if( branchQualifier.length != Xid.MAXBQUALSIZE )
+ {
+ return false;
+ }
+ for( int i = 8; i < branchQualifier.length; i++ )
+ {
+ if( branchQualifier[i] != m_baseId[i] )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Xid recover( int formatId, byte[] globalTransactionid, byte[]
branchQualifier )
+ {
+ return new XidImpl( formatId, globalTransactionid, branchQualifier );
+ }
+
+/*
+ public static final GBeanInfo GBEAN_INFO;
+
+ static {
+ GBeanInfoBuilder infoFactory = new
GBeanInfoBuilder(XidFactoryImpl.class, NameFactory.JTA_RESOURCE);
+ infoFactory.addInterface(XidFactory.class);
+ GBEAN_INFO = infoFactory.getBeanInfo();
+ }
+
+ public static GBeanInfo getGBeanInfo() {
+ return GBEAN_INFO;
+ }
+*/
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImpl.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImpl.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,141 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import javax.transaction.xa.Xid;
+
+/**
+ * Unique id for a transaction.
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public class XidImpl
+ implements Xid, Serializable
+{
+ private static int FORMAT_ID = 0x4765526f; // Gero
+ private final int m_formatId;
+ private final byte[] m_globalId;
+ private final byte[] m_branchId;
+ private int m_hash; //apparently never used by our code, so don't
compute it.
+
+ /**
+ * Constructor taking a global id (for the main transaction)
+ * @param globalId the global transaction id
+ */
+ public XidImpl( byte[] globalId )
+ {
+ this.m_formatId = FORMAT_ID;
+ this.m_globalId = globalId;
+ //this.hash = hash(0, globalId);
+ m_branchId = new byte[ Xid.MAXBQUALSIZE ];
+ }
+
+ /**
+ * Constructor for a branch id
+ * @param global the xid of the global transaction this branch belongs to
+ * @param branch the branch id
+ */
+ public XidImpl( Xid global, byte[] branch )
+ {
+ this.m_formatId = FORMAT_ID;
+ //int hash;
+ if( global instanceof XidImpl )
+ {
+ m_globalId = ( (XidImpl) global).m_globalId;
+ //hash = ((XidImpl) global).hash;
+ }
+ else
+ {
+ m_globalId = global.getGlobalTransactionId();
+ //hash = hash(0, globalId);
+ }
+ m_branchId = branch;
+ //this.hash = hash(hash, branchId);
+ }
+
+ public XidImpl( int formatId, byte[] globalId, byte[] branchId )
+ {
+ this.m_formatId = formatId;
+ this.m_globalId = globalId;
+ this.m_branchId = branchId;
+ }
+
+ private int hash( int hash, byte[] id )
+ {
+ for( int i = 0; i < id.length; i++ )
+ {
+ hash = hash * 37 + id[ i ];
+ }
+ return hash;
+ }
+
+ public int getFormatId()
+ {
+ return m_formatId;
+ }
+
+ public byte[] getGlobalTransactionId()
+ {
+ return (byte[]) m_globalId.clone();
+ }
+
+ public byte[] getBranchQualifier()
+ {
+ return (byte[]) m_branchId.clone();
+ }
+
+ public boolean equals(Object obj)
+ {
+ if( obj instanceof XidImpl == false )
+ {
+ return false;
+ }
+ XidImpl other = (XidImpl) obj;
+ return m_formatId == other.m_formatId
+ && Arrays.equals( m_globalId, other.m_globalId )
+ && Arrays.equals( m_branchId, other.m_branchId );
+ }
+
+ public int hashCode()
+ {
+ if (m_hash == 0)
+ {
+ m_hash = hash( hash( 0, m_globalId ), m_branchId );
+ }
+ return m_hash;
+ }
+
+ public String toString()
+ {
+ StringBuffer s = new StringBuffer();
+ s.append( "[globalId=" );
+ for( int i = 0; i < m_globalId.length; i++ )
+ {
+ s.append( Integer.toHexString( m_globalId[ i ] ) );
+ }
+ s.append( ",branchId=" );
+ for( int i = 0; i < m_branchId.length; i++ )
+ {
+ s.append( Integer.toHexString( m_branchId[ i ] ) );
+ }
+ s.append( "]" );
+ return s.toString();
+ }
+}

Added:
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImporter.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/main/net/dpml/tx/manager/XidImporter.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,50 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.manager;
+
+import java.util.Map;
+
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAException;
+import javax.transaction.Transaction;
+import javax.transaction.SystemException;
+
+/**
+ *
+ *
+ * @version $Rev: 55410 $ $Date: 2004-10-24 10:14:58 +0800 (Sun, 24 Oct
2004) $
+ *
+ * */
+public interface XidImporter
+{
+
+ Transaction importXid( Xid xid, long transactionTimeoutMillis )
+ throws XAException, SystemException;
+
+ void commit( Transaction tx, boolean onePhase )
+ throws XAException;
+
+ void forget( Transaction tx )
+ throws XAException;
+
+ int prepare( Transaction tx )
+ throws XAException;
+
+ void rollback( Transaction tx )
+ throws XAException;
+}

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/context/TransactionContextManagerTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/context/TransactionContextManagerTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,106 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.context;
+
+import javax.transaction.xa.Xid;
+
+import junit.framework.TestCase;
+import net.dpml.tx.ImportedTransactionActiveException;
+import net.dpml.tx.context.TransactionContextManager;
+import net.dpml.tx.manager.TransactionManagerImpl;
+import net.dpml.tx.manager.XidFactory;
+import net.dpml.tx.manager.XidFactoryImpl;
+
+
+/**
+ *
+ *
+ * @version $Rev: 54013 $ $Date: 2004-10-07 13:49:55 -0700 (Thu, 07 Oct
2004) $
+ *
+ */
+public class TransactionContextManagerTest extends TestCase
+{
+
+ private TransactionContextManager transactionContextManager;
+ private XidFactory xidFactory = new XidFactoryImpl(
"geronimo.test.tm"
+ .getBytes() );
+
+ protected void setUp()
+ throws Exception
+ {
+ TransactionManagerImpl tm = new TransactionManagerImpl( 1000, null,
null );
+ transactionContextManager = new TransactionContextManager( tm, tm );
+ }
+
+ protected void tearDown()
+ throws Exception
+ {
+ transactionContextManager = null;
+ }
+
+ public void testImportedTxLifecycle()
+ throws Exception
+ {
+ Xid xid = xidFactory.createXid();
+ transactionContextManager.begin( xid, 1000 );
+ transactionContextManager.end( xid );
+ transactionContextManager.begin( xid, 1000 );
+ transactionContextManager.end( xid );
+ transactionContextManager.prepare( xid );
+ transactionContextManager.commit( xid, false );
+ }
+
+ public void testNoConcurrentWorkSameXid()
+ throws Exception
+ {
+ Xid xid = xidFactory.createXid();
+ transactionContextManager.begin( xid, 1000 );
+ try
+ {
+ transactionContextManager.begin( xid, 1000 );
+ fail( "should not be able begin same xid twice" );
+ } catch( ImportedTransactionActiveException e )
+ {
+ //expected
+ } finally
+ {
+ transactionContextManager.end( xid );
+ transactionContextManager.rollback( xid );
+ }
+ }
+
+ public void testOnlyOneImportedTxAtATime()
+ throws Exception
+ {
+ Xid xid1 = xidFactory.createXid();
+ Xid xid2 = xidFactory.createXid();
+ transactionContextManager.begin( xid1, 1000 );
+ try
+ {
+ transactionContextManager.begin( xid2, 1000 );
+ fail( "should not be able to begin a 2nd tx without ending the
first" );
+ } catch( IllegalStateException e )
+ {
+ //expected
+ } finally
+ {
+ transactionContextManager.end( xid1 );
+ transactionContextManager.rollback( xid1 );
+ }
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/log/AbstractLogTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/log/AbstractLogTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,235 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.log;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+
+import javax.transaction.xa.Xid;
+
+import junit.framework.TestCase;
+import net.dpml.tx.log.XidImpl2;
+import net.dpml.tx.manager.TransactionLog;
+
+
+/**
+ *
+ *
+ * @version $Rev: 54973 $ $Date: 2004-10-18 04:06:11 +0800 (Mon, 18 Oct
2004) $
+ *
+ */
+public abstract class AbstractLogTest extends TestCase
+{
+ private Object startBarrier = new Object();
+ private Object stopBarrier = new Object();
+ private int startedThreads = 0;
+ private int stoppedThreads = 0;
+ long totalDuration = 0;
+ private Xid xid;
+ private List names;
+ final Object mutex = new Object();
+ long totalXidCount = 0;
+ private Writer resultsXML;
+ private Writer resultsCSV;
+
+ public void testDummy()
+ throws Exception
+ {
+ }
+
+ public void testTransactionLog()
+ throws Exception
+ {
+ File resultFileXML = new File( getResultFileName() + ".xml" );
+ resultsXML = new FileWriter( resultFileXML );
+ resultsXML.write( "<log-test>\n" );
+ File resultFileCSV = new File( getResultFileName() + ".csv" );
+ resultsCSV = new FileWriter( resultFileCSV );
+ resultsCSV
+ .write(
"workerCount,xidCount,TotalXids,missingXids,DurationMilliseconds,XidsPerSecond,AverageForceTime,AverageBytesPerForce,AverageLatency\n"
);
+ int xidCount = Integer.getInteger( "xa.log.test.xid.count", 50
).intValue();
+ int minWorkerCount = Integer.getInteger(
"xa.log.test.worker.count.min", 20 ).intValue();
+ int maxWorkerCount = Integer.getInteger(
"xa.log.test.worker.count.max", 40 ).intValue();
+ int workerCountStep = Integer.getInteger(
"xa.log.test.worker.count.step", 20 ).intValue();
+ int repCount = Integer.getInteger( "xa.log.test.repetition.count", 1
).intValue();
+ long maxTime = Long.getLong( "xa.log.test.max.time.seconds", 30
).longValue() * 1000;
+ int overtime = 0;
+ try
+ {
+ for( int workers = minWorkerCount; workers <= maxWorkerCount;
workers += workerCountStep )
+ {
+ for( int reps = 0; reps < repCount; reps++ )
+ {
+ if( testTransactionLog( workers, xidCount ) > maxTime )
+ {
+ overtime++;
+ if( overtime > 1 )
+ {
+ return;
+ }
+ }
+ resultsCSV.flush();
+ resultsXML.flush();
+ }
+ }
+ } finally
+ {
+ resultsXML.write( "</log-test>\n" );
+ resultsXML.flush();
+ resultsXML.close();
+ resultsCSV.flush();
+ resultsCSV.close();
+ }
+ }
+
+ protected abstract String getResultFileName();
+
+ public long testTransactionLog( int workers, int xidCount )
+ throws Exception
+ {
+ TransactionLog transactionLog = createTransactionLog();
+
+ xid = new XidImpl2( new byte[ Xid.MAXGTRIDSIZE ] );
+ //TODO Supply an actual list
+ names = Collections.EMPTY_LIST;
+
+ long startTime = journalTest( transactionLog, workers, xidCount );
+
+ long stopTime = System.currentTimeMillis();
+
+ printSpeedReport( transactionLog, startTime, stopTime, workers,
xidCount );
+ closeTransactionLog( transactionLog );
+ return stopTime - startTime;
+ }
+
+ protected abstract void closeTransactionLog( TransactionLog
transactionLog )
+ throws Exception;
+
+ protected abstract TransactionLog createTransactionLog()
+ throws Exception;
+
+ private long journalTest( final TransactionLog logger, final int
workers, final int xidCount )
+ throws Exception
+ {
+ totalXidCount = 0;
+ startedThreads = 0;
+ stoppedThreads = 0;
+ totalDuration = 0;
+ for( int i = 0; i < workers; i++ )
+ {
+ new Thread()
+ {
+ public void run()
+ {
+ long localXidCount = 0;
+ boolean exception = false;
+ long localDuration = 0;
+ try
+ {
+ synchronized( startBarrier )
+ {
+ ++startedThreads;
+ startBarrier.notifyAll();
+ while( startedThreads < ( workers + 1 ) )
+ startBarrier.wait();
+ }
+ long localStartTime = System.currentTimeMillis();
+
+ for( int jj = 0; jj < xidCount; jj++ )
+ {
+ // journalize COMMITTING record
+ Object logMark = logger.prepare( xid, names );
+ //localXidCount++;
+
+ // journalize FORGET record
+ logger.commit( xid, logMark );
+ localXidCount++;
+ }
+ localDuration = System.currentTimeMillis() -
localStartTime;
+ } catch( Exception e )
+ {
+ System.err.println( Thread.currentThread().getName()
);
+ e.printStackTrace( System.err );
+ exception = true;
+ } finally
+ {
+ synchronized( mutex )
+ {
+ totalXidCount += localXidCount;
+ totalDuration += localDuration;
+ }
+ synchronized( stopBarrier )
+ {
+ ++stoppedThreads;
+ stopBarrier.notifyAll();
+ }
+ }
+
+ }
+ }.start();
+ }
+
+ // Wait for all the workers to be ready..
+ long startTime = 0;
+ synchronized( startBarrier )
+ {
+ while( startedThreads < workers )
+ startBarrier.wait();
+ ++startedThreads;
+ startBarrier.notifyAll();
+ startTime = System.currentTimeMillis();
+ }
+
+ // Wait for all the workers to finish.
+ synchronized( stopBarrier )
+ {
+ while( stoppedThreads < workers )
+ stopBarrier.wait();
+ }
+
+ return startTime;
+
+ }
+
+ void printSpeedReport( TransactionLog logger, long startTime, long
stopTime, int workers,
+ int xidCount )
+ throws IOException
+ {
+ long mc = ( (long) xidCount ) * workers;
+ long duration = ( stopTime - startTime );
+ long xidsPerSecond = ( totalXidCount * 1000 / ( duration ) );
+ int averageForceTime = logger.getAverageForceTime();
+ int averageBytesPerForce = logger.getAverageBytesPerForce();
+ long averageLatency = totalDuration / totalXidCount;
+ resultsXML.write( "<run><workers>" + workers +
"</workers><xids-per-thread>" + xidCount
+ + "</xids-per-thread><expected-total-xids>" + mc
+ + "</expected-total-xids><missing-xids>" + ( mc -
totalXidCount )
+ + "</missing-xids><totalDuration-milliseconds>" +
duration
+ + "</totalDuration-milliseconds><xids-per-second>"
+ xidsPerSecond
+ + "</xids-per-second></run>\n" );
+ resultsXML.write( logger.getXMLStats() + "\n" );
+ resultsCSV.write( "" + workers + "," + xidCount + "," + mc + "," + (
mc - totalXidCount )
+ + "," + duration + "," + xidsPerSecond + "," +
averageForceTime + ","
+ + averageBytesPerForce + "," + averageLatency +
"\n" );
+
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/AbstractRecoveryTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/AbstractRecoveryTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,314 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.LogException;
+import net.dpml.tx.manager.NamedXAResource;
+import net.dpml.tx.manager.Recovery;
+import net.dpml.tx.manager.RecoveryImpl;
+import net.dpml.tx.manager.TransactionBranchInfo;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.XidFactory;
+import net.dpml.tx.manager.XidFactoryImpl;
+
+
+/**
+ * This is just a unit test for recovery, depending on proper behavior of the
+ * log(s) it uses.
+ *
+ * @version $Rev: 54973 $ $Date: 2004-10-18 04:06:11 +0800 (Mon, 18 Oct
2004) $
+ *
+ */
+public abstract class AbstractRecoveryTest extends TestCase
+{
+
+ private static final String RM1 = "rm1";
+ private static final String RM2 = "rm2";
+ private static final String RM3 = "rm3";
+ private static final int XID_COUNT = 3;
+
+ protected TransactionLog m_txLog;
+
+ protected final XidFactory m_xidFactory = new XidFactoryImpl();
+ private int m_branchCounter;
+
+ public void testDummy()
+ throws Exception
+ {
+ }
+
+ public void test2ResOnlineAfterRecoveryStart()
+ throws Exception
+ {
+ Xid[] xids = getXidArray( XID_COUNT );
+ MockXAResource xares1 = new MockXAResource( RM1, xids );
+ MockXAResource xares2 = new MockXAResource( RM2, xids );
+ MockTransactionInfo[] txInfos = makeTxInfos( xids );
+ addBranch( txInfos, xares1 );
+ addBranch( txInfos, xares2 );
+ prepareLog( m_txLog, txInfos );
+ prepareForReplay();
+ Recovery recovery = new RecoveryImpl( m_txLog, m_xidFactory );
+ recovery.recoverLog();
+ assertTrue( ! recovery.hasRecoveryErrors() );
+ assertTrue( recovery.getExternalXids().isEmpty() );
+ assertTrue( ! recovery.localRecoveryComplete() );
+ recovery.recoverResourceManager( xares1 );
+ assertTrue( ! recovery.localRecoveryComplete() );
+ assertEquals( XID_COUNT, xares1.committed.size() );
+ recovery.recoverResourceManager( xares2 );
+ assertEquals( XID_COUNT, xares2.committed.size() );
+ assertTrue( recovery.localRecoveryComplete() );
+ }
+
+ public void test3ResOnlineAfterRecoveryStart()
+ throws Exception
+ {
+ Xid[] xids12 = getXidArray( XID_COUNT );
+ List xids12List = Arrays.asList( xids12 );
+ Xid[] xids13 = getXidArray( XID_COUNT );
+ List xids13List = Arrays.asList( xids13 );
+ Xid[] xids23 = getXidArray( XID_COUNT );
+ List xids23List = Arrays.asList( xids23 );
+ ArrayList tmp = new ArrayList();
+ tmp.addAll( xids12List );
+ tmp.addAll( xids13List );
+ Xid[] xids1 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+ tmp.clear();
+ tmp.addAll( xids12List );
+ tmp.addAll( xids23List );
+ Xid[] xids2 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+ tmp.clear();
+ tmp.addAll( xids13List );
+ tmp.addAll( xids23List );
+ Xid[] xids3 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+
+ MockXAResource xares1 = new MockXAResource( RM1, xids1 );
+ MockXAResource xares2 = new MockXAResource( RM2, xids2 );
+ MockXAResource xares3 = new MockXAResource( RM3, xids3 );
+ MockTransactionInfo[] txInfos12 = makeTxInfos( xids12 );
+ addBranch( txInfos12, xares1 );
+ addBranch( txInfos12, xares2 );
+ prepareLog( m_txLog, txInfos12 );
+ MockTransactionInfo[] txInfos13 = makeTxInfos( xids13 );
+ addBranch( txInfos13, xares1 );
+ addBranch( txInfos13, xares3 );
+ prepareLog( m_txLog, txInfos13 );
+ MockTransactionInfo[] txInfos23 = makeTxInfos( xids23 );
+ addBranch( txInfos23, xares2 );
+ addBranch( txInfos23, xares3 );
+ prepareLog( m_txLog, txInfos23 );
+ prepareForReplay();
+ Recovery recovery = new RecoveryImpl( m_txLog, m_xidFactory );
+ recovery.recoverLog();
+ assertTrue( ! recovery.hasRecoveryErrors() );
+ assertTrue( recovery.getExternalXids().isEmpty() );
+ assertEquals( XID_COUNT * 3, recovery.localUnrecoveredCount() );
+ recovery.recoverResourceManager( xares1 );
+ assertEquals( XID_COUNT * 3, recovery.localUnrecoveredCount() );
+ assertEquals( XID_COUNT * 2, xares1.committed.size() );
+ recovery.recoverResourceManager( xares2 );
+ assertEquals( XID_COUNT * 2, recovery.localUnrecoveredCount() );
+ assertEquals( XID_COUNT * 2, xares2.committed.size() );
+ recovery.recoverResourceManager( xares3 );
+ assertEquals( 0, recovery.localUnrecoveredCount() );
+ assertEquals( XID_COUNT * 2, xares3.committed.size() );
+
+ }
+
+ protected abstract void prepareForReplay()
+ throws Exception;
+
+ private void prepareLog( TransactionLog txLog, MockTransactionInfo[]
txInfos )
+ throws LogException
+ {
+ for( int i = 0; i < txInfos.length; i++ )
+ {
+ MockTransactionInfo txInfo = txInfos[ i ];
+ txLog.prepare( txInfo.m_globalXid, txInfo.m_branches );
+ }
+ }
+
+ private Xid[] getXidArray( int i )
+ {
+ Xid[] xids = new Xid[ i ];
+ for( int j = 0; j < xids.length; j++ )
+ {
+ xids[ j ] = m_xidFactory.createXid();
+ }
+ return xids;
+ }
+
+ private void addBranch( MockTransactionInfo[] txInfos, MockXAResource
xaRes )
+ {
+ for( int i = 0; i < txInfos.length; i++ )
+ {
+ MockTransactionInfo txInfo = txInfos[ i ];
+ Xid globalXid = txInfo.m_globalXid;
+ Xid branchXid = m_xidFactory.createBranch( globalXid,
m_branchCounter++ );
+ txInfo.m_branches.add( new MockTransactionBranchInfo(
xaRes.getName(), branchXid ) );
+ }
+ }
+
+ private MockTransactionInfo[] makeTxInfos( Xid[] xids )
+ {
+ MockTransactionInfo[] txInfos = new MockTransactionInfo[ xids.length
];
+ for( int i = 0; i < xids.length; i++ )
+ {
+ Xid xid = xids[ i ];
+ txInfos[ i ] = new MockTransactionInfo( xid, new ArrayList() );
+ }
+ return txInfos;
+ }
+
+
+ private static class MockXAResource
+ implements NamedXAResource
+ {
+
+ private final String m_name;
+ private final Xid[] m_xids;
+ private final List committed = new ArrayList();
+ private final List rolledBack = new ArrayList();
+
+ public MockXAResource( String name, Xid[] xids )
+ {
+ this.m_name = name;
+ this.m_xids = xids;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public void commit( Xid xid, boolean onePhase )
+ throws XAException
+ {
+ committed.add( xid );
+ }
+
+ public void end( Xid xid, int flags )
+ throws XAException
+ {
+ }
+
+ public void forget( Xid xid )
+ throws XAException
+ {
+ }
+
+ public int getTransactionTimeout()
+ throws XAException
+ {
+ return 0;
+ }
+
+ public boolean isSameRM( XAResource xaResource )
+ throws XAException
+ {
+ return false;
+ }
+
+ public int prepare( Xid xid )
+ throws XAException
+ {
+ return 0;
+ }
+
+ public Xid[] recover( int flag )
+ throws XAException
+ {
+ return m_xids;
+ }
+
+ public void rollback( Xid xid )
+ throws XAException
+ {
+ rolledBack.add( xid );
+ }
+
+ public boolean setTransactionTimeout( int seconds )
+ throws XAException
+ {
+ return false;
+ }
+
+ public void start( Xid xid, int flags )
+ throws XAException
+ {
+ }
+
+ public List getCommitted()
+ {
+ return committed;
+ }
+
+ public List getRolledBack()
+ {
+ return rolledBack;
+ }
+
+ }
+
+
+ private static class MockTransactionInfo
+ {
+ private Xid m_globalXid;
+ private List m_branches;
+
+ public MockTransactionInfo( Xid globalXid, List branches )
+ {
+ this.m_globalXid = globalXid;
+ this.m_branches = branches;
+ }
+ }
+
+
+ private static class MockTransactionBranchInfo
+ implements TransactionBranchInfo
+ {
+ private final String m_name;
+ private final Xid m_branchXid;
+
+ public MockTransactionBranchInfo( String name, Xid branchXid )
+ {
+ this.m_name = name;
+ this.m_branchXid = branchXid;
+ }
+
+ public String getResourceName()
+ {
+ return m_name;
+ }
+
+ public Xid getBranchXid()
+ {
+ return m_branchXid;
+ }
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLog.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLog.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,97 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.xa.Xid;
+import net.dpml.tx.manager.LogException;
+import net.dpml.tx.manager.Recovery;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.XidFactory;
+
+
+/**
+ *
+ *
+ * @version $Rev: 54973 $ $Date: 2004-10-18 04:06:11 +0800 (Mon, 18 Oct
2004) $
+ *
+ */
+public class MockLog
+ implements TransactionLog
+{
+
+ final Map prepared = new HashMap();
+ final List committed = new ArrayList();
+ final List rolledBack = new ArrayList();
+
+ public void begin( Xid xid )
+ throws LogException
+ {
+ }
+
+ public Object prepare( Xid xid, List branches )
+ throws LogException
+ {
+ Object mark = new Object();
+ Recovery.XidBranchesPair xidBranchesPair = new
Recovery.XidBranchesPair( xid, mark );
+ xidBranchesPair.getBranches().addAll( branches );
+ prepared.put( xid, xidBranchesPair );
+ return mark;
+ }
+
+ public void commit( Xid xid, Object logMark )
+ throws LogException
+ {
+ committed.add( xid );
+ }
+
+ public void rollback( Xid xid, Object logMark )
+ throws LogException
+ {
+ rolledBack.add( xid );
+ }
+
+ public Collection recover( XidFactory xidFactory )
+ throws LogException
+ {
+ Map copy = new HashMap( prepared );
+ copy.keySet().removeAll( committed );
+ copy.keySet().removeAll( rolledBack );
+ return copy.values();
+ }
+
+ public String getXMLStats()
+ {
+ return null;
+ }
+
+ public int getAverageForceTime()
+ {
+ return 0;
+ }
+
+ public int getAverageBytesPerForce()
+ {
+ return 0;
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLogRecoveryTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockLogRecoveryTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,45 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ */
+public class MockLogRecoveryTest extends AbstractRecoveryTest
+{
+
+ protected void setUp()
+ throws Exception
+ {
+ m_txLog = new MockLog();
+ }
+
+ protected void tearDown()
+ throws Exception
+ {
+ m_txLog = null;
+ }
+
+ protected void prepareForReplay()
+ throws Exception
+ {
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResource.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResource.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,205 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import net.dpml.tx.manager.NamedXAResource;
+
+
+/**
+ * @version $Rev: 55097 $ $Date: 2004-10-20 04:35:46 +0800 (Wed, 20 Oct
2004) $
+ */
+public class MockResource
+ implements NamedXAResource
+{
+ private String m_xaResourceName = "mockResource";
+ private Xid m_currentXid;
+ private MockResourceManager m_manager;
+ private int m_timeout = 0;
+ private boolean m_prepared;
+ private boolean m_committed;
+ private boolean m_rolledback;
+ private Set m_preparedXids = new HashSet();
+ private Set m_knownXids = new HashSet();
+ private Set m_finishedXids = new HashSet(); //end was
+ // called
with
+ //
TMSUCCESS or
+ // TMFAIL
+
+ public MockResource( MockResourceManager manager, String xaResourceName )
+ {
+ this.m_manager = manager;
+ this.m_xaResourceName = xaResourceName;
+ }
+
+ public int getTransactionTimeout()
+ throws XAException
+ {
+ return m_timeout;
+ }
+
+ public boolean setTransactionTimeout( int seconds )
+ throws XAException
+ {
+ return false;
+ }
+
+ public Xid getCurrentXid()
+ {
+ return m_currentXid;
+ }
+
+ public void start( Xid xid, int flags )
+ throws XAException
+ {
+ if( this.m_currentXid != null )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ if( flags == XAResource.TMRESUME && ! m_knownXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ if( m_finishedXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ if( ( flags & XAResource.TMJOIN ) != 0 )
+ {
+ m_manager.join( xid, this );
+ } else
+ {
+ m_manager.newTx( xid, this );
+ }
+ this.m_currentXid = xid;
+ if( ! m_knownXids.contains( xid ) )
+ {
+ m_knownXids.add( xid );
+ }
+ }
+
+ public void end( Xid xid, int flags )
+ throws XAException
+ {
+ if( ! m_knownXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ if( flags == XAResource.TMSUSPEND )
+ {
+ if( m_currentXid == null )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ } else if( this.m_currentXid != xid )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ } else if( flags == XAResource.TMFAIL || flags ==
XAResource.TMSUCCESS )
+ {
+ if( m_finishedXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ m_finishedXids.add( xid );
+ }
+ this.m_currentXid = null;
+ }
+
+ public int prepare( Xid xid )
+ throws XAException
+ {
+ if( ! m_finishedXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ m_prepared = true;
+ m_preparedXids.add( xid );
+ return XAResource.XA_OK;
+ }
+
+ public void commit( Xid xid, boolean onePhase )
+ throws XAException
+ {
+ if( ! m_finishedXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ m_preparedXids.remove( xid );
+ m_committed = true;
+ }
+
+ public void rollback( Xid xid )
+ throws XAException
+ {
+ if( ! m_finishedXids.contains( xid ) )
+ {
+ throw new XAException( XAException.XAER_PROTO );
+ }
+ m_rolledback = true;
+ m_preparedXids.remove( xid );
+ m_manager.forget( xid, this );
+ }
+
+ public boolean isSameRM( XAResource xaResource )
+ throws XAException
+ {
+ if( xaResource instanceof MockResource )
+ {
+ return m_manager == ( (MockResource) xaResource ).m_manager;
+ }
+ return false;
+ }
+
+ public void forget( Xid xid )
+ throws XAException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Xid[] recover( int flag )
+ throws XAException
+ {
+ return (Xid[]) m_preparedXids.toArray( new Xid[
m_preparedXids.size() ] );
+ }
+
+ public boolean isPrepared()
+ {
+ return m_prepared;
+ }
+
+ public boolean isCommitted()
+ {
+ return m_committed;
+ }
+
+ public boolean isRolledback()
+ {
+ return m_rolledback;
+ }
+
+ public String getName()
+ {
+ return m_xaResourceName;
+ }
+
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResourceManager.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/MockResourceManager.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,106 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import net.dpml.tx.manager.NamedXAResource;
+import net.dpml.tx.manager.ResourceManager;
+
+
+/**
+ *
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ */
+public class MockResourceManager
+ implements ResourceManager
+{
+ private boolean m_willCommit;
+ private Map m_xids = new HashMap();
+
+ private NamedXAResource m_resources;
+ private NamedXAResource m_returnedResources;
+
+ public MockResourceManager( boolean willCommit )
+ {
+ this.m_willCommit = willCommit;
+ }
+
+ public MockResource getResource( String xaResourceName )
+ {
+ MockResource mockResource = new MockResource( this, xaResourceName );
+ m_resources = mockResource;
+ return mockResource;
+ }
+
+ public void join( Xid xid, XAResource xaRes )
+ throws XAException
+ {
+ Set resSet = (Set) m_xids.get( xid );
+ if( resSet == null )
+ {
+ throw new XAException( XAException.XAER_NOTA );
+ }
+ resSet.add( xaRes );
+ }
+
+ public void newTx( Xid xid, XAResource xaRes )
+ throws XAException
+ {
+ if( m_xids.containsKey( xid ) )
+ {
+ throw new XAException( XAException.XAER_DUPID );
+ }
+ Set resSet = new HashSet();
+ resSet.add( xaRes );
+ m_xids.put( xid, resSet );
+ }
+
+ public void forget( Xid xid, XAResource xaRes )
+ throws XAException
+ {
+ if( m_xids.remove( xid ) == null )
+ {
+ throw new XAException( XAException.XAER_NOTA );
+ }
+ }
+
+ public NamedXAResource getRecoveryXAResources()
+ throws SystemException
+ {
+ return m_resources;
+ }
+
+ public void returnResource( NamedXAResource xaResource )
+ {
+ m_returnedResources = xaResource;
+ }
+
+ public boolean areAllResourcesReturned()
+ {
+ return m_returnedResources != null && m_returnedResources ==
m_resources;
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/ProtocolTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/ProtocolTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,84 @@
+package net.dpml.tx.tests.manager;
+
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.TransactionManagerImpl;
+
+
+/**
+ */
+public class ProtocolTest extends TestCase
+{
+
+ private TransactionManagerImpl m_tm;
+ private MockResourceManager m_mrm1, m_mrm2;
+ private MockResource m_mr11;
+ private MockResource m_mr12;
+ private MockResource m_mr21;
+ private MockResource m_mr22;
+
+ protected void setUp()
+ throws Exception
+ {
+ m_tm = new TransactionManagerImpl( 1000, null, null );
+ m_mrm1 = new MockResourceManager( true );
+ m_mrm2 = new MockResourceManager( true );
+ m_mr11 = new MockResource( m_mrm1, "mr11" );
+ m_mr12 = new MockResource( m_mrm1, "mr12" );
+ m_mr21 = new MockResource( m_mrm2, "mr21" );
+ m_mr22 = new MockResource( m_mrm2, "mr22" );
+ }
+
+ public void testOnePhaseCommit()
+ throws Exception
+ {
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_mr11 );
+ tx.delistResource( m_mr11, XAResource.TMSUSPEND );
+ m_tm.commit();
+ }
+
+ public void testOnePhaseCommiTwoResources()
+ throws Exception
+ {
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_mr11 );
+ tx.delistResource( m_mr11, XAResource.TMSUSPEND );
+ tx.enlistResource( m_mr12 );
+ tx.delistResource( m_mr12, XAResource.TMSUSPEND );
+ m_tm.commit();
+ }
+
+ public void testTwoPhaseCommit()
+ throws Exception
+ {
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_mr11 );
+ tx.delistResource( m_mr11, XAResource.TMSUSPEND );
+ tx.enlistResource( m_mr21 );
+ tx.delistResource( m_mr21, XAResource.TMSUSPEND );
+ m_tm.commit();
+ }
+
+ public void testTwoPhaseCommit4Resources()
+ throws Exception
+ {
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_mr11 );
+ tx.delistResource( m_mr11, XAResource.TMSUSPEND );
+ tx.enlistResource( m_mr12 );
+ tx.delistResource( m_mr12, XAResource.TMSUSPEND );
+ tx.enlistResource( m_mr21 );
+ tx.delistResource( m_mr21, XAResource.TMSUSPEND );
+ tx.enlistResource( m_mr22 );
+ tx.delistResource( m_mr22, XAResource.TMSUSPEND );
+ m_tm.commit();
+ }
+
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/RecoveryTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/RecoveryTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,322 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.LogException;
+import net.dpml.tx.manager.NamedXAResource;
+import net.dpml.tx.manager.Recovery;
+import net.dpml.tx.manager.RecoveryImpl;
+import net.dpml.tx.manager.TransactionBranchInfo;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.XidFactory;
+import net.dpml.tx.manager.XidFactoryImpl;
+
+
+/**
+ * This is just a unit test for recovery, depending on proper behavior of the
+ * log(s) it uses.
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 17:56:06 +0800 (Tue, 14 Sep
2004) $
+ *
+ */
+public class RecoveryTest extends TestCase
+{
+ private final String RM1 = "rm1";
+ private final String RM2 = "rm2";
+ private final String RM3 = "rm3";
+
+ XidFactory m_xidFactory = new XidFactoryImpl();
+
+ public void testCommittedRMToBeRecovered()
+ throws Exception
+ {
+ MockLog mockLog = new MockLog();
+ Xid[] xids = getXidArray( 1 );
+ // specifies an empty Xid array such that this XAResource has nothing
+ // to recover. This means that the presumed abort protocol has failed
+ // right after the commit of the RM and before the force-write of the
+ // committed log record.
+ MockXAResource xares1 = new MockXAResource( RM1, new Xid[ 0 ] );
+ MockTransactionInfo[] txInfos = makeTxInfos( xids );
+ addBranch( txInfos, xares1 );
+ prepareLog( mockLog, txInfos );
+ Recovery recovery = new RecoveryImpl( mockLog, m_xidFactory );
+ recovery.recoverLog();
+ assertTrue( ! recovery.hasRecoveryErrors() );
+ assertTrue( recovery.getExternalXids().isEmpty() );
+ assertTrue( ! recovery.localRecoveryComplete() );
+ recovery.recoverResourceManager( xares1 );
+ assertEquals( 0, xares1.m_committed.size() );
+ assertTrue( recovery.localRecoveryComplete() );
+
+ }
+
+ public void test2ResOnlineAfterRecoveryStart()
+ throws Exception
+ {
+ MockLog mockLog = new MockLog();
+ Xid[] xids = getXidArray( 3 );
+ MockXAResource xares1 = new MockXAResource( RM1, xids );
+ MockXAResource xares2 = new MockXAResource( RM2, xids );
+ MockTransactionInfo[] txInfos = makeTxInfos( xids );
+ addBranch( txInfos, xares1 );
+ addBranch( txInfos, xares2 );
+ prepareLog( mockLog, txInfos );
+ Recovery recovery = new RecoveryImpl( mockLog, m_xidFactory );
+ recovery.recoverLog();
+ assertTrue( ! recovery.hasRecoveryErrors() );
+ assertTrue( recovery.getExternalXids().isEmpty() );
+ assertTrue( ! recovery.localRecoveryComplete() );
+ recovery.recoverResourceManager( xares1 );
+ assertTrue( ! recovery.localRecoveryComplete() );
+ assertEquals( 3, xares1.m_committed.size() );
+ recovery.recoverResourceManager( xares2 );
+ assertEquals( 3, xares2.m_committed.size() );
+ assertTrue( recovery.localRecoveryComplete() );
+
+ }
+
+ private void addBranch( MockTransactionInfo[] txInfos, MockXAResource
xaRes )
+ {
+ for( int i = 0; i < txInfos.length; i++ )
+ {
+ MockTransactionInfo txInfo = txInfos[ i ];
+ txInfo.m_branches.add( new MockTransactionBranchInfo(
xaRes.getName() ) );
+ }
+ }
+
+ private MockTransactionInfo[] makeTxInfos( Xid[] xids )
+ {
+ MockTransactionInfo[] txInfos = new MockTransactionInfo[ xids.length
];
+ for( int i = 0; i < xids.length; i++ )
+ {
+ Xid xid = xids[ i ];
+ txInfos[ i ] = new MockTransactionInfo( xid, new ArrayList() );
+ }
+ return txInfos;
+ }
+
+ public void test3ResOnlineAfterRecoveryStart()
+ throws Exception
+ {
+ MockLog mockLog = new MockLog();
+ Xid[] xids12 = getXidArray( 3 );
+ List xids12List = Arrays.asList( xids12 );
+ Xid[] xids13 = getXidArray( 3 );
+ List xids13List = Arrays.asList( xids13 );
+ Xid[] xids23 = getXidArray( 3 );
+ List xids23List = Arrays.asList( xids23 );
+ ArrayList tmp = new ArrayList();
+ tmp.addAll( xids12List );
+ tmp.addAll( xids13List );
+ Xid[] xids1 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+ tmp.clear();
+ tmp.addAll( xids12List );
+ tmp.addAll( xids23List );
+ Xid[] xids2 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+ tmp.clear();
+ tmp.addAll( xids13List );
+ tmp.addAll( xids23List );
+ Xid[] xids3 = (Xid[]) tmp.toArray( new Xid[ 6 ] );
+
+ MockXAResource xares1 = new MockXAResource( RM1, xids1 );
+ MockXAResource xares2 = new MockXAResource( RM2, xids2 );
+ MockXAResource xares3 = new MockXAResource( RM3, xids3 );
+ MockTransactionInfo[] txInfos12 = makeTxInfos( xids12 );
+ addBranch( txInfos12, xares1 );
+ addBranch( txInfos12, xares2 );
+ prepareLog( mockLog, txInfos12 );
+ MockTransactionInfo[] txInfos13 = makeTxInfos( xids13 );
+ addBranch( txInfos13, xares1 );
+ addBranch( txInfos13, xares3 );
+ prepareLog( mockLog, txInfos13 );
+ MockTransactionInfo[] txInfos23 = makeTxInfos( xids23 );
+ addBranch( txInfos23, xares2 );
+ addBranch( txInfos23, xares3 );
+ prepareLog( mockLog, txInfos23 );
+ Recovery recovery = new RecoveryImpl( mockLog, m_xidFactory );
+ recovery.recoverLog();
+ assertTrue( ! recovery.hasRecoveryErrors() );
+ assertTrue( recovery.getExternalXids().isEmpty() );
+ assertEquals( 9, recovery.localUnrecoveredCount() );
+ recovery.recoverResourceManager( xares1 );
+ assertEquals( 9, recovery.localUnrecoveredCount() );
+ assertEquals( 6, xares1.m_committed.size() );
+ recovery.recoverResourceManager( xares2 );
+ assertEquals( 6, recovery.localUnrecoveredCount() );
+ assertEquals( 6, xares2.m_committed.size() );
+ recovery.recoverResourceManager( xares3 );
+ assertEquals( 0, recovery.localUnrecoveredCount() );
+ assertEquals( 6, xares3.m_committed.size() );
+
+ }
+
+ private void prepareLog( TransactionLog txLog, MockTransactionInfo[]
txInfos )
+ throws LogException
+ {
+ for( int i = 0; i < txInfos.length; i++ )
+ {
+ MockTransactionInfo txInfo = txInfos[ i ];
+ txLog.prepare( txInfo.m_globalXid, txInfo.m_branches );
+ }
+ }
+
+ private Xid[] getXidArray( int i )
+ {
+ Xid[] xids = new Xid[ i ];
+ for( int j = 0; j < xids.length; j++ )
+ {
+ xids[ j ] = m_xidFactory.createXid();
+ }
+ return xids;
+ }
+
+
+ private static class MockXAResource
+ implements NamedXAResource
+ {
+
+ private final String m_name;
+ private final Xid[] m_xids;
+ private final List m_committed = new ArrayList();
+ private final List m_rolledBack = new ArrayList();
+
+ public MockXAResource( String name, Xid[] xids )
+ {
+ this.m_name = name;
+ this.m_xids = xids;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public void commit( Xid xid, boolean onePhase )
+ throws XAException
+ {
+ m_committed.add( xid );
+ }
+
+ public void end( Xid xid, int flags )
+ throws XAException
+ {
+ }
+
+ public void forget( Xid xid )
+ throws XAException
+ {
+ }
+
+ public int getTransactionTimeout()
+ throws XAException
+ {
+ return 0;
+ }
+
+ public boolean isSameRM( XAResource xaResource )
+ throws XAException
+ {
+ return false;
+ }
+
+ public int prepare( Xid xid )
+ throws XAException
+ {
+ return 0;
+ }
+
+ public Xid[] recover( int flag )
+ throws XAException
+ {
+ return m_xids;
+ }
+
+ public void rollback( Xid xid )
+ throws XAException
+ {
+ m_rolledBack.add( xid );
+ }
+
+ public boolean setTransactionTimeout( int seconds )
+ throws XAException
+ {
+ return false;
+ }
+
+ public void start( Xid xid, int flags )
+ throws XAException
+ {
+ }
+
+ public List getCommitted()
+ {
+ return m_committed;
+ }
+
+ public List getRolledBack()
+ {
+ return m_rolledBack;
+ }
+
+ }
+
+
+ private static class MockTransactionInfo
+ {
+ private Xid m_globalXid;
+ private List m_branches;
+
+ public MockTransactionInfo( Xid globalXid, List branches )
+ {
+ this.m_globalXid = globalXid;
+ this.m_branches = branches;
+ }
+ }
+
+
+ private static class MockTransactionBranchInfo
+ implements TransactionBranchInfo
+ {
+ private String m_name;
+
+ public MockTransactionBranchInfo( String name )
+ {
+ this.m_name = name;
+ }
+
+ public String getResourceName()
+ {
+ return m_name;
+ }
+
+ public Xid getBranchXid()
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TestTransactionManager.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TestTransactionManager.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,137 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import javax.transaction.Status;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.TransactionManagerImpl;
+
+
+/**
+ *
+ *
+ * @version $Rev: 111396 $ $Date: 2004-12-09 23:41:29 +0800 (Thu, 09 Dec
2004) $
+ */
+public class TestTransactionManager extends TestCase
+{
+ TransactionManager m_tm;
+ private MockResourceManager m_rm1;
+ private MockResourceManager m_rm2;
+ private MockResourceManager m_rm3;
+
+ public void testNoTransaction()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertNull( m_tm.getTransaction() );
+ }
+
+ public void testNoResource()
+ throws Exception
+ {
+ Transaction tx;
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ tx = m_tm.getTransaction();
+ assertNotNull( tx );
+ assertEquals( Status.STATUS_ACTIVE, tx.getStatus() );
+ m_tm.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ m_tm.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ }
+
+ public void testTxOp()
+ throws Exception
+ {
+ Transaction tx;
+ m_tm.begin();
+ tx = m_tm.getTransaction();
+ tx.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+
+ m_tm.begin();
+ assertFalse( tx.equals( m_tm.getTransaction() ) );
+ m_tm.rollback();
+ }
+
+ public void testSuspend()
+ throws Exception
+ {
+ Transaction tx;
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ tx = m_tm.getTransaction();
+ assertNotNull( tx );
+ assertEquals( Status.STATUS_ACTIVE, tx.getStatus() );
+
+ tx = m_tm.suspend();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertNull( m_tm.getTransaction() );
+
+ m_tm.resume( tx );
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ assertEquals( tx, m_tm.getTransaction() );
+
+ m_tm.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertNull( m_tm.getTransaction() );
+ }
+
+ public void testOneResource()
+ throws Exception
+ {
+ Transaction tx;
+ MockResource res1 = m_rm1.getResource( "rm1" );
+ m_tm.begin();
+ tx = m_tm.getTransaction();
+ assertNull( res1.getCurrentXid() );
+ assertTrue( tx.enlistResource( res1 ) );
+ assertNotNull( res1.getCurrentXid() );
+ assertTrue( tx.delistResource( res1, XAResource.TMFAIL ) );
+ assertNull( res1.getCurrentXid() );
+ m_tm.rollback();
+
+ m_tm.begin();
+ tx = m_tm.getTransaction();
+ assertTrue( tx.enlistResource( res1 ) );
+ m_tm.rollback();
+ assertNull( res1.getCurrentXid() );
+ }
+
+ protected void setUp()
+ throws Exception
+ {
+ m_tm = new TransactionManagerImpl( 10 * 1000, null, null );
+ m_rm1 = new MockResourceManager( true );
+ m_rm2 = new MockResourceManager( true );
+ m_rm3 = new MockResourceManager( false );
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TransactionManagerImplTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/TransactionManagerImplTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,372 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.TransactionManagerImpl;
+import net.dpml.tx.manager.XidFactory;
+import net.dpml.tx.manager.XidFactoryImpl;
+
+
+/**
+ * @version $Rev: 128441 $ $Date: 2005-01-28 06:56:21 +0800 (Fri, 28 Jan
2005) $
+ */
+public class TransactionManagerImplTest extends TestCase
+{
+
+ MockResourceManager m_rm1 = new MockResourceManager(
true );
+ MockResource m_r1_1 = m_rm1.getResource( "rm1_1" );
+ MockResource m_r1_2 = m_rm1.getResource( "rm1_2" );
+ MockResourceManager m_rm2 = new MockResourceManager(
true );
+ MockResource m_r2_1 = m_rm2.getResource( "rm2_1" );
+ MockResource m_r2_2 = m_rm2.getResource( "rm2_2" );
+
+ TransactionLog m_transactionLog = new MockLog();
+
+ Collection m_resourceManagers = new ArrayList();
+ TransactionManagerImpl m_tm;
+
+ protected void setUp()
+ throws Exception
+ {
+ m_tm = new TransactionManagerImpl( 10, m_transactionLog,
m_resourceManagers );
+ }
+
+ protected void tearDown()
+ throws Exception
+ {
+ m_tm = null;
+ }
+
+ public void testNoResourcesCommit()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ m_tm.commit();
+ assertNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ Transaction tx = m_tm.getTransaction();
+ assertNotNull( tx );
+ tx.commit();
+ assertNotNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ }
+
+ public void testNoResourcesMarkRollbackOnly()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ m_tm.getTransaction().setRollbackOnly();
+ assertEquals( Status.STATUS_MARKED_ROLLBACK, m_tm.getStatus() );
+ try
+ {
+ m_tm.commit();
+ fail( "tx should not commit" );
+ } catch( RollbackException e )
+ {
+ //expected
+ }
+ assertNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ Transaction tx = m_tm.getTransaction();
+ assertNotNull( tx );
+ tx.setRollbackOnly();
+ assertEquals( Status.STATUS_MARKED_ROLLBACK, tx.getStatus() );
+ try
+ {
+ tx.commit();
+ fail( "tx should not commit" );
+ } catch( RollbackException e )
+ {
+ //expected
+ }
+ assertNotNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ }
+
+ public void testNoResourcesRollback()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ m_tm.rollback();
+ assertNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ Transaction tx = m_tm.getTransaction();
+ assertNotNull( tx );
+ tx.rollback();
+ assertNotNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+
+ //check rollback when marked rollback only
+ m_tm.begin();
+ assertEquals( Status.STATUS_ACTIVE, m_tm.getStatus() );
+ m_tm.getTransaction().setRollbackOnly();
+ assertEquals( Status.STATUS_MARKED_ROLLBACK, m_tm.getStatus() );
+ m_tm.rollback();
+ assertNull( m_tm.getTransaction() );
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ }
+
+ public void testOneResourceCommit()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.commit();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( m_r1_1.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() );
+ assertTrue( ! m_r1_1.isRolledback() );
+ }
+
+ public void testOneResourceMarkedRollback()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.setRollbackOnly();
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ try
+ {
+ tx.commit();
+ fail( "tx should roll back" );
+ } catch( RollbackException e )
+ {
+ //expected
+ }
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( ! m_r1_1.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() );
+ assertTrue( m_r1_1.isRolledback() );
+ }
+
+ public void testOneResourceRollback()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( ! m_r1_1.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() );
+ assertTrue( m_r1_1.isRolledback() );
+ }
+
+ public void testTwoResourceOneRMCommit()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.enlistResource( m_r1_2 );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.commit();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( m_r1_1.isCommitted() ^ m_r1_2.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() & ! m_r1_2.isPrepared() );
+ assertTrue( ! m_r1_1.isRolledback() & ! m_r1_2.isRolledback() );
+ }
+
+ public void testTwoResourceOneRMMarkRollback()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.enlistResource( m_r1_2 );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.setRollbackOnly();
+ try
+ {
+ tx.commit();
+ fail( "tx should roll back" );
+ } catch( RollbackException e )
+ {
+ //expected
+ }
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( ! m_r1_1.isCommitted() & ! m_r1_2.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() & ! m_r1_2.isPrepared() );
+ assertTrue( m_r1_1.isRolledback() ^ m_r1_2.isRolledback() );
+ }
+
+ public void testTwoResourcesOneRMRollback()
+ throws Exception
+ {
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.enlistResource( m_r1_2 );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.setRollbackOnly();
+ tx.rollback();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( ! m_r1_1.isCommitted() & ! m_r1_2.isCommitted() );
+ assertTrue( ! m_r1_1.isPrepared() & ! m_r1_2.isPrepared() );
+ assertTrue( m_r1_1.isRolledback() ^ m_r1_2.isRolledback() );
+ }
+
+ public void testFourResourceTwoRMCommit()
+ throws Exception
+ {
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ m_tm.begin();
+ Transaction tx = m_tm.getTransaction();
+ tx.enlistResource( m_r1_1 );
+ tx.enlistResource( m_r1_2 );
+ tx.enlistResource( m_r2_1 );
+ tx.enlistResource( m_r2_2 );
+ tx.delistResource( m_r1_1, XAResource.TMSUCCESS );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.delistResource( m_r2_1, XAResource.TMSUCCESS );
+ tx.delistResource( m_r2_2, XAResource.TMSUCCESS );
+ tx.commit();
+ assertEquals( Status.STATUS_NO_TRANSACTION, m_tm.getStatus() );
+ assertTrue( ( m_r1_1.isCommitted() & m_r1_1.isPrepared() )
+ ^ ( m_r1_2.isCommitted() & m_r1_2.isPrepared() ) );
+ assertTrue( ! m_r1_1.isRolledback() & ! m_r1_2.isRolledback() );
+ assertTrue( ( m_r2_1.isCommitted() & m_r2_1.isPrepared() )
+ ^ ( m_r2_2.isCommitted() & m_r2_2.isPrepared() ) );
+ assertTrue( ! m_r2_1.isRolledback() & ! m_r2_2.isRolledback() );
+ }
+
+ //BE VERY CAREFUL!! the ResourceManager only "recovers" the LAST resource
+ // it creates.
+ //This test depends on using the resource that will be recovered by the
+ // resource manager.
+ public void testSimpleRecovery()
+ throws Exception
+ {
+ //create a transaction in our own transaction manager
+ Xid xid = m_tm.getXidFactory().createXid();
+ Transaction tx = m_tm.importXid( xid, 0 );
+ m_tm.resume( tx );
+ assertSame( tx, m_tm.getTransaction() );
+ tx.enlistResource( m_r1_2 );
+ tx.enlistResource( m_r2_2 );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.delistResource( m_r2_2, XAResource.TMSUCCESS );
+ m_tm.suspend();
+ m_tm.prepare( tx );
+ //recover
+ m_tm.getRecovery().recoverLog();
+ m_resourceManagers.add( m_rm1 );
+ assertTrue( m_r1_2.isCommitted() );
+ assertTrue( ! m_r2_2.isCommitted() );
+ m_resourceManagers.add( m_rm2 );
+ assertTrue( m_r2_2.isCommitted() );
+ assertTrue( m_tm.getRecovery().localRecoveryComplete() );
+ }
+
+ public void testImportedXidRecovery()
+ throws Exception
+ {
+ //create a transaction from an external transaction manager.
+ XidFactory xidFactory2 = new XidFactoryImpl( "tm2".getBytes() );
+ Xid xid = xidFactory2.createXid();
+ Transaction tx = m_tm.importXid( xid, 0 );
+ m_tm.resume( tx );
+ assertSame( tx, m_tm.getTransaction() );
+ tx.enlistResource( m_r1_2 );
+ tx.enlistResource( m_r2_2 );
+ tx.delistResource( m_r1_2, XAResource.TMSUCCESS );
+ tx.delistResource( m_r2_2, XAResource.TMSUCCESS );
+ m_tm.suspend();
+ m_tm.prepare( tx );
+ //recover
+ m_tm.getRecovery().recoverLog();
+ m_resourceManagers.add( m_rm1 );
+ assertTrue( ! m_r1_2.isCommitted() );
+ assertTrue( ! m_r2_2.isCommitted() );
+ m_resourceManagers.add( m_rm2 );
+ assertTrue( ! m_r2_2.isCommitted() );
+ //there are no transactions started here, so local recovery is
complete
+ assertTrue( m_tm.getRecovery().localRecoveryComplete() );
+ Map recovered = m_tm.getExternalXids();
+ assertEquals( 1, recovered.size() );
+ assertEquals( xid, recovered.keySet().iterator().next() );
+ }
+
+ public void testTimeout()
+ throws Exception
+ {
+ long timeout = m_tm.getTransactionTimeoutMilliseconds( 0L );
+ m_tm.setTransactionTimeout( (int) timeout / 4000 );
+ m_tm.begin();
+ System.out.println( "Test to sleep for" + timeout + " secs" );
+ Thread.sleep( timeout );
+ try
+ {
+ m_tm.commit();
+ fail( "Tx Should get Rollback exception" );
+ } catch( RollbackException rex )
+ {
+ // Caught expected exception
+ }
+
+ // Now test if the default timeout is active
+ m_tm.begin();
+ System.out.println( "Test to sleep for" + ( timeout / 2 ) + " secs"
);
+ Thread.sleep( ( timeout / 2 ) );
+ m_tm.commit();
+ // Its a failure if exception occurs.
+ }
+
+ public void testResourceManagerContract()
+ throws Exception
+ {
+ m_resourceManagers.add( m_rm1 );
+ assertTrue( m_rm1.areAllResourcesReturned() );
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XATransactionTester.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XATransactionTester.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,152 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import java.sql.Connection;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.sql.XAConnection;
+import javax.sql.XADataSource;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import net.dpml.tx.manager.LogException;
+import net.dpml.tx.manager.TransactionLog;
+import net.dpml.tx.manager.TransactionManagerImpl;
+import net.dpml.tx.manager.XidFactory;
+
+
+/**
+ *
+ *
+ *
+ * @version $Rev: 111396 $ $Date: 2004-12-09 23:41:29 +0800 (Thu, 09 Dec
2004) $
+ */
+public class XATransactionTester
+{
+ private TransactionManager m_manager;
+ private XADataSource m_ds;
+ private Xid m_xid;
+
+ public static void main( String[] args )
+ throws Exception
+ {
+ new XATransactionTester().run( args );
+ }
+
+ public void run( String[] args )
+ throws Exception
+ {
+ m_ds = getDataSource( args );
+ XAConnection xaConn = m_ds.getXAConnection( "test", "test" );
+ XAResource xaRes = xaConn.getXAResource();
+ m_manager = new TransactionManagerImpl( 10, new DummyLog(), null );
+ Connection c = xaConn.getConnection();
+ Statement s = c.createStatement();
+
+ m_manager.begin();
+ m_manager.getTransaction().enlistResource( xaRes );
+ s.execute( "UPDATE XA_TEST SET X=X+1" );
+ m_manager.getTransaction().delistResource( xaRes,
XAResource.TMSUCCESS );
+ m_manager.commit();
+
+ /*
+ * manager.begin(); manager.getTransaction().enlistResource(xaRes);
xid =
+ * new XidImpl(xid, 1); System.out.println("xid = " + xid);
+ * s.execute("UPDATE XA_TEST SET X=X+1");
+ *
+ * xaRes.end(xid, XAResource.TMSUCCESS); xaRes.prepare(xid);
c.close();
+ */
+
+ /*
+ * Xid[] prepared = xaRes.recover(XAResource.TMNOFLAGS); for (int i
= 0;
+ * i < prepared.length; i++) { Xid xid = prepared[i]; StringBuffer s
=
+ * new StringBuffer();
+ * s.append(Integer.toHexString(xid.getFormatId())).append('.');
byte[]
+ * globalId = xid.getGlobalTransactionId(); for (int j = 0; j <
+ * globalId.length; j++) {
s.append(Integer.toHexString(globalId[j])); }
+ *
+ * System.out.println("recovery = " + s); xaRes.forget(xid); }
+ */
+
+ }
+
+ /*
+ * @todo get something that loads this from a file
+ */
+ private XADataSource getDataSource( String[] args )
+ throws Exception
+ {
+ // oracle.jdbc.xa.client.OracleXADataSource ds = new
+ // oracle.jdbc.xa.client.OracleXADataSource();
+ //
ds.setConnectionURL("jdbc:oracle:thin:@localhost:1521:ABU");
+ // return ds;
+ return null;
+ }
+
+
+ private class DummyLog
+ implements TransactionLog
+ {
+
+ public void begin( Xid xid )
+ throws LogException
+ {
+ XATransactionTester.this.m_xid = xid;
+ }
+
+ public Object prepare( Xid xid, List branches )
+ throws LogException
+ {
+ return new Object();
+ }
+
+ public void commit( Xid xid, Object logMark )
+ throws LogException
+ {
+ }
+
+ public void rollback( Xid xid, Object logMark )
+ throws LogException
+ {
+ }
+
+ public Collection recover( XidFactory xidFactory )
+ throws LogException
+ {
+ return new ArrayList();
+ }
+
+ public String getXMLStats()
+ {
+ return null;
+ }
+
+ public int getAverageForceTime()
+ {
+ return 0;
+ }
+
+ public int getAverageBytesPerForce()
+ {
+ return 0;
+ }
+ }
+}
\ No newline at end of file

Added:
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XidImporterTest.java
==============================================================================
--- (empty file)
+++
development/laboratory/planet/components/tx/impl/src/test/net/dpml/tx/tests/manager/XidImporterTest.java
Sun Feb 13 09:38:17 2005
@@ -0,0 +1,169 @@
+/**
+ *
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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 net.dpml.tx.tests.manager;
+
+import javax.transaction.xa.Xid;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.XAException;
+import javax.transaction.Transaction;
+import javax.transaction.Status;
+
+import junit.framework.TestCase;
+import net.dpml.tx.manager.TransactionManagerImpl;
+import net.dpml.tx.manager.XidFactory;
+import net.dpml.tx.manager.XidFactoryImpl;
+import net.dpml.tx.manager.XidImporter;
+
+
+/**
+ *
+ *
+ * @version $Rev: 111396 $ $Date: 2004-12-09 23:41:29 +0800 (Thu, 09 Dec
2004) $
+ *
+ */
+public class XidImporterTest extends TestCase
+{
+
+ MockResourceManager rm1 = new MockResourceManager( true );
+ MockResource r1_1 = new MockResource( rm1, "rm1" );
+ MockResource r1_2 = new MockResource( rm1, "rm1" );
+ MockResourceManager rm2 = new MockResourceManager( true );
+ MockResource r2_1 = new MockResource( rm2, "rm2" );
+ MockResource r2_2 = new MockResource( rm2, "rm2" );
+
+ XidImporter tm;
+ XidFactory xidFactory = new XidFactoryImpl();
+
+ protected void setUp()
+ throws Exception
+ {
+ tm = new TransactionManagerImpl( 10 * 1000, null, null );
+ }
+
+ public void testImportXid()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ assertNotNull( tx );
+ assertEquals( Status.STATUS_ACTIVE, tx.getStatus() );
+ }
+
+ public void testNoResourcesCommitOnePhase()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tm.commit( tx, true );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ }
+
+ public void testNoResourcesCommitTwoPhase()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ assertEquals( XAResource.XA_RDONLY, tm.prepare( tx ) );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ }
+
+ public void testNoResourcesMarkRollback()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tx.setRollbackOnly();
+ try
+ {
+ tm.prepare( tx );
+ fail( "should throw rollback exception in an XAException" );
+ } catch( XAException e )
+ {
+ }
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ }
+
+ public void testNoResourcesRollback()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tm.rollback( tx );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ }
+
+ public void testOneResourceOnePhaseCommit()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tx.enlistResource( r1_1 );
+ tx.delistResource( r1_1, XAResource.TMSUCCESS );
+ tm.commit( tx, true );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ }
+
+ public void testOneResourceTwoPhaseCommit()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tx.enlistResource( r1_1 );
+ tx.delistResource( r1_1, XAResource.TMSUCCESS );
+ assertEquals( XAResource.XA_OK, tm.prepare( tx ) );
+ assertTrue( ! r1_1.isCommitted() );
+ assertTrue( r1_1.isPrepared() );
+ assertTrue( ! r1_1.isRolledback() );
+ tm.commit( tx, false );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ assertTrue( r1_1.isCommitted() );
+ assertTrue( r1_1.isPrepared() );
+ assertTrue( ! r1_1.isRolledback() );
+ }
+
+ public void testFourResourceTwoPhaseCommit()
+ throws Exception
+ {
+ Xid externalXid = xidFactory.createXid();
+ Transaction tx = tm.importXid( externalXid, 0 );
+ tx.enlistResource( r1_1 );
+ tx.enlistResource( r1_2 );
+ tx.enlistResource( r2_1 );
+ tx.enlistResource( r2_2 );
+ tx.delistResource( r1_1, XAResource.TMSUCCESS );
+ tx.delistResource( r1_2, XAResource.TMSUCCESS );
+ tx.delistResource( r2_1, XAResource.TMSUCCESS );
+ tx.delistResource( r2_2, XAResource.TMSUCCESS );
+ assertEquals( XAResource.XA_OK, tm.prepare( tx ) );
+ assertTrue( ! r1_1.isCommitted() & ! r1_2.isCommitted() );
+ assertTrue( r1_1.isPrepared() ^ r1_2.isPrepared() );
+ assertTrue( ! r1_1.isRolledback() & ! r1_2.isRolledback() );
+ assertTrue( ! r2_1.isCommitted() & ! r2_2.isCommitted() );
+ assertTrue( r2_1.isPrepared() ^ r2_2.isPrepared() );
+ assertTrue( ! r2_1.isRolledback() & ! r2_2.isRolledback() );
+ tm.commit( tx, false );
+ assertEquals( Status.STATUS_NO_TRANSACTION, tx.getStatus() );
+ assertTrue( ( r1_1.isCommitted() & r1_1.isPrepared() )
+ ^ ( r1_2.isCommitted() & r1_2.isPrepared() ) );
+ assertTrue( ! r1_1.isRolledback() & ! r1_2.isRolledback() );
+ assertTrue( ( r2_1.isCommitted() & r2_1.isPrepared() )
+ ^ ( r2_2.isCommitted() & r2_2.isPrepared() ) );
+ assertTrue( ! r2_1.isRolledback() & ! r2_2.isRolledback() );
+ }
+
+}
\ No newline at end of file



  • svn commit: r1771 - in development/laboratory/planet/components/tx/impl/src: main/net main/net/dpml main/net/dpml/tx main/net/dpml/tx/context main/net/dpml/tx/log main/net/dpml/tx/manager test/net test/net/dpml test/net/dpml/tx test/net/dpml/tx/tests test/net/dpml/tx/tests/context test/net/dpml/tx/tests/log test/net/dpml/tx/tests/manager, niclas, 02/13/2005

Archive powered by MHonArc 2.6.24.

Top of Page