Skip to content

Create More WebSocket CheckPoint Tests #31264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions dev/io.openliberty.checkpoint_fat_wsoc/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="fat/src"/>
<classpathentry kind="src" path="test-applications/basic.war/src"/>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
23 changes: 23 additions & 0 deletions dev/io.openliberty.checkpoint_fat_wsoc/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>io.openliberty.checkpoint_fat_wsoc</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>bndtools.core.bndbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>bndtools.core.bndnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
compileErrorAction=build
eclipse.preferences.version=1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#Ant properties
#Automatically generated by the ant prepare.settings.files task
eclipse.preferences.version=1
encoding/<project>=UTF-8

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.javadoc=true
org.eclipse.jdt.ui.ondemandthreshold=99
org.eclipse.jdt.ui.staticondemandthreshold=99
org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/** */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=false
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=true
sp_cleanup.remove_unused_imports=true
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
35 changes: 35 additions & 0 deletions dev/io.openliberty.checkpoint_fat_wsoc/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#*******************************************************************************
# Copyright (c) 2025 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#*******************************************************************************
-include= ~../cnf/resources/bnd/bundle.props
bVersion=1.0

src: \
fat/src,\
test-applications/basic.war/src

fat.project: true

tested.features: \
checkpoint,\
servlet-5.0,\
servlet-6.0,\
servlet-6.1,\
websocket-2.0,\
websocket-2.1,\
websocket-2.2


-buildpath: \
org.asynchttpclient:async-http-client;version=2.12.3,\
io.openliberty.io.netty;version=latest,\
com.ibm.websphere.appserver.api.wsoc;version=latest,\
com.ibm.websphere.javaee.servlet.4.0;version=latest,\
com.ibm.ws.wsoc.1.1;version=latest,\
com.ibm.websphere.javaee.websocket.1.0;version=latest
19 changes: 19 additions & 0 deletions dev/io.openliberty.checkpoint_fat_wsoc/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

dependencies {
requiredLibs "org.asynchttpclient:async-http-client:2.12.3",
"org.asynchttpclient:async-http-client-netty-utils:2.12.3",
"com.typesafe.netty:netty-reactive-streams:2.0.4",
"org.reactivestreams:reactive-streams:1.0.4",
'junit:junit:4.12',
project(":io.openliberty.io.netty")
}
addRequiredLibraries.dependsOn addJakartaTransformer
6 changes: 6 additions & 0 deletions dev/io.openliberty.checkpoint_fat_wsoc/cognitiveMetadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
# YML metadata file made use of by the Cognitive Ecosystem.
# Reference Cognitive Metadata is available at: https://github.com/OpenLiberty/open-liberty/tree/integration/dev/build.example_fat/cognitiveMetadata.yml
# description: Uncomment this field and add a description to give some notes about this bucket.
# triageNotes: Uncomment this field if there are some short notes you would like Pipeline Monitors to see when triaging this bucket.
functionalArea: InstantOn
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@

/*******************************************************************************
* Copyright (c) 2025 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package io.openliberty.checkpoint.fat;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.lang.StringBuilder;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.asynchttpclient.Dsl;
import org.asynchttpclient.ws.WebSocket;
import org.asynchttpclient.ws.WebSocketListener;
import org.asynchttpclient.ws.WebSocketUpgradeHandler;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.ibm.websphere.simplicity.ShrinkHelper;

import componenttest.annotation.CheckpointTest;
import componenttest.annotation.Server;
import componenttest.custom.junit.runner.FATRunner;
import componenttest.topology.impl.LibertyServer;
import io.openliberty.checkpoint.spi.CheckpointPhase;

/**
* Basic WebSockets tests to ensure the behavior works as expected after a checkpoint restore.
*/
@RunWith(FATRunner.class)
@CheckpointTest
public class BasicTest {

@Server("basicWsocServer")
public static LibertyServer server;

private static final Logger LOG = Logger.getLogger(BasicTest.class.getName());

private static final String APP_NAME = "basic";

private static Boolean IS_EXPECTED_RESULT = false;

private static CountDownLatch latch; // Used to address timing issues between when the message is recived and when the assert is checked

@BeforeClass
public static void setUp() throws Exception {

// Build the war app and add the dependencies
ShrinkHelper.defaultDropinApp(server, APP_NAME + ".war", "io.openliberty.wsoc.basic");

server.setCheckpoint(CheckpointPhase.AFTER_APP_START, false,
server -> {
assertNotNull("'SRVE0169I: Loading Web Module: " + APP_NAME + "' message not found in log before rerstore",
server.waitForStringInLogUsingMark("SRVE0169I: .*" + APP_NAME, 0));
assertNotNull("'CWWKZ0001I: Application " + APP_NAME + " started' message not found in log.",
server.waitForStringInLogUsingMark("CWWKZ0001I: .*" + APP_NAME, 0));
});
server.startServer();
server.checkpointRestore();
}

@AfterClass
public static void tearDown() throws Exception {

if (server != null && server.isStarted()) {
server.stopServer();
}

}

@Before
public void resetResult() throws Exception {
IS_EXPECTED_RESULT = false;
latch = null;
}

private WebSocketUpgradeHandler createWebSocketUpgradeHandler(Object expectedResult) {

latch = new CountDownLatch(1); // forces the asserts in each test to be checked once the message is recieved (via countdown / await)

WebSocketUpgradeHandler.Builder upgradeHandlerBuilder = new WebSocketUpgradeHandler.Builder();
WebSocketUpgradeHandler wsHandler = upgradeHandlerBuilder
.addWebSocketListener(new WebSocketListener() {
@Override
public void onOpen(WebSocket websocket) {
// WebSocket connection opened
LOG.info("Opened Websocket");
}

@Override
public void onClose(WebSocket websocket, int code, String reason) {
// WebSocket connection closed
LOG.info("Closed Websocket");
}

@Override
public void onError(Throwable t) {
// WebSocket connection error
LOG.info("Session Error Occurred: " + t);
}

@Override
public void onBinaryFrame(byte[] payload, boolean finalFragment, int rsv) {
// Log message
StringBuilder sb = new StringBuilder();
for (byte b : payload) {
sb.append((char) b);
}
LOG.info("Debugging binary message: " + sb.toString());
IS_EXPECTED_RESULT = Arrays.equals(payload, (byte[]) expectedResult);
latch.countDown();
}

@Override
public void onTextFrame(String payload, boolean finalFragment, int rsv) {
// Log message
LOG.info("Debugging text message: " + payload);
IS_EXPECTED_RESULT = expectedResult.equals(payload);
latch.countDown();
}
})
.build();
return wsHandler;
}

/*
* Tested the PathParam annotation
*/
@Test
public void testAnnotatedByteArray() throws Exception {
Object expectedResult = "test message".getBytes();

WebSocketUpgradeHandler wsHandler = createWebSocketUpgradeHandler(expectedResult);

WebSocket webSocketClient = Dsl.asyncHttpClient()
.prepareGet("ws://" +
server.getHostname() + ":" +
server.getHttpDefaultPort() + "/" +
APP_NAME +
"/annotatedByteArray/true")
.setRequestTimeout(5000)
.execute(wsHandler)
.get();

if (webSocketClient.isOpen()) {
LOG.info("sending message");
webSocketClient.sendBinaryFrame("test message".getBytes());
}
latch.await(3L, TimeUnit.SECONDS);
webSocketClient.sendCloseFrame();
assertTrue("Results do not match! ", IS_EXPECTED_RESULT);
}

/*
* Verifies the decoder annotation is picked up
*/
@Test
public void testDecoder() throws Exception {
Object expectedResult = "[class io.openliberty.wsoc.basic.BinaryStreamDecoder]";

WebSocketUpgradeHandler wsHandler = createWebSocketUpgradeHandler(expectedResult);
WebSocket webSocketClient = Dsl.asyncHttpClient()
.prepareGet("ws://" +
server.getHostname() + ":" +
server.getHttpDefaultPort() + "/" +
APP_NAME +
"/defaults")
.setRequestTimeout(5000)
.execute(wsHandler)
.get();

if (webSocketClient.isOpen()) {
LOG.info("sending message");
webSocketClient.sendTextFrame("decoders");
}
latch.await(3L, TimeUnit.SECONDS);
webSocketClient.sendCloseFrame();
assertTrue("Results do not match! ", IS_EXPECTED_RESULT);
}

/*
* Verifies the WsWsocServerContainer#doUprade works (deprecated in 2.1)
*/
@Test
public void testUpgrade() throws Exception {
Object expectedResult = "got your message hello world";

WebSocketUpgradeHandler wsHandler = createWebSocketUpgradeHandler(expectedResult);
WebSocket webSocketClient = Dsl.asyncHttpClient()
.prepareGet("ws://" +
server.getHostname() + ":" +
server.getHttpDefaultPort() + "/" +
APP_NAME +
"/upgradeEcho")
.setRequestTimeout(5000)
.execute(wsHandler)
.get();

if (webSocketClient.isOpen()) {
LOG.info("sending message");
webSocketClient.sendTextFrame("hello world");
}
latch.await(3L, TimeUnit.SECONDS);
webSocketClient.sendCloseFrame();
assertTrue("Results do not match! ", IS_EXPECTED_RESULT);
}

}
Loading