diff --git a/java/src/org/openqa/selenium/manager/SeleniumManager.java b/java/src/org/openqa/selenium/manager/SeleniumManager.java index 9547b4204e220..e2ded0068303e 100644 --- a/java/src/org/openqa/selenium/manager/SeleniumManager.java +++ b/java/src/org/openqa/selenium/manager/SeleniumManager.java @@ -16,18 +16,14 @@ // under the License. package org.openqa.selenium.manager; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static org.openqa.selenium.Platform.MAC; import static org.openqa.selenium.Platform.WINDOWS; import java.io.IOException; import java.io.InputStream; -import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -36,6 +32,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Beta; +import org.openqa.selenium.BuildInfo; import org.openqa.selenium.Capabilities; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.Platform; @@ -63,20 +60,31 @@ public class SeleniumManager { private static final Logger LOG = Logger.getLogger(SeleniumManager.class.getName()); private static final String SELENIUM_MANAGER = "selenium-manager"; + private static final String DEFAULT_CACHE_PATH = "~/.cache/selenium"; + private static final String BINARY_PATH_FORMAT = "/manager/%s/%s"; + private static final String HOME = "~"; + private static final String CACHE_PATH_ENV = "SE_CACHE_PATH"; + private static final String BETA_PREFIX = "0."; + private static final String EXE = ".exe"; private static volatile SeleniumManager manager; - private final String managerPath = System.getenv("SE_MANAGER_PATH"); private Path binary = managerPath == null ? null : Paths.get(managerPath); + private String seleniumManagerVersion; + private boolean binaryInTemporalFolder = false; /** Wrapper for the Selenium Manager binary. */ private SeleniumManager() { + BuildInfo info = new BuildInfo(); + String releaseLabel = info.getReleaseLabel(); + int lastDot = releaseLabel.lastIndexOf("."); + seleniumManagerVersion = BETA_PREFIX + releaseLabel.substring(0, lastDot); if (managerPath == null) { Runtime.getRuntime() .addShutdownHook( new Thread( () -> { - if (binary != null && Files.exists(binary)) { + if (binaryInTemporalFolder && binary != null && Files.exists(binary)) { try { Files.delete(binary); } catch (IOException e) { @@ -161,23 +169,27 @@ private static Result runCommand(Path binary, List arguments) { */ private synchronized Path getBinary() { if (binary == null) { - Platform current = Platform.getCurrent(); - String folder = "linux"; - String extension = ""; - if (current.is(WINDOWS)) { - extension = ".exe"; - folder = "windows"; - } else if (current.is(MAC)) { - folder = "macos"; - } - String binaryPath = String.format("%s/%s%s", folder, SELENIUM_MANAGER, extension); - try (InputStream inputStream = this.getClass().getResourceAsStream(binaryPath)) { - Path tmpPath = Files.createTempDirectory(SELENIUM_MANAGER + System.nanoTime()); + try { + Platform current = Platform.getCurrent(); + String folder = "linux"; + String extension = ""; + if (current.is(WINDOWS)) { + extension = EXE; + folder = "windows"; + } else if (current.is(MAC)) { + folder = "macos"; + } - deleteOnExit(tmpPath); + binary = getBinaryInCache(SELENIUM_MANAGER + extension); + if (!binary.toFile().exists()) { + String binaryPathInJar = String.format("%s/%s%s", folder, SELENIUM_MANAGER, extension); + try (InputStream inputStream = this.getClass().getResourceAsStream(binaryPathInJar)) { + binary.getParent().toFile().mkdirs(); + Files.copy(inputStream, binary); + } + binary.toFile().setExecutable(true); + } - binary = tmpPath.resolve(SELENIUM_MANAGER + extension); - Files.copy(inputStream, binary, REPLACE_EXISTING); } catch (Exception e) { throw new WebDriverException("Unable to obtain Selenium Manager Binary", e); } @@ -192,35 +204,6 @@ private synchronized Path getBinary() { return binary; } - private void deleteOnExit(Path tmpPath) { - Runtime.getRuntime() - .addShutdownHook( - new Thread( - () -> { - try { - Files.walkFileTree( - tmpPath, - new SimpleFileVisitor() { - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) - throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - // Do nothing. We're just tidying up. - } - })); - } - /** * Returns the browser binary path when present in the vendor options * @@ -319,4 +302,25 @@ private Level getLogLevel() { } return level; } + + private Path getBinaryInCache(String binaryName) throws IOException { + String cachePath = DEFAULT_CACHE_PATH.replace(HOME, System.getProperty("user.home")); + + // Look for cache path as env + String cachePathEnv = System.getenv(CACHE_PATH_ENV); + if (cachePathEnv != null) { + cachePath = cachePathEnv; + } + + // If cache path is not writable, SM will be extracted to a temporal folder + Path cacheParent = Paths.get(cachePath); + if (!Files.isWritable(cacheParent)) { + cacheParent = Files.createTempDirectory(SELENIUM_MANAGER); + binaryInTemporalFolder = true; + } + + return Paths.get( + cacheParent.toString(), + String.format(BINARY_PATH_FORMAT, seleniumManagerVersion, binaryName)); + } }