TransferArgs.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.client;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseNoDataException;
import org.waarp.common.guid.LongUuid;
import org.waarp.common.json.JsonHandler;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.openr66.client.utils.OutputFormat;
import org.waarp.openr66.client.utils.OutputFormat.OUTPUTFORMAT;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.configuration.Messages;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static org.waarp.common.database.DbConstant.*;
/**
* Transfer arguments:<br>
* <br>
* -to <arg> Specify the requested Host<br>
* (-id <arg>| Specify the id of transfer<br>
* (-file <arg> Specify the file path to operate on<br>
* -rule <arg>)) Specify the Rule<br>
* [-block <arg>] Specify the block size<br>
* [-nofollow] Specify the transfer should not integrate a FOLLOW id<br>
* [-md5] Specify the option to have a hash computed for the
* transfer<br>
* [-delay <arg>] Specify the delay time as an epoch time or '+' a delay in ms<br>
* [-start <arg>] Specify the start time as yyyyMMddHHmmss<br>
* [-info <arg>) Specify the transfer information (generally in last position)<br>
* [-nolog] Specify to not log anything included database once the
* transfer is done<br>
* [-notlogWarn | Specify to log final result as Info if OK<br>
* -logWarn] Specify to log final result as Warn if OK<br>
*/
public class TransferArgs {
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(TransferArgs.class);
private static final String FILE = "file";
public static final String FILE_ARG = "-" + FILE;
private static final Option FILE_OPTION =
Option.builder(FILE).required(false).hasArg(true)
.desc("Specify the file path to operate on").build();
private static final String TO = "to";
public static final String TO_ARG = "-" + TO;
private static final Option TO_OPTION =
Option.builder(TO).required(true).hasArg(true)
.desc("Specify the requested Host").build();
private static final String RULE = "rule";
public static final String RULE_ARG = "-" + RULE;
private static final Option RULE_OPTION =
Option.builder(RULE).required(false).hasArg(true).desc("Specify the Rule")
.build();
private static final String ID_FIELD = "id";
public static final String ID_ARG = "-" + ID_FIELD;
private static final Option ID_OPTION =
Option.builder(ID_FIELD).required(false).hasArg(true)
.desc("Specify the id of transfer").build();
private static final String NO_FOLLOW = "nofollow";
public static final String NO_FOLLOW_ARG = "-" + NO_FOLLOW;
private static final Option NO_FOLLOW_OPTION =
Option.builder(NO_FOLLOW).required(false).hasArg(false)
.desc("Specify if the transfer should not integrate a FOLLOW id")
.build();
public static final String FOLLOW_JSON_KEY = "follow";
private static final String INFO = "info";
public static final String INFO_ARG = "-" + INFO;
private static final Option INFO_OPTION =
Option.builder(INFO).required(false).hasArg(true)
.desc("Specify the transfer information").build();
private static final String HASH = "md5";
public static final String HASH_ARG = "-" + HASH;
private static final Option HASH_OPTION =
Option.builder(HASH).required(false).hasArg(false)
.desc("Specify the option to have a hash computed for the transfer")
.build();
private static final String BLOCK = "block";
public static final String BLOCK_ARG = "-" + BLOCK;
private static final Option BLOCK_OPTION =
Option.builder(BLOCK).required(false).hasArg(true)
.desc("Specify the block size").build();
private static final String START = "start";
public static final String START_ARG = "-" + START;
private static final Option START_OPTION =
Option.builder(START).required(false).hasArg(true)
.desc("Specify the start time as yyyyMMddHHmmss").build();
private static final String DELAY = "delay";
public static final String DELAY_ARG = "-" + DELAY;
private static final Option DELAY_OPTION =
Option.builder(DELAY).required(false).hasArg(true).desc(
"Specify the delay time as an epoch time or '+' a delay in ms")
.build();
private static final String LOGWARN = "logWarn";
public static final String LOGWARN_ARG = "-" + LOGWARN;
private static final Option LOGWARN_OPTION =
Option.builder(LOGWARN).required(false).hasArg(false)
.desc("Specify to log final result as Warn if OK").build();
private static final String NOTLOGWARN = "notlogWarn";
public static final String NOTLOGWARN_ARG = "-" + NOTLOGWARN;
private static final Option NOTLOGWARN_OPTION =
Option.builder(NOTLOGWARN).required(false).hasArg(false)
.desc("Specify to log final result as Info if OK").build();
private static final String NOTLOG = "nolog";
public static final String NOTLOG_ARG = "-" + NOTLOG;
private static final Option NOTLOG_OPTION =
Option.builder(NOTLOG).required(false).hasArg(false).desc(
"Specify to not log anything included database once the " +
"transfer is done").build();
private static final OptionGroup LOGWARN_OPTIONS =
new OptionGroup().addOption(LOGWARN_OPTION).addOption(NOTLOGWARN_OPTION);
private static final OptionGroup DELAY_OPTIONS =
new OptionGroup().addOption(DELAY_OPTION).addOption(START_OPTION);
private static final String QUIET = "quiet";
private static final String CSV = "csv";
private static final String XML = "xml";
private static final String JSON = "json";
private static final String PROPERTY = "property";
public static final String QUIET_ARG = "-" + QUIET;
public static final String CSV_ARG = "-" + CSV;
public static final String XML_ARG = "-" + XML;
public static final String JSON_ARG = "-" + JSON;
public static final String PROPERTY_ARG = "-" + PROPERTY;
private static final Option QUIET_OPTION =
Option.builder(QUIET).required(false).hasArg(false).desc(
"meaning no output at all (logs are not changed, exit value still " +
"uses 0 as Success, 1 as Warning and others as Failure)").build();
private static final Option CSV_OPTION =
Option.builder(CSV).required(false).hasArg(false).desc(
"meaning output will be in CSV format (2 lines, 1 with title, 1 " +
"with content, separator is ';')").build();
private static final Option XML_OPTION =
Option.builder(XML).required(false).hasArg(false)
.desc("meaning output will be in XML").build();
private static final Option JSON_OPTION =
Option.builder(JSON).required(false).hasArg(false)
.desc("meaning output will be in JSON").build();
private static final Option PROPERTY_OPTION =
Option.builder(PROPERTY).required(false).hasArg(false)
.desc("meaning output will be in Property format (name=value)")
.build();
private static final OptionGroup OUTPUT_OPTIONS =
new OptionGroup().addOption(QUIET_OPTION).addOption(CSV_OPTION)
.addOption(XML_OPTION).addOption(JSON_OPTION)
.addOption(PROPERTY_OPTION);
private static final Options TRANSFER_OPTIONS =
new Options().addOption(FILE_OPTION).addOption(TO_OPTION)
.addOption(NO_FOLLOW_OPTION).addOption(RULE_OPTION)
.addOption(ID_OPTION).addOption(INFO_OPTION)
.addOption(HASH_OPTION).addOption(BLOCK_OPTION)
.addOptionGroup(DELAY_OPTIONS).addOption(NOTLOG_OPTION)
.addOptionGroup(LOGWARN_OPTIONS)
.addOptionGroup(OUTPUT_OPTIONS);
public static final String SEPARATOR_SEND = "--";
/**
* Print to standard output the help of this command
*/
public static void printHelp() {
final HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("Transfer", TRANSFER_OPTIONS);
}
/**
* Analyze the parameters according to TransferArgs options
* <br><br>
* Transfer arguments:<br>
* <br>
* -to <arg> Specify the requested Host<br>
* (-id <arg>| Specify the id of transfer<br>
* (-file <arg> Specify the file path to operate on<br>
* -rule <arg>)) Specify the Rule<br>
* [-block <arg>] Specify the block size<br>
* [-nofollow] Specify the transfer should not integrate a FOLLOW id<br>
* [-md5] Specify the option to have a hash computed for the
* transfer<br>
* [-delay <arg>] Specify the delay time as an epoch time or '+' a delay in ms<br>
* [-start <arg>] Specify the start time as yyyyMMddHHmmss<br>
* [-info <arg>) Specify the transfer information (generally in last position)<br>
* [-nolog] Specify to not log anything included database once the
* transfer is done<br>
* [-notlogWarn | Specify to log final result as Info if OK<br>
* -logWarn] Specify to log final result as Warn if OK<br>
*
* @param rank the rank to analyze from
* @param args the argument to analyze
* @param analyseFollow if True, will check follow possible argument in info
*
* @return the TransferArgs or null if an error occurs
*/
public static TransferArgs getParamsInternal(final int rank,
final String[] args,
final boolean analyseFollow) {
if (args == null || args.length == 0) {
logger.error("Arguments cannot be empty or null");
return null;
}
final String[] realArgs = getRealArgs(rank, args);
// Now set default values from configuration
final TransferArgs transferArgs1 = new TransferArgs();
transferArgs1.setBlockSize(Configuration.configuration.getBlockSize());
final CommandLineParser parser = new DefaultParser();
try {
final CommandLine cmd = parser.parse(TRANSFER_OPTIONS, realArgs, true);
if (getTransferMinimalArgs(transferArgs1, cmd)) {
return null;
}
if (checkDelayStart(transferArgs1, cmd)) {
return null;
}
if (checkExtraTransferArgs(transferArgs1, cmd)) {
return null;
}
checkLog(transferArgs1, cmd);
checkOutput(cmd);
} catch (final ParseException e) {
printHelp();
logger.error("Arguments are incorrect {}", e.getMessage());
return null;
}
return finalizeTransferArgs(analyseFollow, transferArgs1);
}
/**
* Check extra arguments for Transfer
*
* @param transferArgs1
* @param cmd
*
* @return the TransferArgs or null
*/
private static boolean checkExtraTransferArgs(
final TransferArgs transferArgs1, final CommandLine cmd) {
if (!cmd.hasOption(NO_FOLLOW)) {
transferArgs1.setFollowId("");
}
if (cmd.hasOption(INFO)) {
transferArgs1.setTransferInfo(cmd.getOptionValue(INFO));
}
if (cmd.hasOption(HASH)) {
transferArgs1.setMD5(true);
}
if (cmd.hasOption(BLOCK)) {
try {
transferArgs1.setBlockSize(Integer.parseInt(cmd.getOptionValue(BLOCK)));
} catch (final NumberFormatException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
logger.error(Messages.getString("AbstractTransfer.20") + " block");
//$NON-NLS-1$
return true;
}
if (transferArgs1.getBlockSize() < 100) {
logger.error(Messages.getString("AbstractTransfer.1") +
transferArgs1.getBlockSize());
//$NON-NLS-1$
return true;
}
}
return false;
}
/**
* Check minimal argyments for Transfer
*
* @param transferArgs1
* @param cmd
*
* @return True if an error occurs
*/
private static boolean getTransferMinimalArgs(
final TransferArgs transferArgs1, final CommandLine cmd) {
if (cmd.hasOption(TO)) {
transferArgs1.setRemoteHost(cmd.getOptionValue(TO));
if (Configuration.configuration.getAliases().containsKey(
transferArgs1.getRemoteHost())) {
transferArgs1.setRemoteHost(Configuration.configuration.getAliases()
.get(
transferArgs1.getRemoteHost()));
}
}
if (cmd.hasOption(FILE)) {
transferArgs1.setFilename(cmd.getOptionValue(FILE));
transferArgs1.setFilename(transferArgs1.getFilename().replace('ยง', '*'));
}
if (cmd.hasOption(RULE)) {
transferArgs1.setRulename(cmd.getOptionValue(RULE));
}
if (cmd.hasOption(ID_FIELD)) {
try {
transferArgs1.setId(Long.parseLong(cmd.getOptionValue(ID_FIELD)));
} catch (final NumberFormatException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
logger.error(Messages.getString("AbstractTransfer.20") + " id");
//$NON-NLS-1$
return true;
}
}
return false;
}
/**
* Finalize the real arguments to parse (stopping at "--" and starting at
* rank)
*
* @param rank
* @param args
*
* @return the real arguments
*/
private static String[] getRealArgs(final int rank, final String[] args) {
String[] realArgs =
rank == 0? args : Arrays.copyOfRange(args, rank, args.length);
for (int i = rank; i < args.length; i++) {
if (SEPARATOR_SEND.equals(args[i])) {
realArgs = Arrays.copyOfRange(args, rank, i);
break;
}
}
return realArgs;
}
/**
* Finalize the TransferArgs
*
* @param analyseFollow
* @param transferArgs1
*
* @return the TransferArgs or null
*/
private static TransferArgs finalizeTransferArgs(final boolean analyseFollow,
final TransferArgs transferArgs1) {
if (transferArgs1.getTransferInfo() == null) {
transferArgs1.setTransferInfo(AbstractTransfer.NO_INFO_ARGS);
}
if (analyseFollow) {
analyzeFollow(transferArgs1);
}
if (transferArgs1.getRemoteHost() != null &&
transferArgs1.getRulename() != null &&
transferArgs1.getFilename() != null) {
return transferArgs1;
} else if (transferArgs1.getId() != ILLEGALVALUE &&
transferArgs1.getRemoteHost() != null) {
try {
final DbTaskRunner runner = new DbTaskRunner(transferArgs1.getId(),
transferArgs1.getRemoteHost());
transferArgs1.setRulename(runner.getRuleId());
transferArgs1.setFilename(runner.getOriginalFilename());
return transferArgs1;
} catch (final WaarpDatabaseNoDataException e) {
logger.error("No transfer found with this id and partner");
logger.error(Messages.getString("AbstractBusinessRequest.NeedMoreArgs",
"(-to -rule -file | -to -id with " +
"existing id transfer): {}")
//$NON-NLS-1$
, e.getMessage());
return null;
} catch (final WaarpDatabaseException e) {
logger.error(Messages.getString("AbstractBusinessRequest.NeedMoreArgs",
"(-to -rule -file | -to -id) with a " +
"correct database connexion: {}",
e.getMessage()));//$NON-NLS-1$
return null;
}
}
logger.error(Messages.getString("AbstractBusinessRequest.NeedMoreArgs",
"(-to -rule -file | -to -id)") +
//$NON-NLS-1$
AbstractTransfer.INFO_ARGS);
return null;
}
/**
* Check LOG and NOT LOG options
*
* @param transferArgs1
* @param cmd
*/
private static void checkLog(final TransferArgs transferArgs1,
final CommandLine cmd) {
if (cmd.hasOption(LOGWARN)) {
transferArgs1.setNormalInfoAsWarn(true);
}
if (cmd.hasOption(NOTLOGWARN)) {
transferArgs1.setNormalInfoAsWarn(false);
}
if (cmd.hasOption(NOTLOG)) {
transferArgs1.setNolog(true);
}
}
/**
* Check if any output format specification is set
*
* @param cmd
*/
private static void checkOutput(final CommandLine cmd) {
if (cmd.hasOption(QUIET)) {
OutputFormat.setDefaultOutput(OUTPUTFORMAT.QUIET);
} else if (cmd.hasOption(CSV)) {
OutputFormat.setDefaultOutput(OUTPUTFORMAT.CSV);
} else if (cmd.hasOption(XML)) {
OutputFormat.setDefaultOutput(OUTPUTFORMAT.XML);
} else if (cmd.hasOption(JSON)) {
OutputFormat.setDefaultOutput(OUTPUTFORMAT.JSON);
} else if (cmd.hasOption(PROPERTY)) {
OutputFormat.setDefaultOutput(OUTPUTFORMAT.PROPERTY);
}
}
/**
* Check DELAY or START
*
* @param transferArgs1
* @param cmd
*
* @return True if an error occurs
*/
private static boolean checkDelayStart(final TransferArgs transferArgs1,
final CommandLine cmd) {
if (cmd.hasOption(START)) {
final Date date;
final SimpleDateFormat dateFormat =
new SimpleDateFormat(AbstractTransfer.TIMESTAMP_FORMAT);
try {
date = dateFormat.parse(cmd.getOptionValue(START));
transferArgs1.setStartTime(new Timestamp(date.getTime()));
} catch (final java.text.ParseException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
logger.error(Messages.getString("AbstractTransfer.20") + " StartTime");
//$NON-NLS-1$
return true;
}
}
if (cmd.hasOption(DELAY)) {
final String delay = cmd.getOptionValue(DELAY);
try {
if (delay.charAt(0) == '+') {
transferArgs1.setStartTime(new Timestamp(
System.currentTimeMillis() + Long.parseLong(delay.substring(1))));
} else {
transferArgs1.setStartTime(new Timestamp(Long.parseLong(delay)));
}
} catch (final NumberFormatException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
logger.error(Messages.getString("AbstractTransfer.20") + " Delay");
//$NON-NLS-1$
return true;
}
}
return false;
}
/**
* Get all Info in case of last argument
*
* @param transferArgs the original TransferArgs
* @param rank the rank to start from in args array
* @param args the original arguments
* @param copiedInfo might be null, original information to copy
*/
public static void getAllInfo(final TransferArgs transferArgs, final int rank,
final String[] args, final String copiedInfo) {
if (transferArgs != null) {
final StringBuilder builder = new StringBuilder();
if (copiedInfo != null) {
builder.append(copiedInfo);
}
for (int i = rank; i < args.length; i++) {
if (INFO_ARG.equalsIgnoreCase(args[i])) {
i++;
if (builder.length() == 0) {
builder.append(args[i].trim());
} else {
builder.append(' ').append(args[i].trim());
}
i++;
while (i < args.length) {
builder.append(' ').append(args[i].trim());
i++;
}
}
}
transferArgs.setTransferInfo(builder.toString());
TransferArgs.analyzeFollow(transferArgs);
}
}
/**
* Mainly Junit, but also special command where Follow Id might be lost or
* not set
*
* @param abstractTransfer
*/
public static void forceAnalyzeFollow(
final AbstractTransfer abstractTransfer) {
if (abstractTransfer.transferArgs.getFollowId() == null) {
abstractTransfer.transferArgs.setFollowId("");
analyzeFollow(abstractTransfer.transferArgs);
} else if (!abstractTransfer.transferArgs.getFollowId().isEmpty() &&
abstractTransfer.transferArgs.getTransferInfo() != null) {
if (!abstractTransfer.transferArgs.getTransferInfo()
.contains(FOLLOW_JSON_KEY)) {
// Add FOLLOW ID to transferArgs
final Map<String, Object> map = new HashMap<String, Object>();
map.put(FOLLOW_JSON_KEY,
Long.parseLong(abstractTransfer.transferArgs.getFollowId()));
abstractTransfer.transferArgs.setTransferInfo(
abstractTransfer.transferArgs.getTransferInfo() + " " +
JsonHandler.writeAsStringEscaped(map));
}
}
}
/**
* Analyze Follow option: fill it if Follow is "" (do not if null).
* It corrects argument of information if needed.
*
* @param transferArgs1 the current TransferArgs
*/
public static void analyzeFollow(final TransferArgs transferArgs1) {
if (transferArgs1.getFollowId() != null &&
transferArgs1.getTransferInfo() != null) {
final Map<String, Object> map =
DbTaskRunner.getMapFromString(transferArgs1.getTransferInfo());
if (map.containsKey(FOLLOW_JSON_KEY)) {
transferArgs1.setFollowId(map.get(FOLLOW_JSON_KEY).toString());
}
if (transferArgs1.getFollowId().isEmpty()) {
final long longUuid = LongUuid.getLongUuid();
map.put(FOLLOW_JSON_KEY, longUuid);
transferArgs1.setTransferInfo(transferArgs1.getTransferInfo() + " " +
JsonHandler.writeAsStringEscaped(map));
transferArgs1.setFollowId(Long.toString(longUuid));
} else {
if (map.size() > 1) {
transferArgs1.setTransferInfo(transferArgs1.getTransferInfo() + " " +
JsonHandler.writeAsStringEscaped(map));
}
}
}
}
private String filename;
private String rulename;
private String transferInfo;
private boolean isMD5;
private String remoteHost;
private int blocksize = 0x10000; // 64K
private long id = ILLEGALVALUE;
private Timestamp startTime;
private String followId;
private boolean normalInfoAsWarn = true;
private boolean nolog = false;
/**
* Empty constructor
*/
public TransferArgs() {
// Empty
}
public final String getFilename() {
return filename;
}
public final TransferArgs setFilename(final String filename) {
this.filename = filename;
return this;
}
public final String getRulename() {
return rulename;
}
public final TransferArgs setRulename(final String rulename) {
this.rulename = rulename;
return this;
}
public final String getTransferInfo() {
return transferInfo;
}
public final TransferArgs setTransferInfo(final String transferInfo) {
this.transferInfo = transferInfo;
return this;
}
public final boolean isMD5() {
return isMD5;
}
public final TransferArgs setMD5(final boolean md5) {
isMD5 = md5;
return this;
}
public final String getRemoteHost() {
return remoteHost;
}
public final TransferArgs setRemoteHost(final String remoteHost) {
this.remoteHost = remoteHost;
return this;
}
public final int getBlockSize() {
return blocksize;
}
public final TransferArgs setBlockSize(final int blocksize) {
this.blocksize = blocksize;
return this;
}
public final long getId() {
return id;
}
public final TransferArgs setId(final long id) {
this.id = id;
return this;
}
public final Timestamp getStartTime() {
return startTime;
}
public final TransferArgs setStartTime(final Timestamp startTime) {
this.startTime = startTime;
return this;
}
public final String getFollowId() {
return followId;
}
public final TransferArgs setFollowId(final String followId) {
this.followId = followId;
return this;
}
public final boolean isNormalInfoAsWarn() {
return normalInfoAsWarn;
}
public final TransferArgs setNormalInfoAsWarn(
final boolean normalInfoAsWarn) {
this.normalInfoAsWarn = normalInfoAsWarn;
return this;
}
public final boolean isNolog() {
return nolog;
}
public final TransferArgs setNolog(final boolean nolog) {
this.nolog = nolog;
return this;
}
}