/*******************************************************************************
* Copyright 2014 JHC Systems Limited
*
* Licensed 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 jhc.redsniff.webdriver.factory;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.BrowserType;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.Duration;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.SECONDS;
public final class WebDriverFactory {
protected interface WebDriverCreator {
WebDriver createWebDriver(URL url, String driverClassName, Capabilities capabilities);
}
protected static class RemoteWebDriverCreator implements WebDriverCreator {
@Override
public WebDriver createWebDriver(URL url, String driverClassName, Capabilities capabilities) {
return new RemoteWebDriver(url, capabilities);
}
}
protected static class LocalWebDriverCreator implements WebDriverCreator {
@Override
public WebDriver createWebDriver(URL url, String driverClassName, Capabilities capabilities) {
String driverMode = capabilities.getBrowserName();
WebDriver driver;
if ((driverClassName != null) && (!driverClassName.isEmpty())) {
try {
Class<?> driverClass = Class.forName(driverClassName);
driver = (WebDriver) driverClass.newInstance();
} catch (Exception e) {
throw new AssertionError("Unable to load driver: " + driverClassName + " due to " + e, e);
}
} else if (driverMode.equals(BrowserType.IE)) {
driver = new InternetExplorerDriver(); // flaky
} else if (driverMode.equals(BrowserType.FIREFOX)) {
driver=new FirefoxDriver();
} else if (driverMode.equals(BrowserType.CHROME)) {
driver=new ChromeDriver();
} else if (driverMode.equals(BrowserType.HTMLUNIT)) {
HtmlUnitDriver htmlUnitdriver = new HtmlUnitDriver(BrowserVersion.FIREFOX_38);
htmlUnitdriver.setJavascriptEnabled(true);
driver=htmlUnitdriver;
} else {
throw new AssertionError("driverMode: " + driverMode + " not recognised");
}
return driver;
}
}
protected static class DefaultWebDriverCreator implements WebDriverCreator {
private final WebDriverCreator localWebDriverCreator;
private final WebDriverCreator remoteWebDriverCreator;
public DefaultWebDriverCreator(
WebDriverCreator localWebDriverCreator,
WebDriverCreator remoteWebDriverCreator) {
this.localWebDriverCreator = localWebDriverCreator;
this.remoteWebDriverCreator = remoteWebDriverCreator;
}
@Override
public WebDriver createWebDriver(URL url, String driverClassName, Capabilities capabilities) {
return url != null ?
remoteWebDriverCreator.createWebDriver(url, driverClassName, capabilities) :
localWebDriverCreator.createWebDriver(url, driverClassName, capabilities);
}
}
private static class BasicDriverHolder {
private WebDriver theDriver;
private WebDriver get() {
return theDriver;
}
private void set(WebDriver driver) {
theDriver = driver;
}
}
private final Log log = LogFactory.getLog(WebDriverFactory.class);
private final WebDriverFactoryConfiguration configuration;
private final WebDriverCreator webDriverCreator;
private final BasicDriverHolder driverHolder = new BasicDriverHolder();
//private static ThreadLocal<WebDriver> driverHolder = new ThreadLocal<WebDriver>();
private final AtomicInteger instanceCount = new AtomicInteger();
private static WebDriverFactoryConfiguration defaultConfiguration = defaultConfiguration();
private static WebDriverFactory defaultInstance = new WebDriverFactory(defaultConfiguration);
private static Map<WebDriverFactoryConfiguration, WebDriverFactory> instanceByConfiguration =
new HashMap<WebDriverFactoryConfiguration, WebDriverFactory>();
static {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
WebDriverFactory.cleanup();
}
});
}
protected WebDriverFactory(WebDriverFactoryConfiguration configuration, WebDriverCreator webDriverCreator) {
this.configuration = configuration;
this.webDriverCreator = webDriverCreator;
}
private WebDriverFactory(WebDriverFactoryConfiguration configuration) {
this(configuration, defaultWebDriverCreator());
}
public static WebDriverFactory getInstance(WebDriverFactoryConfiguration configuration) {
if ((configuration == null) || (configuration == defaultConfiguration)) {
return defaultInstance;
}
synchronized (instanceByConfiguration) {
WebDriverFactory factory = instanceByConfiguration.get(configuration);
if (factory == null) {
factory = new WebDriverFactory(configuration);
instanceByConfiguration.put(configuration.clone(), factory);
}
return factory;
}
}
public static WebDriverFactory getInstance() {
return defaultInstance;
}
private static WebDriverCreator defaultWebDriverCreator() {
return new DefaultWebDriverCreator(
new LocalWebDriverCreator(),
new RemoteWebDriverCreator());
}
protected void setInstance(WebDriverFactory factory) {
defaultInstance = factory;
}
protected void setInstance(WebDriverFactoryConfiguration configuration, WebDriverFactory factory) {
synchronized (instanceByConfiguration) {
instanceByConfiguration.put(configuration.clone(), factory);
}
}
//TODO - make this use capabilities syntax - investigate
/**
* @deprecated use {@link #requireDriver()} and {@link #releaseDriver()}
*/
@Deprecated
public static WebDriver initDriver() {
return getInstance().getOrCreateDriver();
}
public WebDriver requireDriver() {
instanceCount.incrementAndGet();
return getOrCreateDriver();
}
public void releaseDriver() {
if (instanceCount.decrementAndGet() == 0) {
this.end();
}
}
public static void cleanup() {
defaultInstance.doCleanup();
synchronized (instanceByConfiguration) {
for (WebDriverFactory factory: instanceByConfiguration.values()) {
factory.doCleanup();
}
instanceByConfiguration.clear();
}
}
private static WebDriverFactoryConfiguration defaultConfiguration() {
return WebDriverFactoryConfiguration.forSystemProperties();
}
private WebDriver getOrCreateDriver() {
return activeDriver() != null ? activeDriver() : createDriver(this.configuration);
}
private WebDriver createDriver(WebDriverFactoryConfiguration configuration) {
WebDriver driver = webDriverCreator.createWebDriver(
configuration.getUrl(),
configuration.getDriverClassName(),
configuration.getCapabilities());
driverHolder.set(driver);
log.info("CREATED DRIVER: " + driver.getClass().getName());
return driver;
}
public void doCleanup() {
if (this.activeDriver() != null) {
log.info("driver not ended, endinging it on shutdown: " + this.activeDriver());
end();
instanceCount.set(0);
}
}
public void end() {
log.info("KILLED DRIVER");
activeDriver().quit();
driverHolder.set(null);
}
// TODO use non-static method
@Deprecated
public static WebDriver getWebDriver() {
return getInstance().getDriver();
}
public WebDriver getDriver() {
WebDriver driver = this.activeDriver();
if (driver == null) {
log.info("driver is null");
}
return driver;
}
public void setWebDriver(WebDriver driver) {
driverHolder.set(driver);
}
public Duration getInitialAjaxDelayForDriver() {
WebDriver webDriver = activeDriver();
if (webDriver instanceof HtmlUnitDriver) {
return null;
//new Duration(0, MILLISECONDS);
} else if (webDriver instanceof ChromeDriver) {
return null;
} else {
return new Duration(1, SECONDS);
}
}
protected WebDriver activeDriver() {
return driverHolder.get();
}
public Class<? extends WebDriver> getDriverClass() {
WebDriver driver = driverHolder.get();
return driver == null ? null : driver.getClass();
}
protected WebDriverFactoryConfiguration getConfiguration() {
return configuration;
}
protected WebDriverCreator getWebDriverCreator() {
return webDriverCreator;
}
}