Skip to content

Commit 594e146

Browse files
authored
S3: servlets only for hub; S4: add servlet support (#279)
1 parent 375f80a commit 594e146

File tree

8 files changed

+203
-101
lines changed

8 files changed

+203
-101
lines changed

selenium4Deps.gradle

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ sourceSets {
2525
dependencies {
2626
constraints {
2727
api 'com.nordstrom.tools:testng-foundation:5.1.1-j11'
28-
api 'org.seleniumhq.selenium:selenium-grid:4.25.0'
29-
api 'org.seleniumhq.selenium:selenium-support:4.25.0'
30-
api 'org.seleniumhq.selenium:selenium-chrome-driver:4.25.0'
31-
api 'org.seleniumhq.selenium:selenium-edge-driver:4.25.0'
32-
api 'org.seleniumhq.selenium:selenium-firefox-driver:4.25.0'
28+
api 'org.seleniumhq.selenium:selenium-grid:4.26.0'
29+
api 'org.seleniumhq.selenium:selenium-support:4.26.0'
30+
api 'org.seleniumhq.selenium:selenium-chrome-driver:4.26.0'
31+
api 'org.seleniumhq.selenium:selenium-edge-driver:4.26.0'
32+
api 'org.seleniumhq.selenium:selenium-firefox-driver:4.26.0'
3333
api 'org.seleniumhq.selenium:selenium-opera-driver:4.4.0'
34-
api 'org.seleniumhq.selenium:selenium-safari-driver:4.25.0'
35-
api 'com.nordstrom.ui-tools:htmlunit-remote:4.25.0'
36-
api 'org.seleniumhq.selenium:htmlunit3-driver:4.25.0'
37-
api 'org.htmlunit:htmlunit:4.5.0'
34+
api 'org.seleniumhq.selenium:selenium-safari-driver:4.26.0'
35+
api 'com.nordstrom.ui-tools:htmlunit-remote:4.26.0'
36+
api 'org.seleniumhq.selenium:htmlunit3-driver:4.26.0'
37+
api 'org.htmlunit:htmlunit:4.6.0'
3838
api 'com.codeborne:phantomjsdriver:1.5.0'
3939
api 'org.apache.httpcomponents:httpclient:4.5.14'
4040
api 'org.eclipse.jetty:jetty-servlet:9.4.50.v20221201'

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

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
126126
*/
127127
GRID_PLUGINS("selenium.grid.plugins", null),
128128

129+
/**
130+
* This setting specifies a comma-delimited list of fully-qualified names of servlet classes to extend the
131+
* capabilities of the local <b>Selenium Grid</b>.
132+
* <p>
133+
* <b>NOTE</b>: For <b>Selenium 3</b>, the specified servlets are hosted by the <b>Grid</b> hub server.
134+
* For <b>Selenium 4</b>, they're hosted by the {@link com.nordstrom.automation.selenium.examples.ServletContainer
135+
* ServletContainer} class.
136+
* <p>
137+
* name: <b>selenium.grid.servlets</b><br>
138+
* default: {@code null}
139+
*/
140+
GRID_SERVLETS("selenium.grid.servlets", null),
141+
129142
/**
130143
* This setting specifies whether the local <b>Selenium Grid</b> instance will be shut down at the end of the
131144
* test run.
@@ -192,15 +205,6 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
192205
*/
193206
SLOT_MATCHER("selenium.slot.matcher", FoundationSlotMatcher.class.getName()),
194207

195-
/**
196-
* This setting specifies a comma-delimited list of fully-qualified names of servlet classes to extend the
197-
* capabilities of the local <b>Selenium Grid</b> hub server.
198-
* <p>
199-
* name: <b>selenium.hub.servlets</b><br>
200-
* default: {@code null}
201-
*/
202-
HUB_SERVLETS("selenium.hub.servlets", null),
203-
204208
/**
205209
* This setting specifies whether to launch the local <b>Selenium Grid</b> hub server with JDWP debugging.
206210
* <p>
@@ -218,15 +222,6 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
218222
*/
219223
NODE_CONFIG("selenium.node.config", null),
220224

221-
/**
222-
* This setting specifies a comma-delimited list of fully-qualified names of servlet classes to extend the
223-
* capabilities of local <b>Selenium Grid</b> node servers.
224-
* <p>
225-
* name: <b>selenium.node.servlets</b><br>
226-
* default: {@code null}
227-
*/
228-
NODE_SERVLETS("selenium.node.servlets", null),
229-
230225
/**
231226
* This setting specifies whether to launch the local <b>Selenium Grid</b> node server with JDWP debugging.
232227
* <p>
@@ -974,14 +969,14 @@ public String[] getDependencyContexts() {
974969
*
975970
* @return collection of specified hub servlets (may be empty)
976971
*/
977-
public Set<String> getHubServlets() {
972+
public Set<String> getGridServlets() {
978973
Set<String> servlets = new HashSet<>();
979-
// get specified hub servlet classes
980-
String hubServlets = getString(SeleniumSettings.HUB_SERVLETS.key());
974+
// get specified grid servlet classes
975+
String gridServlets = getString(SeleniumSettings.GRID_SERVLETS.key());
981976
// if servlets are specified
982-
if (!(hubServlets == null || hubServlets.isEmpty())) {
977+
if (!(gridServlets == null || gridServlets.isEmpty())) {
983978
// collect servlet names, minus leading/trailing white space
984-
servlets.addAll(Arrays.asList(hubServlets.trim().split("\\s*,\\s*")));
979+
servlets.addAll(Arrays.asList(gridServlets.trim().split("\\s*,\\s*")));
985980
}
986981
// if example page feature is specified
987982
if (getBoolean(SeleniumSettings.GRID_EXAMPLES.key())) {
@@ -995,23 +990,6 @@ public Set<String> getHubServlets() {
995990
return servlets;
996991
}
997992

998-
/**
999-
* Get the collection of servlets to install on Selenium Grid nodes.
1000-
*
1001-
* @return collection of specified node servlets (may be empty)
1002-
*/
1003-
public Set<String> getNodeServlets() {
1004-
Set<String> servlets = new HashSet<>();
1005-
// get specified node servlet classes
1006-
String nodeServlets = getString(SeleniumSettings.NODE_SERVLETS.key());
1007-
// if servlets are specified
1008-
if (!(nodeServlets == null || nodeServlets.isEmpty())) {
1009-
// collect servlet names, minus leading/trailing white space
1010-
servlets.addAll(Arrays.asList(nodeServlets.trim().split("\\s*,\\s*")));
1011-
}
1012-
return servlets;
1013-
}
1014-
1015993
/**
1016994
* Get the target platform for the current test context.
1017995
*
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.nordstrom.automation.selenium.examples;
2+
3+
import java.util.List;
4+
5+
import com.beust.jcommander.Parameter;
6+
7+
public class ServletFlags {
8+
9+
@Parameter(
10+
names = {"-p", "--port"},
11+
description = "Port to listen on. (default: 8080)")
12+
private int port = 8080;
13+
14+
@Parameter(
15+
names = "--servlet",
16+
description = "Fully qualified servlet class name (may be specified more than once)",
17+
required = true)
18+
private List<String> servlets;
19+
20+
public int getPort() {
21+
return port;
22+
}
23+
24+
public List<String> getServlets() {
25+
if ( ! (servlets == null || servlets.isEmpty())) {
26+
return servlets;
27+
}
28+
throw new IllegalStateException("At least one servlet class must be specified");
29+
}
30+
}

src/main/java/com/nordstrom/automation/selenium/servlet/ExamplePageLauncher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ private Container() {
2929
argsList.add("-cp");
3030
argsList.add(JarUtils.getContextPaths(ServletContainer.getDependencyContexts()).get(0));
3131
argsList.add(ServletContainer.class.getName());
32+
argsList.addAll(ServletContainer.getServletArgs());
3233

3334
builder = new ProcessBuilder(argsList);
3435
builder.environment().put("PATH", PathUtils.getSystemPath());

src/selenium3/java/com/nordstrom/automation/selenium/SeleniumConfig.java

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.nio.file.Path;
1212
import java.nio.file.Paths;
1313
import java.util.Arrays;
14+
import java.util.HashSet;
1415
import java.util.List;
1516
import java.util.Map;
1617
import java.util.Objects;
@@ -240,8 +241,8 @@ public Path createHubConfig() throws IOException {
240241

241242
String slotMatcher = getString(SeleniumSettings.SLOT_MATCHER.key());
242243

243-
// get configured hub servlet collection
244-
Set<String> servlets = getHubServlets();
244+
// get configured grid servlet collection
245+
Set<String> servlets = getGridServlets();
245246
// merge with hub template servlets
246247
servlets.addAll(hubConfig.servlets);
247248

@@ -289,9 +290,12 @@ public Path createNodeConfig(String capabilities, URL hubUrl) throws IOException
289290
}
290291

291292
// get configured node servlet collection
292-
Set<String> servlets = getNodeServlets();
293-
// merge with node template servlets
294-
servlets.addAll(nodeConfig.servlets);
293+
Set<String> servlets = new HashSet<>(nodeConfig.servlets);
294+
// if remote shutdown feature is specified
295+
if (getBoolean(SeleniumSettings.GRID_LIFECYCLE.key())) {
296+
// add lifecycle servlet to the collection
297+
servlets.add(LifecycleServlet.class.getName());
298+
}
295299

296300
// strip extension to get template base path
297301
String configPathBase = nodeConfigPath.substring(0, nodeConfigPath.length() - 5);
@@ -315,20 +319,6 @@ public Path createNodeConfig(String capabilities, URL hubUrl) throws IOException
315319
return filePath;
316320
}
317321

318-
/**
319-
* {@inheritDoc}
320-
*/
321-
@Override
322-
public Set<String> getNodeServlets() {
323-
Set<String> servlets = super.getNodeServlets();
324-
// if remote shutdown feature is specified
325-
if (getBoolean(SeleniumSettings.GRID_LIFECYCLE.key())) {
326-
// add lifecycle servlet to the collection
327-
servlets.add(LifecycleServlet.class.getName());
328-
}
329-
return servlets;
330-
}
331-
332322
/**
333323
* {@inheritDoc}
334324
*/
Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package com.nordstrom.automation.selenium.examples;
22

3+
import java.lang.reflect.InvocationTargetException;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import java.util.Objects;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
9+
import java.util.stream.Stream;
10+
11+
import javax.servlet.annotation.WebServlet;
12+
import javax.servlet.http.HttpServlet;
13+
314
import org.seleniumhq.jetty9.server.Server;
415
import org.seleniumhq.jetty9.servlet.ServletContextHandler;
516
import org.seleniumhq.jetty9.servlet.ServletHolder;
617

7-
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet;
8-
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameA_Servlet;
9-
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameB_Servlet;
10-
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameC_Servlet;
11-
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameD_Servlet;
18+
import com.beust.jcommander.JCommander;
19+
import com.nordstrom.automation.selenium.SeleniumConfig;
1220

1321
public class ServletContainer {
1422

@@ -19,25 +27,65 @@ public class ServletContainer {
1927
};
2028

2129
public static String[] getDependencyContexts() {
22-
return DEPENDENCY_CONTEXTS;
30+
SeleniumConfig config = SeleniumConfig.getConfig();
31+
Set<String> servlets = config.getGridServlets();
32+
Collections.addAll(servlets, DEPENDENCY_CONTEXTS);
33+
return servlets.toArray(new String[0]);
34+
}
35+
36+
public static List<String> getServletArgs() {
37+
SeleniumConfig config = SeleniumConfig.getConfig();
38+
return config.getGridServlets().stream().flatMap(s -> Stream.of("--servlet", s)).collect(Collectors.toList());
2339
}
2440

2541
public static void main(String[] args) throws Exception {
26-
int port = 8080;
42+
ServletFlags flags = new ServletFlags();
43+
JCommander.newBuilder().addObject(flags).build().parse(args);
2744

28-
Server server = new Server(port);
29-
3045
ServletContextHandler context = new ServletContextHandler();
31-
context.setContextPath("/");
32-
33-
context.addServlet(new ServletHolder(new ExamplePageServlet()), "/grid/admin/ExamplePageServlet");
34-
context.addServlet(new ServletHolder(new FrameA_Servlet()), "/grid/admin/FrameA_Servlet");
35-
context.addServlet(new ServletHolder(new FrameB_Servlet()), "/grid/admin/FrameB_Servlet");
36-
context.addServlet(new ServletHolder(new FrameC_Servlet()), "/grid/admin/FrameC_Servlet");
37-
context.addServlet(new ServletHolder(new FrameD_Servlet()), "/grid/admin/FrameD_Servlet");
46+
for (String servletClassName : flags.getServlets()) {
47+
addServlet(context, servletClassName);
48+
}
3849

50+
Server server = new Server(flags.getPort());
3951
server.setHandler(context);
4052
server.start();
4153
server.join();
4254
}
55+
56+
private static void addServlet(final ServletContextHandler context, final String servletClassName) {
57+
Class<?> clazz;
58+
Object instance;
59+
HttpServlet servlet;
60+
ServletHolder holder;
61+
WebServlet webServlet;
62+
String[] pathSpecs;
63+
64+
try {
65+
clazz = Class.forName(servletClassName);
66+
instance = clazz.getDeclaredConstructor().newInstance();
67+
servlet = (HttpServlet) instance;
68+
} catch (ClassNotFoundException e) {
69+
throw new RuntimeException("Failed getting class for name: " + servletClassName, e);
70+
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
71+
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
72+
throw new RuntimeException("Failed instantiating servlet: " + servletClassName, e);
73+
} catch (ClassCastException e) {
74+
throw new RuntimeException("Class '" + servletClassName + "' does not extend 'HttpServlet'");
75+
}
76+
77+
webServlet = Objects.requireNonNull(clazz.getAnnotation(WebServlet.class),
78+
"Failed getting 'WebServlet' annotation of servlet: " + servletClassName);
79+
pathSpecs = webServlet.urlPatterns();
80+
81+
if (pathSpecs.length == 0) {
82+
throw new RuntimeException(
83+
"No URL patterns specified in 'WebServlet' annotation of servlet: " + servletClassName);
84+
}
85+
86+
holder = new ServletHolder(servlet);
87+
for (String pathSpec : pathSpecs) {
88+
context.addServlet(holder, pathSpec);
89+
}
90+
}
4391
}

0 commit comments

Comments
 (0)