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

org.openqa.selenium.grid.node.remote.RemoteNode Maven / Gradle / Ivy

// 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.grid.node.remote;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.opentelemetry.trace.Tracer;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.NoSuchSessionException;
import org.openqa.selenium.grid.component.HealthCheck;
import org.openqa.selenium.grid.data.CreateSessionRequest;
import org.openqa.selenium.grid.data.CreateSessionResponse;
import org.openqa.selenium.grid.data.NodeStatus;
import org.openqa.selenium.grid.data.Session;
import org.openqa.selenium.grid.node.Node;
import org.openqa.selenium.grid.web.Values;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.json.JsonInput;
import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.remote.http.HttpClient;
import org.openqa.selenium.remote.http.HttpHandler;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.tracing.HttpTracing;

import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import static org.openqa.selenium.net.Urls.fromUri;
import static org.openqa.selenium.remote.http.Contents.reader;
import static org.openqa.selenium.remote.http.Contents.string;
import static org.openqa.selenium.remote.http.Contents.utf8String;
import static org.openqa.selenium.remote.http.HttpMethod.DELETE;
import static org.openqa.selenium.remote.http.HttpMethod.GET;
import static org.openqa.selenium.remote.http.HttpMethod.POST;

public class RemoteNode extends Node {

  public static final Json JSON = new Json();
  private final HttpHandler client;
  private final URI externalUri;
  private final Set capabilities;
  private final HealthCheck healthCheck;

  public RemoteNode(
      Tracer tracer,
      HttpClient.Factory clientFactory,
      UUID id,
      URI externalUri,
      Collection capabilities) {
    super(tracer, id, externalUri);
    this.externalUri = Objects.requireNonNull(externalUri);
    this.capabilities = ImmutableSet.copyOf(capabilities);

    this.client = Objects.requireNonNull(clientFactory).createClient(fromUri(externalUri));

    this.healthCheck = new RemoteCheck();
  }

  @Override
  public boolean isSupporting(Capabilities capabilities) {
    return this.capabilities.stream()
        .anyMatch(caps -> caps.getCapabilityNames().stream()
            .allMatch(name -> Objects.equals(
                caps.getCapability(name),
                capabilities.getCapability(name))));

  }

  @Override
  public Optional newSession(CreateSessionRequest sessionRequest) {
    Objects.requireNonNull(sessionRequest, "Capabilities for session are not set");

    HttpRequest req = new HttpRequest(POST, "/se/grid/node/session");
    HttpTracing.inject(tracer, tracer.getCurrentSpan(), req);
    req.setContent(utf8String(JSON.toJson(sessionRequest)));

    HttpResponse res = client.execute(req);

    return Optional.ofNullable(Values.get(res, CreateSessionResponse.class));
  }

  @Override
  protected boolean isSessionOwner(SessionId id) {
    Objects.requireNonNull(id, "Session ID has not been set");

    HttpRequest req = new HttpRequest(GET, "/se/grid/node/owner/" + id);
    HttpTracing.inject(tracer, tracer.getCurrentSpan(), req);

    HttpResponse res = client.execute(req);

    return Values.get(res, Boolean.class) == Boolean.TRUE;
  }

  @Override
  public Session getSession(SessionId id) throws NoSuchSessionException {
    Objects.requireNonNull(id, "Session ID has not been set");

    HttpRequest req = new HttpRequest(GET, "/se/grid/node/session/" + id);
    HttpTracing.inject(tracer, tracer.getCurrentSpan(), req);

    HttpResponse res = client.execute(req);

    return Values.get(res, Session.class);
  }

  @Override
  public HttpResponse executeWebDriverCommand(HttpRequest req) {
    return client.execute(req);
  }

  @Override
  public void stop(SessionId id) throws NoSuchSessionException {
    Objects.requireNonNull(id, "Session ID has not been set");
    HttpRequest req = new HttpRequest(DELETE, "/se/grid/node/session/" + id);
    HttpTracing.inject(tracer, tracer.getCurrentSpan(), req);

    HttpResponse res = client.execute(req);

    Values.get(res, Void.class);
  }

  @Override
  public NodeStatus getStatus() {
    HttpRequest req = new HttpRequest(GET, "/status");
    HttpTracing.inject(tracer, tracer.getCurrentSpan(), req);

    HttpResponse res = client.execute(req);

    try (Reader reader = reader(res);
         JsonInput in = JSON.newInput(reader)) {
      in.beginObject();

      // Skip everything until we find "value"
      while (in.hasNext()) {
        if ("value".equals(in.nextName())) {
          in.beginObject();

          while (in.hasNext()) {
            if ("node".equals(in.nextName())) {
              return in.read(NodeStatus.class);
            } else {
              in.skipValue();
            }
          }

          in.endObject();
        } else {
          in.skipValue();
        }
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }

    throw new IllegalStateException("Unable to read status");
  }

  @Override
  public HealthCheck getHealthCheck() {
    return healthCheck;
  }

  private Map toJson() {
    return ImmutableMap.of(
        "id", getId(),
        "uri", externalUri,
        "capabilities", capabilities);
  }

  private class RemoteCheck implements HealthCheck {
    @Override
    public Result check() {
      HttpRequest req = new HttpRequest(GET, "/status");

      try {
        HttpResponse res = client.execute(req);

        if (res.getStatus() == 200) {
          return new Result(true, externalUri + " is ok");
        }
        return new Result(
            false,
            String.format(
                "An error occurred reading the status of %s: %s",
                externalUri,
                string(res)));
      } catch (RuntimeException e) {
        return new Result(
            false,
            "Unable to determine node status: " + e.getMessage());
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy