Java tutorial
/* * Copyright (C) 2016 Stratio (http://stratio.com) * * 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.apache.sqoop.shell.utils; import org.apache.commons.cli.CommandLine; import org.apache.commons.lang.StringUtils; import org.apache.sqoop.model.InputEditable; import org.apache.sqoop.model.MBooleanInput; import org.apache.sqoop.model.MDateTimeInput; import org.apache.sqoop.model.MLink; import org.apache.sqoop.model.MEnumInput; import org.apache.sqoop.model.MConfig; import org.apache.sqoop.model.MInput; import org.apache.sqoop.model.MIntegerInput; import org.apache.sqoop.model.MListInput; import org.apache.sqoop.model.MLongInput; import org.apache.sqoop.model.MMapInput; import org.apache.sqoop.model.MJob; import org.apache.sqoop.model.MNamedElement; import org.apache.sqoop.model.MStringInput; import org.apache.sqoop.validation.Message; import org.apache.sqoop.validation.Status; import org.joda.time.DateTime; import java.io.IOException; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.ResourceBundle; import jline.console.ConsoleReader; import static org.apache.sqoop.shell.ShellEnvironment.*; /** * Convenient methods for retrieving user input and CLI options. */ public final class ConfigFiller { /** * Internal input that will be reused for loading names for link and * job objects. */ private static MStringInput nameInput = new MStringInput("object-name", false, InputEditable.ANY, StringUtils.EMPTY, (short) 25, Collections.EMPTY_LIST); /** * Fill job object based on CLI options. * * @param line Associated console reader object * @param job Job that user is suppose to fill in * @return True if we filled all inputs, false if user has stopped processing * @throws IOException */ public static boolean fillJob(CommandLine line, MJob job) throws IOException { job.setName(line.getOptionValue("name")); return fillJobConfig(line, job.getFromJobConfig().getConfigs(), job.getToJobConfig().getConfigs(), job.getDriverConfig().getConfigs()); } /** * Fill link object based on CLI options. * * @param line Associated command line options * @param link Link that user is suppose to fill in * @return True if we filled all inputs, false if user has stopped processing * @throws IOException */ public static boolean fillLink(CommandLine line, MLink link) throws IOException { link.setName(line.getOptionValue("name")); return fillLinkConfig(line, link.getConnectorLinkConfig().getConfigs()); } /** * Load CLI options for link configs * * @param line CLI options container * @param linkConfig from config to read or edit * @return * @throws IOException */ public static boolean fillLinkConfig(CommandLine line, List<MConfig> linkConfig) throws IOException { return fillConfigs("link", linkConfig, line); } /** * Load CLI options for job configs * * @param line CLI options container * @param fromConfig from config to read or edit * @param toConfig to config to read or edit * @param driverConfig driver config to read or edit * @return * @throws IOException */ public static boolean fillJobConfig(CommandLine line, List<MConfig> fromConfig, List<MConfig> toConfig, List<MConfig> driverConfig) throws IOException { return fillConfigs("from", fromConfig, line) && fillConfigs("to", toConfig, line) && fillConfigs("driver", driverConfig, line); } /** * Load all CLI options for a list of configs. * * @param prefix placed at the beginning of the CLI option key * @param configs Forms to read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillConfigs(String prefix, List<MConfig> configs, CommandLine line) throws IOException { for (MConfig config : configs) { if (!fillConfig(prefix, config, line)) { return false; } } return true; } /** * Load all CLI options for a particular config. * * @param prefix placed at the beginning of the CLI option key * @param config Config to read or edit * @param line CLI options container * @return * @throws IOException */ @SuppressWarnings("rawtypes") static boolean fillConfig(String prefix, MConfig config, CommandLine line) throws IOException { for (MInput input : config.getInputs()) { if (!fillInput(prefix, input, line)) { return false; } } return true; } /** * Load CLI option. * Chooses the appropriate 'fill' method to use based on input type. * * Keys for CLI options are automatically created from the 'prefix' argument * and 'input' argument: <prefix>-<config name>-<input name> * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ @SuppressWarnings("rawtypes") public static boolean fillInput(String prefix, MInput input, CommandLine line) throws IOException { // Based on the input type, let's perconfig specific load switch (input.getType()) { case STRING: assert input instanceof MStringInput; return fillInputString(prefix, (MStringInput) input, line); case INTEGER: assert input instanceof MIntegerInput; return fillInputInteger(prefix, (MIntegerInput) input, line); case LONG: assert input instanceof MLongInput; return fillInputLong(prefix, (MLongInput) input, line); case BOOLEAN: assert input instanceof MBooleanInput; return fillInputBoolean(prefix, (MBooleanInput) input, line); case MAP: assert input instanceof MMapInput; return fillInputMap(prefix, (MMapInput) input, line); case ENUM: assert input instanceof MEnumInput; return fillInputEnum(prefix, (MEnumInput) input, line); case LIST: assert input instanceof MListInput; return fillInputList(prefix, (MListInput) input, line); case DATETIME: assert input instanceof MDateTimeInput; return fillInputDateTime(prefix, (MDateTimeInput) input, line); default: println("Unsupported data type " + input.getType()); return true; } } /** * Load CLI options for datetime type. * * Supports the following two types of format: * <ul> * <li> ISO8601 format (yyyy-MM-ddTHH:mm:ss.SSSZZ) * <li> Long which represents the milliseconds from 1970-01-01T00:00:00Z * </ul> * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputDateTime(String prefix, MDateTimeInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { DateTime dt = parseDateTime(line.getOptionValue(opt)); if (dt == null) { errorMessage(input, "Input is not valid DateTime format"); return false; } input.setValue(dt); } else { input.setEmpty(); } return true; } private static DateTime parseDateTime(String value) { DateTime dt = null; try { dt = new DateTime(Long.parseLong(value)); } catch (NumberFormatException nfe) { // value is not numeric string try { dt = DateTime.parse(value); } catch (IllegalArgumentException iae) { // value is not valid ISO8601 format } } return dt; } /** * Load CLI options for list type. * * Parses elements that take the config "<element1>&<element2>&...". * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputList(String prefix, MListInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { String value = line.getOptionValue(opt); List<String> values = new LinkedList<String>(); for (String element : value.split("&")) { values.add(element); } input.setValue(values); } else { input.setEmpty(); } return true; } /** * Load CLI option for enum type. * * Currently only supports numeric values. * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputEnum(String prefix, MEnumInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { String value = line.getOptionValue(opt); int index = java.util.Arrays.asList(input.getValues()).indexOf(value); if (index < 0) { errorMessage(input, String.format("Invalid option %s. Please use one of %s.", value, StringUtils.join(input.getValues(), ", "))); return false; } input.setValue(value); } else { input.setEmpty(); } return true; } /** * Load CLI options for map type. * * Parses Key-Value pairs that take the config "<key>=<value>&<key>=<value>&...". * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputMap(String prefix, MMapInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { String value = line.getOptionValue(opt); Map<String, String> values = new HashMap<String, String>(); String[] entries = value.split("&"); for (String entry : entries) { String[] keyValue = null; if (entry.contains("=")) { keyValue = entry.split("=", 2); } if (keyValue != null && keyValue.length == 2) { values.put(keyValue[0], keyValue[1]); } else { errorMessage(input, "Don't know what to do with " + entry); return false; } } input.setValue(values); } else { input.setEmpty(); } return true; } /** * Load integer input from CLI option. * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputInteger(String prefix, MIntegerInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { try { input.setValue(Integer.valueOf(line.getOptionValue(ConfigOptions.getOptionKey(prefix, input)))); } catch (NumberFormatException ex) { errorMessage(input, "Input is not valid integer number"); return false; } } else { input.setEmpty(); } return true; } /** * Load long input from CLI option. * * @param prefix * placed at the beginning of the CLI option key * @param input * Input that we should read or edit * @param line * CLI options container * @return * @throws IOException */ static boolean fillInputLong(String prefix, MLongInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { try { input.setValue(Long.valueOf(line.getOptionValue(ConfigOptions.getOptionKey(prefix, input)))); } catch (NumberFormatException ex) { errorMessage(input, "Input is not a valid long "); return false; } } else { input.setEmpty(); } return true; } /** * Load string input from CLI option. * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputString(String prefix, MStringInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { String value = line.getOptionValue(ConfigOptions.getOptionKey(prefix, input)); if ((input.getMaxLength() >= 0) && (value.length() > input.getMaxLength())) { errorMessage(input, "Size of input exceeds allowance for this input" + " field. Maximal allowed size is " + input.getMaxLength()); return false; } input.setValue(value); } else { input.setEmpty(); } return true; } /** * Load boolean input from CLI option. * * @param prefix placed at the beginning of the CLI option key * @param input Input that we should read or edit * @param line CLI options container * @return * @throws IOException */ static boolean fillInputBoolean(String prefix, MBooleanInput input, CommandLine line) throws IOException { String opt = ConfigOptions.getOptionKey(prefix, input); if (line.hasOption(opt)) { input.setValue(Boolean.valueOf(line.getOptionValue(ConfigOptions.getOptionKey(prefix, input)))); } else { input.setEmpty(); } return true; } /** * Fill link object based on user input. * * @param reader Associated console reader object * @param link Link that user is suppose to fill in * @param linkConfigBundle Connector resource bundle * @return True if we filled all inputs, false if user has stopped processing * @throws IOException */ public static boolean fillLinkWithBundle(ConsoleReader reader, MLink link, ResourceBundle linkConfigBundle) throws IOException { link.setName(getName(reader, link.getName())); return fillLinkConfigWithBundle(reader, link.getConnectorLinkConfig().getConfigs(), linkConfigBundle); } /** * Fill job object based on user input. * * @param reader Associated console reader object * @param job Job that user is suppose to fill in * @param fromConfigBundle Connector resource bundle * @param driverConfigBundle Driver config resource bundle * @return True if we filled all inputs, false if user has stopped processing * @throws IOException */ public static boolean fillJobWithBundle(ConsoleReader reader, MJob job, ResourceBundle fromConfigBundle, ResourceBundle toConfigBundle, ResourceBundle driverConfigBundle) throws IOException { job.setName(getName(reader, job.getName())); return fillJobConfigWithBundle(reader, job.getFromJobConfig().getConfigs(), fromConfigBundle, job.getToJobConfig().getConfigs(), toConfigBundle, job.getDriverConfig().getConfigs(), driverConfigBundle); } public static boolean fillLinkConfigWithBundle(ConsoleReader reader, List<MConfig> linkConfig, ResourceBundle linkConfigBundle) throws IOException { if (!fillConfigsWithBundle(linkConfig, reader, linkConfigBundle)) { return false; } return true; } public static boolean fillJobConfigWithBundle(ConsoleReader reader, List<MConfig> fromConfig, ResourceBundle fromConfigBundle, List<MConfig> toConfig, ResourceBundle toConfigBundle, List<MConfig> driverConfig, ResourceBundle driverConfigBundle) throws IOException { // Job From config if (!fillConfigsWithBundle(fromConfig, reader, fromConfigBundle)) { return false; } // Job To config if (!fillConfigsWithBundle(toConfig, reader, toConfigBundle)) { return false; } // Job Driver config if (!fillConfigsWithBundle(driverConfig, reader, driverConfigBundle)) { return false; } return true; } public static boolean fillConfigsWithBundle(List<MConfig> configs, ConsoleReader reader, ResourceBundle configBundle) throws IOException { for (MConfig config : configs) { if (!fillConfigWithBundle(config, reader, configBundle)) { return false; } } return true; } @SuppressWarnings("rawtypes") public static boolean fillConfigWithBundle(MConfig config, ConsoleReader reader, ResourceBundle bundle) throws IOException { println(""); println(bundle.getString(config.getLabelKey())); // Print out config validation printValidationMessage(config, false); println(""); for (MInput input : config.getInputs()) { if (!fillInputWithBundle(input, reader, bundle)) { return false; } } return true; } @SuppressWarnings("rawtypes") static boolean fillInputWithBundle(MInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { // Print out validation printValidationMessage(input, false); // Based on the input type, let's perconfig specific load switch (input.getType()) { case STRING: return fillInputStringWithBundle((MStringInput) input, reader, bundle); case INTEGER: return fillInputIntegerWithBundle((MIntegerInput) input, reader, bundle); case LONG: return fillInputLongWithBundle((MLongInput) input, reader, bundle); case BOOLEAN: return fillInputBooleanWithBundle((MBooleanInput) input, reader, bundle); case MAP: return fillInputMapWithBundle((MMapInput) input, reader, bundle); case ENUM: return fillInputEnumWithBundle((MEnumInput) input, reader, bundle); case LIST: return fillInputListWithBundle((MListInput) input, reader, bundle); case DATETIME: return fillInputDateTimeWithBundle((MDateTimeInput) input, reader, bundle); default: println("Unsupported data type " + input.getType()); return true; } } /** * Load user input for datetime type. * * This implementation supports the following two types of format: * <ul> * <li> ISO8601 format (yyyy-MM-ddTHH:mm:ss.SSSZZ) * <li> Long which represents the milliseconds from 1970-01-01T00:00:00Z * </ul> * * If user did not enter anything (empty input) finish loading and return from function. * * @param input Input that we should read or edit * @param reader Associated console reader * @param bundle Resource bundle * @return True if user wish to continue with loading additional inputs * @throws IOException */ static boolean fillInputDateTimeWithBundle(MDateTimeInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { generatePrompt(reader, bundle, input); if (!input.isEmpty() && !input.isSensitive()) { reader.putString(input.getValue().toString()); } // Get the data String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { return false; } else if (userTyped.isEmpty()) { input.setEmpty(); } else { DateTime dt = parseDateTime(userTyped); if (dt == null) { errorMessage("Input is not a valid DateTime"); return fillInputDateTimeWithBundle(input, reader, bundle); } input.setValue(dt); } return true; } /** * Load user input for list type. * * This implementation will load one element at a time. If user did not * enter anything (empty input) finish loading and return from function. * * @param input Input that we should read or edit * @param reader Associated console reader * @param bundle Resource bundle * @return True if user wish to continue with loading additional inputs * @throws IOException */ static boolean fillInputListWithBundle(MListInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { // Special prompt in List case println(bundle.getString(input.getLabelKey()) + ": "); // Internal loading list List<String> values = input.getValue(); if (values == null) { values = new LinkedList<String>(); } String userTyped; while (true) { // Print all current items in each iteration // However do not printout if this input contains sensitive information. println("There are currently " + values.size() + " values in the list:"); if (!input.isSensitive()) { for (String value : values) { println(value); } } // Special prompt for List element reader.print("element# "); reader.flush(); if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { // Finish loading and return back to Sqoop shell return false; } else if (userTyped.isEmpty()) { // User has finished loading data to List input, either set input empty // if there are no elements or propagate elements to the input if (values.size() == 0) { input.setEmpty(); } else { input.setValue(values); } return true; } else { values.add(userTyped); } } } /** * Load user input for enum type. * * Print out numbered list of all available options and let user choose one * item from that. * * @param input Input that we should read or edit * @param reader Associated console reader * @param bundle Resource bundle * @return True if user with to continue with loading addtional inputs * @throws IOException */ static boolean fillInputEnumWithBundle(MEnumInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { // Prompt in enum case println(bundle.getString(input.getLabelKey()) + ": "); // Indexes int i = -1; int lastChoice = -1; // Print out all values as a numbered list for (String value : input.getValues()) { i++; println(" " + i + " : " + value); // Only show last choice if not sensitive if (!input.isEmpty() && value.equals(input.getValue()) && !input.isSensitive()) { lastChoice = i; } } // Prompt reader.print("Choose: "); // Fill previously filled index when available if (lastChoice != -1) { reader.putString(Integer.toString(lastChoice)); } reader.flush(); String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { return false; } else if (userTyped.isEmpty()) { input.setEmpty(); } else { Integer index; try { index = Integer.valueOf(userTyped); if (index < 0 || index >= input.getValues().length) { errorMessage("Invalid index"); return fillInputEnumWithBundle(input, reader, bundle); } input.setValue(input.getValues()[index]); } catch (NumberFormatException ex) { errorMessage("Input is not valid integer number"); return fillInputEnumWithBundle(input, reader, bundle); } } return true; } /** * Load user input for map type. * * This implementation will load one map entry at the time. Current flows is * as follows: if user did not enter anything (empty input) finish loading * and return from function. If user specified input with equal sign (=), * lets add new key value pair. Otherwise consider entire input as a key name * and try to remove it from the map. * * Please note that following code do not supports equal sign in property * name. It's however perfectly fine to have equal sign in value. * * @param input Input that we should read or edit * @param reader Associated console reader * @param bundle Resource bundle * @return True if user wish to continue with loading additional inputs * @throws IOException */ static boolean fillInputMapWithBundle(MMapInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { // Special prompt in Map case println(bundle.getString(input.getLabelKey()) + ": "); // Internal loading map Map<String, String> values = input.getValue(); if (values == null) { values = new HashMap<String, String>(); } String userTyped; while (true) { // Print all current items in each iteration // However do not printout if this input contains sensitive information. println("There are currently " + values.size() + " values in the map:"); if (!input.isSensitive()) { for (Map.Entry<String, String> entry : values.entrySet()) { println(entry.getKey() + " = " + entry.getValue()); } } // Special prompt for Map entry reader.print("entry# "); reader.flush(); if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { // Finish loading and return back to Sqoop shell return false; } else if (userTyped.isEmpty()) { // User has finished loading data to Map input, either set input empty // if there are no entries or propagate entries to the input if (values.size() == 0) { input.setEmpty(); } else { input.setValue(values); } return true; } else { // User has specified regular input, let's check if it contains equals // sign. Save new entry (or update existing one) if it does. Otherwise // try to remove entry that user specified. if (userTyped.contains("=")) { String[] keyValue = userTyped.split("=", 2); if (keyValue.length == 2) { values.put(handleUserInput(keyValue[0]), handleUserInput(keyValue[1])); } else { errorMessage("Don't know what to do with " + userTyped); } } else { String key = handleUserInput(userTyped); if (values.containsKey(key)) { values.remove(key); } else { errorMessage("Don't know what to do with " + userTyped); } } } } } /** * Handle special cases in user input. * * Preserve null and empty values, remove whitespace characters before and * after loaded string and de-quote the string if it's quoted (to preserve * spaces for example). * * @param input String loaded from user * @return Unquoted transconfiged string */ private static String handleUserInput(String input) { // Preserve null and empty values if (input == null) { return null; } if (input.isEmpty()) { return input; } // Removes empty characters at the begging and end of loaded string input = input.trim(); int lastIndex = input.length() - 1; char first = input.charAt(0); char last = input.charAt(lastIndex); // Remove quoting if present if (first == '\'' && last == '\'') { input = input.substring(1, lastIndex); } else if (first == '"' && last == '"') { input = input.substring(1, lastIndex); } // Return final string return input; } static boolean fillInputIntegerWithBundle(MIntegerInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { generatePrompt(reader, bundle, input); // Fill already filled data when available // However do not printout if this input contains sensitive information. if (!input.isEmpty() && !input.isSensitive()) { reader.putString(input.getValue().toString()); } // Get the data String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { return false; } else if (userTyped.isEmpty()) { input.setEmpty(); } else { Integer value; try { value = Integer.valueOf(userTyped); input.setValue(value); } catch (NumberFormatException ex) { errorMessage("Input is not valid integer number"); return fillInputIntegerWithBundle(input, reader, bundle); } input.setValue(Integer.valueOf(userTyped)); } return true; } static boolean fillInputLongWithBundle(MLongInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { generatePrompt(reader, bundle, input); if (!input.isEmpty() && !input.isSensitive()) { reader.putString(input.getValue().toString()); } // Get the data String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { return false; } else if (userTyped.isEmpty()) { input.setEmpty(); } else { Long value; try { value = Long.valueOf(userTyped); input.setValue(value); } catch (NumberFormatException ex) { errorMessage("Input is not a valid long"); return fillInputLongWithBundle(input, reader, bundle); } input.setValue(Long.valueOf(userTyped)); } return true; } /** * Load string input from the user. * * @param input Input that we should load in * @param reader Associated console reader * @param bundle Resource bundle for this input * @return * @throws IOException */ static boolean fillInputStringWithBundle(MStringInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { generatePrompt(reader, bundle, input); // Fill already filled data when available // However do not printout if this input contains sensitive information. if (!input.isEmpty() && !input.isSensitive()) { reader.putString(input.getValue()); reader.flush(); } // Get the data String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { // Propagate end of loading process return false; } else if (userTyped.isEmpty()) { // Empty input in case that nothing was given input.setEmpty(); } else { // Set value that user has entered input.setValue(userTyped); // Check that it did not exceeds maximal allowance for given input if ((input.getMaxLength() >= 0) && (userTyped.length() > input.getMaxLength())) { errorMessage("Size of input exceeds allowance for this input" + " field. Maximal allowed size is " + input.getMaxLength()); return fillInputStringWithBundle(input, reader, bundle); } } return true; } /** * Load boolean input from the user. * * @param input Input that we should load in * @param reader Associated console reader * @param bundle Resource bundle for this input * @return * @throws IOException */ static boolean fillInputBooleanWithBundle(MBooleanInput input, ConsoleReader reader, ResourceBundle bundle) throws IOException { generatePrompt(reader, bundle, input); // Fill already filled data when available // However do not printout if this input contains sensitive information. if (!input.isEmpty() && !input.isSensitive()) { reader.putString(input.getValue().toString()); } // Get the data String userTyped; if (input.isSensitive()) { userTyped = reader.readLine('*'); } else { userTyped = reader.readLine(); } if (userTyped == null) { // Propagate end of loading process return false; } else if (userTyped.isEmpty()) { // Empty input in case that nothing was given input.setEmpty(); } else { Boolean value; try { value = Boolean.valueOf(userTyped); input.setValue(value); } catch (NumberFormatException ex) { errorMessage("Input is not a valid boolean"); return fillInputBooleanWithBundle(input, reader, bundle); } // Set value that user has entered input.setValue(Boolean.valueOf(userTyped)); } return true; } @SuppressWarnings("rawtypes") static void generatePrompt(ConsoleReader reader, ResourceBundle bundle, MInput input) throws IOException { reader.print(bundle.getString(input.getLabelKey()) + ": "); reader.flush(); } static String getName(ConsoleReader reader, String name) throws IOException { if (name == null) { nameInput.setEmpty(); } else { nameInput.setValue(name); } do { fillInputStringWithBundle(nameInput, reader, getResourceBundle()); if (StringUtils.isEmpty(nameInput.getValue())) { errorMessage(nameInput, "Job name or link name cannot be null"); } else { break; } } while (true); return nameInput.getValue(); } /** * Print validation message in cases that it's not in state "OK" * * @param element Validated element */ static void printValidationMessage(MNamedElement element, boolean includeInputPrefix) { if (element.getValidationStatus() == Status.getDefault()) { return; } for (Message message : element.getValidationMessages()) switch (message.getStatus()) { case ERROR: if (includeInputPrefix) { errorMessage(element, message.getMessage()); } else { errorMessage(message.getMessage()); } break; case WARNING: if (includeInputPrefix) { warningMessage(element, message.getMessage()); } else { warningMessage(message.getMessage()); } break; default: // Simply ignore all other states for the moment break; } } public static void errorMessage(String message) { println("Error message: @|red " + message + " |@"); } static void errorMessage(MNamedElement input, String message) { print(input.getName()); print(": "); errorMessage(message); } static void warningMessage(String message) { println("Warning message: @|yellow " + message + " |@"); } static void warningMessage(MNamedElement input, String message) { print(input.getName()); print(": "); warningMessage(message); } public static void errorIntroduction() { println("\n @|red There are issues with entered data, please revise your input:|@"); } // link object has the connector link config public static void printLinkValidationMessages(MLink link) { for (MConfig config : link.getConnectorLinkConfig().getConfigs()) { for (MInput<?> input : config.getInputs()) { printValidationMessage(input, true); } } } // job has the from/to and the driver config public static void printJobValidationMessages(MJob job) { for (MConfig config : job.getFromJobConfig().getConfigs()) { for (MInput<?> input : config.getInputs()) { printValidationMessage(input, true); } } for (MConfig config : job.getToJobConfig().getConfigs()) { for (MInput<?> input : config.getInputs()) { printValidationMessage(input, true); } } for (MConfig config : job.getDriverConfig().getConfigs()) { for (MInput<?> input : config.getInputs()) { printValidationMessage(input, true); } } } private ConfigFiller() { // Do not instantiate } }