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

com.thoughtworks.selenium.webdriven.WebDriverBackedSeleniumServlet Maven / Gradle / Ivy

There is a newer version: 4.5.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 com.thoughtworks.selenium.webdriven;

import static java.util.concurrent.TimeUnit.MINUTES;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;

import com.thoughtworks.selenium.CommandProcessor;
import com.thoughtworks.selenium.SeleniumException;

import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.remote.server.ActiveSession;
import org.openqa.selenium.remote.server.ActiveSessionFactory;
import org.openqa.selenium.remote.server.ActiveSessionListener;
import org.openqa.selenium.remote.server.ActiveSessions;
import org.openqa.selenium.remote.NewSessionPayload;
import org.openqa.selenium.remote.server.NewSessionPipeline;
import org.openqa.selenium.remote.server.WebDriverServlet;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * An implementation of the original selenium rc server endpoint, using a webdriver-backed selenium
 * in order to get things working.
 */
public class WebDriverBackedSeleniumServlet extends HttpServlet {

  // Prepare the shared set of thingies
  private static final Map PROCESSORS = new ConcurrentHashMap<>();

  private NewSessionPipeline pipeline;
  private ActiveSessions sessions;
  private ActiveSessionListener listener;

  @Override
  public void init() throws ServletException {
    super.init();
    Object attribute = getServletContext().getAttribute(WebDriverServlet.ACTIVE_SESSIONS_KEY);
    if (attribute == null) {
      attribute = new ActiveSessions(5, MINUTES);
    }
    sessions = (ActiveSessions) attribute;

    listener = new ActiveSessionListener() {
      @Override
      public void onStop(ActiveSession session) {
        PROCESSORS.remove(session.getId());
      }
    };

    this.pipeline = NewSessionPipeline.builder().add(new ActiveSessionFactory()).create();
  }

  @Override
  public void destroy() {
    if (listener != null) {
      sessions.removeListener(listener);
    }
    super.destroy();
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String cmd = req.getParameter("cmd");
    SessionId sessionId = new SessionId(req.getParameter("sessionId"));
    String[] args = deserializeArgs(req);

    if (cmd == null) {
      resp.sendError(HttpServletResponse.SC_NOT_FOUND);
      return;
    }

    StringBuilder printableArgs = new StringBuilder("[");
    Joiner.on(", ").appendTo(printableArgs, args);
    printableArgs.append("]");
    log( String.format("Command request: %s%s on session %s", cmd, printableArgs, sessionId));

    if ("getNewBrowserSession".equals(cmd)) {
      // Figure out what to do. If the first arg is "*webdriver", check for a session id and use
      // that existing session if present. Otherwise, start a new session with whatever comes to
      // hand. If, however, the first parameter specifies something else, then create a session
      // using a webdriver-backed instance of that.
      startNewSession(resp, args[0], args[1], args.length == 4 ? args[3] : "");
      return;
    } else if ("testComplete".equals(cmd)) {
      CommandProcessor commandProcessor = PROCESSORS.get(sessionId);

      sessions.invalidate(sessionId);

      if (commandProcessor == null) {
        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }
      sendResponse(resp, null);
      return;
    }

    // Common case.
    CommandProcessor commandProcessor = PROCESSORS.get(sessionId);
    if (commandProcessor == null) {
      resp.sendError(HttpServletResponse.SC_NOT_FOUND);
      return;
    }
    try {
      String result = commandProcessor.doCommand(cmd, args);
      sendResponse(resp, result);
    } catch (SeleniumException e) {
      sendError(resp, e.getMessage());
    }
  }

  private void startNewSession(
    HttpServletResponse resp,
    String browserString,
    String baseUrl,
    String options) throws IOException {
    SessionId sessionId = null;

    if (options.startsWith("webdriver.remote.sessionid")) {
      // We may have a hit
      List split = Splitter.on("=")
        .omitEmptyStrings()
        .trimResults()
        .limit(2)
        .splitToList(options);
      if (!"webdriver.remote.sessionid".equals(split.get(0))) {
        log( "Unable to find existing webdriver session. Wrong parameter name: " + options);
        sendError(
          resp,
          "Unable to find existing webdriver session. Wrong parameter name: " + options);
        return;
      }
      if (split.size() != 2) {
        log("Attempted to find webdriver id, but none specified. Bailing");
        sendError(resp, "Unable to find existing webdriver session. No ID specified");
        return;
      }
      sessionId = new SessionId(split.get(1));
    }

    if (sessionId == null) {
      // Let's see if the user chose "webdriver" or something specific.
      DesiredCapabilities caps;
      switch (browserString) {
        case "*webdriver":
          caps = new DesiredCapabilities();
          break;

        case "*chrome":
        case "*firefox":
        case "*firefoxproxy":
        case "*firefoxchrome":
        case "*pifirefox":
          caps = DesiredCapabilities.firefox();
          break;

        case "*iehta":
        case "*iexplore":
        case "*iexploreproxy":
        case "*piiexplore":
          caps = DesiredCapabilities.internetExplorer();
          break;

        case "*googlechrome":
          caps = DesiredCapabilities.chrome();
          break;

        case "*MicrosoftEdge":
          caps = DesiredCapabilities.edge();
          break;

        case "*opera":
        case "*operablink":
          caps = DesiredCapabilities.operaBlink();
          break;

        case "*safari":
        case "*safariproxy":
          caps = DesiredCapabilities.safari();
          break;

        default:
          sendError(resp, "Unable to match browser string: " + browserString);
          return;
      }

      try (NewSessionPayload payload = NewSessionPayload.create(caps)) {
        ActiveSession session = pipeline.createNewSession(payload);
        sessions.put(session);
        sessionId = session.getId();
      } catch (Exception e) {
        log("Unable to start session", e);
        sendError(
          resp,
          "Unable to start session. Cause can be found in logs. Message is: " + e.getMessage());
        return;
      }
    }

    ActiveSession session = sessions.get(sessionId);
    if (session == null) {
      log("Attempt to use non-existent session: " + sessionId);
      sendError(resp, "Attempt to use non-existent session: " + sessionId);
      return;
    }

    PROCESSORS.put(sessionId, new WebDriverCommandProcessor(baseUrl, session.getWrappedDriver()));

    sendResponse(resp, sessionId.toString());
  }

  private void sendResponse(HttpServletResponse resp, String result) throws IOException {
    resp.setStatus(HttpServletResponse.SC_OK);
    resp.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
    resp.getWriter().append("OK").append(result == null ? "" : "," + result);
    resp.flushBuffer();
  }

  private void sendError(HttpServletResponse resp, String result) throws IOException {
    resp.setStatus(HttpServletResponse.SC_OK);
    resp.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
    resp.getWriter().append("ERROR").append(result == null ? "" : ": " + result);
    resp.flushBuffer();
  }

  private String[] deserializeArgs(HttpServletRequest req) {
    // 5 was picked as the maximum length used by the `start` command
    List args = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
      String value = req.getParameter(String.valueOf(i + 1));
      if (value != null) {
        args.add(value);
      } else {
        break;
      }
    }
    return args.toArray(new String[args.size()]);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy