Package org.axonframework.eventsourcing

Source Code of org.axonframework.eventsourcing.CachingEventSourcingRepositoryTest$InMemoryEventStore

/*
* 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.eventsourcing;

import net.sf.ehcache.CacheManager;
import org.axonframework.cache.Cache;
import org.axonframework.cache.EhCacheAdapter;
import org.axonframework.domain.DomainEventMessage;
import org.axonframework.domain.DomainEventStream;
import org.axonframework.domain.EventMessage;
import org.axonframework.domain.SimpleDomainEventStream;
import org.axonframework.domain.StubAggregate;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventstore.EventStore;
import org.axonframework.eventstore.PartialStreamSupport;
import org.axonframework.repository.AggregateNotFoundException;
import org.axonframework.unitofwork.CurrentUnitOfWork;
import org.axonframework.unitofwork.DefaultUnitOfWork;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

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

    private CachingEventSourcingRepository<StubAggregate> testSubject;
    private EventBus mockEventBus;
    private InMemoryEventStore mockEventStore;
    private Cache cache;
    private net.sf.ehcache.Cache ehCache;

    @Before
    public void setUp() {
        mockEventStore = spy(new InMemoryEventStore());
        testSubject = new CachingEventSourcingRepository<StubAggregate>(new StubAggregateFactory(), mockEventStore);
        mockEventBus = mock(EventBus.class);
        testSubject.setEventBus(mockEventBus);

        final CacheManager cacheManager = CacheManager.getInstance();
        ehCache = cacheManager.getCache("testCache");
        cache = spy(new EhCacheAdapter(ehCache));
        testSubject.setCache(cache);
    }

    @After
    public void tearDown() {
        while (CurrentUnitOfWork.isStarted()) {
            CurrentUnitOfWork.get().rollback();
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testAggregatesRetrievedFromCache() {
        DefaultUnitOfWork.startAndGet();
        final StubAggregate aggregate1 = new StubAggregate();
        aggregate1.doSomething();

        // ensure the cached aggregate has been committed before being cached.
        doThrow(new AssertionError("Aggregate should not have a null version when cached"))
                .when(cache).put(eq(aggregate1.getIdentifier()), argThat(new TypeSafeMatcher<StubAggregate>() {
            @Override
            public boolean matchesSafely(StubAggregate item) {
                return item.getVersion() == null;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("An aggregate with a non-null version");
            }
        }));

        testSubject.add(aggregate1);
        CurrentUnitOfWork.commit();

        DefaultUnitOfWork.startAndGet();
        StubAggregate reloadedAggregate1 = testSubject.load(aggregate1.getIdentifier(), null);
        assertSame(aggregate1, reloadedAggregate1);
        aggregate1.doSomething();
        aggregate1.doSomething();
        CurrentUnitOfWork.commit();

        DefaultUnitOfWork.startAndGet();
        DomainEventStream events = mockEventStore.readEvents("mock", aggregate1.getIdentifier());
        List<EventMessage> eventList = new ArrayList<EventMessage>();
        while (events.hasNext()) {
            eventList.add(events.next());
        }
        assertEquals(3, eventList.size());
        verify(mockEventBus).publish(isA(EventMessage.class));
        verify(mockEventBus).publish(isA(EventMessage.class), isA(EventMessage.class));
        verifyNoMoreInteractions(mockEventBus);
        ehCache.removeAll();

        reloadedAggregate1 = testSubject.load(aggregate1.getIdentifier(), null);

        assertNotSame(aggregate1, reloadedAggregate1);
        assertEquals(aggregate1.getVersion(),
                     reloadedAggregate1.getVersion());
    }

    @Test
    public void testLoadAggregateWithExpectedVersion_ConcurrentModificationsDetected() {
        final ConflictResolver conflictResolver = mock(ConflictResolver.class);
        testSubject.setConflictResolver(conflictResolver);
        StubAggregate aggregate1 = new StubAggregate();

        DefaultUnitOfWork.startAndGet();
        aggregate1.doSomething();
        aggregate1.doSomething();
        testSubject.add(aggregate1);
        CurrentUnitOfWork.commit();

        assertNotNull(((StubAggregate) cache.get(aggregate1.getIdentifier())).getVersion());

        DefaultUnitOfWork.startAndGet();
        StubAggregate loadedAggregate = testSubject.load(aggregate1.getIdentifier(), 0L);
        loadedAggregate.doSomething();
        CurrentUnitOfWork.commit();

        assertEquals(3, mockEventStore.readEventsAsList(aggregate1.getIdentifier()).size());

        verify(conflictResolver).resolveConflicts(argThat(new TypeSafeMatcher<List<DomainEventMessage>>() {
            @Override
            public boolean matchesSafely(List<DomainEventMessage> item) {
                return item.size() == 1 && item.get(0).getSequenceNumber() == 2;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("a list with 1 event, having sequence number 2");
            }
        }), argThat(new TypeSafeMatcher<List<DomainEventMessage>>() {
            @Override
            public boolean matchesSafely(List<DomainEventMessage> item) {
                return item.size() == 1 && item.get(0).getSequenceNumber() == 1;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("a list with 1 event, having sequence number 1");
            }
        }));
    }

    @Test
    public void testLoadAggregateFromCacheWithExpectedVersion_ConcurrentModificationsDetected() {
        // configure a repository that uses a PartialStreamSupport Event store
        mockEventStore = spy(new PartialReadingInMemoryEventStore());
        testSubject = new CachingEventSourcingRepository<StubAggregate>(new StubAggregateFactory(), mockEventStore);
        testSubject.setEventBus(mockEventBus);
        testSubject.setCache(cache);


        final ConflictResolver conflictResolver = mock(ConflictResolver.class);
        testSubject.setConflictResolver(conflictResolver);
        StubAggregate aggregate1 = new StubAggregate();

        DefaultUnitOfWork.startAndGet();
        aggregate1.doSomething();
        aggregate1.doSomething();
        testSubject.add(aggregate1);
        CurrentUnitOfWork.commit();

        assertNotNull(((StubAggregate) cache.get(aggregate1.getIdentifier())).getVersion());

        DefaultUnitOfWork.startAndGet();
        StubAggregate loadedAggregate = testSubject.load(aggregate1.getIdentifier(), 0L);
        loadedAggregate.doSomething();
        CurrentUnitOfWork.commit();

        assertEquals(3, mockEventStore.readEventsAsList(aggregate1.getIdentifier()).size());

        verify(mockEventStore, never()).readEvents(anyString(), anyObject());

        verify(conflictResolver).resolveConflicts(argThat(new TypeSafeMatcher<List<DomainEventMessage>>() {
            @Override
            public boolean matchesSafely(List<DomainEventMessage> item) {
                return item.size() == 1 && item.get(0).getSequenceNumber() == 2;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("a list with 1 event, having sequence number 2");
            }
        }), argThat(new TypeSafeMatcher<List<DomainEventMessage>>() {
            @Override
            public boolean matchesSafely(List<DomainEventMessage> item) {
                return item.size() == 1 && item.get(0).getSequenceNumber() == 1;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("a list with 1 event, having sequence number 1");
            }
        }));
    }

    @Test
    public void testLoadDeletedAggregate() {
        DefaultUnitOfWork.startAndGet();
        StubAggregate aggregate1 = new StubAggregate();
        testSubject.add(aggregate1);
        CurrentUnitOfWork.commit();

        Object identifier = aggregate1.getIdentifier();

        DefaultUnitOfWork.startAndGet();
        aggregate1.delete();
        CurrentUnitOfWork.commit();

        DefaultUnitOfWork.startAndGet();
        try {
            testSubject.load(identifier);
            fail("Expected AggregateDeletedException");
        } catch (AggregateDeletedException e) {
            assertTrue(e.getMessage().contains(identifier.toString()));
        } finally {
            CurrentUnitOfWork.commit();
        }
    }

    @Test
    public void testCacheClearedAfterRollbackOfAddedAggregate() {
        DefaultUnitOfWork.startAndGet();
        StubAggregate aggregate1 = new StubAggregate("id1");
        aggregate1.doSomething();
        testSubject.add(aggregate1);
        doThrow(new RuntimeException("Mock - simulate failure")).when(mockEventBus).publish(isA(EventMessage.class));
        try {
            CurrentUnitOfWork.get().commit();
        } catch (RuntimeException e) {
            // whatever
        }
        Object identifier = aggregate1.getIdentifier();
        assertEquals(null, cache.get(identifier));
    }

    private static class StubAggregateFactory extends AbstractAggregateFactory<StubAggregate> {

        @Override
        public StubAggregate doCreateAggregate(Object aggregateIdentifier, DomainEventMessage firstEvent) {
            return new StubAggregate(aggregateIdentifier);
        }

        @Override
        public String getTypeIdentifier() {
            return "mock";
        }

        @Override
        public Class<StubAggregate> getAggregateType() {
            return StubAggregate.class;
        }
    }

    private class PartialReadingInMemoryEventStore extends InMemoryEventStore implements PartialStreamSupport {

        @Override
        public DomainEventStream readEvents(String type, Object identifier, long firstSequenceNumber) {
            return readEvents(type, identifier, firstSequenceNumber, Long.MAX_VALUE);
        }

        @Override
        public DomainEventStream readEvents(String type, Object identifier, long firstSequenceNumber,
                                            long lastSequenceNumber) {
            if (!store.containsKey(identifier)) {
                throw new AggregateNotFoundException(identifier, "Aggregate not found");
            }
            final List<DomainEventMessage> events = store.get(identifier);
            List<DomainEventMessage> filteredEvents = new ArrayList<DomainEventMessage>();
            for (DomainEventMessage message : events) {
                if (message.getSequenceNumber() >= firstSequenceNumber
                        && message.getSequenceNumber() <= lastSequenceNumber) {
                    filteredEvents.add(message);
                }
            }
            return new SimpleDomainEventStream(filteredEvents);
        }
    }

    private class InMemoryEventStore implements EventStore {

        protected Map<Object, List<DomainEventMessage>> store = new HashMap<Object, List<DomainEventMessage>>();

        @Override
        public void appendEvents(String identifier, DomainEventStream events) {
            while (events.hasNext()) {
                DomainEventMessage next = events.next();
                if (!store.containsKey(next.getAggregateIdentifier())) {
                    store.put(next.getAggregateIdentifier(), new ArrayList<DomainEventMessage>());
                }
                List<DomainEventMessage> eventList = store.get(next.getAggregateIdentifier());
                eventList.add(next);
            }
        }

        @Override
        public DomainEventStream readEvents(String type, Object identifier) {
            if (!store.containsKey(identifier)) {
                throw new AggregateNotFoundException(identifier, "Aggregate not found");
            }
            return new SimpleDomainEventStream(store.get(identifier));
        }

        public List<DomainEventMessage> readEventsAsList(Object identifier) {
            return store.get(identifier);
        }
    }
}
TOP

Related Classes of org.axonframework.eventsourcing.CachingEventSourcingRepositoryTest$InMemoryEventStore

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.