Skip to content

Commit 9ad424d

Browse files
committed
Add settings for PUB/SUB port assignments
1 parent 3bbe47f commit 9ad424d

32 files changed

Lines changed: 913 additions & 38 deletions

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
profile=selenium4
2-
version=28.0.0-SNAPSHOT
2+
version=34.0.1-SNAPSHOT

src/main/java/com/nordstrom/automation/selenium/AbstractSeleniumConfig.java

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.http.client.utils.URIBuilder;
3030
import org.openqa.selenium.Capabilities;
3131
import org.openqa.selenium.SearchContext;
32+
import org.openqa.selenium.net.PortProber;
3233
import org.slf4j.Logger;
3334
import org.slf4j.LoggerFactory;
3435

@@ -44,6 +45,7 @@
4445
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameC_Servlet;
4546
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameD_Servlet;
4647
import com.nordstrom.automation.selenium.support.SearchContextWait;
48+
import com.nordstrom.automation.selenium.utility.GridHubPortAllocator;
4749
import com.nordstrom.automation.selenium.utility.HostUtils;
4850
import com.nordstrom.automation.settings.SettingsCore;
4951
import com.nordstrom.common.base.UncheckedThrow;
@@ -159,8 +161,8 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
159161
* name: <b>selenium.grid.launcher</b><br>
160162
* default: (populated by {@link SeleniumConfig#getDefaults() getDefaults()})
161163
* <ul>
162-
* <li>Selenium 3: <b>org.openqa.grid.selenium.GridLauncherV3</b></li>
163164
* <li>Selenium 4: <b>org.openqa.selenium.grid.Bootstrap</b></li>
165+
* <li>Selenium 3: <b>org.openqa.grid.selenium.GridLauncherV3</b></li>
164166
* </ul>
165167
*/
166168
GRID_LAUNCHER("selenium.grid.launcher", null),
@@ -178,29 +180,45 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
178180
* This setting specifies the configuration file name/path for the local <b>Selenium Grid</b> hub server.
179181
* <p>
180182
* name: <b>selenium.hub.config</b><br>
181-
* Selenium 3: <b>hubConfig-s3.json</b><br>
182-
* Selenium 4: <b>hubConfig-s4.json</b>
183+
* Selenium 4: <b>hubConfig-s4.json</b><br>
184+
* Selenium 3: <b>hubConfig-s3.json</b>
183185
*/
184186
HUB_CONFIG("selenium.hub.config", null),
185187

186188
/**
187189
* This is the URL for the <b>Selenium Grid</b> endpoint: [scheme:][//authority]/wd/hub
188190
* <p>
189191
* name: <b>selenium.hub.host</b><br>
190-
* Selenium 3: <b>http://&lt;{@code localhost}&gt;:4445/wd/hub</b><br>
191-
* Selenium 4: <b>http://&lt;{@code localhost}&gt;:4446/wd/hub</b>
192+
* Selenium 4: <b>http://&lt;{@code localhost}&gt;:4444/wd/hub</b><br>
193+
* Selenium 3: <b>http://&lt;{@code localhost}&gt;:4445/wd/hub</b>
192194
*/
193195
HUB_HOST("selenium.hub.host", null),
194196

195197
/**
196198
* This is the port assigned to the local <b>Selenium Grid</b> hub server.
197199
* <p>
198200
* name: <b>selenium.hub.port</b><br>
199-
* Selenium 3: <b>4445</b><br>
200-
* Selenium 4: <b>4446</b>
201+
* Selenium 4: <b>4444</b><br>
202+
* Selenium 3: <b>4445</b>
201203
*/
202204
HUB_PORT("selenium.hub.port", null),
203205

206+
/**
207+
* This is the port used by local <b>Selenium Grid</b> components for publishing events.
208+
* <p>
209+
* name: <b>selenium.publish.port</b><br>
210+
* default: <b>4442</b>
211+
*/
212+
PUBLISH_PORT("selenium.publish.port", "4442"),
213+
214+
/**
215+
* This is the port used by local <b>Selenium Grid</b> components for subscribing to events.
216+
* <p>
217+
* name: <b>selenium.subscribe.port</b><br>
218+
* default: <b>4443</b>
219+
*/
220+
SUBSCRIBE_PORT("selenium.subscribe.port", "4443"),
221+
204222
/**
205223
* This setting specifies the slot matcher used by the local <b>Selenium Grid</b> hub server.
206224
*
@@ -221,8 +239,8 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
221239
* This setting specifies the configuration template name/path for local <b>Selenium Grid</b> node servers.
222240
* <p>
223241
* name: <b>selenium.node.config</b><br>
224-
* Selenium 3: <b>nodeConfig-s3.json</b><br>
225-
* Selenium 4: <b>nodeConfig-s4.json</b>
242+
* Selenium 4: <b>nodeConfig-s4.json</b><br>
243+
* Selenium 3: <b>nodeConfig-s3.json</b>
226244
*/
227245
NODE_CONFIG("selenium.node.config", null),
228246

@@ -354,11 +372,20 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
354372
*/
355373
CONTEXT_PLATFORM("selenium.context.platform", "support"),
356374

375+
/**
376+
* This setting specifies the port that the {@code Appium} server listens on.
377+
* <p>
378+
* name: <b>appium.server.port</b><br>
379+
* default: <b>4723</b>
380+
*/
381+
APPIUM_SERVER_PORT("appium.server.port", "4723"),
382+
357383
/**
358384
* This setting specifies the path to an {@code Appium} configuration file provided to the server
359385
* when it's launched as a local <b>Selenium Grid</b> node server.
360386
* <p>
361-
* <b>NOTE</b>: If specified, this setting
387+
* <b>NOTE</b>: If specified, the config file indicated by this setting provides base values for the options
388+
* it declares. These values can be supplemented or overridden via the {@link #APPIUM_CLI_ARGS} setting.
362389
* <p>
363390
* name: <b>appium.config.path</b><br>
364391
* default: {@code null}
@@ -693,6 +720,22 @@ public synchronized URL getHubUrl() {
693720
return hubUrl;
694721
}
695722

723+
/**
724+
* Get the <b>Selenium Grid</b> event bus 'publish' URL.
725+
*
726+
* @return URL for publishing <b>Grid</b> events
727+
* @see SeleniumSettings#PUBLISH_PORT
728+
*/
729+
public abstract String getPublishUrl();
730+
731+
/**
732+
* Get the <b>Selenium Grid</b> event bus 'subscribe' URL.
733+
*
734+
* @return URL for subscribing to <b>Grid</b> events
735+
* @see SeleniumSettings#SUBSCRIBE_PORT
736+
*/
737+
public abstract String getSubscribeUrl();
738+
696739
/**
697740
* Get object that represents the active Selenium Grid.
698741
*
@@ -822,6 +865,16 @@ public Path getHubConfigPath() {
822865
return hubConfigPath;
823866
}
824867

868+
/**
869+
* Get the port that the {@code Appium} server listens on.
870+
*
871+
* @return {@link SeleniumSettings#APPIUM_SERVER_PORT APPIUM_SERVER_PORT} if defined and available;
872+
* otherwise random available port
873+
*/
874+
public int getAppiumServerPort() {
875+
return getAvailablePort(SeleniumSettings.APPIUM_SERVER_PORT);
876+
}
877+
825878
/**
826879
* Get the path to the Appium configuration.
827880
*
@@ -1117,4 +1170,25 @@ public boolean appiumWithPM2() {
11171170
public String getSettingsPath() {
11181171
return SETTINGS_FILE;
11191172
}
1173+
1174+
/**
1175+
* Get available port for the specified setting.
1176+
*
1177+
* @param setting setting to check for port availability
1178+
* @return value of setting if defined and available; otherwise random available port
1179+
*/
1180+
protected int getAvailablePort(final SeleniumSettings setting) {
1181+
int port = getInt(setting.key(), -1);
1182+
if (port != -1) {
1183+
if (!GridHubPortAllocator.isFree(port)) {
1184+
LOGGER.warn("{} port '{}' is unavailable; finding free port", setting.name(), port);
1185+
port = -1;
1186+
}
1187+
}
1188+
if (port == -1) {
1189+
port = PortProber.findFreePort();
1190+
System.setProperty(setting.key(), Integer.toString(port));
1191+
}
1192+
return port;
1193+
}
11201194
}

src/main/java/com/nordstrom/automation/selenium/DriverPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ default LocalGridServer create(SeleniumConfig config, String launcherClassName,
109109
* @throws IOException if an I/O error occurs
110110
*/
111111
LocalGridServer create(SeleniumConfig config, String launcherClassName, String[] dependencyContexts,
112-
URL hubUrl, final Path workingPath, final Path outputPath) throws IOException;
112+
URL hubUrl, Path workingPath, Path outputPath) throws IOException;
113113

114114
/**
115115
* Get constructor for this driver's {@link RemoteWebDriver} implementation.

src/main/java/com/nordstrom/automation/selenium/core/ServerPidFinder.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,25 @@
1313
public class ServerPidFinder {
1414

1515
private enum PidFinder {
16-
WINDOWS("cmd.exe", "/c", "for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d ^| findstr LISTENING') do @echo %%a"),
17-
MAC_UNIX("sh", "-c", "lsof -iTCP:%d -sTCP:LISTEN -t");
16+
WINDOWS("cmd.exe",
17+
"/c",
18+
"for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d ^| findstr LISTENING') do @echo %%a",
19+
"for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d') do @echo %%a"),
20+
MAC_UNIX("sh",
21+
"-c",
22+
"lsof -nP -iTCP:%d -sTCP:LISTEN -t",
23+
"lsof -nP -iTCP:%d -t");
1824

1925
private String executable;
2026
private String commandOption;
21-
private String commandFormat;
27+
private String listenMode;
28+
private String anyMode;
2229

23-
PidFinder(String execuable, String commandOption, String commandFormat) {
30+
PidFinder(String execuable, String commandOption, String listenMode, String anyMode) {
2431
this.executable = execuable;
2532
this.commandOption = commandOption;
26-
this.commandFormat = commandFormat;
33+
this.listenMode = listenMode;
34+
this.anyMode = anyMode;
2735
}
2836

2937
String getExecutable() {
@@ -34,8 +42,8 @@ String getOption() {
3442
return commandOption;
3543
}
3644

37-
String getCommand(int port) {
38-
return String.format(commandFormat, port);
45+
String getCommand(int port, boolean listen) {
46+
return String.format(listen ? listenMode : anyMode, port);
3947
}
4048
}
4149

@@ -50,16 +58,17 @@ private ServerPidFinder() {
5058
* Get the process ID of the server listening to the specified port.
5159
*
5260
* @param port {@code localhost} port to check
61+
* @param listen {@code true} to require LISTEN mode; {@code false} to accept any mode
5362
* @return if found, ID of listening process; otherwise {@code null}
5463
*/
55-
public static String getPidOfServerAt(int port) {
64+
public static String getPidOfServerAt(int port, boolean listen) {
5665
String pid = null;
5766

5867
try {
5968
PidFinder finder =
6069
OSInfo.getDefault().getType() == OSInfo.OSType.WINDOWS ? PidFinder.WINDOWS : PidFinder.MAC_UNIX;
61-
62-
ProcessBuilder pb = new ProcessBuilder(finder.getExecutable(), finder.getOption(), finder.getCommand(port));
70+
ProcessBuilder pb =
71+
new ProcessBuilder(finder.getExecutable(), finder.getOption(), finder.getCommand(port, listen));
6372
Process process = pb.start();
6473

6574
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.nordstrom.automation.selenium.grid;
2+
3+
public final class BackendCapabilities {
4+
5+
private final boolean dynamicPorts;
6+
private final boolean managedNodes;
7+
private final boolean gracefulShutdown;
8+
private final boolean healthCheck;
9+
private final boolean externalDiscovery;
10+
11+
public BackendCapabilities(boolean dynamicPorts,
12+
boolean managedNodes,
13+
boolean gracefulShutdown,
14+
boolean healthCheck,
15+
boolean externalDiscovery) {
16+
this.dynamicPorts = dynamicPorts;
17+
this.managedNodes = managedNodes;
18+
this.gracefulShutdown = gracefulShutdown;
19+
this.healthCheck = healthCheck;
20+
this.externalDiscovery = externalDiscovery;
21+
}
22+
23+
public boolean supportsDynamicPorts() { return dynamicPorts; }
24+
25+
public boolean supportsManagedNodes() { return managedNodes; }
26+
27+
public boolean supportsGracefulShutdown() { return gracefulShutdown; }
28+
29+
public boolean supportsHealthCheck() { return healthCheck; }
30+
31+
public boolean supportsExternalDiscovery() { return externalDiscovery; }
32+
33+
public static BackendCapabilities selenium3() {
34+
return new BackendCapabilities(
35+
true,
36+
true,
37+
false,
38+
true,
39+
true
40+
);
41+
}
42+
43+
public static BackendCapabilities selenium4() {
44+
return new BackendCapabilities(
45+
true,
46+
true,
47+
true,
48+
true,
49+
true
50+
);
51+
}
52+
53+
BackendCapabilities dockerCaps = new BackendCapabilities(
54+
false,
55+
false,
56+
true,
57+
true,
58+
true
59+
);
60+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.nordstrom.automation.selenium.grid;
2+
3+
import com.nordstrom.automation.selenium.interfaces.BackendCapabilities;
4+
import com.nordstrom.automation.selenium.interfaces.GridBackend;
5+
6+
public class GridControlPlane implements GridBackend {
7+
8+
private final GridBackend backend;
9+
10+
public GridControlPlane(GridBackend backend) {
11+
this.backend = backend;
12+
}
13+
14+
public HubInstance startHub(HubSpec spec) {
15+
return backend.startHub(spec);
16+
}
17+
18+
public NodeInstance startNode(NodeSpec spec) {
19+
return backend.startNode(spec);
20+
}
21+
22+
public void requestHubShutdown(HubInstance instance) {
23+
backend.requestHubShutdown(instance);
24+
}
25+
26+
public void requestNodeShutdown(NodeInstance node) {
27+
backend.requestNodeShutdown(node);
28+
}
29+
30+
public BackendCapabilities capabilities() {
31+
return backend.capabilities();
32+
}
33+
34+
}

0 commit comments

Comments
 (0)