| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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.apache.spark.launcher; |
| |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Parser for spark-submit command line options. |
| * <p> |
| * This class encapsulates the parsing code for spark-submit command line options, so that there |
| * is a single list of options that needs to be maintained (well, sort of, but it makes it harder |
| * to break things). |
| */ |
| class SparkSubmitOptionParser { |
| |
| // The following constants define the "main" name for the available options. They're defined |
| // to avoid copy & paste of the raw strings where they're needed. |
| // |
| // The fields are not static so that they're exposed to Scala code that uses this class. See |
| // SparkSubmitArguments.scala. That is also why this class is not abstract - to allow code to |
| // easily use these constants without having to create dummy implementations of this class. |
| protected final String CLASS = "--class"; |
| protected final String CONF = "--conf"; |
| protected final String DEPLOY_MODE = "--deploy-mode"; |
| protected final String DRIVER_CLASS_PATH = "--driver-class-path"; |
| protected final String DRIVER_CORES = "--driver-cores"; |
| protected final String DRIVER_JAVA_OPTIONS = "--driver-java-options"; |
| protected final String DRIVER_LIBRARY_PATH = "--driver-library-path"; |
| protected final String DRIVER_MEMORY = "--driver-memory"; |
| protected final String EXECUTOR_MEMORY = "--executor-memory"; |
| protected final String FILES = "--files"; |
| protected final String JARS = "--jars"; |
| protected final String KILL_SUBMISSION = "--kill"; |
| protected final String MASTER = "--master"; |
| protected final String NAME = "--name"; |
| protected final String PACKAGES = "--packages"; |
| protected final String PACKAGES_EXCLUDE = "--exclude-packages"; |
| protected final String PROPERTIES_FILE = "--properties-file"; |
| protected final String PROXY_USER = "--proxy-user"; |
| protected final String PY_FILES = "--py-files"; |
| protected final String REPOSITORIES = "--repositories"; |
| protected final String STATUS = "--status"; |
| protected final String TOTAL_EXECUTOR_CORES = "--total-executor-cores"; |
| |
| // Options that do not take arguments. |
| protected final String HELP = "--help"; |
| protected final String SUPERVISE = "--supervise"; |
| protected final String USAGE_ERROR = "--usage-error"; |
| protected final String VERBOSE = "--verbose"; |
| protected final String VERSION = "--version"; |
| |
| // Standalone-only options. |
| |
| // YARN-only options. |
| protected final String ARCHIVES = "--archives"; |
| protected final String EXECUTOR_CORES = "--executor-cores"; |
| protected final String KEYTAB = "--keytab"; |
| protected final String NUM_EXECUTORS = "--num-executors"; |
| protected final String PRINCIPAL = "--principal"; |
| protected final String QUEUE = "--queue"; |
| |
| /** |
| * This is the canonical list of spark-submit options. Each entry in the array contains the |
| * different aliases for the same option; the first element of each entry is the "official" |
| * name of the option, passed to {@link #handle(String, String)}. |
| * <p> |
| * Options not listed here nor in the "switch" list below will result in a call to |
| * {@link #handleUnknown(String)}. |
| * <p> |
| * These two arrays are visible for tests. |
| */ |
| final String[][] opts = { |
| { ARCHIVES }, |
| { CLASS }, |
| { CONF, "-c" }, |
| { DEPLOY_MODE }, |
| { DRIVER_CLASS_PATH }, |
| { DRIVER_CORES }, |
| { DRIVER_JAVA_OPTIONS }, |
| { DRIVER_LIBRARY_PATH }, |
| { DRIVER_MEMORY }, |
| { EXECUTOR_CORES }, |
| { EXECUTOR_MEMORY }, |
| { FILES }, |
| { JARS }, |
| { KEYTAB }, |
| { KILL_SUBMISSION }, |
| { MASTER }, |
| { NAME }, |
| { NUM_EXECUTORS }, |
| { PACKAGES }, |
| { PACKAGES_EXCLUDE }, |
| { PRINCIPAL }, |
| { PROPERTIES_FILE }, |
| { PROXY_USER }, |
| { PY_FILES }, |
| { QUEUE }, |
| { REPOSITORIES }, |
| { STATUS }, |
| { TOTAL_EXECUTOR_CORES }, |
| }; |
| |
| /** |
| * List of switches (command line options that do not take parameters) recognized by spark-submit. |
| */ |
| final String[][] switches = { |
| { HELP, "-h" }, |
| { SUPERVISE }, |
| { USAGE_ERROR }, |
| { VERBOSE, "-v" }, |
| { VERSION }, |
| }; |
| |
| /** |
| * Parse a list of spark-submit command line options. |
| * <p> |
| * See SparkSubmitArguments.scala for a more formal description of available options. |
| * |
| * @throws IllegalArgumentException If an error is found during parsing. |
| */ |
| protected final void parse(List<String> args) { |
| Pattern eqSeparatedOpt = Pattern.compile("(--[^=]+)=(.+)"); |
| |
| int idx = 0; |
| for (idx = 0; idx < args.size(); idx++) { |
| String arg = args.get(idx); |
| String value = null; |
| |
| Matcher m = eqSeparatedOpt.matcher(arg); |
| if (m.matches()) { |
| arg = m.group(1); |
| value = m.group(2); |
| } |
| |
| // Look for options with a value. |
| String name = findCliOption(arg, opts); |
| if (name != null) { |
| if (value == null) { |
| if (idx == args.size() - 1) { |
| throw new IllegalArgumentException( |
| String.format("Missing argument for option '%s'.", arg)); |
| } |
| idx++; |
| value = args.get(idx); |
| } |
| if (!handle(name, value)) { |
| break; |
| } |
| continue; |
| } |
| |
| // Look for a switch. |
| name = findCliOption(arg, switches); |
| if (name != null) { |
| if (!handle(name, null)) { |
| break; |
| } |
| continue; |
| } |
| |
| if (!handleUnknown(arg)) { |
| break; |
| } |
| } |
| |
| if (idx < args.size()) { |
| idx++; |
| } |
| handleExtraArgs(args.subList(idx, args.size())); |
| } |
| |
| /** |
| * Callback for when an option with an argument is parsed. |
| * |
| * @param opt The long name of the cli option (might differ from actual command line). |
| * @param value The value. This will be <i>null</i> if the option does not take a value. |
| * @return Whether to continue parsing the argument list. |
| */ |
| protected boolean handle(String opt, String value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Callback for when an unrecognized option is parsed. |
| * |
| * @param opt Unrecognized option from the command line. |
| * @return Whether to continue parsing the argument list. |
| */ |
| protected boolean handleUnknown(String opt) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Callback for remaining command line arguments after either {@link #handle(String, String)} or |
| * {@link #handleUnknown(String)} return "false". This will be called at the end of parsing even |
| * when there are no remaining arguments. |
| * |
| * @param extra List of remaining arguments. |
| */ |
| protected void handleExtraArgs(List<String> extra) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private String findCliOption(String name, String[][] available) { |
| for (String[] candidates : available) { |
| for (String candidate : candidates) { |
| if (candidate.equals(name)) { |
| return candidates[0]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| } |