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

Java tutorial

Introduction

Here is the source code for org.codice.ddf.configuration.migration.ImportMigrationExternalEntryImpl.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 com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang.Validate;
import org.codice.ddf.configuration.migration.util.AccessUtils;
import org.codice.ddf.migration.MigrationException;
import org.codice.ddf.migration.MigrationReport;
import org.codice.ddf.migration.MigrationWarning;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class provides an implementation of the {@link
 * org.codice.ddf.migration.ImportMigrationEntry} representing an external file that was exported.
 */
public class ImportMigrationExternalEntryImpl extends ImportMigrationEntryImpl {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImportMigrationExternalEntryImpl.class);

    private final String checksum;

    private final boolean softlink;

    ImportMigrationExternalEntryImpl(ImportMigrationContextImpl context, Map<String, Object> metadata) {
        super(context, JsonUtils.getStringFrom(metadata, MigrationEntryImpl.METADATA_NAME, true),
                !JsonUtils.getBooleanFrom(metadata, MigrationEntryImpl.METADATA_FOLDER, false));
        this.checksum = JsonUtils.getStringFrom(metadata, MigrationEntryImpl.METADATA_CHECKSUM, false);
        this.softlink = JsonUtils.getBooleanFrom(metadata, MigrationEntryImpl.METADATA_SOFTLINK, false);
    }

    @Override
    public long getLastModifiedTime() {
        return -1L;
    }

    @Override
    public boolean restore(boolean required) {
        if (restored == null) {
            // until proven otherwise in case the next line throws an exception
            super.restored = false;
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Verifying {}{}...", (required ? "required " : ""), toDebugString());
            }
            super.restored = verifyRealFileOrDirectory(required);
        }
        return restored;
    }

    @Override
    public boolean restore(boolean required, PathMatcher filter) {
        Validate.notNull(filter, "invalid null path filter");
        if (restored == null) {
            this.restored = false; // until proven otherwise
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Verifying {}{} with path filter...", (required ? "required " : ""), toDebugString());
            }
            if (filter.matches(getPath())) {
                super.restored = verifyRealFileOrDirectory(required);
            } else {
                super.restored = verifyRealFileOrDirectoryWhenFilterNotMatching(required);
            }
        }
        return restored;
    }

    @Override
    protected Optional<InputStream> getInputStream(boolean checkAccess) throws IOException {
        return Optional.empty();
    }

    @Override
    protected String toDebugString() {
        return String.format("external file [%s] from [%s]", getAbsolutePath(), getPath());
    }

    @VisibleForTesting
    String getChecksum() {
        return checksum;
    }

    @VisibleForTesting
    boolean isSoftlink() {
        return softlink;
    }

    private boolean verifyRealFileOrDirectoryWhenFilterNotMatching(boolean required) {
        // we have a filter that doesn't match this entry, so treat it as if the file was not
        // there to start with
        if (required) {
            getReport().record(new MigrationException(Messages.IMPORT_PATH_ERROR, getAbsolutePath(),
                    "it does not match filter"));
            return false;
        } // else - optional so no warnings/errors - just skip it and treat it as successful
        return true;
    }

    /**
     * Verifies the corresponding existing file or directory to see if it matches the original one
     * based on the exported info.
     *
     * @param required <code>true</code> if the file or directory was required to be exported; <code>
     * false</code> if it was optional
     * @return <code>false</code> if an error was detected during verification; <code>true</code>
     *     otherwise
     */
    @SuppressWarnings("squid:S3725" /* Files.isRegularFile() is used for consistency and to make sure that softlinks are not followed. not worried about performance here */)
    private boolean verifyRealFileOrDirectory(boolean required) {
        return AccessUtils.doPrivileged(() -> {
            final MigrationReport report = getReport();
            final Path apath = getAbsolutePath();
            final File file = getFile();

            if (!file.exists()) {
                if (required) {
                    report.record(new MigrationException(Messages.IMPORT_PATH_ERROR, apath, "does not exist"));
                    return false;
                }
                return true;
            }
            if (softlink) {
                if (!Files.isSymbolicLink(apath)) {
                    report.record(
                            new MigrationWarning(Messages.IMPORT_PATH_WARNING, apath, "is not a symbolic link"));
                    return false;
                }
            } else if (isFile() && !Files.isRegularFile(apath, LinkOption.NOFOLLOW_LINKS)) {
                report.record(new MigrationWarning(Messages.IMPORT_PATH_WARNING, apath, "is not a regular file"));
            } else if (isDirectory() && !Files.isDirectory(apath, LinkOption.NOFOLLOW_LINKS)) {
                report.record(
                        new MigrationWarning(Messages.IMPORT_PATH_WARNING, apath, "is not a regular directory"));
            }
            return verifyChecksum();
        });
    }

    private boolean verifyChecksum() {
        if (checksum != null) {
            final MigrationReport report = getReport();
            final Path apath = getAbsolutePath();

            try {
                final String rchecksum = getContext().getPathUtils().getChecksumFor(apath);

                if (!rchecksum.equals(checksum)) {
                    report.record(new MigrationWarning(Messages.IMPORT_CHECKSUM_MISMATCH_WARNING, apath));
                    return false;
                }
            } catch (IOException e) {
                LOGGER.info("failed to compute MD5 checksum for '" + getName() + "': ", e);
                report.record(new MigrationWarning(Messages.IMPORT_CHECKSUM_COMPUTE_WARNING, apath, e));
                return false;
            }
        }
        return true;
    }

    /**
     * The superclass implementation is sufficient for our needs.
     *
     * @param o the object to check
     * @return true if equal
     */
    @Override
    public boolean equals(Object o) {
        return super.equals(o);
    }

    /**
     * The superclass implementation is sufficient for our needs.
     *
     * @return the hashcode
     */
    @Override
    public int hashCode() {
        return super.hashCode();
    }
}