AbstractTransfer.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.waarp.common.command.exception.CommandAbstractException;
import org.waarp.common.database.data.AbstractDbData.UpdatedInfo;
import org.waarp.common.database.exception.WaarpDatabaseException;
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.FIELDS;
import org.waarp.openr66.commander.ClientRunner;
import org.waarp.openr66.configuration.FileBasedConfiguration;
import org.waarp.openr66.context.ErrorCode;
import org.waarp.openr66.context.R66Result;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.filesystem.R66Dir;
import org.waarp.openr66.context.filesystem.R66File;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.database.data.DbRule;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.configuration.Messages;
import org.waarp.openr66.protocol.configuration.PartnerConfiguration;
import org.waarp.openr66.protocol.exception.OpenR66DatabaseGlobalException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
import org.waarp.openr66.protocol.localhandler.packet.AbstractLocalPacket;
import org.waarp.openr66.protocol.localhandler.packet.InformationPacket.ASKENUM;
import org.waarp.openr66.protocol.localhandler.packet.RequestPacket;
import org.waarp.openr66.protocol.localhandler.packet.ValidPacket;
import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
import org.waarp.openr66.protocol.utils.ChannelUtils;
import org.waarp.openr66.protocol.utils.FileUtils;
import org.waarp.openr66.protocol.utils.R66Future;
import java.io.File;
import java.net.SocketAddress;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static org.waarp.common.database.DbConstant.*;
import static org.waarp.common.file.filesystembased.FilesystemBasedFileImpl.*;
/**
* Abstract class for Transfer operation
*/
public abstract class AbstractTransfer implements Runnable {
private static final Pattern COMPILE_NEWLINE = Pattern.compile("\n");
public static final String TIMESTAMP_FORMAT = "yyyyMMddHHmmss";
/**
* Internal Logger
*/
protected static WaarpLogger logger;
protected static final String INFO_ARGS =
Messages.getString("AbstractTransfer.0") //$NON-NLS-1$
+ Messages.getString("Message.OutputFormat");
protected static final String NO_INFO_ARGS = "noinfo";
protected final TransferArgs transferArgs = new TransferArgs();
protected final R66Future future;
protected boolean normalInfoAsWarn = true;
/**
* @param clasz Class of Client Transfer
* @param future
* @param transferArgs
*/
protected AbstractTransfer(final Class<?> clasz, final R66Future future,
final TransferArgs transferArgs) {
this(clasz, future, transferArgs.getFilename(), transferArgs.getRulename(),
transferArgs.getTransferInfo(), transferArgs.isMD5(),
transferArgs.getRemoteHost(), transferArgs.getBlockSize(),
transferArgs.getId(), transferArgs.getStartTime());
}
/**
* @param clasz Class of Client Transfer
* @param future
* @param filename
* @param rulename
* @param transferInfo
* @param isMD5
* @param remoteHost
* @param blocksize
* @param id
*/
protected AbstractTransfer(final Class<?> clasz, final R66Future future,
final String filename, final String rulename,
final String transferInfo, final boolean isMD5,
final String remoteHost, final int blocksize,
final long id, final Timestamp timestart) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(clasz);
}
this.future = future;
this.transferArgs.setFilename(filename);
this.transferArgs.setRulename(rulename);
this.transferArgs.setTransferInfo(transferInfo);
this.transferArgs.setMD5(isMD5);
if (Configuration.configuration.getAliases().containsKey(remoteHost)) {
this.transferArgs.setRemoteHost(
Configuration.configuration.getAliases().get(remoteHost));
} else {
this.transferArgs.setRemoteHost(remoteHost);
}
this.transferArgs.setBlockSize(blocksize);
this.transferArgs.setId(id);
transferArgs.setStartTime(timestart);
}
/**
* @param host
* @param localChannelReference
* @param packet
* @param future
*
* @return True if OK
*/
public static boolean sendValidPacket(final DbHostAuth host,
final LocalChannelReference localChannelReference,
final AbstractLocalPacket packet,
final R66Future future) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
true);
} catch (final OpenR66ProtocolPacketException e) {
RequestTransfer.logger.error(
Messages.getString("RequestTransfer.63") + host); //$NON-NLS-1$
localChannelReference.close();
RequestTransfer.logger.debug("Bad Protocol", e);
future.setResult(
new R66Result(e, null, true, ErrorCode.TransferError, null));
future.setFailure(e);
return false;
}
future.awaitOrInterruptible();
localChannelReference.close();
return true;
}
/**
* Initiate the Request and return a potential DbTaskRunner
*
* @return null if an error occurs or a DbTaskRunner
*/
protected final DbTaskRunner initRequest() {
final DbRule dbRule;
try {
dbRule = new DbRule(transferArgs.getRulename());
} catch (final WaarpDatabaseException e) {
logger.error("Cannot get Rule: " + transferArgs.getRulename() + ": {}",
e.getMessage(), e);
future.setResult(
new R66Result(new OpenR66DatabaseGlobalException(e), null, true,
ErrorCode.Internal, null));
future.setFailure(e);
return null;
}
int mode = dbRule.getMode();
if (transferArgs.isMD5()) {
mode = RequestPacket.getModeMD5(mode);
}
final DbTaskRunner taskRunner;
if (transferArgs.getId() != ILLEGALVALUE) {
try {
taskRunner = new DbTaskRunner(transferArgs.getId(),
transferArgs.getRemoteHost());
} catch (final WaarpDatabaseException e) {
logger.error("Cannot get task: {}", e.getMessage());
future.setResult(
new R66Result(new OpenR66DatabaseGlobalException(e), null, true,
ErrorCode.QueryRemotelyUnknown, null));
future.setFailure(e);
return null;
}
// requested
taskRunner.setSenderByRequestToValidate(true);
if (transferArgs.getTransferInfo() != null &&
!transferArgs.getTransferInfo().equals(NO_INFO_ARGS)) {
taskRunner.setFileInformation(transferArgs.getTransferInfo());
}
if (transferArgs.getStartTime() != null) {
taskRunner.setStart(transferArgs.getStartTime());
}
} else {
long originalSize = -1;
if (RequestPacket.isSendMode(mode) &&
!RequestPacket.isThroughMode(mode)) {
File file = new File(transferArgs.getFilename());
// Change dir
try {
final R66Session session = new R66Session(false);
session.getAuth().specialNoSessionAuth(false,
Configuration.configuration.getHostId());
session.getDir().changeDirectory(dbRule.getSendPath());
final R66File filer66 =
FileUtils.getFile(logger, session, transferArgs.getFilename(),
true, true, false, null);
file = filer66.getTrueFile();
} catch (final CommandAbstractException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
} catch (final OpenR66RunnerErrorException ignored) {
SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
}
if (canRead(file)) {
originalSize = file.length();
if (originalSize == 0) {
originalSize = -1;
}
}
}
logger.debug("Filesize: {}", originalSize);
final String sep =
PartnerConfiguration.getSeparator(transferArgs.getRemoteHost());
final RequestPacket request =
new RequestPacket(transferArgs.getRulename(), mode,
transferArgs.getFilename(),
transferArgs.getBlockSize(), 0,
transferArgs.getId(),
transferArgs.getTransferInfo(), originalSize, sep);
// Not isRecv since it is the requester, so send => isRetrieve is true
final boolean isRetrieve = !RequestPacket.isRecvMode(request.getMode());
try {
taskRunner = new DbTaskRunner(dbRule, isRetrieve, request,
transferArgs.getRemoteHost(),
transferArgs.getStartTime());
} catch (final WaarpDatabaseException e) {
logger.error("Cannot get task: {}", e.getMessage());
future.setResult(
new R66Result(new OpenR66DatabaseGlobalException(e), null, true,
ErrorCode.Internal, null));
future.setFailure(e);
return null;
}
}
try {
taskRunner.saveStatus();
} catch (final OpenR66RunnerErrorException e) {
logger.error("Cannot save task: {}", e.getMessage());
future.setResult(
new R66Result(new OpenR66DatabaseGlobalException(e), null, true,
ErrorCode.Internal, null));
future.setFailure(e);
return null;
}
return taskRunner;
}
protected static String rhost;
protected static String localFilename;
protected static String rule;
protected static String transferInfo;
protected static boolean ismd5;
protected static int block = 0x10000; // 64K
// as
// default
protected static boolean nolog;
protected static long idt = ILLEGALVALUE;
protected static Timestamp ttimestart;
protected static boolean snormalInfoAsWarn = true;
protected static String sFollowId;
protected final static void clear() {
rhost = null;
localFilename = null;
rule = null;
transferInfo = null;
ismd5 = false;
block = 0x10000; // 64K
nolog = false;
idt = ILLEGALVALUE;
ttimestart = null;
snormalInfoAsWarn = true;
sFollowId = null;
}
/**
* Parse the parameter and set current values
*
* @param args
* @param submitOnly True if the client is only a submitter (through
* database)
*
* @return True if all parameters were found and correct
*/
protected static boolean getParams(final String[] args,
final boolean submitOnly) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
if (args.length < 2) {
logger.error(INFO_ARGS);
return false;
}
if (submitOnly) {
if (!FileBasedConfiguration.setSubmitClientConfigurationFromXml(
Configuration.configuration, args[0])) {
logger.error(Messages.getString(
"Configuration.NeedCorrectConfig")); //$NON-NLS-1$
return false;
}
} else if (!FileBasedConfiguration.setClientConfigurationFromXml(
Configuration.configuration, args[0])) {
logger.error(
Messages.getString("Configuration.NeedCorrectConfig")); //$NON-NLS-1$
return false;
}
final TransferArgs transferArgsLocal = getParamsInternal(1, args);
if (transferArgsLocal == null) {
return false;
}
rhost = transferArgsLocal.getRemoteHost();
localFilename = transferArgsLocal.getFilename();
rule = transferArgsLocal.getRulename();
transferInfo = transferArgsLocal.getTransferInfo();
ismd5 = transferArgsLocal.isMD5();
block = transferArgsLocal.getBlockSize();
idt = transferArgsLocal.getId();
ttimestart = transferArgsLocal.getStartTime();
sFollowId = transferArgsLocal.getFollowId();
return true;
}
/**
* Internal getParams without configuration initialization, but still as
* first argument<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 which rank to start on args
* @param args
*
* @return TransferArgs if OK, null if wrong initialization
*/
public static TransferArgs getParamsInternal(final int rank,
final String[] args) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
final TransferArgs transferArgs1 =
TransferArgs.getParamsInternal(rank, args, true);
if (transferArgs1 == null) {
return null;
}
snormalInfoAsWarn = transferArgs1.isNormalInfoAsWarn();
nolog = transferArgs1.isNolog();
return transferArgs1;
}
/**
* Shared code for finalize one Transfer request in error
*
* @param runner
* @param taskRunner
*/
protected final void finalizeInErrorTransferRequest(final ClientRunner runner,
final DbTaskRunner taskRunner,
final ErrorCode code) {
if (runner.getLocalChannelReference() != null) {
runner.getLocalChannelReference().setErrorMessage(code.getMesg(), code);
}
taskRunner.setErrorTask();
try {
taskRunner.forceSaveStatus();
taskRunner.run();
taskRunner.saveStatus();
} catch (final OpenR66RunnerErrorException e1) {
runner.changeUpdatedInfo(UpdatedInfo.INERROR, code, true);
}
}
public final void setNormalInfoAsWarn(final boolean normalInfoAsWarn1) {
normalInfoAsWarn = normalInfoAsWarn1;
}
public final List<String> getRemoteFiles(final String[] localfilenames,
final String requested,
final NetworkTransaction networkTransaction) {
final List<String> files = new ArrayList<String>();
for (final String filenameNew : localfilenames) {
if (!(filenameNew.contains("*") || filenameNew.contains("?") ||
filenameNew.contains("~"))) {
files.add(filenameNew);
} else {
// remote query
final R66Future futureInfo = new R66Future(true);
logger.info("{} {} to {}", Messages.getString("Transfer.3"),
filenameNew, requested); //$NON-NLS-1$
final RequestInformation info =
new RequestInformation(futureInfo, requested,
transferArgs.getRulename(), filenameNew,
(byte) ASKENUM.ASKLIST.ordinal(), -1, false,
networkTransaction);
info.run();
futureInfo.awaitOrInterruptible();
if (futureInfo.isSuccess()) {
final ValidPacket valid =
(ValidPacket) futureInfo.getResult().getOther();
if (valid != null) {
final String line = valid.getSheader();
final String[] lines = COMPILE_NEWLINE.split(line);
for (final String string : lines) {
final File tmpFile = new File(string);
files.add(tmpFile.getPath());
}
}
} else {
logger.error(Messages.getString("Transfer.6") + filenameNew + " to " +
requested + ": " + (futureInfo.getCause() == null? "" :
futureInfo.getCause().getMessage())); //$NON-NLS-1$
}
}
}
return files;
}
public final List<String> getLocalFiles(final DbRule dbrule,
final String[] localfilenames) {
final List<String> files = new ArrayList<String>();
final R66Session session = new R66Session(false);
session.getAuth().specialNoSessionAuth(false,
Configuration.configuration.getHostId());
final R66Dir dir = new R66Dir(session);
try {
dir.changeDirectory(dbrule.getSendPath());
} catch (final CommandAbstractException e) {
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
if (localfilenames != null) {
for (final String filenameNew : localfilenames) {
if (!(filenameNew.contains("*") || filenameNew.contains("?") ||
filenameNew.contains("~"))) {
logger.info("Direct add: {}", filenameNew);
files.add(filenameNew);
} else {
// local: must check
logger.info("Local Ask for {} from {}", filenameNew,
dir.getFullPath());
final List<String> list;
try {
list = dir.list(filenameNew);
if (list != null) {
files.addAll(list);
}
} catch (final CommandAbstractException e) {
logger.warn(
Messages.getString("Transfer.14") + filenameNew + " : " +
e.getMessage()); //$NON-NLS-1$
}
}
}
}
return files;
}
/**
* Helper to connect
*
* @param host
* @param future
* @param networkTransaction
*
* @return localChannelReference not null if ok
*/
public static LocalChannelReference tryConnect(final DbHostAuth host,
final R66Future future,
final NetworkTransaction networkTransaction) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
logger.info("Try RequestTransfer to {}", host);
final SocketAddress socketAddress;
try {
socketAddress = host.getSocketAddress();
} catch (final IllegalArgumentException e) {
logger.debug("Cannot connect to {}", host);
future.setResult(new R66Result(new OpenR66ProtocolNoConnectionException(
"Cannot connect to server " + host.getHostid()), null, true,
ErrorCode.ConnectionImpossible, null));
future.setFailure(future.getResult().getException());
return null;
}
final boolean isSSL = host.isSsl();
final LocalChannelReference localChannelReference =
networkTransaction.createConnectionWithRetry(socketAddress, isSSL,
future);
if (localChannelReference == null) {
logger.debug("Cannot connect to {}", host);
future.setResult(new R66Result(new OpenR66ProtocolNoConnectionException(
"Cannot connect to server " + host.getHostid()), null, true,
ErrorCode.ConnectionImpossible, null));
future.setFailure(future.getResult().getException());
return null;
}
return localChannelReference;
}
protected static void prepareKoOutputFormat(final R66Future future,
final R66Result result,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
partialOutputFormat(result.getRunner(), outputFormat);
if (result.getRunner().getErrorInfo() == ErrorCode.Warning) {
outputFormat.setValue(FIELDS.status.name(), 1);
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("Transfer.Status") +
Messages.getString(
"RequestInformation.Warned")); //$NON-NLS-1$
} else {
outputFormat.setValue(FIELDS.status.name(), 2);
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("Transfer.Status") +
Messages.getString(
"RequestInformation.Failure")); //$NON-NLS-1$
}
final String mesg = future.getCause() != null//NOSONAR
? future.getCause().getMessage() : "";
if (result.getRunner().getErrorInfo() == ErrorCode.Warning) {
logger.warn(outputFormat.loggerOut() + " : {}" + mesg);
} else {
logger.error(outputFormat.loggerOut() + " : {}" + mesg);
}
if (future.getCause() != null) {
outputFormat.setValue(FIELDS.error.name(),
future.getCause().getMessage());
}
}
protected static void prepareKoOutputFormat(final R66Future future,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
outputFormat.setValue(FIELDS.status.name(), 2);
outputFormat.setValue(FIELDS.statusTxt.name(), Messages.getString(
"Transfer.FailedNoId")); //$NON-NLS-1$
outputFormat.setValue(FIELDS.remote.name(), rhost);
logger.error(outputFormat.loggerOut(), future.getCause());
outputFormat.setValue(FIELDS.error.name(), future.getCause().getMessage());
}
protected static void prepareOkOutputFormat(final long delay,
final R66Result result,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
if (result.getRunner().getErrorInfo() == ErrorCode.Warning) {
outputFormat.setValue(FIELDS.status.name(), 1);
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("Transfer.Status") +
Messages.getString(
"RequestInformation.Warned")); //$NON-NLS-1$
} else {
outputFormat.setValue(FIELDS.status.name(), 0);
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("Transfer.Status") +
Messages.getString(
"RequestInformation.Success")); //$NON-NLS-1$
}
partialOutputFormat(result.getRunner(), outputFormat);
outputFormat.setValue("filefinal", result.getFile() != null?
result.getFile().toString() : "no file");
outputFormat.setValue("delay", delay);
}
private static void partialOutputFormat(final DbTaskRunner runner,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
outputFormat.setValue(FIELDS.remote.name(), rhost);
outputFormat.setValue(FIELDS.ruleid.name(), runner.getRuleId());
outputFormat.setValueString(runner.getJson());
outputFormat.setValue(FIELDS.statusCode.name(),
runner.getErrorInfo().getCode());
outputFormat.setValue(FIELDS.specialid.name(), runner.getSpecialId());
outputFormat.setValue(FIELDS.finalPath.name(), runner.getFilename());
outputFormat.setValue(FIELDS.requested.name(), runner.getRequested());
outputFormat.setValue(FIELDS.requester.name(), runner.getRequester());
outputFormat.setValue(FIELDS.fileInformation.name(),
runner.getFileInformation());
outputFormat.setValue(FIELDS.transferInformation.name(),
runner.getTransferInfo());
outputFormat.setValue(FIELDS.originalSize.name(), runner.getOriginalSize());
outputFormat.setValue(FIELDS.originalPath.name(),
runner.getOriginalFilename());
}
public static void prepareSubmitKoOutputFormat(final R66Future future,
final DbTaskRunner runner,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
outputFormat.setValue(FIELDS.status.name(), 2);
if (runner == null) {
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("SubmitTransfer.3") +
Messages.getString(
"Transfer.FailedNoId")); //$NON-NLS-1$
outputFormat.setValue(FIELDS.remote.name(), rhost);
} else {
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("SubmitTransfer.3") +
Messages.getString(
"RequestInformation.Failure")); //$NON-NLS-1$
partialOutputFormat(runner, outputFormat);
}
logger.error(outputFormat.loggerOut(), future.getCause());
if (future.getCause() != null) {
outputFormat.setValue(FIELDS.error.name(),
future.getCause().getMessage());
}
}
public static void prepareSubmitOkOutputFormat(final DbTaskRunner runner,
final OutputFormat outputFormat) {
if (logger == null) {
logger = WaarpLoggerFactory.getLogger(AbstractTransfer.class);
}
outputFormat.setValue(FIELDS.status.name(), 0);
outputFormat.setValue(FIELDS.statusTxt.name(),
Messages.getString("SubmitTransfer.3") +
Messages.getString(
"RequestInformation.Success")); //$NON-NLS-1$
partialOutputFormat(runner, outputFormat);
}
}