How to use NodeRemovedEvent class of org.openqa.selenium.grid.data package

Best Selenium code snippet using org.openqa.selenium.grid.data.NodeRemovedEvent

Run Selenium automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

copy
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package org.openqa.selenium.grid.distributor.local;
19
20import com.google.common.collect.ImmutableSet;
21import org.openqa.selenium.Beta;
22import org.openqa.selenium.Capabilities;
23import org.openqa.selenium.ImmutableCapabilities;
24import org.openqa.selenium.SessionNotCreatedException;
25import org.openqa.selenium.concurrent.Regularly;
26import org.openqa.selenium.events.EventBus;
27import org.openqa.selenium.grid.config.Config;
28import org.openqa.selenium.grid.data.CreateSessionRequest;
29import org.openqa.selenium.grid.data.CreateSessionResponse;
30import org.openqa.selenium.grid.data.DistributorStatus;
31import org.openqa.selenium.grid.data.NodeAddedEvent;
32import org.openqa.selenium.grid.data.NodeDrainComplete;
33import org.openqa.selenium.grid.data.NodeId;
34import org.openqa.selenium.grid.data.NodeRemovedEvent;
35import org.openqa.selenium.grid.data.NodeStatus;
36import org.openqa.selenium.grid.data.NodeStatusEvent;
37import org.openqa.selenium.grid.data.Slot;
38import org.openqa.selenium.grid.data.SlotId;
39import org.openqa.selenium.grid.distributor.Distributor;
40import org.openqa.selenium.grid.distributor.selector.DefaultSlotSelector;
41import org.openqa.selenium.grid.log.LoggingOptions;
42import org.openqa.selenium.grid.node.HealthCheck;
43import org.openqa.selenium.grid.node.Node;
44import org.openqa.selenium.grid.node.remote.RemoteNode;
45import org.openqa.selenium.grid.security.Secret;
46import org.openqa.selenium.grid.server.BaseServerOptions;
47import org.openqa.selenium.grid.server.EventBusOptions;
48import org.openqa.selenium.grid.server.NetworkOptions;
49import org.openqa.selenium.grid.sessionmap.SessionMap;
50import org.openqa.selenium.grid.sessionmap.config.SessionMapOptions;
51import org.openqa.selenium.internal.Require;
52import org.openqa.selenium.remote.http.HttpClient;
53import org.openqa.selenium.remote.tracing.Tracer;
54import org.openqa.selenium.status.HasReadyState;
55
56import java.time.Duration;
57import java.util.ArrayList;
58import java.util.HashMap;
59import java.util.List;
60import java.util.Map;
61import java.util.Optional;
62import java.util.Set;
63import java.util.concurrent.locks.Lock;
64import java.util.concurrent.locks.ReadWriteLock;
65import java.util.concurrent.locks.ReentrantReadWriteLock;
66import java.util.function.Supplier;
67import java.util.logging.Level;
68import java.util.logging.Logger;
69
70import static com.google.common.collect.ImmutableSet.toImmutableSet;
71import static org.openqa.selenium.grid.data.Availability.DOWN;
72import static org.openqa.selenium.grid.data.Availability.DRAINING;
73
74public class LocalDistributor extends Distributor {
75
76  private static final Logger LOG = Logger.getLogger(LocalDistributor.class.getName());
77
78  private final Tracer tracer;
79  private final EventBus bus;
80  private final HttpClient.Factory clientFactory;
81  private final SessionMap sessions;
82  private final Secret registrationSecret;
83  private final Regularly hostChecker = new Regularly("distributor host checker");
84  private final Map<NodeId, Runnable> allChecks = new HashMap<>();
85
86  private final ReadWriteLock lock = new ReentrantReadWriteLock(/* fair */ true);
87  private final GridModel model;
88  private final Map<NodeId, Node> nodes;
89
90  public LocalDistributor(
91      Tracer tracer,
92      EventBus bus,
93      HttpClient.Factory clientFactory,
94      SessionMap sessions,
95      Secret registrationSecret) {
96    super(tracer, clientFactory, new DefaultSlotSelector(), sessions, registrationSecret);
97    this.tracer = Require.nonNull("Tracer", tracer);
98    this.bus = Require.nonNull("Event bus", bus);
99    this.clientFactory = Require.nonNull("HTTP client factory", clientFactory);
100    this.sessions = Require.nonNull("Session map", sessions);
101    this.model = new GridModel(bus, registrationSecret);
102    this.nodes = new HashMap<>();
103
104    this.registrationSecret = Require.nonNull("Registration secret", registrationSecret);
105
106    bus.addListener(NodeStatusEvent.listener(this::register));
107    bus.addListener(NodeStatusEvent.listener(model::refresh));
108    bus.addListener(NodeDrainComplete.listener(this::remove));
109  }
110
111  public static Distributor create(Config config) {
112    Tracer tracer = new LoggingOptions(config).getTracer();
113    EventBus bus = new EventBusOptions(config).getEventBus();
114    HttpClient.Factory clientFactory = new NetworkOptions(config).getHttpClientFactory(tracer);
115    SessionMap sessions = new SessionMapOptions(config).getSessionMap();
116    BaseServerOptions serverOptions = new BaseServerOptions(config);
117
118    return new LocalDistributor(tracer, bus, clientFactory, sessions, serverOptions.getRegistrationSecret());
119  }
120
121  @Override
122  public boolean isReady() {
123    try {
124      return ImmutableSet.of(bus, sessions).parallelStream()
125        .map(HasReadyState::isReady)
126        .reduce(true, Boolean::logicalAnd);
127    } catch (RuntimeException e) {
128      return false;
129    }
130  }
131
132  private void register(NodeStatus status) {
133    Require.nonNull("Node", status);
134
135    Lock writeLock = lock.writeLock();
136    writeLock.lock();
137    try {
138      if (nodes.containsKey(status.getId())) {
139        return;
140      }
141
142      Set<Capabilities> capabilities = status.getSlots().stream()
143        .map(Slot::getStereotype)
144        .map(ImmutableCapabilities::copyOf)
145        .collect(toImmutableSet());
146
147      // A new node! Add this as a remote node, since we've not called add
148      RemoteNode remoteNode = new RemoteNode(
149        tracer,
150        clientFactory,
151        status.getId(),
152        status.getUri(),
153        registrationSecret,
154        capabilities);
155
156      add(remoteNode);
157    } finally {
158      writeLock.unlock();
159    }
160  }
161
162  @Override
163  public LocalDistributor add(Node node) {
164    Require.nonNull("Node", node);
165
166    LOG.info(String.format("Added node %s at %s.", node.getId(), node.getUri()));
167
168    nodes.put(node.getId(), node);
169    model.add(node.getStatus());
170
171    // Extract the health check
172    Runnable runnableHealthCheck = asRunnableHealthCheck(node);
173    allChecks.put(node.getId(), runnableHealthCheck);
174    hostChecker.submit(runnableHealthCheck, Duration.ofMinutes(5), Duration.ofSeconds(30));
175
176    bus.fire(new NodeAddedEvent(node.getId()));
177
178    return this;
179  }
180
181  private Runnable asRunnableHealthCheck(Node node) {
182    HealthCheck healthCheck = node.getHealthCheck();
183    NodeId id = node.getId();
184    return () -> {
185      HealthCheck.Result result;
186      try {
187        result = healthCheck.check();
188      } catch (Exception e) {
189        LOG.log(Level.WARNING, "Unable to process node " + id, e);
190        result = new HealthCheck.Result(DOWN, "Unable to run healthcheck. Assuming down");
191      }
192
193      Lock writeLock = lock.writeLock();
194      writeLock.lock();
195      try {
196        model.setAvailability(id, result.getAvailability());
197      } finally {
198        writeLock.unlock();
199      }
200    };
201  }
202
203  @Override
204  public boolean drain(NodeId nodeId) {
205    Node node = nodes.get(nodeId);
206    if (node == null) {
207      LOG.info("Asked to drain unregistered node " + nodeId);
208      return false;
209    }
210
211    Lock writeLock = lock.writeLock();
212    writeLock.lock();
213    try {
214      node.drain();
215      model.setAvailability(nodeId, DRAINING);
216    } finally {
217      writeLock.unlock();
218    }
219
220    return node.isDraining();
221  }
222
223  public void remove(NodeId nodeId) {
224    Lock writeLock = lock.writeLock();
225    writeLock.lock();
226    try {
227      model.remove(nodeId);
228      Runnable runnable = allChecks.remove(nodeId);
229      if (runnable != null) {
230        hostChecker.remove(runnable);
231      }
232    } finally {
233      writeLock.unlock();
234      bus.fire(new NodeRemovedEvent(nodeId));
235    }
236  }
237
238  @Override
239  public DistributorStatus getStatus() {
240    Lock readLock = this.lock.readLock();
241    readLock.lock();
242    try {
243      return new DistributorStatus(model.getSnapshot());
244    } finally {
245      readLock.unlock();
246    }
247  }
248
249  @Beta
250  public void refresh() {
251    List<Runnable> allHealthChecks = new ArrayList<>();
252
253    Lock readLock = this.lock.readLock();
254    readLock.lock();
255    try {
256      allHealthChecks.addAll(allChecks.values());
257    } finally {
258      readLock.unlock();
259    }
260
261    allHealthChecks.parallelStream().forEach(Runnable::run);
262  }
263
264  @Override
265  protected Set<NodeStatus> getAvailableNodes() {
266    Lock readLock = this.lock.readLock();
267    readLock.lock();
268    try {
269      return model.getSnapshot().stream()
270        .filter(node -> !DOWN.equals(node.getAvailability()))
271        .collect(toImmutableSet());
272    } finally {
273      readLock.unlock();
274    }
275  }
276
277  @Override
278  protected Supplier<CreateSessionResponse> reserve(SlotId slotId, CreateSessionRequest request) {
279    Require.nonNull("Slot ID", slotId);
280    Require.nonNull("New Session request", request);
281
282    Lock writeLock = this.lock.writeLock();
283    writeLock.lock();
284    try {
285      Node node = nodes.get(slotId.getOwningNodeId());
286      if (node == null) {
287        return () -> {
288          throw new SessionNotCreatedException("Unable to find node");
289        };
290      }
291
292      model.reserve(slotId);
293
294      return () -> {
295        Optional<CreateSessionResponse> response = node.newSession(request);
296
297        if (!response.isPresent()) {
298          model.setSession(slotId, null);
299          throw new SessionNotCreatedException("Unable to create session for " + request);
300        }
301
302        model.setSession(slotId, response.get().getSession());
303
304        return response.get();
305      };
306
307    } finally {
308      writeLock.unlock();
309    }
310  }
311}
312
Full Screen
copy
1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18package org.openqa.selenium.grid.distributor.local;
19
20import com.google.common.collect.ImmutableSet;
21import org.openqa.selenium.events.EventBus;
22import org.openqa.selenium.grid.data.Availability;
23import org.openqa.selenium.grid.data.NodeDrainComplete;
24import org.openqa.selenium.grid.data.NodeDrainStarted;
25import org.openqa.selenium.grid.data.NodeId;
26import org.openqa.selenium.grid.data.NodeRemovedEvent;
27import org.openqa.selenium.grid.data.NodeStatus;
28import org.openqa.selenium.grid.data.NodeStatusEvent;
29import org.openqa.selenium.grid.data.Session;
30import org.openqa.selenium.grid.data.SessionClosedEvent;
31import org.openqa.selenium.grid.data.Slot;
32import org.openqa.selenium.grid.data.SlotId;
33import org.openqa.selenium.grid.security.Secret;
34import org.openqa.selenium.internal.Require;
35import org.openqa.selenium.remote.SessionId;
36
37import java.time.Instant;
38import java.util.HashSet;
39import java.util.Iterator;
40import java.util.Map;
41import java.util.Optional;
42import java.util.Set;
43import java.util.concurrent.ConcurrentHashMap;
44import java.util.concurrent.locks.Lock;
45import java.util.concurrent.locks.ReadWriteLock;
46import java.util.concurrent.locks.ReentrantReadWriteLock;
47import java.util.logging.Logger;
48
49import static org.openqa.selenium.grid.data.Availability.DOWN;
50import static org.openqa.selenium.grid.data.Availability.DRAINING;
51import static org.openqa.selenium.grid.data.Availability.UP;
52
53public class GridModel {
54
55  private static final Logger LOG = Logger.getLogger(GridModel.class.getName());
56  private static final SessionId RESERVED = new SessionId("reserved");
57  private final ReadWriteLock lock = new ReentrantReadWriteLock(/* fair */ true);
58  private final Map<Availability, Set<NodeStatus>> nodes = new ConcurrentHashMap<>();
59  private final EventBus events;
60
61  public GridModel(EventBus events, Secret registrationSecret) {
62    this.events = Require.nonNull("Event bus", events);
63    Require.nonNull("Registration secret", registrationSecret);
64
65    events.addListener(NodeDrainStarted.listener(nodeId -> setAvailability(nodeId, DRAINING)));
66    events.addListener(NodeDrainComplete.listener(this::remove));
67    events.addListener(NodeRemovedEvent.listener(this::remove));
68    events.addListener(NodeStatusEvent.listener(status -> refresh(status)));
69
70    events.addListener(SessionClosedEvent.listener(this::release));
71  }
72
73  public GridModel add(NodeStatus node) {
74    Require.nonNull("Node", node);
75
76    Lock writeLock = lock.writeLock();
77    writeLock.lock();
78    try {
79      // If we've already added the node, remove it.
80      for (Set<NodeStatus> nodes : nodes.values()) {
81        Iterator<NodeStatus> iterator = nodes.iterator();
82        while (iterator.hasNext()) {
83          NodeStatus next = iterator.next();
84
85          // If the ID is the same, we're re-adding a node. If the URI is the same a node probably restarted
86          if (next.getId().equals(node.getId()) || next.getUri().equals(node.getUri())) {
87            LOG.info(String.format("Re-adding node with id %s and URI %s.", node.getId(), node.getUri()));
88            iterator.remove();
89          }
90        }
91      }
92
93      // Nodes are initially added in the "down" state until something changes their availability
94      nodes(DOWN).add(node);
95    } finally {
96      writeLock.unlock();
97    }
98
99    return this;
100  }
101
102  public GridModel refresh(NodeStatus status) {
103    Require.nonNull("Node status", status);
104
105    Lock writeLock = lock.writeLock();
106    writeLock.lock();
107    try {
108      AvailabilityAndNode availabilityAndNode = findNode(status.getId());
109
110      if (availabilityAndNode == null) {
111        return this;
112      }
113
114      // if the node was marked as "down", keep it down until a healthcheck passes:
115      // just because the node can hit the event bus doesn't mean it's reachable
116      if (DOWN.equals(availabilityAndNode.availability)) {
117        nodes(DOWN).remove(availabilityAndNode.status);
118        nodes(DOWN).add(status);
119        return this;
120      }
121
122      // But do trust the node if it tells us it's draining
123      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
124      nodes(status.getAvailability()).add(status);
125      return this;
126    } finally {
127      writeLock.unlock();
128    }
129  }
130
131  public GridModel remove(NodeId id) {
132    Require.nonNull("Node ID", id);
133
134    Lock writeLock = lock.writeLock();
135    writeLock.lock();
136    try {
137      AvailabilityAndNode availabilityAndNode = findNode(id);
138      if (availabilityAndNode == null) {
139        return this;
140      }
141
142      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
143      return this;
144    } finally {
145      writeLock.unlock();
146    }
147  }
148
149  public Availability setAvailability(NodeId id, Availability availability) {
150    Require.nonNull("Node ID", id);
151    Require.nonNull("Availability", availability);
152
153    Lock writeLock = lock.writeLock();
154    writeLock.lock();
155    try {
156      AvailabilityAndNode availabilityAndNode = findNode(id);
157
158      if (availabilityAndNode == null) {
159        return DOWN;
160      }
161
162      if (availability.equals(availabilityAndNode.availability)) {
163        return availability;
164      }
165
166      nodes(availabilityAndNode.availability).remove(availabilityAndNode.status);
167      nodes(availability).add(availabilityAndNode.status);
168
169      LOG.info(String.format(
170        "Switching node %s (uri: %s) from %s to %s",
171        id,
172        availabilityAndNode.status.getUri(),
173        availabilityAndNode.availability,
174        availability));
175      return availabilityAndNode.availability;
176    } finally {
177      writeLock.unlock();
178    }
179  }
180
181  public boolean reserve(SlotId slotId) {
182    Lock writeLock = lock.writeLock();
183    writeLock.lock();
184    try {
185      AvailabilityAndNode node = findNode(slotId.getOwningNodeId());
186      if (node == null) {
187        LOG.warning(String.format("Asked to reserve slot on node %s, but unable to find node", slotId.getOwningNodeId()));
188        return false;
189      }
190
191      if (!UP.equals(node.availability)) {
192        LOG.warning(String.format(
193          "Asked to reserve a slot on node %s, but not is %s",
194          slotId.getOwningNodeId(),
195          node.availability));
196        return false;
197      }
198
199      Optional<Slot> maybeSlot = node.status.getSlots().stream()
200        .filter(slot -> slotId.equals(slot.getId()))
201        .findFirst();
202
203      if (!maybeSlot.isPresent()) {
204        LOG.warning(String.format(
205          "Asked to reserve slot on node %s, but no slot with id %s found",
206          node.status.getId(),
207          slotId));
208        return false;
209      }
210
211      reserve(node.status, maybeSlot.get());
212      return true;
213    } finally {
214      writeLock.unlock();
215    }
216  }
217
218  public Set<NodeStatus> getSnapshot() {
219    Lock readLock = this.lock.readLock();
220    readLock.lock();
221    try {
222      ImmutableSet.Builder<NodeStatus> snapshot = ImmutableSet.builder();
223      for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
224        entry.getValue().stream()
225          .map(status -> rewrite(status, entry.getKey()))
226          .forEach(snapshot::add);
227      }
228      return snapshot.build();
229    } finally {
230      readLock.unlock();
231    }
232  }
233
234  private Set<NodeStatus> nodes(Availability availability) {
235    return nodes.computeIfAbsent(availability, ignored -> new HashSet<>());
236  }
237
238  private AvailabilityAndNode findNode(NodeId id) {
239    for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
240      for (NodeStatus nodeStatus : entry.getValue()) {
241        if (id.equals(nodeStatus.getId())) {
242          return new AvailabilityAndNode(entry.getKey(), nodeStatus);
243        }
244      }
245    }
246    return null;
247  }
248
249  private NodeStatus rewrite(NodeStatus status, Availability availability) {
250    return new NodeStatus(
251      status.getId(),
252      status.getUri(),
253      status.getMaxSessionCount(),
254      status.getSlots(),
255      availability);
256  }
257
258  private void release(SessionId id) {
259    if (id == null) {
260      return;
261    }
262
263    Lock writeLock = lock.writeLock();
264    writeLock.lock();
265    try {
266      for (Map.Entry<Availability, Set<NodeStatus>> entry : nodes.entrySet()) {
267        for (NodeStatus node : entry.getValue()) {
268          for (Slot slot : node.getSlots()) {
269            if (!slot.getSession().isPresent()) {
270              continue;
271            }
272
273            if (id.equals(slot.getSession().get().getId())) {
274              Slot released = new Slot(
275                slot.getId(),
276                slot.getStereotype(),
277                slot.getLastStarted(),
278                Optional.empty());
279              amend(entry.getKey(), node, released);
280              return;
281            }
282          }
283        }
284      }
285    } finally {
286      writeLock.unlock();
287    }
288  }
289
290  private void reserve(NodeStatus status, Slot slot) {
291    Instant now = Instant.now();
292
293    Slot reserved = new Slot(
294      slot.getId(),
295      slot.getStereotype(),
296      now,
297      Optional.of(new Session(
298        RESERVED,
299        status.getUri(),
300        slot.getStereotype(),
301        slot.getStereotype(),
302        now)));
303
304    amend(UP, status, reserved);
305  }
306
307  public void setSession(SlotId slotId, Session session) {
308    Require.nonNull("Slot ID", slotId);
309
310    AvailabilityAndNode node = findNode(slotId.getOwningNodeId());
311    if (node == null) {
312      LOG.warning("Grid model and reality have diverged. Unable to find node " + slotId.getOwningNodeId());
313      return;
314    }
315
316    Optional<Slot> maybeSlot = node.status.getSlots().stream()
317      .filter(slot -> slotId.equals(slot.getId()))
318      .findFirst();
319
320    if (!maybeSlot.isPresent()) {
321      LOG.warning("Grid model and reality have diverged. Unable to find slot " + slotId);
322      return;
323    }
324
325    Slot slot = maybeSlot.get();
326    Optional<Session> maybeSession = slot.getSession();
327    if (!maybeSession.isPresent()) {
328      LOG.warning("Grid model and reality have diverged. Slot is not reserved. " + slotId);
329      return;
330    }
331
332    Session current = maybeSession.get();
333    if (!RESERVED.equals(current.getId())) {
334      LOG.warning("Gid model and reality have diverged. Slot has session and is not reserved. " + slotId);
335      return;
336    }
337
338    Slot updated = new Slot(
339      slot.getId(),
340      slot.getStereotype(),
341      session == null ? slot.getLastStarted() : session.getStartTime(),
342      Optional.ofNullable(session));
343
344    amend(node.availability, node.status, updated);
345  }
346
347  private void amend(Availability availability, NodeStatus status, Slot slot) {
348    Set<Slot> newSlots = new HashSet<>(status.getSlots());
349    newSlots.removeIf(s -> s.getId().equals(slot.getId()));
350    newSlots.add(slot);
351
352    nodes(availability).remove(status);
353    nodes(availability).add(new NodeStatus(
354      status.getId(),
355      status.getUri(),
356      status.getMaxSessionCount(),
357      newSlots,
358      status.getAvailability()));
359  }
360
361  private static class AvailabilityAndNode {
362    public final Availability availability;
363    public final NodeStatus status;
364
365    public AvailabilityAndNode(Availability availability, NodeStatus status) {
366      this.availability = availability;
367      this.status = status;
368    }
369  }
370}
371
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

...Most popular Stackoverflow questions on NodeRemovedEvent

    No relevant questions found for this class 😞

Most used methods in NodeRemovedEvent

Run Selenium Automation Tests on LambdaTest Cloud Grid

Trigger Selenium automation tests on a cloud-based Grid of 3000+ real browsers and operating systems.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)