All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eventsourcing.migrations.LayoutMigration Maven / Gradle / Ivy

There is a newer version: 0.4.6
Show newest version
/**
 * Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.eventsourcing.migrations;

import com.eventsourcing.*;
import com.eventsourcing.events.EventCausalityEstablished;
import com.eventsourcing.layout.Layout;
import com.eventsourcing.migrations.events.EntityLayoutIntroduced;
import com.eventsourcing.migrations.events.EntityLayoutReplaced;
import com.googlecode.cqengine.resultset.ResultSet;
import lombok.SneakyThrows;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static com.eventsourcing.index.EntityQueryFactory.all;
import static com.eventsourcing.index.EntityQueryFactory.equal;

public class LayoutMigration {

    private Layout oldLayout;
    private Layout newLayout;
    private Function transformation;

    private final Class oldClass;
    private final Class newClass;

    private final boolean includeLayout;

    @SneakyThrows
    public LayoutMigration(Class oldClass, Class newClass, Function transformation, boolean includeLayout) {
        this.includeLayout = includeLayout;
        this.oldLayout = Layout.forClass(oldClass);
        this.oldClass = oldClass;
        this.newLayout = Layout.forClass(newClass);
        this.newClass = newClass;
        this.transformation = transformation;

    }

    public LayoutMigration(Class oldClass, Class newClass, Function transformation) {
        this(oldClass, newClass, transformation, true);
    }


    public Stream events(Repository repository, LockProvider lockProvider) throws Exception {
        Lock lock = lockProvider.lock(oldClass.getName());
        Stream acc = Stream.empty();
        Optional oldLayoutIntroduciton = layoutIntroduction(repository, oldLayout);
        if (!oldLayoutIntroduciton.isPresent()) {
            acc = Stream.concat(acc, Stream.of(makeLayoutIntroduction(oldLayout)));
        }
        Optional newLayoutIntroduction = layoutIntroduction(repository, newLayout);
        UUID newLayoutIntroductionUUID;
        if (!newLayoutIntroduction.isPresent()) {
            EntityLayoutIntroduced introduction = makeLayoutIntroduction(newLayout);
            acc = Stream.concat(acc, Stream.of(introduction));
            newLayoutIntroductionUUID = introduction.uuid();
        } else {
            newLayoutIntroductionUUID = newLayoutIntroduction.get().uuid();
        }
        EntityLayoutReplaced replacement = EntityLayoutReplaced.builder()
                                                               .fingerprint(oldLayout.getHash())
                                                               .replacement(newLayoutIntroductionUUID)
                                                               .build();
        acc = Stream.concat(acc, Stream.of(replacement));
        ResultSet> resultSet = repository.query(oldClass, all(oldClass));
        Iterator> iterator = resultSet.iterator();
        Stream stream = StreamSupport
                .stream(Spliterators.spliterator(iterator, resultSet.size(), Spliterator.IMMUTABLE), false)
                .flatMap(h -> {
                    B transformed = transformation.apply(h.get());
                    try (ResultSet> causality = repository
                            .query(EventCausalityEstablished.class, equal(EventCausalityEstablished.EVENT, h.uuid()))) {
                        Stream> causalityStream = StreamSupport
                                .stream(Spliterators.spliterator(causality.iterator(), causality.size(),
                                                                 Spliterator.IMMUTABLE), false);
                        Function, Event> entityHandleFunction = handle ->
                                EventCausalityEstablished.builder()
                                        .event(transformed.uuid())
                                        .command(handle.get().command()).build();
                        return Stream.concat(Stream.of(transformed), causalityStream.map(entityHandleFunction));
                    }
                });
        stream.onClose(resultSet::close);
        acc = Stream.concat(acc, stream);
        lock.unlock();
        return acc;
    }

    private Optional layoutIntroduction(Repository repository, Layout layout) {
        try (ResultSet> resultSet = repository
                .query(EntityLayoutIntroduced.class,
                       equal(EntityLayoutIntroduced.FINGERPRINT, layout.getHash()))) {
            if (resultSet.isEmpty()) {
                return Optional.empty();
            } else {
                return Optional.of(resultSet.uniqueResult().get());
            }
        }
    }

    private EntityLayoutIntroduced makeLayoutIntroduction(Layout layout) {
        EntityLayoutIntroduced.EntityLayoutIntroducedBuilder builder = EntityLayoutIntroduced.builder();
        builder.fingerprint(layout.getHash());
        if (includeLayout) {
            builder.layout(Optional.of(layout));
        }
        return builder.build();
    }
}