19
19
20
20
import java .util .ArrayList ;
21
21
import java .util .Arrays ;
22
+ import java .util .HashMap ;
22
23
import java .util .List ;
23
24
import java .util .Map ;
24
25
26
+ import static org .apache .spark .launcher .CommandBuilderUtils .*;
27
+
25
28
/**
26
29
* Command line interface for the Spark launcher. Used internally by Spark scripts.
27
30
*/
28
- class Main extends LauncherCommon {
31
+ class Main {
29
32
30
33
/**
31
34
* Usage: Main [class] [class args]
@@ -51,36 +54,119 @@ public static void main(String[] argsArray) throws Exception {
51
54
String className = args .remove (0 );
52
55
53
56
boolean printLaunchCommand ;
54
- AbstractLauncher <?> launcher ;
57
+ CommandBuilder builder ;
55
58
try {
56
59
if (className .equals ("org.apache.spark.deploy.SparkSubmit" )) {
57
- launcher = new SparkSubmitCliLauncher (args );
60
+ builder = new SparkSubmitCommandBuilder (args );
58
61
} else {
59
- launcher = new SparkClassLauncher (className , args );
62
+ builder = new SparkClassLauncher (className , args );
60
63
}
61
64
printLaunchCommand = !isEmpty (System .getenv ("SPARK_PRINT_LAUNCH_COMMAND" ));
62
65
} catch (IllegalArgumentException e ) {
63
- launcher = new UsageLauncher ();
66
+ builder = new UsageLauncher ();
64
67
printLaunchCommand = false ;
65
68
}
66
69
67
- List <String > cmd = launcher .buildShellCommand ();
70
+ Map <String , String > env = new HashMap <String , String >();
71
+ List <String > cmd = builder .buildCommand (env );
68
72
if (printLaunchCommand ) {
69
73
System .err .println ("Spark Command: " + join (" " , cmd ));
70
74
System .err .println ("========================================" );
71
75
}
72
76
73
77
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 ) );
76
80
} else {
77
- for (String c : cmd ) {
81
+ List <String > bashCmd = prepareForBash (cmd , env );
82
+ for (String c : bashCmd ) {
78
83
System .out .print (c );
79
84
System .out .print ('\0' );
80
85
}
81
86
}
82
87
}
83
88
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
+
84
170
/**
85
171
* Internal launcher used when command line parsing fails. This will behave differently depending
86
172
* on the platform:
@@ -94,10 +180,10 @@ public static void main(String[] argsArray) throws Exception {
94
180
* should check for this variable and print its usage, since batch scripts don't really support
95
181
* the "export -f" functionality used in bash.
96
182
*/
97
- private static class UsageLauncher extends AbstractLauncher < UsageLauncher > {
183
+ private static class UsageLauncher implements CommandBuilder {
98
184
99
185
@ Override
100
- List <String > buildLauncherCommand (Map <String , String > env ) {
186
+ public List <String > buildCommand (Map <String , String > env ) {
101
187
if (isWindows ()) {
102
188
return Arrays .asList ("set SPARK_LAUNCHER_USAGE_ERROR=1" );
103
189
} else {
0 commit comments