Package org.axonframework.test.saga

Source Code of org.axonframework.test.saga.AnnotatedSagaTestFixture$ReturnResultFromStub

/*
* Copyright (c) 2010-2014. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.axonframework.test.saga;

import org.axonframework.commandhandling.gateway.DefaultCommandGateway;
import org.axonframework.commandhandling.gateway.GatewayProxyFactory;
import org.axonframework.domain.EventMessage;
import org.axonframework.domain.GenericDomainEventMessage;
import org.axonframework.domain.GenericEventMessage;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventhandling.SimpleEventBus;
import org.axonframework.saga.GenericSagaFactory;
import org.axonframework.saga.annotation.AbstractAnnotatedSaga;
import org.axonframework.saga.annotation.AnnotatedSagaManager;
import org.axonframework.saga.repository.inmemory.InMemorySagaRepository;
import org.axonframework.test.FixtureResourceParameterResolverFactory;
import org.axonframework.test.eventscheduler.StubEventScheduler;
import org.axonframework.test.utils.AutowiredResourceInjector;
import org.axonframework.test.utils.CallbackBehavior;
import org.axonframework.test.utils.RecordingCommandBus;
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.joda.time.Duration;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
* Fixture for testing Annotated Sagas based on events and time passing. This fixture allows resources to be configured
* for the sagas to use.
*
* @author Allard Buijze
* @since 1.1
*/
public class AnnotatedSagaTestFixture implements FixtureConfiguration, ContinuedGivenState {

    private final StubEventScheduler eventScheduler;
    private final AnnotatedSagaManager sagaManager;
    private final List<Object> registeredResources = new LinkedList<Object>();

    private Map<Object, AggregateEventPublisherImpl> aggregatePublishers =
            new HashMap<Object, AggregateEventPublisherImpl>();
    private FixtureExecutionResultImpl fixtureExecutionResult;
    private final RecordingCommandBus commandBus;

    /**
     * Creates an instance of the AnnotatedSagaTestFixture to test sagas of the given <code>sagaType</code>.
     *
     * @param sagaType The type of saga under test
     */
    @SuppressWarnings({"unchecked"})
    public AnnotatedSagaTestFixture(Class<? extends AbstractAnnotatedSaga> sagaType) {
        eventScheduler = new StubEventScheduler();
        GenericSagaFactory genericSagaFactory = new GenericSagaFactory();
        genericSagaFactory.setResourceInjector(new AutowiredResourceInjector(registeredResources));
        EventBus eventBus = new SimpleEventBus();
        InMemorySagaRepository sagaRepository = new InMemorySagaRepository();
        sagaManager = new AnnotatedSagaManager(sagaRepository, genericSagaFactory, sagaType);
        sagaManager.setSuppressExceptions(false);

        registeredResources.add(eventBus);
        commandBus = new RecordingCommandBus();
        registeredResources.add(commandBus);
        registeredResources.add(eventScheduler);
        registeredResources.add(new DefaultCommandGateway(commandBus));
        fixtureExecutionResult = new FixtureExecutionResultImpl(sagaRepository, eventScheduler, eventBus, commandBus,
                                                                sagaType);
        FixtureResourceParameterResolverFactory.clear();
        for (Object resource : registeredResources) {
            FixtureResourceParameterResolverFactory.registerResource(resource);
        }
    }

    @Override
    public FixtureExecutionResult whenTimeElapses(Duration elapsedTime) {
        try {
            fixtureExecutionResult.startRecording();
            for (EventMessage event : eventScheduler.advanceTime(elapsedTime)) {
                sagaManager.handle(event);
            }
        } finally {
            FixtureResourceParameterResolverFactory.clear();
        }
        return fixtureExecutionResult;
    }

    @Override
    public FixtureExecutionResult whenTimeAdvancesTo(DateTime newDateTime) {
        try {
            fixtureExecutionResult.startRecording();
            for (EventMessage event : eventScheduler.advanceTime(newDateTime)) {
                sagaManager.handle(event);
            }
        } finally {
            FixtureResourceParameterResolverFactory.clear();
        }

        return fixtureExecutionResult;
    }

    @Override
    public void registerResource(Object resource) {
        registeredResources.add(resource);
        FixtureResourceParameterResolverFactory.registerResource(resource);
    }

    @Override
    public void setCallbackBehavior(CallbackBehavior callbackBehavior) {
        commandBus.setCallbackBehavior(callbackBehavior);
    }

    @Override
    public GivenAggregateEventPublisher givenAggregate(Object aggregateIdentifier) {
        return getPublisherFor(aggregateIdentifier);
    }

    @Override
    public ContinuedGivenState givenAPublished(Object event) {
        sagaManager.handle(GenericEventMessage.asEventMessage(event));
        return this;
    }

    @Override
    public WhenState givenNoPriorActivity() {
        return this;
    }

    @Override
    public GivenAggregateEventPublisher andThenAggregate(Object aggregateIdentifier) {
        return givenAggregate(aggregateIdentifier);
    }

    @Override
    public ContinuedGivenState andThenTimeElapses(final Duration elapsedTime) {
        for (EventMessage event : eventScheduler.advanceTime(elapsedTime)) {
            sagaManager.handle(event);
        }
        return this;
    }

    @Override
    public ContinuedGivenState andThenTimeAdvancesTo(final DateTime newDateTime) {
        for (EventMessage event : eventScheduler.advanceTime(newDateTime)) {
            sagaManager.handle(event);
        }
        return this;
    }

    @Override
    public ContinuedGivenState andThenAPublished(Object event) {
        sagaManager.handle(GenericEventMessage.asEventMessage(event));
        return this;
    }

    @Override
    public WhenAggregateEventPublisher whenAggregate(Object aggregateIdentifier) {
        fixtureExecutionResult.startRecording();
        return getPublisherFor(aggregateIdentifier);
    }

    @Override
    public FixtureExecutionResult whenPublishingA(Object event) {
        try {
            fixtureExecutionResult.startRecording();
            sagaManager.handle(new GenericEventMessage<Object>(event));
        } finally {
            FixtureResourceParameterResolverFactory.clear();
        }

        return fixtureExecutionResult;
    }

    @Override
    public DateTime currentTime() {
        return eventScheduler.getCurrentDateTime();
    }

    @Override
    public <T> T registerCommandGateway(Class<T> gatewayInterface) {
        return registerCommandGateway(gatewayInterface, null);
    }

    @Override
    public <T> T registerCommandGateway(Class<T> gatewayInterface, final T stubImplementation) {
        GatewayProxyFactory factory = new StubAwareGatewayProxyFactory(stubImplementation,
                                                                       AnnotatedSagaTestFixture.this.commandBus);
        final T gateway = factory.createGateway(gatewayInterface);
        registerResource(gateway);
        return gateway;
    }

    private AggregateEventPublisherImpl getPublisherFor(Object aggregateIdentifier) {
        if (!aggregatePublishers.containsKey(aggregateIdentifier)) {
            aggregatePublishers.put(aggregateIdentifier, new AggregateEventPublisherImpl(aggregateIdentifier));
        }
        return aggregatePublishers.get(aggregateIdentifier);
    }

    private class AggregateEventPublisherImpl implements GivenAggregateEventPublisher, WhenAggregateEventPublisher {

        private final Object aggregateIdentifier;
        private int sequenceNumber = 0;

        public AggregateEventPublisherImpl(Object aggregateIdentifier) {
            this.aggregateIdentifier = aggregateIdentifier;
        }

        @Override
        public ContinuedGivenState published(Object... events) {
            publish(events);
            return AnnotatedSagaTestFixture.this;
        }

        @Override
        public FixtureExecutionResult publishes(Object event) {
            try {
                publish(event);
            } finally {
                FixtureResourceParameterResolverFactory.clear();
            }
            return fixtureExecutionResult;
        }

        private void publish(Object... events) {
            DateTimeUtils.setCurrentMillisFixed(currentTime().getMillis());

            try {
                for (Object event : events) {
                    if (event instanceof EventMessage) {
                        EventMessage eventMessage = (EventMessage) event;
                        sagaManager.handle(new GenericDomainEventMessage<Object>(eventMessage.getIdentifier(),
                                                                                 eventMessage.getTimestamp(),
                                                                                 aggregateIdentifier,
                                                                                 sequenceNumber++,
                                                                                 eventMessage.getPayload(),
                                                                                 eventMessage.getMetaData()));
                    } else {
                        sagaManager.handle(new GenericDomainEventMessage<Object>(aggregateIdentifier,
                                                                                 sequenceNumber++,
                                                                                 event));
                    }
                }
            } finally {
                DateTimeUtils.setCurrentMillisSystem();
            }
        }
    }

    /**
     * GatewayProxyFactory that is aware of a stub implementation that defines the behavior for the callback.
     */
    private static class StubAwareGatewayProxyFactory extends GatewayProxyFactory {

        private final Object stubImplementation;

        public StubAwareGatewayProxyFactory(Object stubImplementation, RecordingCommandBus commandBus) {
            super(commandBus);
            this.stubImplementation = stubImplementation;
        }

        @Override
        protected <R> InvocationHandler<R> wrapToWaitForResult(final InvocationHandler<Future<R>> delegate) {
            return new ReturnResultFromStub<R>(delegate, stubImplementation);
        }

        @Override
        protected <R> InvocationHandler<R> wrapToReturnWithFixedTimeout(
                InvocationHandler<Future<R>> delegate, long timeout, TimeUnit timeUnit) {
            return new ReturnResultFromStub<R>(delegate, stubImplementation);
        }

        @Override
        protected <R> InvocationHandler<R> wrapToReturnWithTimeoutInArguments(
                InvocationHandler<Future<R>> delegate, int timeoutIndex, int timeUnitIndex) {
            return new ReturnResultFromStub<R>(delegate, stubImplementation);
        }
    }

    /**
     * Invocation handler that uses a stub implementation (of not <code>null</code>) to define the value to return from
     * a handler invocation. If none is provided, the returned future is checked for a value. If that future is not
     * "done" (for example because no callback behavior was provided), it returns <code>null</code>.
     *
     * @param <R> The return type of the method invocation
     */
    private static class ReturnResultFromStub<R> implements GatewayProxyFactory.InvocationHandler<R> {

        private final GatewayProxyFactory.InvocationHandler<Future<R>> dispatcher;
        private final Object stubGateway;

        public ReturnResultFromStub(GatewayProxyFactory.InvocationHandler<Future<R>> dispatcher, Object stubGateway) {
            this.dispatcher = dispatcher;
            this.stubGateway = stubGateway;
        }

        @SuppressWarnings("unchecked")
        @Override
        public R invoke(Object proxy, Method invokedMethod, Object[] args) throws Throwable {
            Future<R> future = dispatcher.invoke(proxy, invokedMethod, args);
            if (stubGateway != null) {
                return (R) invokedMethod.invoke(stubGateway, args);
            }
            if (future.isDone()) {
                return future.get();
            }
            return null;
        }
    }
}
TOP

Related Classes of org.axonframework.test.saga.AnnotatedSagaTestFixture$ReturnResultFromStub

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.