All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.manager.SeleniumManager Maven / Gradle / Ivy

Go to download

Selenium automates browsers. That's it! What you do with that power is entirely up to you.

There is a newer version: 4.26.0
Show newest version
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package org.openqa.selenium.manager;

import static org.openqa.selenium.Platform.LINUX;
import static org.openqa.selenium.Platform.MAC;
import static org.openqa.selenium.Platform.UNIX;
import static org.openqa.selenium.Platform.WINDOWS;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
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;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.json.JsonException;
import org.openqa.selenium.manager.SeleniumManagerOutput.Result;
import org.openqa.selenium.os.ExternalProcess;

/**
 * This implementation is still in beta, and may change.
 *
 * 

The Selenium-Manager binaries are distributed in a JAR file * (org.openqa.selenium:selenium-manager) for the Java binding language. Since these binaries are * compressed within these JAR, we need to serialize the proper binary for the current platform * (Windows, macOS, or Linux) as an executable file. To implement this we use a singleton pattern, * since this way, we have a single instance in the JVM, and we reuse the resulting binary for all * the calls to the Selenium Manager singleton during all the Java process lifetime, deleting the * binary (stored as a local temporal file) on runtime shutdown. */ @Beta 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 (binaryInTemporalFolder && binary != null && Files.exists(binary)) { try { Files.delete(binary); } catch (IOException e) { LOG.warning( String.format( "%s deleting temporal file: %s", e.getClass().getSimpleName(), e.getMessage())); } } })); } } public static SeleniumManager getInstance() { if (manager == null) { synchronized (SeleniumManager.class) { if (manager == null) { manager = new SeleniumManager(); } } } return manager; } /** * Executes a process with the given arguments. * * @param arguments the file and arguments to execute. * @return the standard output of the execution. */ private static Result runCommand(Path binary, List arguments) { LOG.fine(String.format("Executing Process: %s", arguments)); String output; int code; try { ExternalProcess process = ExternalProcess.builder().command(binary.toAbsolutePath().toString(), arguments).start(); if (!process.waitFor(Duration.ofHours(1))) { LOG.warning("Selenium Manager did not exit, shutting it down"); process.shutdown(); } code = process.exitValue(); output = process.getOutput(); } catch (Exception e) { throw new WebDriverException("Failed to run command: " + arguments, e); } SeleniumManagerOutput jsonOutput = null; JsonException failedToParse = null; String dump = output; if (!output.isEmpty()) { try { jsonOutput = new Json().toType(output, SeleniumManagerOutput.class); jsonOutput .getLogs() .forEach( logged -> { Level currentLevel = logged.getLevel() == Level.INFO ? Level.FINE : logged.getLevel(); LOG.log(currentLevel, logged.getMessage()); }); dump = jsonOutput.getResult().getMessage(); } catch (JsonException e) { failedToParse = e; } } if (code != 0) { throw new WebDriverException( "Command failed with code: " + code + ", executed: " + arguments + "\n" + dump, failedToParse); } else if (failedToParse != null || jsonOutput == null) { throw new WebDriverException( "Failed to parse json output, executed: " + arguments + "\n" + dump, failedToParse); } return jsonOutput.getResult(); } /** * Determines the correct Selenium Manager binary to use. * * @return the path to the Selenium Manager binary. */ private synchronized Path getBinary() { if (binary == null) { try { Platform current = Platform.getCurrent(); String folder = ""; String extension = ""; if (current.is(WINDOWS)) { extension = EXE; folder = "windows"; } else if (current.is(MAC)) { folder = "macos"; } else if (current.is(LINUX)) { folder = "linux"; } else if (current.is(UNIX)) { LOG.warning( String.format( "Selenium Manager binary may not be compatible with %s; verify settings", current)); folder = "linux"; } else { throw new WebDriverException("Unsupported platform: " + current); } 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); } } catch (Exception e) { throw new WebDriverException("Unable to obtain Selenium Manager Binary", e); } } else if (!Files.exists(binary)) { throw new WebDriverException( String.format("Unable to obtain Selenium Manager Binary at: %s", binary)); } binary.toFile().setExecutable(true); LOG.fine(String.format("Selenium Manager binary found at: %s", binary)); return binary; } /** * Returns the browser binary path when present in the vendor options * * @param options browser options used to start the session * @return the browser binary path when present, only Chrome/Firefox/Edge */ private String getBrowserBinary(Capabilities options) { List vendorOptionsCapabilities = Arrays.asList("moz:firefoxOptions", "goog:chromeOptions", "ms:edgeOptions"); for (String vendorOptionsCapability : vendorOptionsCapabilities) { if (options.asMap().containsKey(vendorOptionsCapability)) { try { @SuppressWarnings("unchecked") Map vendorOptions = (Map) options.getCapability(vendorOptionsCapability); return (String) vendorOptions.get("binary"); } catch (Exception e) { LOG.warning( String.format( "Exception while retrieving the browser binary path. %s: %s", options, e.getMessage())); } } } return null; } /** * Determines the location of the correct driver. * * @param options Browser Options instance. * @return the location of the driver. */ public Result getDriverPath(Capabilities options, boolean offline) { Path binaryFile = getBinary(); if (binaryFile == null) { return null; } List arguments = new ArrayList<>(); arguments.add("--browser"); arguments.add(options.getBrowserName()); arguments.add("--output"); arguments.add("json"); if (!options.getBrowserVersion().isEmpty()) { arguments.add("--browser-version"); arguments.add(options.getBrowserVersion()); // We know the browser binary path, we don't need the browserVersion. // Useful when "beta" is specified as browserVersion, but the browser driver cannot match it. if (options instanceof MutableCapabilities) { ((MutableCapabilities) options).setCapability("browserVersion", (String) null); } } String browserBinary = getBrowserBinary(options); if (browserBinary != null && !browserBinary.isEmpty()) { arguments.add("--browser-path"); arguments.add(browserBinary); } if (getLogLevel().intValue() <= Level.FINE.intValue()) { arguments.add("--debug"); } if (offline) { arguments.add("--offline"); } Proxy proxy = Proxy.extractFrom(options); if (proxy != null) { if (proxy.getSslProxy() != null) { arguments.add("--proxy"); arguments.add(proxy.getSslProxy()); } else if (proxy.getHttpProxy() != null) { arguments.add("--proxy"); arguments.add(proxy.getHttpProxy()); } } Result result = runCommand(binaryFile, arguments); LOG.fine( String.format( "Using driver at location: %s, browser at location %s", result.getDriverPath(), result.getBrowserPath())); return result; } private Level getLogLevel() { Level level = LOG.getLevel(); if (level == null && LOG.getParent() != null) { level = LOG.getParent().getLevel(); } if (level == null) { return Level.INFO; } 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)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy