Package org.axonframework.saga.annotation

Source Code of org.axonframework.saga.annotation.AsyncAnnotatedSagaManagerTest$GenerateErrorOnHandlingEvent

/*
* Copyright (c) 2010-2012. 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.saga.annotation;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.domain.EventMessage;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventhandling.EventProcessingMonitor;
import org.axonframework.saga.Saga;
import org.axonframework.saga.repository.inmemory.InMemorySagaRepository;
import org.junit.*;
import org.mockito.internal.stubbing.answers.*;
import org.mockito.invocation.*;
import org.mockito.stubbing.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static org.axonframework.domain.GenericEventMessage.asEventMessage;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* @author Allard Buijze
*/
public class AsyncAnnotatedSagaManagerTest {

    private static final Logger logger = Logger.getLogger(AsyncSagaEventProcessor.class);
    private static Level oldLevel;

    private AsyncAnnotatedSagaManager testSubject;
    private EventBus eventBus;
    private AsyncAnnotatedSagaManagerTest.StubInMemorySagaRepository sagaRepository;
    private ExecutorService executorService;
    private EventProcessingMonitor mockMonitor;
    private List<EventMessage> ackedMessages;
    private List<EventMessage> failedMessages;

    @BeforeClass
    public static void disableLogging() {
        oldLevel = logger.getLevel();
        logger.setLevel(Level.OFF);
    }

    @AfterClass
    public static void enableLogging() {
        logger.setLevel(oldLevel);
    }

    @SuppressWarnings("unchecked")
    @Before
    public void setUp() {
        eventBus = mock(EventBus.class);
        testSubject = new AsyncAnnotatedSagaManager(StubAsyncSaga.class);
        sagaRepository = new StubInMemorySagaRepository();
        testSubject.setSagaRepository(sagaRepository);
        executorService = Executors.newCachedThreadPool();
        testSubject.setExecutor(executorService);
        testSubject.setProcessorCount(3);
        testSubject.setBufferSize(64);

        mockMonitor = mock(EventProcessingMonitor.class);
        ackedMessages = Collections.synchronizedList(new ArrayList());
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                ackedMessages.addAll(((List) invocationOnMock.getArguments()[0]));
                return null;
            }
        }).when(mockMonitor).onEventProcessingCompleted(isA(List.class));
    }

    @After
    public void tearDown() {
        testSubject.stop();
    }

    @Test
    public void testSingleSagaLifeCycle() throws InterruptedException {
        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());
        for (EventMessage message : createSimpleLifeCycle("one", "two", true)) {
            testSubject.handle(message);
        }
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 second", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());
    }

    @Test(timeout = 10000, expected = AxonConfigurationException.class)
    public void testThreadPoolExecutorHasTooSmallCorePoolSize() throws InterruptedException {
        testSubject.setStartTimeout(100);
        executorService = new ThreadPoolExecutor(1, 3, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(25));
        testSubject.setExecutor(executorService);

        testSubject.start();
    }

    @Test
    public void testSingleSagaLifeCycle_FailedPersistence() throws InterruptedException {
        final StubInMemorySagaRepository spy = spy(sagaRepository);
        testSubject.setSagaRepository(spy);
        Exception failure = new RuntimeException("Mockexception");
        doThrow(failure).doAnswer(new CallsRealMethods()).when(spy).add(isA(Saga.class));
        doThrow(failure).doAnswer(new CallsRealMethods()).when(spy).commit(isA(Saga.class));
        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());
        for (EventMessage message : createSimpleLifeCycle("one", "two", true)) {
            testSubject.handle(message);
        }
        Thread.sleep(100);
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 10 seconds", executorService.awaitTermination(10, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());
    }

   @Test
    public void testSingleSagaLifeCycle_NonTransientFailure() throws InterruptedException {
        final StubInMemorySagaRepository spy = spy(sagaRepository);
        testSubject.setSagaRepository(spy);
        Exception failure = new RuntimeException("Mockexception",
                                                 new AxonConfigurationException("Faking a wrong config"));
        doThrow(failure).when(spy).add(isA(Saga.class));
        doThrow(failure).when(spy).commit(isA(Saga.class));
        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());
        for (EventMessage message : createSimpleLifeCycle("one", "two", true)) {
            testSubject.handle(message);
        }
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 seconds", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 0, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());
    }

    @Test
    public void testSingleSagaLifeCycle_FinalAttemptOnClose() throws InterruptedException {
        testSubject.subscribeEventProcessingMonitor(mockMonitor);
        final StubInMemorySagaRepository spy = spy(sagaRepository);
        testSubject.setSagaRepository(spy);
        Exception failure = new RuntimeException("Mock Exception");
        doThrow(failure).when(spy).commit(isA(Saga.class));
        doThrow(failure).when(spy).add(isA(Saga.class));
        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());


        final List<EventMessage> simpleLifeCycle = createSimpleLifeCycle("one", "two", true);
        for (EventMessage message : simpleLifeCycle) {
            testSubject.handle(message);
        }
        Thread.sleep(500);
        // to make sure at least one failed call was made...
        verify(spy, atLeastOnce()).add(isA(Saga.class));
        doCallRealMethod().when(spy).commit(isA(Saga.class));
        doCallRealMethod().when(spy).add(isA(Saga.class));
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 seconds", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());

        assertEquals(simpleLifeCycle, ackedMessages);
    }

    @Test
    public void testMultipleDisconnectedSagaLifeCycle() throws InterruptedException {
        testSubject.subscribeEventProcessingMonitor(mockMonitor);
        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());
        int expectedMessageCount = 0;
        for (int t = 0; t < 1000; t++) {
            final List<EventMessage> lifeCycle = createSimpleLifeCycle("association-" + t, "newAssociation-" + t,
                                                                       (t & 1) == 0);
            expectedMessageCount += lifeCycle.size();
            for (EventMessage message : lifeCycle) {
                testSubject.handle(message);
            }
        }
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 second", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1000, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());

        assertEquals(expectedMessageCount, ackedMessages.size());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testNullAssociationIgnoresEvent() throws InterruptedException {
        testSubject = new AsyncAnnotatedSagaManager(eventBus, StubAsyncSaga.class, AnotherStubAsyncSaga.class,
                                                    ThirdStubAsyncSaga.class);
        testSubject.setSagaRepository(sagaRepository);
        executorService = Executors.newCachedThreadPool();
        testSubject.setExecutor(executorService);
        testSubject.setProcessorCount(3);
        testSubject.setBufferSize(64);

        testSubject.start();

        testSubject.handle(asEventMessage(new ForceCreateNewEvent(null)));
        testSubject.handle(asEventMessage(new OptionallyCreateNewEvent(null)));

        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 second", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 0, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testExceptionsFromHandlerAreIgnored() throws InterruptedException {
        testSubject = new AsyncAnnotatedSagaManager(eventBus, StubAsyncSaga.class);
        testSubject.setSagaRepository(sagaRepository);
        executorService = Executors.newCachedThreadPool();
        testSubject.setExecutor(executorService);
        testSubject.setProcessorCount(3);
        testSubject.setBufferSize(64);

        testSubject.start();

        testSubject.handle(asEventMessage(new ForceCreateNewEvent("test")));
        testSubject.handle(asEventMessage(new GenerateErrorOnHandlingEvent("test")));

        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 second", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 1, sagaRepository.getLiveSagas());
        logger.setLevel(oldLevel);
    }

    @Test
    public void testMultipleDisconnectedSagaLifeCycle_WithOptionalStart() throws InterruptedException {
        testSubject = new AsyncAnnotatedSagaManager(eventBus, StubAsyncSaga.class, AnotherStubAsyncSaga.class,
                                                    ThirdStubAsyncSaga.class);
        testSubject.setSagaRepository(sagaRepository);
        executorService = Executors.newCachedThreadPool();
        testSubject.setExecutor(executorService);
        testSubject.setProcessorCount(3);
        testSubject.setBufferSize(64);

        testSubject.start();
        assertEquals(0, sagaRepository.getKnownSagas());
        for (int t = 0; t < 500; t++) {
            for (EventMessage message : createSimpleLifeCycle("association-" + t, "newAssociation-" + t, false)) {
                testSubject.handle(message);
            }
        }
        testSubject.stop();
        executorService.shutdown();
        assertTrue("Service refused to stop in 1 second", executorService.awaitTermination(1, TimeUnit.SECONDS));
        assertEquals("Incorrect known saga count", 1500, sagaRepository.getKnownSagas());
        assertEquals("Incorrect live saga count", 0, sagaRepository.getLiveSagas());
    }

    private List<EventMessage> createSimpleLifeCycle(String firstAssociation, String newAssociation,
                                                     boolean includeForceStart) {
        List<EventMessage> publicationList = new ArrayList<EventMessage>();
        if (includeForceStart) {
            publicationList.add(asEventMessage(new ForceCreateNewEvent(firstAssociation)));
        }
        publicationList.add(asEventMessage(new OptionallyCreateNewEvent(firstAssociation)));
        publicationList.add(asEventMessage(new UpdateEvent(firstAssociation)));
        publicationList.add(asEventMessage(new AddAssociationEvent(firstAssociation, newAssociation)));
        publicationList.add(asEventMessage(new OptionallyCreateNewEvent(newAssociation)));
        publicationList.add(asEventMessage(new DeleteEvent(newAssociation)));
        // this exception should never be thrown, as the previous event ends the saga
        return publicationList;
    }

    public static class ThirdStubAsyncSaga extends AnotherStubAsyncSaga {

    }

    public static class AnotherStubAsyncSaga extends StubAsyncSaga {

    }

    public static class StubAsyncSaga extends AbstractAnnotatedSaga {

        @StartSaga(forceNew = false)
        @SagaEventHandler(associationProperty = "association")
        public void handleOptionallyCreateNew(OptionallyCreateNewEvent event) {
        }

        @StartSaga(forceNew = true)
        @SagaEventHandler(associationProperty = "association")
        public void handleOptionallyCreateNew(ForceCreateNewEvent event) {
        }

        @SagaEventHandler(associationProperty = "association")
        public void handleAddAssociation(AddAssociationEvent event) {
            associateWith("association", event.getNewAssociation());
        }

        @SagaEventHandler(associationProperty = "association")
        public void handleUpdate(UpdateEvent event) {
        }

        @SagaEventHandler(associationProperty = "association")
        public void createError(GenerateErrorOnHandlingEvent event) {
            throw new RuntimeException("Stub");
        }

        @EndSaga
        @SagaEventHandler(associationProperty = "association")
        public void handleDelete(DeleteEvent event) {
//            deleteInvocations++;
        }
    }

    private static class OptionallyCreateNewEvent extends AbstractSagaTestEvent {

        private OptionallyCreateNewEvent(String association) {
            super(association);
        }
    }

    private static class ForceCreateNewEvent extends AbstractSagaTestEvent {

        private ForceCreateNewEvent(String association) {
            super(association);
        }
    }

    private static class UpdateEvent extends AbstractSagaTestEvent {

        private UpdateEvent(String association) {
            super(association);
        }
    }

    private static class DeleteEvent extends AbstractSagaTestEvent {

        private DeleteEvent(String association) {
            super(association);
        }
    }

    private static class GenerateErrorOnHandlingEvent extends AbstractSagaTestEvent {

        private GenerateErrorOnHandlingEvent(String association) {
            super(association);
        }
    }

    private static class AddAssociationEvent extends AbstractSagaTestEvent {

        private final String newAssociation;

        private AddAssociationEvent(String association, String newAssociation) {
            super(association);
            this.newAssociation = newAssociation;
        }

        public String getNewAssociation() {
            return newAssociation;
        }
    }

    private static class AbstractSagaTestEvent {

        private final String association;

        private AbstractSagaTestEvent(String association) {
            this.association = association;
        }

        public String getAssociation() {
            return association;
        }
    }

    public static class StubInMemorySagaRepository extends InMemorySagaRepository {

        private Set<String> knownSagas = new ConcurrentSkipListSet<String>();
        private Set<String> liveSagas = new ConcurrentSkipListSet<String>();

        @Override
        public void commit(Saga saga) {
            assertTrue(knownSagas.contains(saga.getSagaIdentifier()));
            if (!saga.isActive()) {
                liveSagas.remove(saga.getSagaIdentifier());
            }
            super.commit(saga);
        }

        @Override
        public void add(Saga saga) {
            knownSagas.add(saga.getSagaIdentifier());
            liveSagas.add(saga.getSagaIdentifier());
            super.add(saga);
        }

        public int getKnownSagas() {
            return knownSagas.size();
        }

        public int getLiveSagas() {
            return liveSagas.size();
        }
    }
}
TOP

Related Classes of org.axonframework.saga.annotation.AsyncAnnotatedSagaManagerTest$GenerateErrorOnHandlingEvent

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.