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

com.machinepublishers.jbrowserdriver.JBrowserDriverServer Maven / Gradle / Ivy

Go to download

A programmable, embedded web browser driver compatible with the Selenium WebDriver spec -- fast, headless, WebKit-based, 100% pure Java, and no browser dependencies

There is a newer version: 1.1.1
Show newest version
/* 
 * jBrowserDriver (TM)
 * Copyright (C) 2014-2016 Machine Publishers, LLC
 * 
 * Sales and support: [email protected]
 * Updates: https://github.com/MachinePublishers/jBrowserDriver
 *
 * 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 com.machinepublishers.jbrowserdriver;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.URL;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.HasInputDevices;
import org.openqa.selenium.internal.FindsByClassName;
import org.openqa.selenium.internal.FindsByCssSelector;
import org.openqa.selenium.internal.FindsById;
import org.openqa.selenium.internal.FindsByLinkText;
import org.openqa.selenium.internal.FindsByName;
import org.openqa.selenium.internal.FindsByTagName;
import org.openqa.selenium.internal.FindsByXPath;
import org.openqa.selenium.internal.Killable;

import com.machinepublishers.jbrowserdriver.AppThread.Pause;
import com.machinepublishers.jbrowserdriver.AppThread.Sync;
import com.sun.javafx.webkit.Accessor;
import com.sun.webkit.WebPage;
import com.sun.webkit.network.CookieManager;

class JBrowserDriverServer extends RemoteObject implements JBrowserDriverRemote,
    WebDriver, JavascriptExecutor, FindsById, FindsByClassName, FindsByLinkText, FindsByName,
    FindsByCssSelector, FindsByTagName, FindsByXPath, HasInputDevices, HasCapabilities,
    TakesScreenshot, Killable {
  private static final AtomicInteger childPort = new AtomicInteger();
  private static final AtomicReference socketFactory = new AtomicReference();
  private static Registry registry;

  /*
   * RMI entry point.
   */
  public static void main(String[] args) {
    try {
      CookieManager.setDefault(new CookieStore());
      try {
        URL.setURLStreamHandlerFactory(new StreamHandler());
      } catch (Throwable t) {
        Field factory = null;
        try {
          factory = URL.class.getDeclaredField("factory");
          factory.setAccessible(true);
          Object curFac = factory.get(null);

          //assume we're in the Eclipse jar-in-jar loader
          Field chainedFactory = curFac.getClass().getDeclaredField("chainFac");
          chainedFactory.setAccessible(true);
          chainedFactory.set(curFac, new StreamHandler());
        } catch (Throwable t2) {
          //this should work regardless
          factory.set(null, new StreamHandler());
        }
      }
      final String host = System.getProperty("java.rmi.server.hostname");
      childPort.set((int) Long.parseLong(args[0]));
      long parentPort = Long.parseLong(args[1]);
      parentPort = parentPort < 0 ? 0 : parentPort;
      long parentAltPort = Long.parseLong(args[2]);
      parentAltPort = parentAltPort < 0 ? 0 : parentAltPort;
      Registry registryTmp = null;
      final int maxTries = 5;
      for (int i = 1; i <= maxTries; i++) {
        try {
          if (childPort.get() <= 0) {
            childPort.set(findPort(host));
          }
          socketFactory.set(new SocketFactory(host,
              new PortGroup(childPort.get(), parentPort, parentAltPort), new SocketLock()));

          registryTmp = LocateRegistry.createRegistry(childPort.get(), socketFactory.get(), socketFactory.get());
          break;
        } catch (Throwable t) {
          if (i == maxTries) {
            Util.handleException(t);
          }
        }
      }
      registry = registryTmp;
      registry.rebind("JBrowserDriverRemote", new JBrowserDriverServer());

      RMISocketFactory.setSocketFactory(socketFactory.get());
      System.out.println("ready on ports " + childPort.get() + "/" + parentPort + "/" + parentAltPort);
    } catch (Throwable t) {
      t.printStackTrace();
      System.exit(1);
    }
  }

  private static int findPort(String host) throws IOException {
    ServerSocket socket = null;
    try {
      socket = new ServerSocket();
      socket.setReuseAddress(true);
      socket.bind(new InetSocketAddress(host, 0));
      return socket.getLocalPort();
    } finally {
      Util.close(socket);
    }
  }

  static int childPort() {
    return childPort.get();
  }

  static SocketFactory socketFactory() {
    return socketFactory.get();
  }

  final AtomicReference context = new AtomicReference();

  public JBrowserDriverServer() throws RemoteException {}

  @Override
  public void setUp(final Settings settings) {
    SettingsManager.register(settings);
    context.set(new Context());
  }

  @Override
  public void storeCapabilities(final Capabilities capabilities) {
    context.get().capabilities.set(capabilities);
  }

  /**
   * Optionally call this method if you want JavaFX initialized and the browser
   * window opened immediately. Otherwise, initialization will happen lazily.
   */
  public void init() {
    context.get().init(this);
  }

  /**
   * Reset the state of the browser. More efficient than quitting the
   * browser and creating a new instance.
   * 
   * @param settings
   *          New settings to take effect, superseding the original ones
   */
  public void reset(final Settings settings) {
    AppThread.exec(Pause.SHORT, new AtomicInteger(-1), new Sync() {
      @Override
      public Object perform() {
        context.get().item().engine.get().getLoadWorker().cancel();
        return null;
      }
    });
    Accessor.getPageFor(context.get().item().engine.get()).stop();
    ((CookieStore) CookieManager.getDefault()).clear();
    StatusMonitor.instance().clearStatusMonitor();
    LogsServer.instance().clear(null);
    SettingsManager.register(settings);
    context.get().reset(this);
  }

  /**
   * Reset the state of the browser. More efficient than quitting the
   * browser and creating a new instance.
   */
  public void reset() {
    reset(SettingsManager.settings());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getPageSource() {
    init();
    WebElement element = ElementServer.create(context.get()).findElementByTagName("html");
    if (element != null) {
      String outerHtml = element.getAttribute("outerHTML");
      if (outerHtml != null && !outerHtml.isEmpty()) {
        return outerHtml;
      }
    }
    WebPage page = Accessor.getPageFor(context.get().item().engine.get());
    String html = page.getHtml(page.getMainFrame());
    if (html != null && !html.isEmpty()) {
      return html;
    }
    return page.getInnerText(page.getMainFrame());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getCurrentUrl() {
    init();
    return AppThread.exec(Pause.NONE, context.get().statusCode, new Sync() {
      public String perform() {
        return context.get().item().view.get().getEngine().getLocation();
      }
    });
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void pageWait() {
    context.get().item().httpListener.get().resetStatusCode();
    getStatusCode();
  }

  public int getStatusCode() {
    return getStatusCode(context.get().timeouts.get().getPageLoadTimeoutMS());
  }

  private int getStatusCode(long waitMS) {
    init();
    try {
      synchronized (context.get().statusCode) {
        if (context.get().statusCode.get() == 0) {
          context.get().statusCode.wait(waitMS);
        }
      }
    } catch (InterruptedException e) {
      LogsServer.instance().exception(e);
    }
    return context.get().statusCode.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getTitle() {
    init();
    return AppThread.exec(Pause.NONE, context.get().statusCode, new Sync() {
      public String perform() {
        return context.get().item().view.get().getEngine().getTitle();
      }
    });
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void get(final String url) {
    init();
    long start = System.currentTimeMillis();
    try {
      AppThread.exec(Pause.SHORT, context.get().statusCode,
          context.get().timeouts.get().getPageLoadTimeoutMS(), new Sync() {
            public Object perform() {
              context.get().item().engine.get().load(url);
              return null;
            }
          });
      long end = System.currentTimeMillis();
      if (context.get().timeouts.get().getPageLoadTimeoutMS() == 0) {
        getStatusCode();
      } else {
        long waitMS = context.get().timeouts.get().getPageLoadTimeoutMS() - (end - start);
        if (waitMS > 0) {
          getStatusCode(waitMS);
        }
      }
    } finally {
      if (context.get().statusCode.get() == 0) {
        AppThread.exec(Pause.SHORT, new AtomicInteger(-1), new Sync() {
          @Override
          public Object perform() {
            context.get().item().engine.get().getLoadWorker().cancel();
            throw new TimeoutException(new StringBuilder()
                .append("Timeout of ")
                .append(context.get().timeouts.get().getPageLoadTimeoutMS())
                .append("ms reached.").toString());
          }
        });
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElement(By by) {
    init();
    return ElementServer.create(context.get()).findElement(by);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElements(By by) {
    init();
    return ElementServer.create(context.get()).findElements(by);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementById(String id) {
    init();
    return ElementServer.create(context.get()).findElementById(id);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsById(String id) {
    init();
    return ElementServer.create(context.get()).findElementsById(id);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByXPath(String expr) {
    init();
    return ElementServer.create(context.get()).findElementByXPath(expr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByXPath(String expr) {
    init();
    return ElementServer.create(context.get()).findElementsByXPath(expr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByLinkText(final String text) {
    init();
    return ElementServer.create(context.get()).findElementByLinkText(text);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByPartialLinkText(String text) {
    init();
    return ElementServer.create(context.get()).findElementByPartialLinkText(text);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByLinkText(String text) {
    init();
    return ElementServer.create(context.get()).findElementsByLinkText(text);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByPartialLinkText(String text) {
    init();
    return ElementServer.create(context.get()).findElementsByPartialLinkText(text);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByClassName(String cssClass) {
    init();
    return ElementServer.create(context.get()).findElementByClassName(cssClass);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByClassName(String cssClass) {
    init();
    return ElementServer.create(context.get()).findElementsByClassName(cssClass);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByName(String name) {
    init();
    return ElementServer.create(context.get()).findElementByName(name);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByName(String name) {
    init();
    return ElementServer.create(context.get()).findElementsByName(name);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByCssSelector(String expr) {
    init();
    return ElementServer.create(context.get()).findElementByCssSelector(expr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByCssSelector(String expr) {
    init();
    return ElementServer.create(context.get()).findElementsByCssSelector(expr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ElementServer findElementByTagName(String tagName) {
    init();
    return ElementServer.create(context.get()).findElementByTagName(tagName);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List findElementsByTagName(String tagName) {
    init();
    return ElementServer.create(context.get()).findElementsByTagName(tagName);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object executeAsyncScript(String script, Object... args) {
    init();
    return ElementServer.create(context.get()).executeAsyncScript(script, args);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object executeScript(String script, Object... args) {
    init();
    return ElementServer.create(context.get()).executeScript(script, args);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public KeyboardServer getKeyboard() {
    init();
    return context.get().keyboard.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public MouseServer getMouse() {
    init();
    return context.get().mouse.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Capabilities getCapabilities() {
    init();
    return context.get().capabilities.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void close() {
    init();
    context.get().removeItem();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getWindowHandle() {
    init();
    return context.get().itemId();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Set getWindowHandles() {
    init();
    return context.get().itemIds();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public OptionsServer manage() {
    init();
    return context.get().options.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public LogsServer logs() {
    return LogsServer.instance();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public NavigationServer navigate() {
    init();
    return context.get().navigation.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void quit() {
    getStatusCode();
    kill();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public TargetLocatorServer switchTo() {
    init();
    return context.get().targetLocator.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void kill() {
    final ContextItem item = context.get().item();
    if (item != null) {
      item.engine.get().getLoadWorker().cancel();
      Accessor.getPageFor(item.engine.get()).stop();
    }
    SettingsManager.register(null);
    StatusMonitor.instance().clearStatusMonitor();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public  X getScreenshotAs(final OutputType outputType) throws WebDriverException {
    return outputType.convertFromPngBytes(getScreenshot());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public byte[] getScreenshot() throws WebDriverException {
    init();
    return context.get().robot.get().screenshot();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public File cacheDir() {
    return StreamConnection.cacheDir();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public File attachmentsDir() {
    return StreamConnection.attachmentsDir();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public File mediaDir() {
    return StreamConnection.mediaDir();
  }
}