package io.eguan.dtx;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.
* #L%
*/
import static io.eguan.dtx.DtxTaskStatus.COMMITTED;
import static io.eguan.dtx.DtxTaskStatus.PREPARED;
import static io.eguan.dtx.DtxTaskStatus.ROLLED_BACK;
import static io.eguan.dtx.DtxTaskStatus.STARTED;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.eguan.dtx.DtxResourceManager;
import io.eguan.dtx.DtxResourceManagerContext;
import io.eguan.dtx.DtxTaskStatus;
import io.eguan.dtx.TransactionManager;
import io.eguan.dtx.DtxMockUtils.CompMatcher;
import io.eguan.dtx.DtxMockUtils.TxNegativeStateMatcher;
import io.eguan.dtx.DtxMockUtils.TxPositiveStateMatcher;
import io.eguan.dtx.DtxMockUtils.WrapMatcher;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.transaction.xa.XAException;
import org.hamcrest.Matcher;
import org.mockito.internal.matchers.And;
import org.mockito.internal.matchers.InstanceOf;
import org.mockito.internal.matchers.Not;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.core.LifecycleService;
/**
* Factory for creating {@link DtxResourceManager}.
*
* @author oodrive
* @author pwehrle
*
*/
public final class DtxDummyRmFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(DtxDummyRmFactory.class);
/**
* Default (fake) transaction payload to be used with mocks.
*/
public static final byte[] DEFAULT_PAYLOAD = "fakePayload".getBytes();
/**
* Bad payload correctly rejected by mocks.
*/
public static final byte[] BAD_PAYLOAD = "badPayload".getBytes();
/**
* A constant default resource {@link UUID}.
*/
public static final UUID DEFAULT_RES_UUID = UUID.fromString("84f63d7e-9081-11e2-b663-180373e16ea9");
/**
* A simple implementation of {@link DtxResourceManagerContext} to be returned by mocks.
*/
public static final DtxResourceManagerContext DEFAULT_TX_CONTEXT = new DefaultContext(DEFAULT_RES_UUID);
private DtxDummyRmFactory() {
throw new AssertionError("Not instantiable");
}
/**
* The default {@link Answer} for a {@link DtxResourceManager#start(byte[])} call.
*
*
*/
public static final class DefaultStartAnswer implements Answer<DtxResourceManagerContext> {
private final UUID resourceId;
/**
* Creates an {@link Answer} returning a {@link DtxResourceManagerContext} instance matching the given resource
* {@link UUID}.
*
* @param resourceId
* a non-<code>null</code> {@link UUID}
*/
DefaultStartAnswer(@Nonnull final UUID resourceId) {
this.resourceId = Objects.requireNonNull(resourceId);
}
@Override
public final DtxResourceManagerContext answer(final InvocationOnMock invocation) throws Throwable {
return doAnswer(invocation, resourceId);
}
/**
* Build and return the default answer for the given invocation.
*
* @param invocation
* a valid {@link InvocationOnMock}
* @param resourceId
* the {@link UUID} of the target resource manager
* @return an instance of {@link DtxResourceManagerContext} matching the invocation
*/
static final DtxResourceManagerContext doAnswer(final InvocationOnMock invocation,
@Nonnull final UUID resourceId) {
final Object[] args = invocation.getArguments();
if (args.length != 1) {
throw new IllegalArgumentException("Wrong number of arguments");
}
return new FakeDtxResourceManagerContext(resourceId, STARTED);
}
}
/**
* The default {@link Answer} returned for a {@link DtxResourceManager#prepare(DtxResourceManagerContext)} call.
*
*
*/
public static final class DefaultPrepareAnswer implements Answer<Boolean> {
@Override
public final Boolean answer(final InvocationOnMock invocation) throws Throwable {
return doAnswer(invocation);
}
/**
* Build and return the default answer for the given invocation.
*
* @param invocation
* a valid {@link InvocationOnMock}
* @return {@link Boolean#TRUE} if the invocation is valid, {@value Boolean#FALSE} otherwise
*/
static final Boolean doAnswer(final InvocationOnMock invocation) {
final Object[] args = invocation.getArguments();
if (args.length != 1) {
throw new IllegalArgumentException("Wrong number of arguments");
}
((DtxResourceManagerContext) args[0]).setTxStatus(PREPARED);
return Boolean.TRUE;
}
}
/**
* The default {@link Answer} returned for a {@link DtxResourceManager#commit(DtxResourceManagerContext)} call.
*
*
*/
public static final class DefaultCommitAnswer implements Answer<Void> {
@Override
public final Void answer(final InvocationOnMock invocation) throws Throwable {
return doAnswer(invocation);
}
/**
* Perform the default answering operations for the given invocation.
*
* @param invocation
* a valid {@link InvocationOnMock}
* @return <code>null</code>
*/
static final Void doAnswer(final InvocationOnMock invocation) {
final Object[] args = invocation.getArguments();
if (args.length != 1) {
throw new IllegalArgumentException("Wrong number of arguments");
}
((DtxResourceManagerContext) invocation.getArguments()[0]).setTxStatus(COMMITTED);
return null;
}
}
/**
* The default {@link Answer} returned for a {@link DtxResourceManager#rollback(DtxResourceManagerContext)} call.
*
*
*/
public static final class DefaultRollbackAnswer implements Answer<Void> {
@Override
public final Void answer(final InvocationOnMock invocation) throws Throwable {
return doAnswer(invocation);
}
/**
* Perform the default answering operations for the given invocation.
*
* @param invocation
* a valid {@link InvocationOnMock}
* @return <code>null</code>
*/
static final Void doAnswer(final InvocationOnMock invocation) {
final Object[] args = invocation.getArguments();
if (args.length != 1) {
throw new IllegalArgumentException("Wrong number of arguments");
}
((DtxResourceManagerContext) invocation.getArguments()[0]).setTxStatus(ROLLED_BACK);
return null;
}
}
/**
* Special {@link Answer} for the {@link DtxResourceManager#prepare(DtxResourceManagerContext)} method that shuts
* down the target Hazelcast node.
*
*
*/
public static final class NodeShutdownStartAnswer implements Answer<DtxResourceManagerContext> {
private static final int BARRIER_WAIT_TIMEOUT_S = 10;
private final LifecycleService targetHzLifecycle;
private final CyclicBarrier barrier;
/**
* Constructs an instance applied to the given {@link LifecycleService}, synchronizing the shutdown on the
* optional {@link CyclicBarrier}.
*
* @param targetHzLifecycle
* the non-<code>null</code> {@link LifecycleService} to shut down
* @param barrier
* the optional {@link CyclicBarrier} to synchronize the shutdown on
*/
NodeShutdownStartAnswer(@Nonnull final LifecycleService targetHzLifecycle, final CyclicBarrier barrier) {
this.targetHzLifecycle = Objects.requireNonNull(targetHzLifecycle);
this.barrier = barrier;
}
@Override
public final DtxResourceManagerContext answer(final InvocationOnMock invocation) throws Throwable {
// optionally waits on the barrier
if (barrier != null) {
LOGGER.debug("waiting for barrier in thread " + Thread.currentThread().getName());
barrier.await(BARRIER_WAIT_TIMEOUT_S, TimeUnit.SECONDS);
LOGGER.debug("barrier gone through in thread " + Thread.currentThread().getName());
}
// shuts down the Hazelcast peer
targetHzLifecycle.shutdown();
assertFalse(targetHzLifecycle.isRunning());
return null;
}
}
/**
* Special {@link Answer} for the {@link DtxResourceManager#prepare(DtxResourceManagerContext)} method that shuts
* down the target Hazelcast node.
*
*
*/
public static final class NodeShutdownPrepareAnswer implements Answer<Boolean> {
private static final int BARRIER_WAIT_TIMEOUT_S = 10;
private final LifecycleService targetHzLifecycle;
private final CyclicBarrier barrier;
/**
* Constructs an instance applied to the given {@link LifecycleService}, synchronizing the shutdown on the
* optional {@link CyclicBarrier}.
*
* @param targetHzLifecycle
* the non-<code>null</code> {@link LifecycleService} to shut down
* @param barrier
* the optional {@link CyclicBarrier} to synchronize the shutdown on
*/
NodeShutdownPrepareAnswer(@Nonnull final LifecycleService targetHzLifecycle, final CyclicBarrier barrier) {
this.targetHzLifecycle = Objects.requireNonNull(targetHzLifecycle);
this.barrier = barrier;
}
@Override
public final Boolean answer(final InvocationOnMock invocation) throws Throwable {
// optionally waits on the barrier
if (barrier != null) {
LOGGER.debug("waiting for barrier in thread " + Thread.currentThread().getName());
barrier.await(BARRIER_WAIT_TIMEOUT_S, TimeUnit.SECONDS);
LOGGER.debug("barrier gone through in thread " + Thread.currentThread().getName());
}
// shuts down the Hazelcast peer
targetHzLifecycle.shutdown();
assertFalse(targetHzLifecycle.isRunning());
return Boolean.FALSE;
}
}
/**
* Wrapping {@link Answer} implementation that can be toggled to throw an exception or the result of an
* {@link Answer} given at construction.
*
* @param <T>
* the return type for the wrapped {@link Answer} type
*
*
*/
public static final class ToggleAnswer<T> implements Answer<T> {
private volatile boolean triggerError = false;
private final int throwMe;
private final Answer<T> defaultAnswer;
/**
* Constructs a generic instance.
*
* @param initialErrorToggle
* <code>true</code> if exceptions shall be thrown, <code>false</code> for normal behavior
* @param errorCode
* the error code of the {@link XAException} to throw
* @param defaultAnswer
* the default {@link Answer} to which to redirect calls when not throwing exceptions
*/
ToggleAnswer(final boolean initialErrorToggle, final int errorCode, final Answer<T> defaultAnswer) {
triggerError = initialErrorToggle;
this.throwMe = errorCode;
this.defaultAnswer = Objects.requireNonNull(defaultAnswer);
}
/**
* Toggles the exception throwing behavior.
*/
public void toggleErrorTrigger() {
triggerError = !triggerError;
}
@Override
public T answer(final InvocationOnMock invocation) throws Throwable {
if (triggerError) {
throw new XAException(throwMe);
}
return defaultAnswer.answer(invocation);
}
}
private static final class DefaultContext extends DtxResourceManagerContext {
protected DefaultContext(final UUID resourceManagerId) throws NullPointerException {
super(resourceManagerId);
}
}
/**
* Creates a mock {@link DtxResourceManager} that correctly mimics error-less transaction processing.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
static DtxResourceManager newResMgrThatDoesEverythingRight(final UUID resourceId) throws XAException {
if (resourceId != null) {
return new DtxResourceManagerBuilder().setId(resourceId).build();
}
return new DtxResourceManagerBuilder().build();
}
/**
* Creates a mock {@link DtxResourceManager} that fails on calls to {@link DtxResourceManager#start(byte[])}.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @param throwMe
* the {@link XAException} to throw
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
static DtxResourceManager newResMgrFailingOnStart(final UUID resourceId, final XAException throwMe)
throws XAException {
return new DtxResourceManagerBuilder().setId(resourceId).setStart(throwMe, null).build();
}
/**
* Creates a mock {@link DtxResourceManager} that fails on calls to
* {@link DtxResourceManager#prepare(DtxResourceManagerContext)}.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @param throwMe
* the {@link XAException} to throw
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
static DtxResourceManager newResMgrFailingOnPrepare(final UUID resourceId, final XAException throwMe)
throws XAException {
return new DtxResourceManagerBuilder().setId(resourceId).setPrepare(throwMe, null).build();
}
/**
* Creates a mock {@link DtxResourceManager} that fails on calls to
* {@link DtxResourceManager#commit(DtxResourceManagerContext)}.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @param throwMe
* the {@link XAException} to throw
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
static DtxResourceManager newResMgrFailingOnCommit(final UUID resourceId, final XAException throwMe)
throws XAException {
return new DtxResourceManagerBuilder().setId(resourceId).setCommit(throwMe, null).build();
}
/**
* Creates a mock {@link DtxResourceManager} that fails on calls to
* {@link DtxResourceManager#rollback(DtxResourceManagerContext)}.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @param throwMe
* the {@link XAException} to throw
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
static DtxResourceManager newResMgrFailingOnRollback(final UUID resourceId, final XAException throwMe)
throws XAException {
return new DtxResourceManagerBuilder().setId(resourceId).setRollback(throwMe, null).build();
}
/**
* Creates a mock {@link DtxResourceManager} that fails on calls to
* {@link DtxResourceManager#rollback(DtxResourceManagerContext)}.
*
* @param resourceId
* the optional ID to set for the result {@link DtxResourceManager}
* @param txManager
* the {@link TransactionManager} to unregister from during the prepare operation
* @return a functional {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
@ParametersAreNonnullByDefault
static DtxResourceManager newResMgrUnregisteringOnPrepare(final UUID resourceId, final TransactionManager txManager)
throws XAException {
final Answer<Boolean> answer = new Answer<Boolean>() {
@Override
public final Boolean answer(final InvocationOnMock invocation) throws Throwable {
final Boolean result = DefaultPrepareAnswer.doAnswer(invocation);
txManager.unregisterResourceManager(resourceId);
return result;
}
};
return new DtxResourceManagerBuilder().setId(resourceId).setPrepare(null, answer).build();
}
/**
* Builder class for mock {@link DtxResourceManager} instances.
*
*
*/
public static final class DtxResourceManagerBuilder {
private final DtxResourceManager result;
private final CompMatcher<byte[]> notPayloadMatcher;
private final WrapMatcher<DtxResourceManagerContext> notFakeCtxMatcher;
private boolean setIdDone = false;
private boolean setStartDone = false;
private boolean setPrepareDone = false;
private boolean setCommitDone = false;
private boolean setRollbackDone = false;
private boolean setPostSyncDone = false;
/**
* Matcher for both context class and resource ID.
*/
private And ctxIdMatcher;
/**
* Matcher for any context instance.
*/
private WrapMatcher<DtxResourceManagerContext> anyCtxMatcher;
/**
* Constructs a new unconfigured instance.
*/
public DtxResourceManagerBuilder() {
result = mock(DtxResourceManager.class);
notPayloadMatcher = new CompMatcher<byte[]>(DEFAULT_PAYLOAD, new Comparator<byte[]>() {
@Override
public final int compare(final byte[] o1, final byte[] o2) {
return Arrays.equals(o1, o2) ? -1 : 0;
}
});
notFakeCtxMatcher = new WrapMatcher<DtxResourceManagerContext>(new Not(new InstanceOf(
FakeDtxResourceManagerContext.class)));
anyCtxMatcher = new WrapMatcher<DtxResourceManagerContext>(new InstanceOf(DtxResourceManagerContext.class));
}
/**
* Defines the {@link DtxResourceManager}'s ID.
*
* @param resourceId
* the {@link UUID} returned by {@link DtxResourceManager#getId()}, if <code>null</code> a random
* value is set
* @return the configured builder
*/
public final DtxResourceManagerBuilder setId(final UUID resourceId) {
// defines getId() behavior
final UUID resultId = resourceId == null ? UUID.randomUUID() : resourceId;
when(result.getId()).thenReturn(resultId);
this.ctxIdMatcher = new And(
Arrays.asList(new Matcher[] { new InstanceOf(FakeDtxResourceManagerContext.class),
new DtxMockUtils.ResourceIdMatcher(resultId) }));
setIdDone = true;
return this;
}
/**
* Defines {@link DtxResourceManager#start(DtxResourceManagerContext)} behavior.
*
* @param xe
* a {@link XAException} to throw, default behavior is assumed when <code>null</code>
* @param customAnswer
* a specific {@link Answer} to return on a perfectly valid call, or <code>null</code> for the
* default answer
* @return the configured builder
* @throws XAException
* if configuration fails
*/
public final DtxResourceManagerBuilder setStart(final XAException xe,
final Answer<DtxResourceManagerContext> customAnswer) throws XAException {
if (!setIdDone) {
throw new XAException("ID was not set before setting start behavior");
}
// default behavior for null
when(result.start(null)).thenThrow(new NullPointerException());
if (xe != null) {
doThrow(xe).when(result).start(argThat(new WrapMatcher<byte[]>(new InstanceOf(byte[].class))));
setStartDone = true;
return this;
}
// if no exception is given, set normal behavior
final Answer<DtxResourceManagerContext> answer = (customAnswer != null) ? customAnswer
: new DefaultStartAnswer(result.getId());
doAnswer(answer).when(result).start(eq(DEFAULT_PAYLOAD));
doThrow(new XAException(XAException.XAER_INVAL)).when(result).start(argThat(notPayloadMatcher));
setStartDone = true;
return this;
}
/**
* Defines {@link DtxResourceManager#prepare(DtxResourceManagerContext)} behavior.
*
* @param xe
* a {@link XAException} to throw, default behavior is assumed when <code>null</code>
* @param customAnswer
* a specific {@link Answer} to return on a perfectly valid call, or <code>null</code> for the
* default answer
* @return the configured builder
* @throws XAException
* if configuration fails
*/
public final DtxResourceManagerBuilder setPrepare(final XAException xe, final Answer<Boolean> customAnswer)
throws XAException {
if (!setIdDone) {
throw new XAException("ID was not set before setting prepare behavior");
}
// default behavior for null
when(result.prepare(null)).thenThrow(new NullPointerException());
if (xe != null) {
doThrow(xe).when(result).prepare(argThat(anyCtxMatcher));
setPrepareDone = true;
return this;
}
final WrapMatcher<DtxResourceManagerContext> startedMatcher = new WrapMatcher<DtxResourceManagerContext>(
new And(Arrays.asList(new Matcher[] { this.ctxIdMatcher, new TxPositiveStateMatcher(STARTED) })));
final TxNegativeStateMatcher noPrepareStateMatcher = new TxNegativeStateMatcher(STARTED);
final Answer<Boolean> normalAnswer = (customAnswer != null) ? customAnswer : new DefaultPrepareAnswer();
doAnswer(normalAnswer).when(result).prepare(argThat(startedMatcher));
doThrow(new XAException(XAException.XAER_INVAL)).when(result).prepare(argThat(notFakeCtxMatcher));
doThrow(new XAException(XAException.XAER_PROTO)).when(result).prepare(argThat(noPrepareStateMatcher));
setPrepareDone = true;
return this;
}
/**
* Defines {@link DtxResourceManager#commit(DtxResourceManagerContext)} behavior.
*
* @param xe
* a {@link XAException} to throw, default behavior is assumed when <code>null</code>
* @param customAnswer
* a specific {@link Answer} to return on a perfectly valid call, or <code>null</code> for the
* default answer
* @return the configured builder
* @throws XAException
* if configuration fails
*/
public final DtxResourceManagerBuilder setCommit(final XAException xe, final Answer<Void> customAnswer)
throws XAException {
if (!setIdDone) {
throw new XAException("ID was not set before setting commit behavior");
}
// default behavior for null
doThrow(new NullPointerException()).when(result).commit(null);
if (xe != null) {
doThrow(xe).when(result).commit(argThat(anyCtxMatcher));
setCommitDone = true;
return this;
}
final WrapMatcher<DtxResourceManagerContext> preparedMatcher = new WrapMatcher<DtxResourceManagerContext>(
new And(Arrays.asList(new Matcher[] { this.ctxIdMatcher, new TxPositiveStateMatcher(PREPARED) })));
final TxNegativeStateMatcher noCommitStateMatcher = new TxNegativeStateMatcher(PREPARED);
final Answer<Void> answer = (customAnswer != null) ? customAnswer : new DefaultCommitAnswer();
doAnswer(answer).when(result).commit(argThat(preparedMatcher));
doThrow(new XAException(XAException.XAER_INVAL)).when(result).commit(argThat(notFakeCtxMatcher));
doThrow(new XAException(XAException.XAER_PROTO)).when(result).commit(argThat(noCommitStateMatcher));
setCommitDone = true;
return this;
}
/**
* Defines {@link DtxResourceManager#rollback(DtxResourceManagerContext)} behavior.
*
* @param xe
* a {@link XAException} to throw, default behavior is assumed when <code>null</code>
* @param customAnswer
* a specific {@link Answer} to return on a perfectly valid call, or <code>null</code> for the
* default answer
* @return the configured builder
* @throws XAException
* if configuration fails
*/
public final DtxResourceManagerBuilder setRollback(final XAException xe, final Answer<Void> customAnswer)
throws XAException {
if (!setIdDone) {
throw new XAException("ID was not set before setting rollback behavior");
}
// default behavior for null
doThrow(new NullPointerException()).when(result).rollback(null);
if (xe != null) {
doThrow(xe).when(result).rollback(argThat(anyCtxMatcher));
setRollbackDone = true;
return this;
}
final WrapMatcher<DtxResourceManagerContext> rollbackMatcher = new WrapMatcher<DtxResourceManagerContext>(
new And(Arrays.asList(new Matcher[] { this.ctxIdMatcher,
new TxPositiveStateMatcher(STARTED, PREPARED) })));
final TxNegativeStateMatcher noRollbackStateMatcher = new TxNegativeStateMatcher(STARTED, PREPARED);
final Answer<Void> answer = (customAnswer != null) ? customAnswer : new DefaultRollbackAnswer();
doAnswer(answer).when(result).rollback(argThat(rollbackMatcher));
doThrow(new XAException(XAException.XAER_INVAL)).when(result).rollback(argThat(notFakeCtxMatcher));
doThrow(new XAException(XAException.XAER_PROTO)).when(result).rollback(argThat(noRollbackStateMatcher));
setRollbackDone = true;
return this;
}
/**
* Defines {@link DtxResourceManager#rollback(DtxResourceManagerContext)} behavior.
*
* @param e
* a {@link XAException} to throw, default behavior is assumed when <code>null</code>
* @param customAnswer
* a specific {@link Answer} to return on a perfectly valid call, or <code>null</code> for the
* default answer
* @return the configured builder
* @throws Exception
* if configuration fails
*/
public final DtxResourceManagerBuilder setPostSync(final Exception e, final Answer<Void> customAnswer)
throws Exception {
if (!setIdDone) {
throw new XAException("ID was not set before setting rollback behavior");
}
// set behavior for exception case
if (e != null) {
doThrow(e).when(result).processPostSync();
setPostSyncDone = true;
return this;
}
if (customAnswer != null) {
doAnswer(customAnswer).when(result).processPostSync();
}
else {
doNothing().when(result).processPostSync();
}
setPostSyncDone = true;
return this;
}
/**
* Builds the configured {@link DtxResourceManager} instance.
*
* @return a functional mock {@link DtxResourceManager}
* @throws XAException
* if construction fails
*/
public final DtxResourceManager build() throws XAException {
if (!setIdDone) {
setId(null);
}
if (!setStartDone) {
setStart(null, null);
}
if (!setPrepareDone) {
setPrepare(null, null);
}
if (!setCommitDone) {
setCommit(null, null);
}
if (!setRollbackDone) {
setRollback(null, null);
}
if (!setPostSyncDone) {
try {
setPostSync(null, null);
}
catch (final Exception e) {
LOGGER.warn("Exception while setting up mock", e);
}
}
return result;
}
}
/**
* {@link DtxResourceManagerContext} implementation to mimic a resource manager specific format.
*
*
*/
private static class FakeDtxResourceManagerContext extends DtxResourceManagerContext {
/**
* Constructor adding the resource manager's ID and initial state to the context.
*
* @param resourceManagerId
* the non-<code>null</code> {@link UUID} of the resource manager
* @param status
* the {@link DtxTaskStatus} to set, will default to {@link DtxTaskStatus#PENDING} if given
* <code>null</code>
* @throws NullPointerException
* if the resource manager ID parameter is <code>null</code>
*/
protected FakeDtxResourceManagerContext(final UUID resourceManagerId, final DtxTaskStatus status)
throws NullPointerException {
super(resourceManagerId, status);
}
}
}