jetty 9.4.x hazelcast remote distribution test (#6068)

* Add distribution test using a remote hazelcast only client true and false, false is failing with CNFE

Signed-off-by: Olivier Lamy <oliver.lamy@gmail.com>
This commit is contained in:
Olivier Lamy 2021-07-13 21:03:54 +10:00 committed by GitHub
parent bbdc459c59
commit a448e46ad7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 542 additions and 1 deletions

View File

@ -11,7 +11,6 @@
<name>Jetty :: Hazelcast Session Manager</name>
<properties>
<hazelcast.version>3.12.10</hazelcast.version>
<bundle-symbolic-name>${project.groupId}.hazelcast</bundle-symbolic-name>
</properties>

View File

@ -26,6 +26,8 @@ lib/hazelcast/hazelcast-client-${hazelcast.version}.jar
[ini]
hazelcast.version?=3.12.10
jetty.webapp.addSystemClasses+=,org.eclipse.jetty.hazelcast.
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.hazelcast.
[license]
Hazelcast is an open source project hosted on Github and released under the Apache 2.0 license.

View File

@ -39,6 +39,7 @@
<maven.resolver.version>1.6.1</maven.resolver.version>
<javax.servlet.api.version>3.1.0</javax.servlet.api.version>
<weld.version>3.1.5.Final</weld.version>
<hazelcast.version>3.12.10</hazelcast.version>
<jboss.logging.version>3.4.1.Final</jboss.logging.version>
<jetty.perf-helper.version>1.0.6</jetty.perf-helper.version>
<ant.version>1.10.9</ant.version>

View File

@ -11,6 +11,7 @@
<properties>
<bundle-symbolic-name>${project.groupId}.tests.distribution</bundle-symbolic-name>
<distribution.debug.port>-1</distribution.debug.port>
</properties>
<dependencies>
@ -89,6 +90,13 @@
<type>war</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-simple-session-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-weld-cdi-webapp</artifactId>
@ -142,6 +150,11 @@
<scope>test</scope>
<type>war</type>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -153,6 +166,8 @@
<systemPropertyVariables>
<mavenRepoPath>${settings.localRepository}</mavenRepoPath>
<jettyVersion>${project.version}</jettyVersion>
<hazelcast.version>${hazelcast.version}</hazelcast.version>
<distribution.debug.port>$(distribution.debug.port}</distribution.debug.port>
</systemPropertyVariables>
</configuration>
</plugin>

View File

@ -152,6 +152,12 @@ public class DistributionTester
commands.add(getJavaExecutable());
commands.addAll(config.jvmArgs);
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");

View File

@ -0,0 +1,263 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
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.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
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.Ignore;
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 AbstractDistributionTest
{
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", "3.12.6"))
.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<String, String> 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");
DistributionTester distribution = DistributionTester.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 (DistributionTester.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 (DistributionTester.Run run2 = distribution.start(argsStart))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 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 (DistributionTester.Run run2 = distribution.start(argsStart))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 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<String, String> 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", "3.12.6"))
.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<String, String> 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");
DistributionTester distribution = DistributionTester.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 (DistributionTester.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<String> argsStart = Arrays.asList(
"jetty.http.port=" + port,
"jetty.session.hazelcast.onlyClient=false",
"jetty.session.hazelcast.configurationLocation=" + hazelcastJettyPath.toAbsolutePath()
);
try (DistributionTester.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 (DistributionTester.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<String, String> 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));
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast-client xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config
http://www.hazelcast.com/schema/client-config/hazelcast-client-config-4.0.xsd"
xmlns="http://www.hazelcast.com/schema/client-config">
<!-- <cluster-name>uat</cluster-name>-->
<network>
<cluster-members>
<address>hazelcast_ip:hazelcast_port</address>
</cluster-members>
</network>
<serialization>
<serializers>
<serializer type-class="org.eclipse.jetty.server.session.SessionData"
class-name="org.eclipse.jetty.hazelcast.session.SessionDataSerializer" />
</serializers>
</serialization>
</hazelcast-client>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
~
~ Licensed 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.
-->
<!--
The default Hazelcast configuration. This is used when no hazelcast.xml is present.
Please see the schema for how to configure Hazelcast at https://hazelcast.com/schema/config/hazelcast-config-3.8.xsd
or the documentation at https://hazelcast.org/documentation/
-->
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.8.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<network>
<port port-count="20">5705</port>
<join>
<multicast enabled="false">
</multicast>
<tcp-ip enabled="true">
<member-list>
<member>hazelcast_ip:hazelcast_port</member>
</member-list>
</tcp-ip>
<aws enabled="false"/>
</join>
</network>
<properties>
<property name="hazelcast.rest.enabled">true</property>
</properties>
<serialization>
<serializers>
<serializer type-class="org.eclipse.jetty.server.session.SessionData"
class-name="org.eclipse.jetty.hazelcast.session.SessionDataSerializer" />
</serializers>
</serialization>
</hazelcast>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
~
~ Licensed 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.
-->
<!--
The default Hazelcast configuration. This is used when no hazelcast.xml is present.
Please see the schema for how to configure Hazelcast at https://hazelcast.com/schema/config/hazelcast-config-3.8.xsd
or the documentation at https://hazelcast.org/documentation/
-->
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.8.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<network>
<reuse-address>true</reuse-address>
<port port-count="20">5701</port>
<join>
<tcp-ip enabled="true">
<!-- <member-list>-->
<!-- <member>host.docker.internal:5705</member>-->
<!-- </member-list>-->
</tcp-ip>
<multicast enabled="false"/>
<aws enabled="false"/>
</join>
</network>
<properties>
<property name="hazelcast.rest.enabled">true</property>
</properties>
<serialization>
<serializers>
<serializer type-class="org.eclipse.jetty.server.session.SessionData"
class-name="org.eclipse.jetty.hazelcast.session.SessionDataSerializer" />
</serializers>
</serialization>
</hazelcast>

View File

@ -1,2 +1,4 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.tests.distribution.LEVEL=DEBUG

View File

@ -39,6 +39,7 @@
<module>test-jndi-webapp</module>
<module>test-http2-webapp</module>
<module>test-simple-webapp</module>
<module>test-simple-session-webapp</module>
<module>test-felix-webapp</module>
<module>test-cdi-common-webapp</module>
<module>test-weld-cdi-webapp</module>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapps-parent</artifactId>
<version>9.4.44-SNAPSHOT</version>
</parent>
<artifactId>test-simple-session-webapp</artifactId>
<packaging>war</packaging>
<name>Test :: Jetty Test Simple Session Webapp</name>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
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;
}
}

View File

@ -0,0 +1,49 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
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());
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
metadata-complete="false"
version="3.1">
<display-name>Very Simple Web Application</display-name>
<servlet>
<servlet-name>SessionTest</servlet-name>
<servlet-class>org.eclipse.jetty.test.session.SessionTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionTest</servlet-name>
<url-pattern>/session</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -7,4 +7,5 @@
version="3.1">
<display-name>Very Simple Web Application</display-name>
</web-app>