diff --git a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod
index 4174d473581..40e322c1672 100644
--- a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod
+++ b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod
@@ -14,7 +14,6 @@ sessions
[files]
maven://com.hazelcast/hazelcast/${hazelcast.version}|lib/hazelcast/hazelcast-${hazelcast.version}.jar
-maven://com.hazelcast/hazelcast-client/${hazelcast.version}|lib/hazelcast/hazelcast-client-${hazelcast.version}.jar
[xml]
etc/sessions/hazelcast/remote.xml
@@ -22,7 +21,6 @@ etc/sessions/hazelcast/remote.xml
[lib]
lib/jetty-hazelcast-${jetty.version}.jar
lib/hazelcast/hazelcast-${hazelcast.version}.jar
-lib/hazelcast/hazelcast-client-${hazelcast.version}.jar
[ini]
hazelcast.version?=4.1
diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml
index 2b81d3afa88..107ba0f26e5 100644
--- a/tests/test-distribution/pom.xml
+++ b/tests/test-distribution/pom.xml
@@ -11,6 +11,7 @@
${project.groupId}.tests.distribution
+ -1
@@ -94,6 +95,13 @@
war
test
+
+ org.eclipse.jetty.tests
+ test-simple-session-webapp
+ ${project.version}
+ war
+ test
+
org.eclipse.jetty.tests
test-weld-cdi-webapp
@@ -167,6 +175,11 @@
test
war
+
+ org.testcontainers
+ testcontainers
+ test
+
@@ -178,6 +191,8 @@
${settings.localRepository}
${project.version}
+ ${hazelcast.version}
+ $(distribution.debug.port}
diff --git a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/JettyHomeTester.java b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/JettyHomeTester.java
index 486a1543d97..ebbb5b43d0e 100644
--- a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/JettyHomeTester.java
+++ b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/JettyHomeTester.java
@@ -149,6 +149,12 @@ public class JettyHomeTester
commands.add(getJavaExecutable());
commands.addAll(config.getJVMArgs());
commands.add("-Djava.io.tmpdir=" + workDir.toAbsolutePath().toString());
+ int debugPort = Integer.getInteger("distribution.debug.port", 0);
+ if (debugPort > 0)
+ {
+ commands.add("-Xdebug");
+ commands.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + debugPort);
+ }
commands.add("-jar");
commands.add(config.jettyHome.toAbsolutePath() + "/start.jar");
diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/HazelcastSessionDistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/HazelcastSessionDistributionTests.java
new file mode 100644
index 00000000000..6fe937dd474
--- /dev/null
+++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/HazelcastSessionDistributionTests.java
@@ -0,0 +1,254 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.tests.distribution;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.BindMode;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HazelcastSessionDistributionTests extends AbstractJettyHomeTest
+{
+ private static final Logger HAZELCAST_LOG = LoggerFactory.getLogger("org.eclipse.jetty.tests.distribution.HazelcastLogs");
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HazelcastSessionDistributionTests.class);
+
+
+ /**
+ * This simulate the onlyClient option which means the JVM running Jetty is only an Hazelcast client and not part
+ * of the cluster
+ */
+ @Test
+ public void testHazelcastRemoteOnlyClient() throws Exception
+ {
+ try (GenericContainer hazelcast =
+ new GenericContainer("hazelcast/hazelcast:" + System.getProperty("hazelcast.version", "4.1"))
+ .withExposedPorts(5701)
+ .waitingFor(Wait.forListeningPort())
+ .withLogConsumer(new Slf4jLogConsumer(HAZELCAST_LOG)))
+ {
+ hazelcast.start();
+ String hazelcastHost = hazelcast.getContainerIpAddress();
+ int hazelcastPort = hazelcast.getMappedPort(5701);
+
+ LOGGER.info("hazelcast started on {}:{}", hazelcastHost, hazelcastPort);
+
+ Map tokenValues = new HashMap<>();
+ tokenValues.put("hazelcast_ip", hazelcastHost);
+ tokenValues.put("hazelcast_port", Integer.toString(hazelcastPort));
+ Path hazelcastJettyPath = Paths.get("target/hazelcast-client.xml");
+ transformFileWithHostAndPort(Paths.get("src/test/resources/hazelcast-client.xml"),
+ hazelcastJettyPath,
+ tokenValues);
+
+ String jettyVersion = System.getProperty("jettyVersion");
+ JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .mavenLocalRepository(System.getProperty("mavenRepoPath"))
+ .build();
+
+ String[] args1 = {
+ "--create-startd",
+ "--approve-all-licenses",
+ "--add-to-start=resources,server,http,webapp,deploy,jmx,servlet,servlets,session-store-hazelcast-remote"
+ };
+ try (JettyHomeTester.Run run1 = distribution.start(args1))
+ {
+ assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ assertEquals(0, run1.getExitValue());
+
+ File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-session-webapp:war:" + jettyVersion);
+ distribution.installWarFile(war, "test");
+
+ int port = distribution.freePort();
+ String[] argsStart = {
+ "jetty.http.port=" + port,
+ "jetty.session.hazelcast.configurationLocation=" + hazelcastJettyPath.toAbsolutePath(),
+ "jetty.session.hazelcast.onlyClient=true"
+ };
+ try (JettyHomeTester.Run run2 = distribution.start(argsStart))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
+
+ startHttpClient();
+ ContentResponse response = client.GET("http://localhost:" + port + "/test/session?action=CREATE");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION CREATED"));
+
+ response = client.GET("http://localhost:" + port + "/test/session?action=READ");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION READ CHOCOLATE THE BEST:FRENCH"));
+ }
+
+ try (JettyHomeTester.Run run2 = distribution.start(argsStart))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
+
+ ContentResponse response = client.GET("http://localhost:" + port + "/test/session?action=READ");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION READ CHOCOLATE THE BEST:FRENCH"));
+ }
+ }
+
+ }
+ }
+
+ @Disabled("not working see https://github.com/hazelcast/hazelcast/issues/18508")
+ /**
+ * This test simulate Hazelcast instance within Jetty a cluster member with an external Hazelcast instance
+ */
+ public void testHazelcastRemoteAndPartOfCluster() throws Exception
+ {
+
+ Map env = new HashMap<>();
+ // -Dhazelcast.local.publicAddress=127.0.0.1:5701
+ env.put("JAVA_OPTS", "-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml");
+ try (GenericContainer hazelcast =
+ new GenericContainer("hazelcast/hazelcast:" + System.getProperty("hazelcast.version", "4.1"))
+ .withExposedPorts(5701, 5705)
+ .withEnv(env)
+ .waitingFor(Wait.forLogMessage(".*is STARTED.*", 1))
+ //.withNetworkMode("host")
+ //.waitingFor(Wait.forListeningPort())
+ .withClasspathResourceMapping("hazelcast-server.xml",
+ "/opt/hazelcast/config_ext/hazelcast.xml",
+ BindMode.READ_ONLY)
+ .withLogConsumer(new Slf4jLogConsumer(HAZELCAST_LOG)))
+ {
+ hazelcast.start();
+ String hazelcastHost = InetAddress.getByName(hazelcast.getContainerIpAddress()).getHostAddress(); // hazelcast.getContainerIpAddress();
+ int hazelcastPort = hazelcast.getMappedPort(5701);
+// int hazelcastMultiCastPort = hazelcast.getMappedPort(54327);
+
+ LOGGER.info("hazelcast started on {}:{}", hazelcastHost, hazelcastPort);
+
+ Map tokenValues = new HashMap<>();
+ tokenValues.put("hazelcast_ip", hazelcastHost);
+ tokenValues.put("hazelcast_port", Integer.toString(hazelcastPort));
+// tokenValues.put("hazelcast_multicast_port", Integer.toString(hazelcastMultiCastPort));
+ Path hazelcastJettyPath = Paths.get("target/hazelcast-jetty.xml");
+ transformFileWithHostAndPort(Paths.get("src/test/resources/hazelcast-jetty.xml"),
+ hazelcastJettyPath,
+ tokenValues);
+
+ String jettyVersion = System.getProperty("jettyVersion");
+ JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .mavenLocalRepository(System.getProperty("mavenRepoPath"))
+ .build();
+
+ String[] args1 = {
+ "--create-startd",
+ "--approve-all-licenses",
+ "--add-to-start=resources,server,http,webapp,deploy,jmx,servlet,servlets,session-store-hazelcast-remote"
+ };
+ try (JettyHomeTester.Run run1 = distribution.start(args1))
+ {
+ assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
+ assertEquals(0, run1.getExitValue());
+
+ File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-session-webapp:war:" + jettyVersion);
+ distribution.installWarFile(war, "test");
+
+ int port = distribution.freePort();
+ List argsStart = Arrays.asList(
+ "jetty.http.port=" + port,
+ "jetty.session.hazelcast.onlyClient=false",
+ "jetty.session.hazelcast.configurationLocation=" + hazelcastJettyPath.toAbsolutePath()
+ );
+
+ try (JettyHomeTester.Run run2 = distribution.start(argsStart))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started @", 60, TimeUnit.SECONDS));
+
+ startHttpClient();
+ ContentResponse response = client.GET("http://localhost:" + port + "/test/session?action=CREATE");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION CREATED"));
+
+ response = client.GET("http://localhost:" + port + "/test/session?action=READ");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION READ CHOCOLATE THE BEST:FRENCH"));
+ }
+
+ LOGGER.info("restarting Jetty");
+
+ try (JettyHomeTester.Run run2 = distribution.start(argsStart))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started @", 15, TimeUnit.SECONDS));
+
+ ContentResponse response = client.GET("http://localhost:" + port + "/test/session?action=READ");
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("SESSION READ CHOCOLATE THE BEST:FRENCH"));
+ }
+ }
+
+ }
+ }
+
+ /**
+ * @param input input file to interpolate
+ * @param output output file of interpolation
+ * @param tokensValues key token to replace, value the value
+ */
+ private void transformFileWithHostAndPort(Path input, Path output, Map tokensValues) throws Exception
+ {
+ StringBuilder fileContent = new StringBuilder();
+ Files.deleteIfExists(output);
+ Files.createFile(output);
+ try (OutputStream outputStream = Files.newOutputStream(output))
+ {
+ Files.readAllLines(input).forEach(line ->
+ {
+ StringBuilder newLine = new StringBuilder(line);
+ tokensValues.forEach((key, value) ->
+ {
+ String interpolated = newLine.toString().replace(key, value);
+ newLine.setLength(0);
+ newLine.append(interpolated);
+ });
+ fileContent.append(newLine.toString());
+ fileContent.append(System.lineSeparator());
+ });
+
+
+ outputStream.write(fileContent.toString().getBytes(StandardCharsets.UTF_8));
+ }
+ }
+
+}
diff --git a/tests/test-distribution/src/test/resources/hazelcast-client.xml b/tests/test-distribution/src/test/resources/hazelcast-client.xml
new file mode 100644
index 00000000000..7b09cd433fa
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/hazelcast-client.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ hazelcast_ip:hazelcast_port
+
+
+
+
+
+
+
+
diff --git a/tests/test-distribution/src/test/resources/hazelcast-jetty.xml b/tests/test-distribution/src/test/resources/hazelcast-jetty.xml
new file mode 100644
index 00000000000..e1122fb30e6
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/hazelcast-jetty.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ 5705
+
+
+
+
+
+ hazelcast_ip:hazelcast_port
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
diff --git a/tests/test-distribution/src/test/resources/hazelcast-server.xml b/tests/test-distribution/src/test/resources/hazelcast-server.xml
new file mode 100644
index 00000000000..9a0d70d97a1
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/hazelcast-server.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+ true
+ 5701
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
diff --git a/tests/test-distribution/src/test/resources/jetty-logging.properties b/tests/test-distribution/src/test/resources/jetty-logging.properties
index 6c7059f135c..cb96a22a250 100644
--- a/tests/test-distribution/src/test/resources/jetty-logging.properties
+++ b/tests/test-distribution/src/test/resources/jetty-logging.properties
@@ -1,3 +1,5 @@
# Jetty Logging using jetty-slf4j-impl
org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false
#org.eclipse.jetty.LEVEL=DEBUG
+
+#org.eclipse.jetty.tests.distribution.LEVEL=DEBUG
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index 5aea6c9e1bc..59c82a85d44 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -32,6 +32,7 @@
test-webapp-rfc2616
test-http2-webapp
+ test-simple-session-webapp
test-felix-webapp
test-cdi-common-webapp
test-weld-cdi-webapp
diff --git a/tests/test-webapps/test-simple-session-webapp/pom.xml b/tests/test-webapps/test-simple-session-webapp/pom.xml
new file mode 100644
index 00000000000..9a930d48e9a
--- /dev/null
+++ b/tests/test-webapps/test-simple-session-webapp/pom.xml
@@ -0,0 +1,24 @@
+
+
+ 4.0.0
+
+ org.eclipse.jetty.tests
+ test-webapps-parent
+ 10.0.7-SNAPSHOT
+
+
+ test-simple-session-webapp
+
+ war
+
+ Test :: Jetty Test Simple Session Webapp
+
+
+
+ org.eclipse.jetty.toolchain
+ jetty-servlet-api
+ provided
+
+
+
+
diff --git a/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/Chocolate.java b/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/Chocolate.java
new file mode 100644
index 00000000000..934751f9ed7
--- /dev/null
+++ b/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/Chocolate.java
@@ -0,0 +1,30 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.test.session;
+
+import java.io.Serializable;
+
+public class Chocolate implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ private static final String THE_BEST_EVER = "FRENCH";
+
+ private String theBest = THE_BEST_EVER;
+
+ public String getTheBest()
+ {
+ return this.theBest;
+ }
+}
diff --git a/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/SessionTest.java b/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/SessionTest.java
new file mode 100644
index 00000000000..edf928a174b
--- /dev/null
+++ b/tests/test-webapps/test-simple-session-webapp/src/main/java/org/eclipse/jetty/test/session/SessionTest.java
@@ -0,0 +1,44 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.test.session;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+public class SessionTest extends HttpServlet
+{
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException
+ {
+ String action = req.getParameter("action");
+ if ("CREATE".equals(action))
+ {
+ HttpSession session = req.getSession(true);
+ session.setAttribute("CHOCOLATE", new Chocolate());
+ resp.getOutputStream().println("SESSION CREATED");
+ }
+ else
+ {
+ HttpSession session = req.getSession(false);
+ Chocolate yummi = (Chocolate)session.getAttribute("CHOCOLATE");
+ resp.getOutputStream().println("SESSION READ CHOCOLATE THE BEST:" + yummi.getTheBest());
+ }
+ }
+}
diff --git a/tests/test-webapps/test-simple-session-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-simple-session-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000000..6da5135a1db
--- /dev/null
+++ b/tests/test-webapps/test-simple-session-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Very Simple Web Application
+
+
+ SessionTest
+ org.eclipse.jetty.test.session.SessionTest
+
+
+
+ SessionTest
+ /session
+
+
+