All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.finos.tracdap.common.startup.StandardArgsProcessor Maven / Gradle / Ivy

/*
 * Licensed to the Fintech Open Source Foundation (FINOS) under one or
 * more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * FINOS licenses this file to you 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.finos.tracdap.common.startup;

import org.finos.tracdap.common.config.ConfigKeys;
import org.finos.tracdap.common.config.ConfigManager;
import org.finos.tracdap.common.exception.EStartup;
import org.apache.commons.cli.*;

import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Collections;
import java.util.stream.Collectors;


/**
 * Process command line arguments for TRAC services and utilities.
 *
 * 

The processor creates a StandardArgs object, which holds the * location of the primary configuration file, a key for unlocking * any secrets held in the configuration and the working directory * of the process. This is enough information to create a * ConfigManager, load in the primary configuration file and * initialise a TRAC service or utility.

* *

The processor has the ability to process --task options. These allow * one or more tasks to be specified on the command line and are * intended for use with command line utilities. To use task processing, * supply a list of available tasks to processArgs(). The processor will * only accept tasks that are in the list of available tasks. If an * available task is passed in with a parameter then tasks with this name * will accept a parameter, otherwise they will not.

* *

For an example of task processing, look in trac-tools/deploy-metadb.

* * @see ConfigManager */ public class StandardArgsProcessor { /** * Read standard args from the command line. * *

This variant of processArgs() does not enable task processing.

* * @param appName Name of the application, displayed in help messages * @param args The command line args received on startup * @param envVariables Environment variables. * @return A set of standard args suitable for creating a ConfigManager * @throws EStartup The command line args could not be parsed, or --help was specified */ public static StandardArgs processArgs(String appName, String[] args, Map envVariables) { return processArgs(appName, args, null, envVariables); } /** * Read standard args from the command line. * *

This variant of processArgs() can be used to enable task processing.

* * @param appName Name of the application, displayed in help messages * @param args The command line args received on startup * @param availableTasks If present, enable task processing and supply the list of available tasks * @param envVariables Environment variables. * @return A set of standard args suitable for creating a ConfigManager * @throws EStartup The command line args could not be parsed, or --help was specified */ public static StandardArgs processArgs(String appName, String[] args, List availableTasks, Map envVariables) { if (envVariables == null) { envVariables = Collections.emptyMap(); } var usingTasks = availableTasks != null && !availableTasks.isEmpty(); var helpOptions = helpOptions(usingTasks); var options = standardOptions(usingTasks); try { var parser = new DefaultParser(); var helpCommand = parser.parse(helpOptions, args, true); handleHelpCommands(appName, helpCommand, options, availableTasks); var command = parser.parse(options, args, false); var workingDir = Paths.get(".").toAbsolutePath().normalize(); var configFile = command.getOptionValue("config"); var secretKey = command.getOptionValue("secret-key"); if (secretKey == null) { secretKey = envVariables.get(ConfigKeys.SECRET_KEY_ENV); } var tasks = usingTasks ? processTasks(command, availableTasks) : null; return new StandardArgs(workingDir, configFile, secretKey, tasks); } catch (ParseException e) { var message = "Invalid command line: " + e.getMessage(); System.err.println(message); var formatter = new HelpFormatter(); formatter.printUsage(new PrintWriter(System.out), 80, appName, options); formatter.printHelp(appName, options); throw EStartup.quietShutdown(-1); } } private static void handleHelpCommands( String appName, CommandLine helpCommand, Options options, List tasks) { if (helpCommand.hasOption("help")) { var formatter = new HelpFormatter(); formatter.printHelp(appName, options); throw EStartup.quietShutdown(0); } if (helpCommand.hasOption("task-list")) { System.out.println(appName + " - available tasks:"); for (var task : tasks) { var taskInfoFormat = "%-35s %s"; var taskUsage = task.hasArg() ? task.getTaskName() + " " + String.join(" ", task.getTaskArgList()) : task.getTaskName(); System.out.printf(taskInfoFormat, taskUsage, task.getTaskDescription()); System.out.println(); } throw EStartup.quietShutdown(0); } } private static List processTasks(CommandLine command, List availableTasks) { var taskMap = availableTasks.stream() .collect(Collectors.toMap(StandardArgs.Task::getTaskName, task -> task)); var taskArgs = command.getOptionValues("task"); var tasks = new ArrayList(); for (var argIndex = 0; argIndex < taskArgs.length; argIndex++) { var taskName = taskArgs[argIndex]; if (!taskMap.containsKey(taskName)) throw new EStartup(String.format("Unknown task: [%s]", taskName)); var taskDef = taskMap.get(taskName); if (taskDef.hasArg()) { if (taskDef.argCount() > taskArgs.length - argIndex - 1) { var message = String.format( "Task [%s] requires %d argument(s): %s", taskName, taskDef.argCount(), String.join(" ", taskDef.getTaskArgList())); throw new EStartup(message); } var args = Arrays.copyOfRange(taskArgs, argIndex + 1, argIndex + 1 + taskDef.argCount()); argIndex += taskDef.argCount(); var task = new StandardArgs.Task(taskName, Arrays.asList(args), ""); tasks.add(task); } else { var task = new StandardArgs.Task(taskName); tasks.add(task); } } return List.copyOf(tasks); } private static Options standardOptions(boolean usingTasks) { return buildOOptions(usingTasks, false); } private static Options helpOptions(boolean usingTasks) { return buildOOptions(usingTasks, true); } private static Options buildOOptions(boolean usingTasks, boolean buildingHelp) { var options = new Options(); options.addOption(Option.builder() .desc("Location of the primary config file") .longOpt("config") .hasArg() .argName("config_file") .required(!buildingHelp) .build()); options.addOption(Option.builder() .desc("Master key used to unlock secrets (use depends on secret.type in the primary config)") .longOpt("secret-key") .hasArg() .argName("secret_key") .build()); options.addOption(Option.builder() .desc("Display this help and then quit") .longOpt("help") .build()); if (usingTasks) { options.addOption(Option.builder() .desc("Perform a specific task") .longOpt("task") .hasArgs() .argName("task") .valueSeparator(':') .required(!buildingHelp) .build()); options.addOption(Option.builder() .desc("Display the list of available tasks and then quit") .longOpt("task-list") .build()); } return options; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy