16
16
// under the License.
17
17
package org .openqa .selenium .manager ;
18
18
19
- import static java .nio .file .StandardCopyOption .REPLACE_EXISTING ;
20
- import static org .openqa .selenium .Platform .MAC ;
21
- import static org .openqa .selenium .Platform .WINDOWS ;
19
+ import org .openqa .selenium .Beta ;
20
+ import org .openqa .selenium .Capabilities ;
21
+ import org .openqa .selenium .Platform ;
22
+ import org .openqa .selenium .Proxy ;
23
+ import org .openqa .selenium .WebDriverException ;
24
+ import org .openqa .selenium .json .Json ;
25
+ import org .openqa .selenium .json .JsonException ;
26
+ import org .openqa .selenium .manager .SeleniumManagerOutput .Result ;
27
+ import org .openqa .selenium .os .CommandLine ;
22
28
23
- import com .google .common .io .CharStreams ;
24
- import java .io .File ;
25
29
import java .io .IOException ;
26
30
import java .io .InputStream ;
27
- import java .io .InputStreamReader ;
28
- import java .nio .charset .StandardCharsets ;
31
+ import java .nio .file .FileVisitResult ;
29
32
import java .nio .file .Files ;
30
33
import java .nio .file .Path ;
34
+ import java .nio .file .SimpleFileVisitor ;
35
+ import java .nio .file .attribute .BasicFileAttributes ;
31
36
import java .util .ArrayList ;
32
37
import java .util .Arrays ;
33
38
import java .util .List ;
34
39
import java .util .Map ;
35
40
import java .util .logging .Level ;
36
41
import java .util .logging .Logger ;
37
- import org .openqa .selenium .Beta ;
38
- import org .openqa .selenium .Capabilities ;
39
- import org .openqa .selenium .Platform ;
40
- import org .openqa .selenium .Proxy ;
41
- import org .openqa .selenium .WebDriverException ;
42
- import org .openqa .selenium .json .Json ;
43
- import org .openqa .selenium .json .JsonException ;
44
- import org .openqa .selenium .manager .SeleniumManagerOutput .Result ;
42
+
43
+ import static java .nio .file .StandardCopyOption .REPLACE_EXISTING ;
44
+ import static org .openqa .selenium .Platform .MAC ;
45
+ import static org .openqa .selenium .Platform .WINDOWS ;
45
46
46
47
/**
47
48
* This implementation is still in beta, and may change.
@@ -67,17 +68,17 @@ public class SeleniumManager {
67
68
68
69
private static volatile SeleniumManager manager ;
69
70
70
- private File binary ;
71
+ private Path binary ;
71
72
72
73
/** Wrapper for the Selenium Manager binary. */
73
74
private SeleniumManager () {
74
75
Runtime .getRuntime ()
75
76
.addShutdownHook (
76
77
new Thread (
77
78
() -> {
78
- if (binary != null && binary .exists ()) {
79
+ if (binary != null && Files .exists (binary )) {
79
80
try {
80
- Files .delete (binary . toPath () );
81
+ Files .delete (binary );
81
82
} catch (IOException e ) {
82
83
LOG .warning (
83
84
String .format (
@@ -102,26 +103,26 @@ public static SeleniumManager getInstance() {
102
103
/**
103
104
* Executes a process with the given arguments.
104
105
*
105
- * @param command the file and arguments to execute.
106
+ * @param arguments the file and arguments to execute.
106
107
* @return the standard output of the execution.
107
108
*/
108
- private static Result runCommand (String ... command ) {
109
- LOG .fine (String .format ("Executing Process: %s" , Arrays .toString (command )));
109
+ private static Result runCommand (Path binary , List <String > arguments ) {
110
+ LOG .fine (String .format ("Executing Process: %s" , arguments ));
111
+
110
112
String output ;
111
113
int code ;
112
114
try {
113
- Process process = new ProcessBuilder (command ).redirectErrorStream (true ).start ();
114
- process .waitFor ();
115
- code = process .exitValue ();
116
- output =
117
- CharStreams .toString (
118
- new InputStreamReader (process .getInputStream (), StandardCharsets .UTF_8 ));
119
- } catch (InterruptedException e ) {
120
- Thread .currentThread ().interrupt ();
121
- throw new WebDriverException (
122
- "Interrupted while running command: " + Arrays .toString (command ), e );
115
+ CommandLine command = new CommandLine (binary .toAbsolutePath ().toString (), arguments .toArray (new String [0 ]));
116
+ command .copyOutputTo (System .err );
117
+ command .executeAsync ();
118
+ command .waitFor (10000 ); // A generous timeout
119
+ if (command .isRunning ()) {
120
+ LOG .warning ("Selenium Manager did not exit" );
121
+ }
122
+ code = command .getExitCode ();
123
+ output = command .getStdOut ();
123
124
} catch (Exception e ) {
124
- throw new WebDriverException ("Failed to run command: " + Arrays . toString ( command ) , e );
125
+ throw new WebDriverException ("Failed to run command: " + arguments , e );
125
126
}
126
127
SeleniumManagerOutput jsonOutput = null ;
127
128
JsonException failedToParse = null ;
@@ -148,13 +149,13 @@ private static Result runCommand(String... command) {
148
149
"Command failed with code: "
149
150
+ code
150
151
+ ", executed: "
151
- + Arrays . toString ( command )
152
+ + arguments
152
153
+ "\n "
153
154
+ dump ,
154
155
failedToParse );
155
156
} else if (failedToParse != null || jsonOutput == null ) {
156
157
throw new WebDriverException (
157
- "Failed to parse json output, executed: " + Arrays . toString ( command ) + "\n " + dump ,
158
+ "Failed to parse json output, executed: " + arguments + "\n " + dump ,
158
159
failedToParse );
159
160
}
160
161
return jsonOutput .result ;
@@ -165,7 +166,7 @@ private static Result runCommand(String... command) {
165
166
*
166
167
* @return the path to the Selenium Manager binary.
167
168
*/
168
- private synchronized File getBinary () {
169
+ private synchronized Path getBinary () {
169
170
if (binary == null ) {
170
171
try {
171
172
Platform current = Platform .getCurrent ();
@@ -180,12 +181,13 @@ private synchronized File getBinary() {
180
181
String binaryPath = String .format ("%s/%s%s" , folder , SELENIUM_MANAGER , extension );
181
182
try (InputStream inputStream = this .getClass ().getResourceAsStream (binaryPath )) {
182
183
Path tmpPath = Files .createTempDirectory (SELENIUM_MANAGER + System .nanoTime ());
183
- File tmpFolder = tmpPath .toFile ();
184
- tmpFolder .deleteOnExit ();
185
- binary = new File (tmpFolder , SELENIUM_MANAGER + extension );
186
- Files .copy (inputStream , binary .toPath (), REPLACE_EXISTING );
184
+
185
+ deleteOnExit (tmpPath );
186
+
187
+ binary = tmpPath .resolve (SELENIUM_MANAGER + extension );
188
+ Files .copy (inputStream , binary , REPLACE_EXISTING );
187
189
}
188
- binary .setExecutable (true );
190
+ binary .toFile (). setExecutable (true );
189
191
} catch (Exception e ) {
190
192
throw new WebDriverException ("Unable to obtain Selenium Manager Binary" , e );
191
193
}
@@ -195,6 +197,30 @@ private synchronized File getBinary() {
195
197
return binary ;
196
198
}
197
199
200
+ private void deleteOnExit (Path tmpPath ) {
201
+ Runtime .getRuntime ().addShutdownHook (new Thread (() -> {
202
+ try {
203
+ Files .walkFileTree (
204
+ tmpPath ,
205
+ new SimpleFileVisitor <Path >() {
206
+ @ Override
207
+ public FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
208
+ Files .delete (dir );
209
+ return FileVisitResult .CONTINUE ;
210
+ }
211
+
212
+ @ Override
213
+ public FileVisitResult visitFile (Path file , BasicFileAttributes attrs ) throws IOException {
214
+ Files .delete (file );
215
+ return FileVisitResult .CONTINUE ;
216
+ }
217
+ });
218
+ } catch (IOException e ) {
219
+ // Do nothing. We're just tidying up.
220
+ }
221
+ }));
222
+ }
223
+
198
224
/**
199
225
* Returns the browser binary path when present in the vendor options
200
226
*
@@ -229,48 +255,48 @@ private String getBrowserBinary(Capabilities options) {
229
255
* @return the location of the driver.
230
256
*/
231
257
public Result getDriverPath (Capabilities options , boolean offline ) {
232
- File binaryFile = getBinary ();
258
+ Path binaryFile = getBinary ();
233
259
if (binaryFile == null ) {
234
260
return null ;
235
261
}
236
- List < String > commandList = new ArrayList <>();
237
- commandList . add ( binaryFile . getAbsolutePath () );
238
- commandList .add ("--browser" );
239
- commandList .add (options .getBrowserName ());
240
- commandList .add ("--output" );
241
- commandList .add ("json" );
262
+
263
+ List < String > arguments = new ArrayList <>( );
264
+ arguments .add ("--browser" );
265
+ arguments .add (options .getBrowserName ());
266
+ arguments .add ("--output" );
267
+ arguments .add ("json" );
242
268
243
269
if (!options .getBrowserVersion ().isEmpty ()) {
244
- commandList .add ("--browser-version" );
245
- commandList .add (options .getBrowserVersion ());
270
+ arguments .add ("--browser-version" );
271
+ arguments .add (options .getBrowserVersion ());
246
272
}
247
273
248
274
String browserBinary = getBrowserBinary (options );
249
275
if (browserBinary != null && !browserBinary .isEmpty ()) {
250
- commandList .add ("--browser-path" );
251
- commandList .add (browserBinary );
276
+ arguments .add ("--browser-path" );
277
+ arguments .add (browserBinary );
252
278
}
253
279
254
280
if (getLogLevel ().intValue () <= Level .FINE .intValue ()) {
255
- commandList .add ("--debug" );
281
+ arguments .add ("--debug" );
256
282
}
257
283
258
284
if (offline ) {
259
- commandList .add ("--offline" );
285
+ arguments .add ("--offline" );
260
286
}
261
287
262
288
Proxy proxy = (Proxy ) options .getCapability ("proxy" );
263
289
if (proxy != null ) {
264
290
if (proxy .getSslProxy () != null ) {
265
- commandList .add ("--proxy" );
266
- commandList .add (proxy .getSslProxy ());
291
+ arguments .add ("--proxy" );
292
+ arguments .add (proxy .getSslProxy ());
267
293
} else if (proxy .getHttpProxy () != null ) {
268
- commandList .add ("--proxy" );
269
- commandList .add (proxy .getHttpProxy ());
294
+ arguments .add ("--proxy" );
295
+ arguments .add (proxy .getHttpProxy ());
270
296
}
271
297
}
272
298
273
- Result result = runCommand (commandList . toArray ( new String [ 0 ]) );
299
+ Result result = runCommand (binaryFile , arguments );
274
300
LOG .fine (
275
301
String .format (
276
302
"Using driver at location: %s, browser at location %s" ,
0 commit comments