1717
1818package org .openqa .selenium .remote .service ;
1919
20+ import static java .nio .file .StandardCopyOption .REPLACE_EXISTING ;
2021import static java .util .Collections .emptyMap ;
2122import static java .util .concurrent .TimeUnit .SECONDS ;
23+ import static org .openqa .selenium .Platform .MAC ;
24+ import static org .openqa .selenium .Platform .WINDOWS ;
2225import static org .openqa .selenium .concurrent .ExecutorServices .shutdownGracefully ;
2326
27+ import com .google .common .base .Charsets ;
2428import com .google .common .collect .ImmutableMap ;
2529
30+ import com .google .common .io .CharStreams ;
2631import org .openqa .selenium .Beta ;
2732import org .openqa .selenium .Capabilities ;
33+ import org .openqa .selenium .Platform ;
2834import org .openqa .selenium .WebDriverException ;
2935import org .openqa .selenium .internal .Require ;
3036import org .openqa .selenium .net .PortProber ;
3642import java .io .File ;
3743import java .io .FileOutputStream ;
3844import java .io .IOException ;
45+ import java .io .InputStream ;
46+ import java .io .InputStreamReader ;
3947import java .io .OutputStream ;
4048import java .net .MalformedURLException ;
4149import java .net .URL ;
50+ import java .nio .file .Files ;
4251import java .time .Duration ;
4352import java .util .List ;
4453import java .util .Map ;
@@ -64,6 +73,12 @@ public class DriverService implements Closeable {
6473 private static final String NAME = "Driver Service Executor" ;
6574 protected static final Duration DEFAULT_TIMEOUT = Duration .ofSeconds (20 );
6675
76+ protected static final String SELENIUM_MANAGER = "selenium-manager" ;
77+ protected static final String EXE = ".exe" ;
78+ protected static final String INFO = "INFO\t " ;
79+
80+ protected static File seleniumManager ;
81+
6782 private final ExecutorService executorService = Executors .newFixedThreadPool (2 , r -> {
6883 Thread thread = new Thread (r );
6984 thread .setName (NAME );
@@ -115,6 +130,48 @@ protected DriverService(
115130 this .url = getUrl (port );
116131 }
117132
133+ protected static String runCommand (String ... command ) {
134+ String output = "" ;
135+ try {
136+ Process process = new ProcessBuilder (command )
137+ .redirectErrorStream (false ).start ();
138+ process .waitFor ();
139+ output = CharStreams .toString (new InputStreamReader (
140+ process .getInputStream (), Charsets .UTF_8 ));
141+ } catch (Exception e ) {
142+ e .printStackTrace ();
143+ }
144+ return output .trim ();
145+ }
146+
147+ protected static File getSeleniumManager () {
148+ if (seleniumManager == null ) {
149+ try {
150+ Platform current = Platform .getCurrent ();
151+ String folder = "linux" ;
152+ String extension = "" ;
153+ if (current .is (WINDOWS )) {
154+ extension = EXE ;
155+ folder = "windows" ;
156+ }
157+ else if (current .is (MAC )) {
158+ folder = "mac" ;
159+ }
160+ String binary = String .format ("%s/%s%s" , folder , SELENIUM_MANAGER , extension );
161+ try (InputStream inputStream = DriverService .class .getResourceAsStream (binary )) {
162+ File tempFolder = Files .createTempDirectory (SELENIUM_MANAGER ).toFile ();
163+ tempFolder .deleteOnExit ();
164+ seleniumManager = new File (tempFolder , SELENIUM_MANAGER + extension );
165+ Files .copy (inputStream , seleniumManager .toPath (), REPLACE_EXISTING );
166+ }
167+ seleniumManager .setExecutable (true );
168+ } catch (Exception e ) {
169+ e .printStackTrace ();
170+ }
171+ }
172+ return seleniumManager ;
173+ }
174+
118175 /**
119176 *
120177 * @param exeName Name of the executable file to look for in PATH
@@ -132,6 +189,18 @@ protected static File findExecutable(
132189 String exeDownload ) {
133190 String defaultPath = new ExecutableFinder ().find (exeName );
134191 String exePath = System .getProperty (exeProperty , defaultPath );
192+
193+ if (exePath == null ) {
194+ File seleniumManager = getSeleniumManager ();
195+ if (seleniumManager != null ) {
196+ String output = runCommand (seleniumManager .getAbsolutePath (),
197+ "--driver" , exeName .replaceAll (EXE , "" ));
198+ if (output .startsWith (INFO )) {
199+ exePath = output .replace (INFO , "" );
200+ }
201+ }
202+ }
203+
135204 Require .state ("The path to the driver executable" , exePath ).nonNull (
136205 "The path to the driver executable must be set by the %s system property;"
137206 + " for more information, see %s. "
0 commit comments