Skip to content

[java] Copy SM binary to cache folder and use it from there (#11359) #12539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 18, 2023
104 changes: 54 additions & 50 deletions java/src/org/openqa/selenium/manager/SeleniumManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -161,23 +169,27 @@ private static Result runCommand(Path binary, List<String> 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);
}
Expand All @@ -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<Path>() {
@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
*
Expand Down Expand Up @@ -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));
}
}