From e6ba70e3fc8c575f98bbc5b40b939462f4102593 Mon Sep 17 00:00:00 2001 From: David Jencks Date: Fri, 19 Feb 2010 20:36:27 +0000 Subject: [PATCH] GERONIMO-5152 refactor what is supplied for recovery attempts to provide structure for retry. Also a bunch of genericization git-svn-id: https://svn.apache.org/repos/asf/geronimo/components/txmanager/trunk@911974 13f79535-47bb-0310-9956-ffa450edef68 --- .../ActivationSpecNamedXAResourceFactory.java | 66 +++++++++ .../connector/ActivationSpecWrapper.java | 1 + .../connector/ResourceAdapterWrapper.java | 15 +- .../outbound/AbstractConnectionManager.java | 37 ++--- .../outbound/GenericConnectionManager.java | 8 +- .../OutboundNamedXAResourceFactory.java | 128 ++++++++++++++++++ .../geronimo/transaction/log/HOWLLog.java | 17 ++- .../manager/GeronimoTransactionManager.java | 4 +- .../manager/NamedXAResourceFactory.java | 36 +++++ .../RecoverableTransactionManager.java | 4 +- .../transaction/manager/Recovery.java | 6 +- .../transaction/manager/RecoveryImpl.java | 53 ++++---- .../transaction/manager/TransactionLog.java | 11 +- .../manager/TransactionManagerImpl.java | 57 +++++--- .../transaction/manager/XidImporter.java | 2 +- .../geronimo/transaction/manager/MockLog.java | 2 +- .../transaction/manager/MockResource.java | 11 +- .../manager/MockResourceManager.java | 7 - .../manager/TransactionManagerImplTest.java | 17 ++- .../manager/XATransactionTester.java | 6 +- 20 files changed, 366 insertions(+), 122 deletions(-) create mode 100644 geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecNamedXAResourceFactory.java create mode 100644 geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/OutboundNamedXAResourceFactory.java create mode 100644 geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/NamedXAResourceFactory.java diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecNamedXAResourceFactory.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecNamedXAResourceFactory.java new file mode 100644 index 0000000..aab2927 --- /dev/null +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecNamedXAResourceFactory.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.geronimo.connector; + +import javax.resource.ResourceException; +import javax.resource.spi.ActivationSpec; +import javax.resource.spi.ResourceAdapter; +import javax.transaction.SystemException; +import javax.transaction.xa.XAResource; +import org.apache.geronimo.transaction.manager.NamedXAResource; +import org.apache.geronimo.transaction.manager.NamedXAResourceFactory; +import org.apache.geronimo.transaction.manager.WrapperNamedXAResource; + +/** + * @version $Rev$ $Date$ + */ +public class ActivationSpecNamedXAResourceFactory implements NamedXAResourceFactory { + + private final String name; + private final ActivationSpec activationSpec; + private final ResourceAdapter resourceAdapter; + + public ActivationSpecNamedXAResourceFactory(String name, ActivationSpec activationSpec, ResourceAdapter resourceAdapter) { + this.name = name; + this.activationSpec = activationSpec; + this.resourceAdapter = resourceAdapter; + } + + public String getName() { + return name; + } + + public NamedXAResource getNamedXAResource() throws SystemException { + try { + XAResource[] xaResources = resourceAdapter.getXAResources(new ActivationSpec[]{activationSpec}); + if (xaResources == null || xaResources.length == 0) { + return null; + } + return new WrapperNamedXAResource(xaResources[0], name); + } catch (ResourceException e) { + throw (SystemException) new SystemException("Could not get XAResource for recovery for mdb: " + name).initCause(e); + } + } + + public void returnNamedXAResource(NamedXAResource namedXAResource) { + // nothing to do AFAICT + } +} diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecWrapper.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecWrapper.java index fd2ddb1..aa0c4bf 100644 --- a/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecWrapper.java +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ActivationSpecWrapper.java @@ -101,6 +101,7 @@ public void activate(final MessageEndpointFactory messageEndpointFactory) throws public void deactivate(final MessageEndpointFactory messageEndpointFactory) { ResourceAdapter resourceAdapter = activationSpec.getResourceAdapter(); if (resourceAdapter != null) { + resourceAdapterWrapper.deregisterRecovery(containerId); resourceAdapterWrapper.endpointDeactivation(messageEndpointFactory, activationSpec); } else { //this should never happen, activation spec should have been registered with r.a. diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/ResourceAdapterWrapper.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ResourceAdapterWrapper.java index 42df1e0..b8b6113 100644 --- a/geronimo-connector/src/main/java/org/apache/geronimo/connector/ResourceAdapterWrapper.java +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/ResourceAdapterWrapper.java @@ -30,6 +30,7 @@ import javax.transaction.xa.XAResource; import org.apache.geronimo.transaction.manager.NamedXAResource; +import org.apache.geronimo.transaction.manager.NamedXAResourceFactory; import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; import org.apache.geronimo.transaction.manager.WrapperNamedXAResource; @@ -124,17 +125,11 @@ public void endpointActivation(final MessageEndpointFactory messageEndpointFacto } public void doRecovery(ActivationSpec activationSpec, String containerId) { - try { - XAResource[] xaResources = getXAResources(new ActivationSpec[]{activationSpec}); - if (xaResources == null || xaResources.length == 0) { - return; - } - NamedXAResource xaResource = new WrapperNamedXAResource(xaResources[0], containerId); - transactionManager.recoverResourceManager(xaResource); - } catch (ResourceException e) { - transactionManager.recoveryError((SystemException) new SystemException("Could not get XAResource for recovery for mdb: " + containerId).initCause(e)); - } + transactionManager.registerNamedXAResourceFactory(new ActivationSpecNamedXAResourceFactory(containerId, activationSpec, resourceAdapter)); + } + public void deregisterRecovery(String containerId) { + transactionManager.unregisterNamedXAResourceFactory(containerId); } public void endpointDeactivation(final MessageEndpointFactory messageEndpointFactory, final ActivationSpec activationSpec) { diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java index ca7007a..d2f6d85 100644 --- a/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/AbstractConnectionManager.java @@ -26,6 +26,7 @@ import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport; import org.apache.geronimo.transaction.manager.NamedXAResource; +import org.apache.geronimo.transaction.manager.NamedXAResourceFactory; import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; /** @@ -34,16 +35,18 @@ public abstract class AbstractConnectionManager implements ConnectionManagerContainer, ConnectionManager, LazyAssociatableConnectionManager, PoolingAttributes { protected final Interceptors interceptors; private final RecoverableTransactionManager transactionManager; + private final String name; //default constructor for use as endpoint - public AbstractConnectionManager() { - interceptors = null; - transactionManager = null; - } +// public AbstractConnectionManager() { +// interceptors = null; +// transactionManager = null; +// } - public AbstractConnectionManager(Interceptors interceptors, RecoverableTransactionManager transactionManager) { + public AbstractConnectionManager(Interceptors interceptors, RecoverableTransactionManager transactionManager, String name) { this.interceptors = interceptors; this.transactionManager = transactionManager; + this.name = name; } public Object createConnectionFactory(ManagedConnectionFactory mcf) throws ResourceException { @@ -53,26 +56,12 @@ public Object createConnectionFactory(ManagedConnectionFactory mcf) throws Resou protected ConnectionManager getConnectionManager() { return this; } - + public void doRecovery(ManagedConnectionFactory managedConnectionFactory) { - try { - if (!getIsRecoverable()) { - return; - } - ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, null); - - ConnectionInfo recoveryConnectionInfo = new ConnectionInfo(mci); - getRecoveryStack().getConnection(recoveryConnectionInfo); - - // For pooled resources, we may now have a new MCI (not the one constructed above). Make sure we use the correct MCI - NamedXAResource xaResource = (NamedXAResource) recoveryConnectionInfo.getManagedConnectionInfo().getXAResource(); - if (xaResource != null) { - transactionManager.recoverResourceManager(xaResource); - getRecoveryStack().returnConnection(recoveryConnectionInfo, ConnectionReturnAction.DESTROY); - } - } catch (ResourceException e) { - transactionManager.recoveryError((SystemException)new SystemException("Could not obtain recovery XAResource for managedConnectionFactory " + managedConnectionFactory).initCause(e)); + if (!getIsRecoverable()) { + return; } + transactionManager.registerNamedXAResourceFactory(new OutboundNamedXAResourceFactory(name, getRecoveryStack(), managedConnectionFactory)); } /** @@ -196,10 +185,12 @@ public void doStart() throws Exception { } public void doStop() throws Exception { + transactionManager.unregisterNamedXAResourceFactory(name); interceptors.getStack().destroy(); } public void doFail() { + transactionManager.unregisterNamedXAResourceFactory(name); interceptors.getStack().destroy(); } } diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java index cc05e30..83eb3b4 100644 --- a/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/GenericConnectionManager.java @@ -37,9 +37,9 @@ public class GenericConnectionManager extends AbstractConnectionManager { protected static final Logger log = LoggerFactory.getLogger(AbstractSinglePoolConnectionInterceptor.class); //default constructor for use as endpoint - public GenericConnectionManager() { - super(); - } +// public GenericConnectionManager() { +// super(); +// } /** * @@ -58,7 +58,7 @@ public GenericConnectionManager(TransactionSupport transactionSupport, RecoverableTransactionManager transactionManager, String name, ClassLoader classLoader) { - super(new InterceptorsImpl(transactionSupport, pooling, subjectSource, name, connectionTracker, transactionManager, classLoader), transactionManager); + super(new InterceptorsImpl(transactionSupport, pooling, subjectSource, name, connectionTracker, transactionManager, classLoader), transactionManager, name); } private static class InterceptorsImpl implements AbstractConnectionManager.Interceptors { diff --git a/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/OutboundNamedXAResourceFactory.java b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/OutboundNamedXAResourceFactory.java new file mode 100644 index 0000000..3ff4f8e --- /dev/null +++ b/geronimo-connector/src/main/java/org/apache/geronimo/connector/outbound/OutboundNamedXAResourceFactory.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.geronimo.connector.outbound; + +import javax.resource.ResourceException; +import javax.resource.spi.ManagedConnectionFactory; +import javax.transaction.SystemException; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; +import org.apache.geronimo.transaction.manager.NamedXAResource; +import org.apache.geronimo.transaction.manager.NamedXAResourceFactory; + +/** + * @version $Rev$ $Date$ + */ +public class OutboundNamedXAResourceFactory implements NamedXAResourceFactory { + + private final String name; + private final ConnectionInterceptor recoveryStack; + private final ManagedConnectionFactory managedConnectionFactory; + + public OutboundNamedXAResourceFactory(String name, ConnectionInterceptor recoveryStack, ManagedConnectionFactory managedConnectionFactory) { + this.name = name; + this.recoveryStack = recoveryStack; + this.managedConnectionFactory = managedConnectionFactory; + } + + public String getName() { + return name; + } + + public NamedXAResource getNamedXAResource() throws SystemException { + try { + ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, null); + + ConnectionInfo recoveryConnectionInfo = new ConnectionInfo(mci); + recoveryStack.getConnection(recoveryConnectionInfo); + + // For pooled resources, we may now have a new MCI (not the one constructed above). Make sure we use the correct MCI + return new NamedXAResourceWithConnectioninfo((NamedXAResource) recoveryConnectionInfo.getManagedConnectionInfo().getXAResource(), recoveryConnectionInfo); + } catch (ResourceException e) { + throw (SystemException) new SystemException("Could not get XAResource for recovery for mcf: " + name).initCause(e); + } + } + + public void returnNamedXAResource(NamedXAResource namedXAResource) { + NamedXAResourceWithConnectioninfo xares = (NamedXAResourceWithConnectioninfo) namedXAResource; + recoveryStack.returnConnection(xares.getConnectionInfo(), ConnectionReturnAction.DESTROY); + } + + private static class NamedXAResourceWithConnectioninfo implements NamedXAResource { + + private final NamedXAResource delegate; + private final ConnectionInfo connectionInfo; + + private NamedXAResourceWithConnectioninfo(NamedXAResource delegate, ConnectionInfo connectionInfo) { + this.delegate = delegate; + this.connectionInfo = connectionInfo; + } + + public ConnectionInfo getConnectionInfo() { + return connectionInfo; + } + + public String getName() { + return delegate.getName(); + } + + public void commit(Xid xid, boolean b) throws XAException { + delegate.commit(xid, b); + } + + public void end(Xid xid, int i) throws XAException { + delegate.end(xid, i); + } + + public void forget(Xid xid) throws XAException { + delegate.forget(xid); + } + + public int getTransactionTimeout() throws XAException { + return delegate.getTransactionTimeout(); + } + + public boolean isSameRM(XAResource xaResource) throws XAException { + return delegate.isSameRM(xaResource); + } + + public int prepare(Xid xid) throws XAException { + return delegate.prepare(xid); + } + + public Xid[] recover(int i) throws XAException { + return delegate.recover(i); + } + + public void rollback(Xid xid) throws XAException { + delegate.rollback(xid); + } + + public boolean setTransactionTimeout(int i) throws XAException { + return delegate.setTransactionTimeout(i); + } + + public void start(Xid xid, int i) throws XAException { + delegate.start(xid, i); + } + } +} diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/log/HOWLLog.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/log/HOWLLog.java index a1f5546..bbf76c2 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/log/HOWLLog.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/log/HOWLLog.java @@ -67,7 +67,7 @@ public class HOWLLog implements TransactionLog { private final XALogger logger; private final Configuration configuration = new Configuration(); private boolean started = false; - private HashMap recovered; + private HashMap recovered; public HOWLLog(String bufferClassName, int bufferSize, @@ -219,7 +219,7 @@ public void doStart() throws Exception { started = true; setLogFileDir(logFileDir); log.debug("Initiating transaction manager recovery"); - recovered = new HashMap(); + recovered = new HashMap(); logger.open(null); @@ -241,15 +241,14 @@ public void doFail() { public void begin(Xid xid) throws LogException { } - public Object prepare(Xid xid, List branches) throws LogException { + public Object prepare(Xid xid, List branches) throws LogException { int branchCount = branches.size(); byte[][] data = new byte[3 + 2 * branchCount][]; data[0] = intToBytes(xid.getFormatId()); data[1] = xid.getGlobalTransactionId(); data[2] = xid.getBranchQualifier(); int i = 3; - for (Iterator iterator = branches.iterator(); iterator.hasNext();) { - TransactionBranchInfo transactionBranchInfo = (TransactionBranchInfo) iterator.next(); + for (TransactionBranchInfo transactionBranchInfo : branches) { data[i++] = transactionBranchInfo.getBranchXid().getBranchQualifier(); data[i++] = transactionBranchInfo.getResourceName().getBytes(); } @@ -315,9 +314,9 @@ public void rollback(Xid xid, Object logMark) throws LogException { } } - public Collection recover(XidFactory xidFactory) throws LogException { + public Collection recover(XidFactory xidFactory) throws LogException { log.debug("Initiating transaction manager recovery"); - Map recovered = new HashMap(); + Map recovered = new HashMap(); ReplayListener replayListener = new GeronimoReplayListener(xidFactory, recovered); logger.replayActiveTx(replayListener); log.debug("In doubt transactions recovered from log"); @@ -352,9 +351,9 @@ private int bytesToInt(byte[] buffer) { private class GeronimoReplayListener implements ReplayListener { private final XidFactory xidFactory; - private final Map recoveredTx; + private final Map recoveredTx; - public GeronimoReplayListener(XidFactory xidFactory, Map recoveredTx) { + public GeronimoReplayListener(XidFactory xidFactory, Map recoveredTx) { this.xidFactory = xidFactory; this.recoveredTx = recoveredTx; } diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/GeronimoTransactionManager.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/GeronimoTransactionManager.java index a6fc4b5..902a8e5 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/GeronimoTransactionManager.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/GeronimoTransactionManager.java @@ -30,6 +30,8 @@ import javax.transaction.xa.Xid; /** + * Adds implementations of XATerminator and XAWork interfaces to basic TransactionManagerImpl + * * @version $Rev$ $Date$ */ public class GeronimoTransactionManager extends TransactionManagerImpl implements XATerminator, XAWork { @@ -195,7 +197,7 @@ public void end(Xid xid) throws XAException, SystemException { throw new XAException("No imported transaction for xid: " + xid); } if (importedTransaction != getTransaction()) { - throw new XAException("Imported transaction is not associated with the curren thread xid: " + xid); + throw new XAException("Imported transaction is not associated with the current thread xid: " + xid); } suspend(); } diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/NamedXAResourceFactory.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/NamedXAResourceFactory.java new file mode 100644 index 0000000..7dbb1c5 --- /dev/null +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/NamedXAResourceFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.geronimo.transaction.manager; + +import javax.transaction.SystemException; + +/** + * @version $Rev$ $Date$ + */ +public interface NamedXAResourceFactory { + + String getName(); + + NamedXAResource getNamedXAResource() throws SystemException; + + void returnNamedXAResource(NamedXAResource namedXAResource); + +} diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoverableTransactionManager.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoverableTransactionManager.java index 61c38f5..7e8d497 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoverableTransactionManager.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoverableTransactionManager.java @@ -28,5 +28,7 @@ public interface RecoverableTransactionManager extends TransactionManager { void recoveryError(Exception e); - void recoverResourceManager(NamedXAResource xaResource); + void registerNamedXAResourceFactory(NamedXAResourceFactory namedXAResourceFactory); + + void unregisterNamedXAResourceFactory(String namedXAResourceFactoryName); } diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/Recovery.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/Recovery.java index 38cbed0..1c5e063 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/Recovery.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/Recovery.java @@ -47,13 +47,13 @@ public interface Recovery { //hard to implement.. needs ExternalTransaction to have a reference to externalXids. // boolean remoteRecoveryComplete(); - Map getExternalXids(); + Map getExternalXids(); public static class XidBranchesPair { private final Xid xid; //set of TransactionBranchInfo - private final Set branches = new HashSet(); + private final Set branches = new HashSet(); private final Object mark; @@ -66,7 +66,7 @@ public Xid getXid() { return xid; } - public Set getBranches() { + public Set getBranches() { return branches; } diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoveryImpl.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoveryImpl.java index 9336a89..ff15e60 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoveryImpl.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/RecoveryImpl.java @@ -51,12 +51,12 @@ public class RecoveryImpl implements Recovery { private final TransactionLog txLog; private final XidFactory xidFactory; - private final Map externalXids = new HashMap(); - private final Map ourXids = new HashMap(); - private final Map nameToOurTxMap = new HashMap(); - private final Map externalGlobalIdMap = new HashMap(); + private final Map externalXids = new HashMap(); + private final Map ourXids = new HashMap(); + private final Map> nameToOurTxMap = new HashMap>(); + private final Map externalGlobalIdMap = new HashMap(); - private final List recoveryErrors = new ArrayList(); + private final List recoveryErrors = new ArrayList(); public RecoveryImpl(final TransactionLog txLog, final XidFactory xidFactory) { this.txLog = txLog; @@ -64,22 +64,21 @@ public RecoveryImpl(final TransactionLog txLog, final XidFactory xidFactory) { } public synchronized void recoverLog() throws XAException { - Collection preparedXids = null; + Collection preparedXids; try { preparedXids = txLog.recover(xidFactory); } catch (LogException e) { throw (XAException) new XAException(XAException.XAER_RMERR).initCause(e); } - for (Iterator iterator = preparedXids.iterator(); iterator.hasNext();) { - XidBranchesPair xidBranchesPair = (Recovery.XidBranchesPair) iterator.next(); + for (XidBranchesPair xidBranchesPair : preparedXids) { Xid xid = xidBranchesPair.getXid(); if (xidFactory.matchesGlobalId(xid.getGlobalTransactionId())) { ourXids.put(new ByteArrayWrapper(xid.getGlobalTransactionId()), xidBranchesPair); - for (Iterator branches = xidBranchesPair.getBranches().iterator(); branches.hasNext();) { - String name = ((TransactionBranchInfo) branches.next()).getResourceName(); - Set transactionsForName = (Set)nameToOurTxMap.get(name); + for (TransactionBranchInfo transactionBranchInfo : xidBranchesPair.getBranches()) { + String name = transactionBranchInfo.getResourceName(); + Set transactionsForName = nameToOurTxMap.get(name); if (transactionsForName == null) { - transactionsForName = new HashSet(); + transactionsForName = new HashSet(); nameToOurTxMap.put(name, transactionsForName); } transactionsForName.add(xidBranchesPair); @@ -99,7 +98,7 @@ public synchronized void recoverResourceManager(NamedXAResource xaResource) thro for (int i = 0; prepared != null && i < prepared.length; i++) { Xid xid = prepared[i]; ByteArrayWrapper globalIdWrapper = new ByteArrayWrapper(xid.getGlobalTransactionId()); - XidBranchesPair xidNamesPair = (XidBranchesPair) ourXids.get(globalIdWrapper); + XidBranchesPair xidNamesPair = ourXids.get(globalIdWrapper); if (xidNamesPair != null) { @@ -125,7 +124,7 @@ public synchronized void recoverResourceManager(NamedXAResource xaResource) thro } } else if (xidFactory.matchesBranchId(xid.getBranchQualifier())) { //our branch, but we did not start this tx. - TransactionImpl externalTx = (TransactionImpl) externalGlobalIdMap.get(xid.getGlobalTransactionId()); + TransactionImpl externalTx = externalGlobalIdMap.get(xid.getGlobalTransactionId()); if (externalTx == null) { //we did not prepare this branch, rollback. try { @@ -141,18 +140,16 @@ public synchronized void recoverResourceManager(NamedXAResource xaResource) thro } //else we had nothing to do with this xid. } - Set transactionsForName = (Set)nameToOurTxMap.get(name); + Set transactionsForName = nameToOurTxMap.get(name); if (transactionsForName != null) { - for (Iterator transactions = transactionsForName.iterator(); transactions.hasNext();) { - XidBranchesPair xidBranchesPair = (XidBranchesPair) transactions.next(); + for (XidBranchesPair xidBranchesPair : transactionsForName) { removeNameFromTransaction(xidBranchesPair, name, false); } } } private boolean isNameInTransaction(XidBranchesPair xidBranchesPair, String name) { - for (Iterator branches = xidBranchesPair.getBranches().iterator(); branches.hasNext();) { - TransactionBranchInfo transactionBranchInfo = (TransactionBranchInfo) branches.next(); + for (TransactionBranchInfo transactionBranchInfo : xidBranchesPair.getBranches()) { if (name.equals(transactionBranchInfo.getResourceName())) { return true; } @@ -187,7 +184,7 @@ public synchronized boolean hasRecoveryErrors() { return !recoveryErrors.isEmpty(); } - public synchronized List getRecoveryErrors() { + public synchronized List getRecoveryErrors() { return Collections.unmodifiableList(recoveryErrors); } @@ -203,8 +200,8 @@ public synchronized int localUnrecoveredCount() { // public boolean remoteRecoveryComplete() { // } - public synchronized Map getExternalXids() { - return new HashMap(externalXids); + public synchronized Map getExternalXids() { + return new HashMap(externalXids); } private static class ByteArrayWrapper { @@ -215,8 +212,8 @@ public ByteArrayWrapper(final byte[] bytes) { assert bytes != null; this.bytes = bytes; int hash = 0; - for (int i = 0; i < bytes.length; i++) { - hash += 37 * bytes[i]; + for (byte aByte : bytes) { + hash += 37 * aByte; } hashCode = hash; } @@ -234,11 +231,13 @@ public int hashCode() { } private static class ExternalTransaction extends TransactionImpl { - private Set resourceNames; + private final Set resourceNames = new HashSet(); - public ExternalTransaction(Xid xid, TransactionLog txLog, Set resourceNames) { + public ExternalTransaction(Xid xid, TransactionLog txLog, Set resourceNames) { super(xid, txLog); - this.resourceNames = resourceNames; + for (TransactionBranchInfo info: resourceNames) { + this.resourceNames.add(info.getResourceName()); + } } public boolean hasName(String name) { diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionLog.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionLog.java index 2a56f9b..3203f67 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionLog.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionLog.java @@ -36,9 +36,10 @@ public interface TransactionLog { * 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 + * @return log mark to use in commit/rollback calls. + * @throws LogException on error */ - Object prepare(Xid xid, List branches) throws LogException; + Object prepare(Xid xid, List branches) throws LogException; void commit(Xid xid, Object logMark) throws LogException; @@ -48,11 +49,11 @@ public interface TransactionLog { * 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 + * @param xidFactory Xid factory * @return Map of recovered xid to List of TransactionBranchInfo representing the branches. - * @throws LogException + * @throws LogException on error */ - Collection recover(XidFactory xidFactory) throws LogException; + Collection recover(XidFactory xidFactory) throws LogException; String getXMLStats(); diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionManagerImpl.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionManagerImpl.java index 888ed01..c1310c1 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionManagerImpl.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/TransactionManagerImpl.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -41,19 +40,21 @@ */ public class TransactionManagerImpl implements TransactionManager, UserTransaction, TransactionSynchronizationRegistry, XidImporter, MonitorableTransactionManager, RecoverableTransactionManager { private static final Logger log = LoggerFactory.getLogger(TransactionManagerImpl.class); + private static final Logger recoveryLog = LoggerFactory.getLogger("RecoveryController"); + protected static final int DEFAULT_TIMEOUT = 600; protected static final byte[] DEFAULT_TM_ID = new byte[] {71,84,77,73,68}; final TransactionLog transactionLog; final XidFactory xidFactory; private final int defaultTransactionTimeoutMilliseconds; - private final ThreadLocal transactionTimeoutMilliseconds = new ThreadLocal(); - private final ThreadLocal threadTx = new ThreadLocal(); - private final ConcurrentHashMap associatedTransactions = new ConcurrentHashMap(); - private static final Logger recoveryLog = LoggerFactory.getLogger("RecoveryController"); + private final ThreadLocal transactionTimeoutMilliseconds = new ThreadLocal(); + private final ThreadLocal threadTx = new ThreadLocal(); + private final ConcurrentHashMap associatedTransactions = new ConcurrentHashMap(); final Recovery recovery; - private final CopyOnWriteArrayList transactionAssociationListeners = new CopyOnWriteArrayList(); - private List recoveryErrors = new ArrayList(); + private final Map namedXAResourceFactories = new ConcurrentHashMap(); + private final CopyOnWriteArrayList transactionAssociationListeners = new CopyOnWriteArrayList(); + private List recoveryErrors = new ArrayList(); // statistics private AtomicLong totalCommits = new AtomicLong(0); private AtomicLong totalRollBacks = new AtomicLong(0); @@ -103,7 +104,7 @@ public TransactionManagerImpl(int defaultTransactionTimeoutSeconds, XidFactory x } public Transaction getTransaction() { - return (Transaction) threadTx.get(); + return threadTx.get(); } private void associate(TransactionImpl tx) throws InvalidTransactionException { @@ -137,7 +138,7 @@ public void setTransactionTimeout(int seconds) throws SystemException { if (seconds == 0) { transactionTimeoutMilliseconds.set(null); } else { - transactionTimeoutMilliseconds.set(new Long(seconds * 1000)); + transactionTimeoutMilliseconds.set((long) seconds * 1000); } } @@ -226,7 +227,7 @@ public void putResource(Object key, Object value) { /** * jta 1.1 method so the jpa implementations can be told to flush their caches. - * @param synchronization + * @param synchronization interposed synchronization */ public void registerInterposedSynchronization(Synchronization synchronization) { TransactionImpl tx = getActiveTransactionImpl(); @@ -272,8 +273,7 @@ public Transaction importXid(Xid xid, long transactionTimeoutMilliseconds) throw if (transactionTimeoutMilliseconds < 0) { throw new SystemException("transaction timeout must be positive or 0 to reset to default"); } - TransactionImpl tx = new TransactionImpl(xid, xidFactory, transactionLog, getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds)); - return tx; + return new TransactionImpl(xid, xidFactory, transactionLog, getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds)); } public void commit(Transaction tx, boolean onePhase) throws XAException { @@ -334,9 +334,9 @@ long getTransactionTimeoutMilliseconds(long transactionTimeoutMilliseconds) { if (transactionTimeoutMilliseconds != 0) { return transactionTimeoutMilliseconds; } - Long timeout = (Long) this.transactionTimeoutMilliseconds.get(); + Long timeout = this.transactionTimeoutMilliseconds.get(); if (timeout != null) { - return timeout.longValue(); + return timeout; } return defaultTransactionTimeoutMilliseconds; } @@ -347,16 +347,28 @@ public void recoveryError(Exception e) { recoveryErrors.add(e); } - public void recoverResourceManager(NamedXAResource xaResource) { + public void registerNamedXAResourceFactory(NamedXAResourceFactory namedXAResourceFactory) { + namedXAResourceFactories.put(namedXAResourceFactory.getName(), namedXAResourceFactory); try { - recovery.recoverResourceManager(xaResource); + NamedXAResource namedXAResource = namedXAResourceFactory.getNamedXAResource(); + try { + recovery.recoverResourceManager(namedXAResource); + } finally { + namedXAResourceFactory.returnNamedXAResource(namedXAResource); + } } catch (XAException e) { recoveryError(e); + } catch (SystemException e) { + recoveryError(e); } } - public Map getExternalXids() { - return new HashMap(recovery.getExternalXids()); + public void unregisterNamedXAResourceFactory(String namedXAResourceFactoryName) { + namedXAResourceFactories.remove(namedXAResourceFactoryName); + } + + public Map getExternalXids() { + return new HashMap(recovery.getExternalXids()); } public void addTransactionAssociationListener(TransactionManagerMonitor listener) { @@ -368,8 +380,7 @@ public void removeTransactionAssociationListener(TransactionManagerMonitor liste } protected void fireThreadAssociated(Transaction tx) { - for (Iterator iterator = transactionAssociationListeners.iterator(); iterator.hasNext();) { - TransactionManagerMonitor listener = (TransactionManagerMonitor) iterator.next(); + for (TransactionManagerMonitor listener : transactionAssociationListeners) { try { listener.threadAssociated(tx); } catch (Exception e) { @@ -379,8 +390,7 @@ protected void fireThreadAssociated(Transaction tx) { } protected void fireThreadUnassociated(Transaction tx) { - for (Iterator iterator = transactionAssociationListeners.iterator(); iterator.hasNext();) { - TransactionManagerMonitor listener = (TransactionManagerMonitor) iterator.next(); + for (TransactionManagerMonitor listener : transactionAssociationListeners) { try { listener.threadUnassociated(tx); } catch (Exception e) { @@ -391,6 +401,7 @@ protected void fireThreadUnassociated(Transaction tx) { /** * Returns the number of active transactions. + * @return the count of active transactions */ public long getActiveCount() { return activeCount.longValue(); @@ -398,6 +409,7 @@ public long getActiveCount() { /** * Return the number of total commits + * @return the number of commits since statistics were reset */ public long getTotalCommits() { return totalCommits.longValue(); @@ -405,6 +417,7 @@ public long getTotalCommits() { /** * Returns the number of total rollbacks + * @return the number of rollbacks since statistics were reset */ public long getTotalRollbacks() { return totalRollBacks.longValue(); diff --git a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/XidImporter.java b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/XidImporter.java index 707d9fe..b12744e 100644 --- a/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/XidImporter.java +++ b/geronimo-transaction/src/main/java/org/apache/geronimo/transaction/manager/XidImporter.java @@ -39,5 +39,5 @@ public interface XidImporter { int prepare(Transaction tx) throws XAException; void rollback(Transaction tx) throws XAException; - Map getExternalXids(); + Map getExternalXids(); } diff --git a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockLog.java b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockLog.java index 08c05d4..8824ad1 100644 --- a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockLog.java +++ b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockLog.java @@ -40,7 +40,7 @@ public class MockLog implements TransactionLog { public void begin(Xid xid) throws LogException { } - public Object prepare(Xid xid, List branches) 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); diff --git a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResource.java b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResource.java index 2148978..f9baf9d 100644 --- a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResource.java +++ b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResource.java @@ -20,6 +20,7 @@ import java.util.Set; import java.util.HashSet; +import javax.transaction.SystemException; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; @@ -27,7 +28,7 @@ /** * @version $Rev$ $Date$ */ -public class MockResource implements NamedXAResource { +public class MockResource implements NamedXAResource, NamedXAResourceFactory { private String xaResourceName = "mockResource"; private Xid currentXid; private MockResourceManager manager; @@ -153,4 +154,12 @@ public String getName() { return xaResourceName; } + public NamedXAResource getNamedXAResource() throws SystemException { + return this; + } + + public void returnNamedXAResource(NamedXAResource namedXAResource) { + if (this != namedXAResource) throw new RuntimeException("Wrong NamedXAResource returned: expected: " + this + " actual: " + namedXAResource); + } + } diff --git a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResourceManager.java b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResourceManager.java index 15ebcc7..82a5050 100644 --- a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResourceManager.java +++ b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/MockResourceManager.java @@ -36,15 +36,12 @@ public class MockResourceManager { private boolean willCommit; private Map xids = new HashMap(); - private NamedXAResource resources; - public MockResourceManager(boolean willCommit) { this.willCommit = willCommit; } public MockResource getResource(String xaResourceName) { MockResource mockResource = new MockResource(this, xaResourceName); - resources = mockResource; return mockResource; } @@ -71,8 +68,4 @@ public void forget(Xid xid, XAResource xaRes) throws XAException { } } - public void doRecovery(RecoverableTransactionManager transactionManager) throws SystemException { - transactionManager.recoverResourceManager(resources); - } - } diff --git a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/TransactionManagerImplTest.java b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/TransactionManagerImplTest.java index b2e15e8..f79f4e9 100644 --- a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/TransactionManagerImplTest.java +++ b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/TransactionManagerImplTest.java @@ -256,14 +256,21 @@ public void testSimpleRecovery() throws Exception { tm.prepare(tx); //recover tm.recovery.recoverLog(); - rm1.doRecovery(tm); + recover(r1_1); + recover(r1_2); assertTrue(r1_2.isCommitted()); assertTrue(!r2_2.isCommitted()); - rm2.doRecovery(tm); + recover(r2_1); + recover(r2_2); assertTrue(r2_2.isCommitted()); assertTrue(tm.recovery.localRecoveryComplete()); } + private void recover(MockResource mr) { + tm.registerNamedXAResourceFactory(mr); + tm.unregisterNamedXAResourceFactory(mr.getName()); + } + public void testImportedXidRecovery() throws Exception { //create a transaction from an external transaction manager. XidFactory xidFactory2 = new XidFactoryImpl("tm2".getBytes()); @@ -279,10 +286,12 @@ public void testImportedXidRecovery() throws Exception { tm.prepare(tx); //recover tm.recovery.recoverLog(); - rm1.doRecovery(tm); + recover(r1_1); + recover(r1_2); assertTrue(!r1_2.isCommitted()); assertTrue(!r2_2.isCommitted()); - rm2.doRecovery(tm); + recover(r2_1); + recover(r2_2); assertTrue(!r2_2.isCommitted()); //there are no transactions started here, so local recovery is complete assertTrue(tm.recovery.localRecoveryComplete()); diff --git a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/XATransactionTester.java b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/XATransactionTester.java index 65d63f9..0af5f91 100644 --- a/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/XATransactionTester.java +++ b/geronimo-transaction/src/test/java/org/apache/geronimo/transaction/manager/XATransactionTester.java @@ -103,7 +103,7 @@ public void begin(Xid xid) throws LogException { XATransactionTester.this.xid = xid; } - public Object prepare(Xid xid, List branches) throws LogException { + public Object prepare(Xid xid, List branches) throws LogException { return new Object(); } @@ -113,8 +113,8 @@ 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 Collection recover(XidFactory xidFactory) throws LogException { + return new ArrayList(); } public String getXMLStats() {