Package org.axonframework.eventstore.fs

Source Code of org.axonframework.eventstore.fs.FileSystemEventStore

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

import org.axonframework.common.Assert;
import org.axonframework.common.io.IOUtils;
import org.axonframework.domain.DomainEventMessage;
import org.axonframework.domain.DomainEventStream;
import org.axonframework.eventstore.EventStore;
import org.axonframework.eventstore.EventStoreException;
import org.axonframework.eventstore.EventStreamNotFoundException;
import org.axonframework.eventstore.SnapshotEventStore;
import org.axonframework.repository.ConflictingModificationException;
import org.axonframework.serializer.Serializer;
import org.axonframework.serializer.xml.XStreamSerializer;
import org.axonframework.upcasting.SimpleUpcasterChain;
import org.axonframework.upcasting.UpcasterAware;
import org.axonframework.upcasting.UpcasterChain;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;

/**
* Implementation of the {@link org.axonframework.eventstore.EventStore} that serializes objects (by default using
* XStream) and
* writes them to files to disk. Each aggregate is represented by a single file.
* <p/>
* Use {@link EventFileResolver} to specify the directory where event files should be stored and written to.
* <p/>
* Note that the resource supplied must point to a folder and should contain a trailing slash. See {@link
* org.springframework.core.io.FileSystemResource#FileSystemResource(String)}.
*
* @author Allard Buijze
* @author Frank Versnel
* @since 0.5
*/
public class FileSystemEventStore implements EventStore, SnapshotEventStore, UpcasterAware {

    private final Serializer eventSerializer;
    private final EventFileResolver eventFileResolver;
    private UpcasterChain upcasterChain = SimpleUpcasterChain.EMPTY;

    /**
     * Basic initialization of the event store. The actual serialization and deserialization is delegated to a {@link
     * org.axonframework.serializer.xml.XStreamSerializer}
     *
     * @param eventFileResolver The EventFileResolver providing access to event files
     */
    public FileSystemEventStore(EventFileResolver eventFileResolver) {
        this(new XStreamSerializer(), eventFileResolver);
    }

    /**
     * Initialize the FileSystemEventStore using the given <code>serializer</code>. The serializer must be capable of
     * serializing the payload and meta data of Event Messages.
     * <p/>
     * <em>Note: the SerializedType of Message Meta Data is not stored. Upon retrieval, it is set to the default value
     * (name = "org.axonframework.domain.MetaData", revision = null). See {@link org.axonframework.serializer.SerializedMetaData#isSerializedMetaData(org.axonframework.serializer.SerializedObject)}</em>
     *
     * @param serializer        The serializer capable of serializing (at least) DomainEvents
     * @param eventFileResolver The EventFileResolver providing access to event files
     */
    public FileSystemEventStore(Serializer serializer, EventFileResolver eventFileResolver) {
        Assert.notNull(serializer, "serializer may not be null");
        Assert.notNull(eventFileResolver, "eventFileResolver may not be null");
        this.eventSerializer = serializer;
        this.eventFileResolver = eventFileResolver;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * This implementation writes events to an event log on the file system. It uses a directory per type of aggregate,
     * containing 1 file per aggregate.
     */
    @Override
    public void appendEvents(String type, DomainEventStream eventsToStore) {
        if (!eventsToStore.hasNext()) {
            return;
        }

        OutputStream out = null;
        try {
            DomainEventMessage next = eventsToStore.next();
            if (next.getSequenceNumber() == 0
                    && eventFileResolver.eventFileExists(type, next.getAggregateIdentifier())) {
                throw new ConflictingModificationException("Could not create event stream for aggregate, such stream "
                                                                   + "already exists, type=" + type + ", id="
                                                                   + next.getAggregateIdentifier());
            }
            out = eventFileResolver.openEventFileForWriting(type, next.getAggregateIdentifier());
            FileSystemEventMessageWriter eventMessageWriter =
                    new FileSystemEventMessageWriter(new DataOutputStream(out), eventSerializer);
            do {
                eventMessageWriter.writeEventMessage(next);
                if (eventsToStore.hasNext()) {
                    next = eventsToStore.next();
                } else {
                    next = null;
                }
            } while (next != null);
        } catch (IOException e) {
            throw new EventStoreException("Unable to store given entity due to an IOException", e);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    @Override
    public DomainEventStream readEvents(String type, Object aggregateIdentifier) {
        try {
            if (!eventFileResolver.eventFileExists(type, aggregateIdentifier)) {
                throw new EventStreamNotFoundException(type, aggregateIdentifier);
            }

            InputStream eventFileInputStream = eventFileResolver.openEventFileForReading(type, aggregateIdentifier);
            DomainEventMessage snapshotEvent;
            try {
                snapshotEvent = readSnapshotEvent(type, aggregateIdentifier, eventFileInputStream);
                // trigger deserialization
                snapshotEvent.getPayload();
            } catch (Exception e) {
                // error while reading snapshot. We ignore it
                snapshotEvent = null;
                // we need to reset the stream, as it points to the event after the snapshot
                IOUtils.closeQuietly(eventFileInputStream);
                eventFileInputStream = eventFileResolver.openEventFileForReading(type, aggregateIdentifier);
            }

            InputStream is = eventFileInputStream;
            if (snapshotEvent != null) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                FileSystemEventMessageWriter snapshotEventMessageWriter =
                        new FileSystemEventMessageWriter(new DataOutputStream(baos), eventSerializer);
                snapshotEventMessageWriter.writeEventMessage(snapshotEvent);
                is = new SequenceInputStream(new ByteArrayInputStream(baos.toByteArray()), eventFileInputStream);
            }

            return new FileSystemBufferedReaderDomainEventStream(is, eventSerializer, upcasterChain);
        } catch (IOException e) {
            throw new EventStoreException(
                    String.format("An error occurred while trying to open the event file "
                                          + "for aggregate type [%s] with identifier [%s]",
                                  type, aggregateIdentifier), e);
        }
    }


    /**
     * {@inheritDoc}
     *
     * @throws EventStoreException when an error occurs while reading or writing to the event logs.
     */
    @Override
    public void appendSnapshotEvent(String type, DomainEventMessage snapshotEvent) throws EventStoreException {
        InputStream eventFile = null;
        try {
            eventFile = eventFileResolver.openEventFileForReading(type, snapshotEvent.getAggregateIdentifier());
            OutputStream snapshotEventFile =
                    eventFileResolver.openSnapshotFileForWriting(type, snapshotEvent.getAggregateIdentifier());
            FileSystemSnapshotEventWriter snapshotEventWriter =
                    new FileSystemSnapshotEventWriter(eventFile, snapshotEventFile, eventSerializer);

            snapshotEventWriter.writeSnapshotEvent(snapshotEvent);
        } catch (IOException e) {
            throw new EventStoreException("Error writing a snapshot event due to an IO exception", e);
        } finally {
            IOUtils.closeQuietly(eventFile);
        }
    }

    private DomainEventMessage readSnapshotEvent(String type, Object identifier, InputStream eventFileInputStream)
            throws IOException {
        DomainEventMessage snapshotEvent = null;
        if (eventFileResolver.snapshotFileExists(type, identifier)) {
            InputStream snapshotEventFile = eventFileResolver.openSnapshotFileForReading(type, identifier);
            FileSystemSnapshotEventReader fileSystemSnapshotEventReader =
                    new FileSystemSnapshotEventReader(eventFileInputStream, snapshotEventFile, eventSerializer);
            snapshotEvent = fileSystemSnapshotEventReader.readSnapshotEvent(type, identifier);
        }
        return snapshotEvent;
    }

    @Override
    public void setUpcasterChain(UpcasterChain upcasterChain) {
        this.upcasterChain = upcasterChain;
    }
}
TOP

Related Classes of org.axonframework.eventstore.fs.FileSystemEventStore

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.