org.codice.ddf.configuration.migration.MigrationReportImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.configuration.migration.MigrationReportImpl.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.configuration.migration;

import java.io.IOException;
import java.time.Instant;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.commons.lang.Validate;
import org.codice.ddf.migration.MigrationException;
import org.codice.ddf.migration.MigrationInformation;
import org.codice.ddf.migration.MigrationMessage;
import org.codice.ddf.migration.MigrationOperation;
import org.codice.ddf.migration.MigrationReport;
import org.codice.ddf.migration.MigrationWarning;
import org.codice.ddf.util.function.ThrowingRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The migration report provides ways to aggregate warnings and errors related to migration
 * operations.
 */
public class MigrationReportImpl implements MigrationReport {
    private static final Logger LOGGER = LoggerFactory.getLogger(MigrationReportImpl.class);

    private final Optional<Consumer<MigrationMessage>> consumer;

    private final Set<MigrationMessage> messages;

    private final Deque<Consumer<MigrationReport>> codes = new LinkedList<>();

    private final MigrationOperation operation;

    private final Instant start;

    private int numInfos = 0;

    private int numWarnings = 0;

    private int numErrors = 0;

    private Optional<Instant> end = Optional.empty();

    /**
     * Creates a new migration report.
     *
     * @param operation the type of migration operation for this report
     * @param consumer an optional consumer to call whenever a new migration message is recorded
     *     during the operation
     * @throws IllegalArgumentException if <code>operation</code> is <code>null</code>
     */
    public MigrationReportImpl(MigrationOperation operation, Optional<Consumer<MigrationMessage>> consumer) {
        Validate.notNull(operation, "invalid null operation");
        this.operation = operation;
        this.consumer = consumer;
        this.start = Instant.now();
        this.messages = new LinkedHashSet<>(); // LinkedHashSet to prevent duplicate and maintain order
    }

    @Override
    public MigrationOperation getOperation() {
        return operation;
    }

    @Override
    public Instant getStartTime() {
        return start;
    }

    @Override
    public Optional<Instant> getEndTime() {
        return end;
    }

    @Override
    public MigrationReportImpl record(MigrationMessage msg) {
        Validate.notNull(msg, "invalid null message");
        final String level;

        if (msg instanceof MigrationException) {
            this.numErrors++;
            level = "error";
        } else if (msg instanceof MigrationWarning) {
            this.numWarnings++;
            level = "warning";
        } else if (msg instanceof MigrationInformation) {
            this.numInfos++;
            level = "info";
        } else {
            level = "message";
        }
        LOGGER.debug("migration {}: {}", level, msg, msg); // 2nd 'msg' is for stack trace
        messages.add(msg);
        consumer.ifPresent(c -> c.accept(msg));
        return this;
    }

    @Override
    public MigrationReport doAfterCompletion(Consumer<MigrationReport> code) {
        Validate.notNull(code, "invalid null code");
        codes.add(code);
        return this;
    }

    @Override
    public Stream<MigrationMessage> messages() {
        return messages.stream();
    }

    @Override
    public boolean wasSuccessful() {
        runCodes();
        return (numErrors == 0);
    }

    @Override
    public boolean wasSuccessful(Runnable code) {
        final int nerrs = this.numErrors;

        if (code != null) {
            code.run();
        }
        return (this.numErrors == nerrs);
    }

    @Override
    public boolean wasIOSuccessful(ThrowingRunnable<IOException> code) throws IOException {
        final int nerrs = this.numErrors;

        if (code != null) {
            code.run();
        }
        return (this.numErrors == nerrs);
    }

    @Override
    public boolean hasInfos() {
        runCodes();
        return (numInfos > 0);
    }

    @Override
    public boolean hasWarnings() {
        runCodes();
        return (numWarnings > 0);
    }

    @Override
    public boolean hasErrors() {
        runCodes();
        return (numErrors > 0);
    }

    @Override
    public void verifyCompletion() {
        runCodes();
        if (numErrors == 0) {
            return;
        }
        final Iterator<MigrationException> i = errors().iterator(); // preserve order
        final MigrationException e = i.next(); // will always be there since numErrors is not 0

        i.forEachRemaining(e::addSuppressed);
        throw e;
    }

    @SuppressWarnings("PMD.DefaultPackage" /* designed to be called from ConfigurationMigrationManager within this package */)
    MigrationReportImpl end() {
        runCodes();
        this.end = Optional.of(Instant.now());
        return this;
    }

    private void runCodes() {
        while (!codes.isEmpty()) {
            codes.removeFirst().accept(this);
        }
    }
}