/*
* Copyright 2015 herd contributors
*
* 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 org.finra.herd.tools.uploader;
import java.io.FileNotFoundException;
import org.apache.commons.cli.Option;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.finra.herd.core.ArgumentParser;
import org.finra.herd.model.dto.RegServerAccessParamsDto;
import org.finra.herd.model.dto.S3FileTransferRequestParamsDto;
import org.finra.herd.tools.common.ToolsCommonConstants;
import org.finra.herd.tools.common.databridge.DataBridgeApp;
/**
* The "main" uploader data bridge application command line tool.
*/
public class UploaderApp extends DataBridgeApp
{
private static final Logger LOGGER = LoggerFactory.getLogger(UploaderApp.class);
// The uploader specific command line options.
private Option maxRetryAttemptsOpt;
private Option retryDelaySecsOpt;
private Option createNewVersionOpt;
private Option rrsOpt;
private Option forceOpt;
// Integer values for command line options that are of type "Integer".
private Integer maxRetryAttempts;
private Integer retryDelaySecs;
// An argument parser for the application.
private ArgumentParser argParser;
private static final Integer MAX_RETRY_ATTEMPTS_DEFAULT = 5; // Default number of business object data registration retry attempts.
private static final Integer MAX_RETRY_ATTEMPTS_MIN = 0; // Minimum number of business object data registration retry attempts.
private static final Integer MAX_RETRY_ATTEMPTS_MAX = 10; // Maximum number of business object data registration retry attempts.
private static final Integer RETRY_DELAY_SECS_DEFAULT = 120; // Default delay in seconds between the business object data registration retry attempts.
private static final Integer RETRY_DELAY_SECS_MIN = 0; // Minimum delay in seconds between the business object data registration retry attempts.
private static final Integer RETRY_DELAY_SECS_MAX = 900; // Maximum delay in seconds between the business object data registration retry attempts.
/**
* Constructs a new UploaderApp instance.
*/
public UploaderApp()
{
argParser = new ArgumentParser("herd-uploader-app");
// Create command line options specific to the uploader. Other common options will be handled by the base class.
createNewVersionOpt = argParser
.addArgument("V", "createNewVersion", false, "If not set, only initial version of the business object data is allowed to be created.", false);
rrsOpt = argParser.addArgument("r", "rrs", false, "If set, the data will be saved in Reduced Redundancy Storage.", false);
maxRetryAttemptsOpt = argParser.addArgument("R", "maxRetryAttempts", true,
"The maximum number of the business object data registration retry attempts that uploader would perform before rolling back the upload.", false);
retryDelaySecsOpt =
argParser.addArgument("D", "retryDelaySecs", true, "The delay in seconds between the business object data registration retry attempts.", false);
forceOpt = argParser.addArgument("f", "force", false,
"If set, allows upload to proceed when the latest version of the business object data has UPLOADING status by invalidating that version.", false);
}
/**
* Parses the command line arguments using the specified argument parser.
*
* @param args the command line arguments.
* @param applicationContext the Spring application context.
*
* @return the return value if the application should exit or null if the application can continue.
*/
@Override
protected ReturnValue parseCommandLineArguments(String[] args, ApplicationContext applicationContext)
{
ReturnValue returnValue = super.parseCommandLineArguments(args, applicationContext);
// Stop the processing if return value is not null.
if (returnValue != null)
{
return returnValue;
}
try
{
// Extract uploader specific Integer option values here to catch any NumberFormatException exceptions.
maxRetryAttempts = argParser.getIntegerValue(maxRetryAttemptsOpt, MAX_RETRY_ATTEMPTS_DEFAULT, MAX_RETRY_ATTEMPTS_MIN, MAX_RETRY_ATTEMPTS_MAX);
retryDelaySecs = argParser.getIntegerValue(retryDelaySecsOpt, RETRY_DELAY_SECS_DEFAULT, RETRY_DELAY_SECS_MIN, RETRY_DELAY_SECS_MAX);
}
catch (Exception ex)
{
// Log a friendly error and return a failure which will cause the application to exit.
LOGGER.error("Error parsing command line arguments: " + ex.getMessage() + "\n" + argParser.getUsageInformation());
return ReturnValue.FAILURE;
}
// The command line arguments were all parsed successfully so return null to continue processing.
return null;
}
/**
* Parses the command line arguments and calls the controller to process the upload.
*
* @param args the command line arguments passed to the program.
*
* @return the return value of the application.
* @throws Exception if there are problems performing the upload.
*/
@Override
public ReturnValue go(String[] args) throws Exception
{
// Create the Spring application context.
ApplicationContext applicationContext = createApplicationContext();
// Parse the command line arguments and return a return value if we shouldn't continue processing (e.g. we displayed usage information, etc.).
ReturnValue returnValue = parseCommandLineArguments(args, applicationContext);
if (returnValue != null)
{
return returnValue;
}
// Create an instance of S3 file transfer request parameters DTO.
S3FileTransferRequestParamsDto params =
S3FileTransferRequestParamsDto.builder().localPath(argParser.getStringValue(localPathOpt)).useRrs(argParser.getBooleanValue(rrsOpt))
.awsAccessKeyId(argParser.getStringValue(s3AccessKeyOpt)).awsSecretKey(argParser.getStringValue(s3SecretKeyOpt))
.s3Endpoint(argParser.getStringValue(s3EndpointOpt)).maxThreads(maxThreads).httpProxyHost(argParser.getStringValue(httpProxyHostOpt))
.httpProxyPort(httpProxyPort).socketTimeout(argParser.getIntegerValue(socketTimeoutOpt)).build();
// Call the controller with the user specified parameters to perform the upload.
UploaderController controller = applicationContext.getBean(UploaderController.class);
RegServerAccessParamsDto regServerAccessParamsDto =
RegServerAccessParamsDto.builder().regServerHost(regServerHost).regServerPort(regServerPort).useSsl(useSsl)
.username(argParser.getStringValue(usernameOpt)).password(argParser.getStringValue(passwordOpt)).build();
controller.performUpload(regServerAccessParamsDto, argParser.getFileValue(manifestPathOpt), params, argParser.getBooleanValue(createNewVersionOpt),
argParser.getBooleanValue(forceOpt), maxRetryAttempts, retryDelaySecs);
// No exceptions were returned so return success.
return ReturnValue.SUCCESS;
}
@Override
public ArgumentParser getArgumentParser()
{
return argParser;
}
/**
* The main method of the Uploader Application.
*
* @param args the command line arguments passed to the program.
*
* @throws FileNotFoundException if the logging file couldn't be found.
*/
@SuppressWarnings("PMD.DoNotCallSystemExit") // Using System.exit is allowed for an actual application to exit.
public static void main(String[] args) throws FileNotFoundException
{
ReturnValue returnValue;
try
{
// Initialize Log4J with the resource. The configuration itself can use "monitorInterval" to have it refresh if it came from a file.
LoggerContext loggerContext = Configurator.initialize(null, ToolsCommonConstants.LOG4J_CONFIG_LOCATION);
// For some initialization errors, a null context will be returned.
if (loggerContext == null)
{
// We shouldn't get here since we already checked if the location existed previously.
throw new IllegalArgumentException("Invalid configuration found at resource location: \"" + ToolsCommonConstants.LOG4J_CONFIG_LOCATION + "\".");
}
UploaderApp uploaderApp = new UploaderApp();
returnValue = uploaderApp.go(args);
}
catch (Exception e)
{
LOGGER.error("Error running herd uploader. {}", e.toString(), e);
returnValue = ReturnValue.FAILURE;
}
// Exit with the return code.
System.exit(returnValue.getReturnCode());
}
}