com.archivas.clienttools.arcmover.cli.ArcCopy.java Source code

Java tutorial

Introduction

Here is the source code for com.archivas.clienttools.arcmover.cli.ArcCopy.java

Source

// Copyright 2007 Hitachi Data Systems
// All Rights Reserved.
//
// 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 com.archivas.clienttools.arcmover.cli;

import com.archivas.clienttools.arcutils.api.JobException;
import com.archivas.clienttools.arcutils.api.JobId;
import com.archivas.clienttools.arcutils.api.ManagedJobStats;
import com.archivas.clienttools.arcutils.api.jobs.CopyJob;
import com.archivas.clienttools.arcutils.api.jobs.ManagedJob;
import com.archivas.clienttools.arcutils.config.HCPMoverProperties;
import com.archivas.clienttools.arcutils.model.ACLMetadata;
import com.archivas.clienttools.arcutils.model.Owner;
import com.archivas.clienttools.arcutils.profile.AbstractProfileBase;
import com.archivas.clienttools.arcutils.profile.ProfileManager;
import com.archivas.clienttools.arcutils.profile.ProfileType;
import com.archivas.clienttools.arcutils.model.CustomMetadata;
import com.archivas.clienttools.arcutils.model.FileMetadata;
import com.archivas.clienttools.arcutils.model.LoadSchedule;
import com.archivas.clienttools.arcutils.model.Retention;
import com.archivas.clienttools.arcutils.utils.FileListParser;
import com.archivas.clienttools.arcutils.utils.FileListParserException;
import com.archivas.clienttools.arcutils.utils.database.DatabaseException;
import org.apache.commons.cli.*;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

@SuppressWarnings({ "UseOfSystemOutOrSystemErr" })
public class ArcCopy extends ManagedCLIJob {
    public static final String PACKAGE_NAME = ArcCopy.class.getPackage().getName();
    public static final String CLASS_FULL_NAME = ArcCopy.class.getName();
    public static final String CLASS_NAME = CLASS_FULL_NAME.substring(PACKAGE_NAME.length() + 1);
    public static Logger LOG = Logger.getLogger(CLASS_FULL_NAME);

    private static String DESTINATION_PROFILE_OPTION = "destination-profile";
    private static String SOURCE_PROFILE_OPTION = "source-profile";
    private static String DESTINATION_PATH_OPTION = "destination-path";
    private static String SOURCE_PATH_OPTION = "source-path";
    private static String HOLD_OPTION = "hold";
    private static String INDEX_OPTION = "index";
    private static String SHRED_OPTION = "shred";
    private static String RETENTION_OPTION = "retention";
    private static String UID_OPTION = "uid";
    private static String GID_OPTION = "gid";
    private static String FILE_PERMS_OPTION = "file-permissions";
    private static String DIR_PERMS_OPTION = "dir-permissions";
    private static String CUSTOM_METADATA_OPTION = "custom-metadata";
    private static String ACL_OPTION = "acl";
    private static String OWNER_OPTION = "owner";
    private static String DOMAIN_OPTION = "domain";

    /**
     * Command Line Options *
     */
    private static Options cliOptions;
    private static int cliOptionsCount = 0;

    static {
        addNextCLIOption(HELP_OPTION);
        addNextCLIOption(DESTINATION_PROFILE_OPTION);
        addNextCLIOption(SOURCE_PROFILE_OPTION);
        addNextCLIOption(DESTINATION_PATH_OPTION);
        addNextCLIOption(SOURCE_PATH_OPTION);
        addNextCLIOption(JOB_NAME);
        addNextCLIOption(HOLD_OPTION);
        addNextCLIOption(INDEX_OPTION);
        addNextCLIOption(SHRED_OPTION);
        addNextCLIOption(RETENTION_OPTION);
        addNextCLIOption(UID_OPTION);
        addNextCLIOption(GID_OPTION);
        addNextCLIOption(FILE_PERMS_OPTION);
        addNextCLIOption(DIR_PERMS_OPTION);
        addNextCLIOption(CUSTOM_METADATA_OPTION);
        addNextCLIOption(ACL_OPTION);
        addNextCLIOption(OWNER_OPTION);
        addNextCLIOption(DOMAIN_OPTION);
        addNextCLIOption(MAX_CONNECTIONS);
        addNextCLIOption(MAX_NODE_CONNECTIONS);
        addNextCLIOption(REDUCED_MAX_CONNECTIONS);
        addNextCLIOption(REDUCED_MAX_NODE_CONNECTIONS);
        addNextCLIOption(REDUCED_START);
        addNextCLIOption(REDUCED_END);
        addNextCLIOption(IGNORE_CONFLICTS);
        addNextCLIOption(EXPORT_RESULTS_TYPE);
        addNextCLIOption(EXPORT_RESULTS_PATH);
        addNextCLIOption(EXPORT_RESULTS_PREFIX);
        addNextCLIOption(RESUME);
        addNextCLIOption(RERUN);
        addNextCLIOption(INSECURE_OPTION);
    }

    private static void addNextCLIOption(String option) {
        cliOrder.put(option, cliOptionsCount++);
    }

    public ArcCopy(String args[], int numCmdLineArgs) {
        super(args, numCmdLineArgs);
        HELP_HEADER = "Copies items from one location to another.  Items to copy are listed in the list_file.\n";
        HELP_USAGE_LINE = commandName
                + " copy -s <source_profile> -d <destination_profile> --destination-path <destination_path> [options] [list_file]";
    }

    @SuppressWarnings({ "static-access", "AccessStaticViaInstance" })
    public Options getOptions() {
        if (cliOptions == null) {
            Options options = new Options();

            // *** Adding a new option needs to be added to the cliOrder list
            // Note, you cannot do required options with this library and help so we have to add all
            // as non-required
            // and deal with it when parsing
            options.addOption(OptionBuilder.withDescription("Displays this help text (the default behavior).")
                    .withLongOpt(HELP_OPTION).create("h"));

            // Required
            options.addOption(OptionBuilder.withArgName("source_profile").hasArg().withDescription(
                    "Source for the copy operation: either a namespace profile name or LFS for the local file system.")
                    .withLongOpt(SOURCE_PROFILE_OPTION).create("s"));
            options.addOption(OptionBuilder.withArgName("destination_profile").hasArg().withDescription(
                    "Destination for the copy operation: either a namespace profile name or LFS for the local file system.")
                    .withLongOpt(DESTINATION_PROFILE_OPTION).create("d"));
            options.addOption(OptionBuilder.withArgName("destination_path").hasArg()
                    .withDescription("Destination directory path.").withLongOpt(DESTINATION_PATH_OPTION).create());

            // Optional
            options.addOption(OptionBuilder.withArgName("source_path").hasArg()
                    .withDescription("Source directory path.").withLongOpt(SOURCE_PATH_OPTION).create());
            options.addOption(OptionBuilder.withArgName("path").hasArg().withDescription(
                    "Custom metadata path.  Can be relative or absolute.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(CUSTOM_METADATA_OPTION).create());

            options.addOption(OptionBuilder.withArgName("path").hasArg().withDescription(
                    "ACL path.  Can be relative or absolute.  This option applies only when copying from the local file system to a HCP 5.0 or later namespace.")
                    .withLongOpt(ACL_OPTION).create());

            options.addOption(OptionBuilder.withArgName("path").hasArg().withDescription(
                    "Owner's name.  This option applies only when copying from the local file system to a HCP 5.0 or later namespace.")
                    .withLongOpt(OWNER_OPTION).create());

            options.addOption(OptionBuilder.withArgName("path").hasArg().withDescription(
                    "Owner's domain.  This option applies only when copying from the local file system to a HCP 5.0 or later namespace.")
                    .withLongOpt(DOMAIN_OPTION).create());

            options.addOption(OptionBuilder.withArgName("true|false").hasArg().withDescription(
                    "Specifies whether copied objects should be marked for indexing.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(INDEX_OPTION).create());
            options.addOption(OptionBuilder.withArgName("true|false").hasArg().withDescription(
                    "Specifies whether copied objects should be marked for shredding.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(SHRED_OPTION).create());
            options.addOption(OptionBuilder.withArgName("true|false").hasArg().withDescription(
                    "Specifies whether copied objects should be placed on hold.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(HOLD_OPTION).create());
            options.addOption(OptionBuilder.withArgName("retention_setting").hasArg().withDescription(
                    "Retention setting for copied objects.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(RETENTION_OPTION).create());
            options.addOption(OptionBuilder.withArgName("integer").hasArg().withDescription(
                    "Group ID to use for the owner of copied objects.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(GID_OPTION).create());
            options.addOption(OptionBuilder.withArgName("integer").hasArg().withDescription(
                    "User ID to use for the owner of copied objects.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(UID_OPTION).create());
            options.addOption(OptionBuilder.withArgName("octal_value").hasArg().withDescription(
                    "POSIX permissions for copied objects; for example, 664.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(FILE_PERMS_OPTION).create());
            options.addOption(OptionBuilder.withArgName("octal_value").hasArg().withDescription(
                    "POSIX permissions for new destination directories; for example, 664.  This option applies only when copying from the local file system to a namespace.")
                    .withLongOpt(DIR_PERMS_OPTION).create());

            options.addOption(OptionBuilder.withArgName("job_name").hasOptionalArg().withDescription(
                    "Reruns the copy job with the given job name if provide, if no name is provided it reruns the last copy job run.  When rerunning you can change the load and export settings.  Any changes to profiles, paths or metadata values will not change what is set in the job.")
                    .withLongOpt(RERUN).create());
            options.addOption(OptionBuilder.withArgName("job_name").hasOptionalArg().withDescription(
                    "Resumes the copy job from where it left off, if no name is provided it resumes the last copy job run.  When rerunning you can change the load and export settings.  Any changes to profiles, paths or metadata values will not change what is set in the job.")
                    .withLongOpt(RESUME).create());
            options.addOption(OptionBuilder.withDescription("Treat conflict (409) failures as successes.")
                    .withLongOpt(IGNORE_CONFLICTS).create());
            // results_types is only not shared because CONFLICT is not a valid option for delete
            // jobs
            options.addOption(OptionBuilder.withArgName("results_types").hasArg().withDescription(
                    "Types of results lists to export: either ALL or a comma-separated list that includes one or more of SUCCESS, FAILURE, JOBLIST and CONFLICT.  If omitted no results lists are exported.")
                    .withLongOpt(EXPORT_RESULTS_TYPE).create());
            getSharedOptions(options);

            cliOptions = options;
        }
        return cliOptions;
    }

    protected void parseArgs() throws ParseException {

        // create the command cmdLine parser
        CommandLineParser parser = new PosixParser();
        CommandLine cmdLine;

        // parse the command cmdLine arguments
        cmdLine = parser.parse(getOptions(), getArgs());

        // Help
        printHelp = cmdLine.hasOption("h");
        if (printHelp) {
            return;
        }

        initializeProfiles(cmdLine.hasOption("insecure"));

        // See how we are getting the input list: file or from stdin
        @SuppressWarnings({ "unchecked" })
        List<String> argList = cmdLine.getArgList();

        // Handle the load schedule and export lists
        LoadSchedule schedule = LoadSchedule.getDefaultLoadSchedule();
        getLoadSchedule(cmdLine, schedule);
        setUpExportListThread(cmdLine);

        // See if we are rerunning, set up the job if we are
        boolean rerunning = handleRerunAndResume(cmdLine, schedule);

        if (rerunning) {
            List<String> extraOptions = new ArrayList<String>();
            if (cmdLine.hasOption(DESTINATION_PROFILE_OPTION)) {
                extraOptions.add(DESTINATION_PROFILE_OPTION);
            }
            if (cmdLine.hasOption(SOURCE_PROFILE_OPTION)) {
                extraOptions.add(SOURCE_PROFILE_OPTION);
            }
            if (cmdLine.hasOption(DESTINATION_PATH_OPTION)) {
                extraOptions.add(DESTINATION_PATH_OPTION);
            }
            if (cmdLine.hasOption(SOURCE_PATH_OPTION)) {
                extraOptions.add(SOURCE_PATH_OPTION);
            }
            if (cmdLine.hasOption(JOB_NAME)) {
                extraOptions.add(JOB_NAME);
            }
            if (cmdLine.hasOption(HOLD_OPTION)) {
                extraOptions.add(HOLD_OPTION);
            }
            if (cmdLine.hasOption(INDEX_OPTION)) {
                extraOptions.add(INDEX_OPTION);
            }
            if (cmdLine.hasOption(SHRED_OPTION)) {
                extraOptions.add(SHRED_OPTION);
            }
            if (cmdLine.hasOption(RETENTION_OPTION)) {
                extraOptions.add(RETENTION_OPTION);
            }
            if (cmdLine.hasOption(UID_OPTION)) {
                extraOptions.add(UID_OPTION);
            }
            if (cmdLine.hasOption(GID_OPTION)) {
                extraOptions.add(GID_OPTION);
            }
            if (cmdLine.hasOption(FILE_PERMS_OPTION)) {
                extraOptions.add(FILE_PERMS_OPTION);
            }
            if (cmdLine.hasOption(DIR_PERMS_OPTION)) {
                extraOptions.add(DIR_PERMS_OPTION);
            }
            if (cmdLine.hasOption(CUSTOM_METADATA_OPTION)) {
                extraOptions.add(CUSTOM_METADATA_OPTION);
            }
            if (cmdLine.hasOption(ACL_OPTION)) {
                extraOptions.add(ACL_OPTION);
            }
            if (cmdLine.hasOption(OWNER_OPTION)) {
                extraOptions.add(OWNER_OPTION);
            }
            if (cmdLine.hasOption(DOMAIN_OPTION)) {
                extraOptions.add(DOMAIN_OPTION);
            }
            if (cmdLine.hasOption(IGNORE_CONFLICTS)) {
                extraOptions.add(IGNORE_CONFLICTS);
            }
            if (!extraOptions.isEmpty()) {
                throw new ParseException("The following supplied options are not allowed with --" + RESUME
                        + " or --" + RERUN + ": " + extraOptions);
            }
            // The list_file is not allowed for rerun/resume
            if (argList.size() > numCmdLineArgs - 1) {
                throw new ParseException(
                        "The list_file argument is not allowed with --" + RESUME + " or --" + RERUN);
            }

        } else {
            if (argList.size() != numCmdLineArgs) {
                throw new ParseException("Missing argument list_file.");
            }

            String listFileName = argList.get(numCmdLineArgs - 1);

            // Required fields
            List<String> missingRequiredOptions = new ArrayList<String>();
            if (!cmdLine.hasOption("s")) {
                missingRequiredOptions.add("s");
            }
            if (!cmdLine.hasOption("d")) {
                missingRequiredOptions.add("d");
            }
            if (!cmdLine.hasOption(DESTINATION_PATH_OPTION)) {
                missingRequiredOptions.add(DESTINATION_PATH_OPTION);
            }

            // Print a friendly string if we are missing more than one require options
            if (missingRequiredOptions.size() > 1) {
                throw new ParseException("Missing required options: " + missingRequiredOptions);
            }

            // Parse require options
            String srcProfileName = getProfileNameFromCmdLineAndValidateExistance(cmdLine, "s");
            String tarProfileName = getProfileNameFromCmdLineAndValidateExistance(cmdLine, "d");
            AbstractProfileBase srcProfile = ProfileManager.getProfileByName(srcProfileName);
            AbstractProfileBase dstProfile = ProfileManager.getProfileByName(tarProfileName);
            String targetPath = validateTargetPath(cmdLine);

            // Optional fields
            String sourcePath = null;
            if (cmdLine.hasOption(SOURCE_PATH_OPTION)) {
                sourcePath = cmdLine.getOptionValue(SOURCE_PATH_OPTION);
            }

            String jobName = null;
            if (cmdLine.hasOption(JOB_NAME)) {
                jobName = cmdLine.getOptionValue(JOB_NAME);
            }

            FileMetadata metadata = new FileMetadata();

            // We ignore the metadata on the commandline going hcp to hcp unless the user has
            // cli.override_metadata
            // set to true in their user.properties file. This is a special hook for service.
            boolean goingFromLFSToHCP = srcProfile.getType() == ProfileType.FILESYSTEM
                    && dstProfile.getType() != ProfileType.FILESYSTEM;
            boolean isCLIMetadataOverrideEnabled = Boolean.parseBoolean(HCPMoverProperties.CLI_OVERRIDE_MD.get());
            if (goingFromLFSToHCP || isCLIMetadataOverrideEnabled) {
                if (cmdLine.hasOption(INDEX_OPTION)) {
                    metadata.setSearchIndex(new Boolean(cmdLine.getOptionValue(INDEX_OPTION)));
                }
                if (cmdLine.hasOption(HOLD_OPTION)) {
                    metadata.setRetentionHold(new Boolean(cmdLine.getOptionValue(HOLD_OPTION)));
                }
                if (cmdLine.hasOption(SHRED_OPTION)) {
                    metadata.setShred(new Boolean(cmdLine.getOptionValue(SHRED_OPTION)));
                }

                if (cmdLine.hasOption(RETENTION_OPTION)) {
                    String cmdLineRetentionValue = cmdLine.getOptionValue(RETENTION_OPTION);
                    Retention retention = Retention.fromHcapValue(cmdLineRetentionValue);
                    metadata.setRetention(retention);
                }

                if (cmdLine.hasOption(CUSTOM_METADATA_OPTION)) {
                    CustomMetadata customMetadata = new CustomMetadata(CustomMetadata.Form.FILE,
                            cmdLine.getOptionValue(CUSTOM_METADATA_OPTION));
                    metadata.setCustomMetadata(customMetadata);
                }

                if (cmdLine.hasOption(ACL_OPTION)) {
                    if (dstProfile.supportsACLs()) {
                        ACLMetadata acl = new ACLMetadata(CustomMetadata.Form.FILE,
                                cmdLine.getOptionValue(ACL_OPTION));
                        metadata.setACL(acl);
                    } else {
                        throw new ParseException("The " + ACL_OPTION
                                + " may only be specified when copying from the local file system to a HCP 5.0 or later namespace.");
                    }
                }

                if (dstProfile.supportsPosixSettings()) {
                    if (cmdLine.hasOption(UID_OPTION)) {
                        metadata.setUid(cmdLine.getOptionValue(UID_OPTION));
                    }
                    if (cmdLine.hasOption(GID_OPTION)) {
                        metadata.setGid(cmdLine.getOptionValue(GID_OPTION));
                    }
                    if (cmdLine.hasOption(FILE_PERMS_OPTION)) {
                        metadata.setFileMode(new Integer(cmdLine.getOptionValue(FILE_PERMS_OPTION)));
                    }
                    if (cmdLine.hasOption(DIR_PERMS_OPTION)) {
                        metadata.setDirMode(new Integer(cmdLine.getOptionValue(DIR_PERMS_OPTION)));
                    }
                } else {
                    List<String> extraOptions = new ArrayList<String>();
                    if (cmdLine.hasOption(UID_OPTION)) {
                        extraOptions.add(UID_OPTION);
                    }
                    if (cmdLine.hasOption(GID_OPTION)) {
                        extraOptions.add(GID_OPTION);
                    }
                    if (cmdLine.hasOption(FILE_PERMS_OPTION)) {
                        extraOptions.add(FILE_PERMS_OPTION);
                    }
                    if (cmdLine.hasOption(DIR_PERMS_OPTION)) {
                        extraOptions.add(DIR_PERMS_OPTION);
                    }
                    if (!extraOptions.isEmpty()) {
                        throw new ParseException(
                                "The following supplied options are only allowed when copying from the local file system to either HCAP 2.x or the default namespace on HCP 3.0 or later: "
                                        + extraOptions);
                    }
                }

                // throw an exception if anonymous user tries to set owner for objects
                if (dstProfile.isAnonymousAccess()) {
                    if (cmdLine.hasOption(OWNER_OPTION)) {
                        throw new ParseException("Cannot use owner option with an anonymous namespace profile.");
                    }
                }

                if (dstProfile.supportsOwner()) {
                    if (cmdLine.hasOption(OWNER_OPTION)) {
                        Owner owner;
                        if (cmdLine.hasOption(DOMAIN_OPTION)) {
                            owner = new Owner(cmdLine.getOptionValue(OWNER_OPTION),
                                    cmdLine.getOptionValue(DOMAIN_OPTION));
                        } else {
                            String ownerName = cmdLine.getOptionValue(OWNER_OPTION);
                            if (ownerName.length() == 0) {
                                owner = new Owner(Owner.OwnerType.PUBLIC);
                            } else {
                                owner = new Owner(ownerName);
                            }
                        }
                        metadata.setOwner(owner);
                    }
                } else {
                    List<String> extraOptions = new ArrayList<String>();
                    if (cmdLine.hasOption(OWNER_OPTION)) {
                        extraOptions.add(OWNER_OPTION);
                    }
                    if (cmdLine.hasOption(DOMAIN_OPTION)) {
                        extraOptions.add(DOMAIN_OPTION);
                    }
                    if (!extraOptions.isEmpty()) {
                        throw new ParseException(
                                "The following supplied options are only allowed when copying from the local file system to an HCP 5.0 or later namespace: "
                                        + extraOptions);
                    }
                }
            } else {
                List<String> extraOptions = new ArrayList<String>();
                if (cmdLine.hasOption(HOLD_OPTION)) {
                    extraOptions.add(HOLD_OPTION);
                }
                if (cmdLine.hasOption(INDEX_OPTION)) {
                    extraOptions.add(INDEX_OPTION);
                }
                if (cmdLine.hasOption(SHRED_OPTION)) {
                    extraOptions.add(SHRED_OPTION);
                }
                if (cmdLine.hasOption(RETENTION_OPTION)) {
                    extraOptions.add(RETENTION_OPTION);
                }
                if (cmdLine.hasOption(UID_OPTION)) {
                    extraOptions.add(UID_OPTION);
                }
                if (cmdLine.hasOption(GID_OPTION)) {
                    extraOptions.add(GID_OPTION);
                }
                if (cmdLine.hasOption(FILE_PERMS_OPTION)) {
                    extraOptions.add(FILE_PERMS_OPTION);
                }
                if (cmdLine.hasOption(DIR_PERMS_OPTION)) {
                    extraOptions.add(DIR_PERMS_OPTION);
                }
                if (cmdLine.hasOption(CUSTOM_METADATA_OPTION)) {
                    extraOptions.add(CUSTOM_METADATA_OPTION);
                }
                if (cmdLine.hasOption(ACL_OPTION)) {
                    extraOptions.add(ACL_OPTION);
                }
                if (cmdLine.hasOption(OWNER_OPTION)) {
                    extraOptions.add(OWNER_OPTION);
                }
                if (cmdLine.hasOption(DOMAIN_OPTION)) {
                    extraOptions.add(DOMAIN_OPTION);
                }
                if (!extraOptions.isEmpty()) {
                    throw new ParseException(
                            "The following supplied options are only allowed when copying from the local file system to HCP: "
                                    + extraOptions);
                }
            }

            boolean ignoreConflicts = cmdLine.hasOption(IGNORE_CONFLICTS);

            // Validate the input file if one was provided
            try {
                FileListParser.validateFile(new File(listFileName), srcProfile, sourcePath, targetPath);
            } catch (IOException e) {
                throw new ParseException("Error parsing input file.  Msg: " + e.getMessage());
            } catch (FileListParserException e) {
                throw new ParseException("Error parsing input file.  Msg: " + e.getMessage());
            }

            // Setup the job with the arguments
            try {
                setupCopyJob(listFileName, dstProfile, targetPath, srcProfile, sourcePath, jobName, metadata,
                        ignoreConflicts, schedule);
                managedJobImpl = arcMover.createManagedJob(managedJob);
            } catch (IllegalArgumentException e) {
                throw new ParseException(
                        "IllegalArgumentException writing to database during file list parsing.  Msg: "
                                + e.getMessage());
            } catch (DatabaseException e) {
                throw new ParseException(
                        "DatabaseException writing to database during file list parsing.  Msg: " + e.getMessage());
            } catch (JobException e) {
                throw new ParseException(
                        "JobException writing to database during file list parsing.  Msg: " + e.getMessage());
            }
        }
    }

    private String validateTargetPath(CommandLine cmdLine) throws ParseException {
        if (!cmdLine.hasOption(DESTINATION_PATH_OPTION)) {
            throw new ParseException("Missing Option: " + DESTINATION_PATH_OPTION);
        }

        String value = cmdLine.getOptionValue(DESTINATION_PATH_OPTION);
        String retval = null;

        if (value != null) {
            retval = value;
        }
        if (retval == null) {
            throw new ParseException("Invalid " + DESTINATION_PATH_OPTION + " : " + value);
        }
        return retval;
    }

    protected void appendAdditionalOutput(ManagedJobStats jobStats, StringBuilder sb, String padding) {
        String newLine = NEWLINE;

        appendLabel(sb, "Failed Directories", padding);
        sb.append(jobStats.getErroredDirCount());
        sb.append(newLine);

        appendLabel(sb, "I/O Rate", padding);
        sb.append(jobStats.getAverageOperationRate());
        sb.append(" operations/second");
        sb.append(newLine);

        appendLabel(sb, "Throughput", padding);
        sb.append(jobStats.getAverageThroughput());
        sb.append(" KB/second");
        sb.append(newLine);
    }

    @Override
    protected String getFormattedJobStats(ManagedJobStats jobStats) {
        String spaces = String.format("%4s", " ");
        return String.format(
                "%1$,d%%" + spaces + "-" + spaces + "%7$,d objects found," + spaces + "%2$,d/%3$,d KB complete,"
                        + spaces + "%4$,d/%5$,d files moved," + spaces + "Throughput = %6$,d KB/sec",
                jobStats.getPercentBytesCompleted(), jobStats.getCompletedBytesInKB(),
                jobStats.getCopyableBytesInKB(), jobStats.getCompletedObjectCount(), jobStats.getTotalObjectCount(),
                jobStats.getAverageThroughput(), jobStats.getDiscoveredObjectCount());
    }

    protected String getErrorMessage() {
        return " failed to copy because: ";
    }

    protected ManagedJob.Type getJobType() {
        return ManagedJob.Type.COPY;
    }

    protected JobId getLastJobID() throws NumberFormatException {
        return new JobId(Long.parseLong(HCPMoverProperties.LAST_COPY_JOB_RUN.get()));
    }

    private void setupCopyJob(String listFileName, AbstractProfileBase dstProfile, String targetPath,
            AbstractProfileBase srcProfile, String sourcePath, String jobName, FileMetadata metadata,
            boolean ignoreConflicts, LoadSchedule schedule) throws DatabaseException {
        CopyJob copyJob = new CopyJob(srcProfile, dstProfile, new File(listFileName));
        managedJob = copyJob;

        managedJob.setTargetPath(dstProfile.encode(targetPath));
        if (sourcePath != null) {
            managedJob.setSourcePath(srcProfile.encode(sourcePath));
        }

        if (jobName != null) {
            managedJob.setUserDefinedJobName(jobName);
        }

        copyJob.setIgnoreConflicts(ignoreConflicts);

        managedJob.setLoadSchedule(schedule);
        ((CopyJob) managedJob).setIngestionMetadata(metadata);
    }

    public boolean supportsConflictReports() {
        return true;
    }
}