Skip to content

Commit e4c80b6

Browse files
author
Marcelo Vanzin
committed
Reorganize the code so that only SparkLauncher is public.
This gets rid of LauncherCommon and AbstractLauncher so that there's only one public class (*) in the library. The other "launchers" were renamed to match what they actually do (build commands), and extend the public SparkLauncher (which actually can launch things). Shell handling code was moved into the Main class, since that's the only place that should care about shells. CommandUtils is currently broken, since it still expects AbstractLauncher to be around; that will be fixed separately. The (*) refers to SparkSubmitOptionParser, which is still public, but will receive a similar privatizing treatment soon.
1 parent e50dc5e commit e4c80b6

File tree

10 files changed

+546
-591
lines changed

10 files changed

+546
-591
lines changed

launcher/src/main/java/org/apache/spark/launcher/AbstractLauncher.java

Lines changed: 0 additions & 499 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.launcher;
19+
20+
import java.io.IOException;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
/**
25+
* Internal interface that defines a command builder.
26+
*/
27+
interface CommandBuilder {
28+
29+
List<String> buildCommand(Map<String, String> env) throws IOException;
30+
31+
}

launcher/src/main/java/org/apache/spark/launcher/LauncherCommon.java renamed to launcher/src/main/java/org/apache/spark/launcher/CommandBuilderUtils.java

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,9 @@
2323
import java.util.Map;
2424

2525
/**
26-
* Configuration key definitions for Spark apps, and some helper methods.
26+
* Helper methods for command builders.
2727
*/
28-
public class LauncherCommon {
29-
30-
/** The Spark master. */
31-
public static final String SPARK_MASTER = "spark.master";
32-
33-
/** Configuration key for the driver memory. */
34-
public static final String DRIVER_MEMORY = "spark.driver.memory";
35-
/** Configuration key for the driver class path. */
36-
public static final String DRIVER_EXTRA_CLASSPATH = "spark.driver.extraClassPath";
37-
/** Configuration key for the driver VM options. */
38-
public static final String DRIVER_EXTRA_JAVA_OPTIONS = "spark.driver.extraJavaOptions";
39-
/** Configuration key for the driver native library path. */
40-
public static final String DRIVER_EXTRA_LIBRARY_PATH = "spark.driver.extraLibraryPath";
41-
42-
/** Configuration key for the executor memory. */
43-
public static final String EXECUTOR_MEMORY = "spark.executor.memory";
44-
/** Configuration key for the executor class path. */
45-
public static final String EXECUTOR_EXTRA_CLASSPATH = "spark.executor.extraClassPath";
46-
/** Configuration key for the executor VM options. */
47-
public static final String EXECUTOR_EXTRA_JAVA_OPTIONS = "spark.executor.extraJavaOptions";
48-
/** Configuration key for the executor native library path. */
49-
public static final String EXECUTOR_EXTRA_LIBRARY_PATH = "spark.executor.extraLibraryOptions";
50-
/** Configuration key for the number of executor CPU cores. */
51-
public static final String EXECUTOR_CORES = "spark.executor.cores";
28+
class CommandBuilderUtils {
5229

5330
/** Returns whether the given string is null or empty. */
5431
static boolean isEmpty(String s) {
@@ -244,7 +221,4 @@ static void checkState(boolean check, String msg, Object... args) {
244221
}
245222
}
246223

247-
// To avoid subclassing outside this package.
248-
LauncherCommon() { }
249-
250224
}

launcher/src/main/java/org/apache/spark/launcher/Main.java

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919

2020
import java.util.ArrayList;
2121
import java.util.Arrays;
22+
import java.util.HashMap;
2223
import java.util.List;
2324
import java.util.Map;
2425

26+
import static org.apache.spark.launcher.CommandBuilderUtils.*;
27+
2528
/**
2629
* Command line interface for the Spark launcher. Used internally by Spark scripts.
2730
*/
28-
class Main extends LauncherCommon {
31+
class Main {
2932

3033
/**
3134
* Usage: Main [class] [class args]
@@ -51,36 +54,119 @@ public static void main(String[] argsArray) throws Exception {
5154
String className = args.remove(0);
5255

5356
boolean printLaunchCommand;
54-
AbstractLauncher<?> launcher;
57+
CommandBuilder builder;
5558
try {
5659
if (className.equals("org.apache.spark.deploy.SparkSubmit")) {
57-
launcher = new SparkSubmitCliLauncher(args);
60+
builder = new SparkSubmitCommandBuilder(args);
5861
} else {
59-
launcher = new SparkClassLauncher(className, args);
62+
builder = new SparkClassLauncher(className, args);
6063
}
6164
printLaunchCommand = !isEmpty(System.getenv("SPARK_PRINT_LAUNCH_COMMAND"));
6265
} catch (IllegalArgumentException e) {
63-
launcher = new UsageLauncher();
66+
builder = new UsageLauncher();
6467
printLaunchCommand = false;
6568
}
6669

67-
List<String> cmd = launcher.buildShellCommand();
70+
Map<String, String> env = new HashMap<String, String>();
71+
List<String> cmd = builder.buildCommand(env);
6872
if (printLaunchCommand) {
6973
System.err.println("Spark Command: " + join(" ", cmd));
7074
System.err.println("========================================");
7175
}
7276

7377
if (isWindows()) {
74-
String cmdLine = join(" ", cmd);
75-
System.out.println(cmdLine);
78+
List<String> winCmd = prepareForWindows(cmd, env);
79+
System.out.println(join(" ", cmd));
7680
} else {
77-
for (String c : cmd) {
81+
List<String> bashCmd = prepareForBash(cmd, env);
82+
for (String c : bashCmd) {
7883
System.out.print(c);
7984
System.out.print('\0');
8085
}
8186
}
8287
}
8388

89+
/**
90+
* Prepare a command line for execution from a Windows batch script.
91+
*
92+
* Two things need to be done:
93+
*
94+
* - If a custom library path is needed, extend PATH to add it. Based on:
95+
* http://superuser.com/questions/223104/setting-environment-variable-for-just-one-command-in-windows-cmd-exe
96+
*
97+
* - Quote all arguments so that spaces are handled as expected. Quotes within arguments are
98+
* "double quoted" (which is batch for escaping a quote). This page has more details about
99+
* quoting and other batch script fun stuff: http://ss64.com/nt/syntax-esc.html
100+
*
101+
* The command is executed using "cmd /c" and formatted as single line, since that's the
102+
* easiest way to consume this from a batch script (see spark-class2.cmd).
103+
*/
104+
private static List<String> prepareForWindows(List<String> cmd, Map<String, String> childEnv) {
105+
StringBuilder cmdline = new StringBuilder("cmd /c \"");
106+
for (Map.Entry<String, String> e : childEnv.entrySet()) {
107+
if (cmdline.length() > 0) {
108+
cmdline.append(" ");
109+
}
110+
cmdline.append(String.format("set %s=%s", e.getKey(), e.getValue()));
111+
cmdline.append(" &&");
112+
}
113+
for (String arg : cmd) {
114+
if (cmdline.length() > 0) {
115+
cmdline.append(" ");
116+
}
117+
cmdline.append(quoteForBatchScript(arg));
118+
}
119+
cmdline.append("\"");
120+
return Arrays.asList(cmdline.toString());
121+
}
122+
123+
/**
124+
* Prepare the command for execution from a bash script. The final command will have commands to
125+
* set up any needed environment variables needed by the child process.
126+
*/
127+
private static List<String> prepareForBash(List<String> cmd, Map<String, String> childEnv) {
128+
if (childEnv.isEmpty()) {
129+
return cmd;
130+
}
131+
132+
List<String> newCmd = new ArrayList<String>();
133+
newCmd.add("env");
134+
135+
for (Map.Entry<String, String> e : childEnv.entrySet()) {
136+
newCmd.add(String.format("%s=%s", e.getKey(), e.getValue()));
137+
}
138+
newCmd.addAll(cmd);
139+
return newCmd;
140+
}
141+
142+
/**
143+
* Quote a command argument for a command to be run by a Windows batch script, if the argument
144+
* needs quoting. Arguments only seem to need quotes in batch scripts if they have whitespace.
145+
*/
146+
private static String quoteForBatchScript(String arg) {
147+
boolean needsQuotes = false;
148+
for (int i = 0; i < arg.length(); i++) {
149+
if (Character.isWhitespace(arg.codePointAt(i))) {
150+
needsQuotes = true;
151+
break;
152+
}
153+
}
154+
if (!needsQuotes) {
155+
return arg;
156+
}
157+
StringBuilder quoted = new StringBuilder();
158+
quoted.append("\"");
159+
for (int i = 0; i < arg.length(); i++) {
160+
int cp = arg.codePointAt(i);
161+
if (cp == '\"') {
162+
quoted.append("\"");
163+
}
164+
quoted.appendCodePoint(cp);
165+
}
166+
quoted.append("\"");
167+
return quoted.toString();
168+
}
169+
84170
/**
85171
* Internal launcher used when command line parsing fails. This will behave differently depending
86172
* on the platform:
@@ -94,10 +180,10 @@ public static void main(String[] argsArray) throws Exception {
94180
* should check for this variable and print its usage, since batch scripts don't really support
95181
* the "export -f" functionality used in bash.
96182
*/
97-
private static class UsageLauncher extends AbstractLauncher<UsageLauncher> {
183+
private static class UsageLauncher implements CommandBuilder {
98184

99185
@Override
100-
List<String> buildLauncherCommand(Map<String, String> env) {
186+
public List<String> buildCommand(Map<String, String> env) {
101187
if (isWindows()) {
102188
return Arrays.asList("set SPARK_LAUNCHER_USAGE_ERROR=1");
103189
} else {

launcher/src/main/java/org/apache/spark/launcher/SparkClassLauncher.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424
import java.util.Map;
2525
import java.util.regex.Pattern;
2626

27+
import static org.apache.spark.launcher.CommandBuilderUtils.*;
28+
2729
/**
2830
* Launcher for internal Spark classes.
2931
* <p/>
3032
* This class handles building the command to launch all internal Spark classes except for
3133
* SparkSubmit (which is handled by the public {@link SparkLauncher} class.
3234
*/
33-
class SparkClassLauncher extends AbstractLauncher<SparkClassLauncher> {
35+
class SparkClassLauncher extends SparkLauncher implements CommandBuilder {
3436

3537
private final String className;
3638
private final List<String> classArgs;
@@ -41,7 +43,7 @@ class SparkClassLauncher extends AbstractLauncher<SparkClassLauncher> {
4143
}
4244

4345
@Override
44-
List<String> buildLauncherCommand(Map<String, String> env) throws IOException {
46+
public List<String> buildCommand(Map<String, String> env) throws IOException {
4547
List<String> javaOptsKeys = new ArrayList<String>();
4648
String memKey = null;
4749
String extraClassPath = null;
@@ -89,7 +91,7 @@ List<String> buildLauncherCommand(Map<String, String> env) throws IOException {
8991
javaOptsKeys.add("SPARK_JAVA_OPTS");
9092
} else {
9193
// Any classes not explicitly listed above are submitted using SparkSubmit.
92-
return buildSparkSubmitCommand(env);
94+
return createSparkSubmitCommand(env);
9395
}
9496

9597
List<String> cmd = buildJavaCommand(extraClassPath);
@@ -106,14 +108,14 @@ List<String> buildLauncherCommand(Map<String, String> env) throws IOException {
106108
return cmd;
107109
}
108110

109-
private List<String> buildSparkSubmitCommand(Map<String, String> env) throws IOException {
111+
private List<String> createSparkSubmitCommand(Map<String, String> env) throws IOException {
110112
List<String> sparkSubmitArgs = new ArrayList<String>(classArgs);
111113
sparkSubmitArgs.add(SparkSubmitOptionParser.CLASS);
112114
sparkSubmitArgs.add(className);
113115

114-
SparkSubmitCliLauncher launcher = new SparkSubmitCliLauncher(true, sparkSubmitArgs);
115-
launcher.setAppResource("spark-internal");
116-
return launcher.buildLauncherCommand(env);
116+
SparkSubmitCommandBuilder builder = new SparkSubmitCommandBuilder(true, sparkSubmitArgs);
117+
builder.setAppResource("spark-internal");
118+
return builder.buildCommand(env);
117119
}
118120

119121
}

0 commit comments

Comments
 (0)