diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml deleted file mode 100644 index 953e14ba8dd21..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/codestart.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: undertow-websockets-example -ref: undertow-websockets -type: code -tags: example -metadata: - title: WebSockets example using Undertow - description: Discover WebSockets using Undertow with this cool supersonic chat example. Open multiple tabs to simulate different users. - path: /supersonic-chat.html - related-guide-section: https://quarkus.io/guides/websockets -language: - base: - dependencies: - - io.quarkus:quarkus-undertow-websockets diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java deleted file mode 100644 index f918cb2635dd8..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/java/org/acme/undertowwebsockets/SupersonicChatSocket.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.acme.undertowwebsockets; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.enterprise.context.ApplicationScoped; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.PathParam; -import javax.websocket.server.ServerEndpoint; - -import org.jboss.logging.Logger; - -@ServerEndpoint("/chat/{username}") -@ApplicationScoped -public class SupersonicChatSocket { - - private static final Logger LOG = Logger.getLogger(SupersonicChatSocket.class); - - Map sessions = new ConcurrentHashMap<>(); - - @OnOpen - public void onOpen(Session session, @PathParam("username") String username) { - sessions.put(username, session); - } - - @OnClose - public void onClose(Session session, @PathParam("username") String username) { - sessions.remove(username); - broadcast("User " + username + " left"); - } - - @OnError - public void onError(Session session, @PathParam("username") String username, Throwable throwable) { - sessions.remove(username); - LOG.error("onError", throwable); - broadcast("User " + username + " left on error: " + throwable); - } - - @OnMessage - public void onMessage(String message, @PathParam("username") String username) { - if (message.equalsIgnoreCase("_ready_")) { - broadcast("User " + username + " joined"); - } else { - broadcast(">> " + username + ": " + message); - } - } - - private void broadcast(String message) { - sessions.values().forEach(s -> { - s.getAsyncRemote().sendObject(message, result -> { - if (result.getException() != null) { - System.out.println("Unable to send message: " + result.getException()); - } - }); - }); - } - -} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html deleted file mode 100644 index 9083febafa014..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/main/resources/META-INF/resources/supersonic-chat.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - Quarkus Supersonic Chat! - - - - - - - - -
-
-
- - -
-
-
-
- -
-
- - -
- -
- - - - - - - - - diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java deleted file mode 100644 index 99f521e51df9e..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/java/src/test/java/org/acme/undertowwebsockets/SupersonicChatSocketTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.acme.undertowwebsockets; - -import java.net.URI; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -import javax.websocket.ClientEndpoint; -import javax.websocket.ContainerProvider; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.test.junit.QuarkusTest; - -@QuarkusTest -public class SupersonicChatSocketTest { - - private static final LinkedBlockingDeque MESSAGES = new LinkedBlockingDeque<>(); - - @TestHTTPResource("/chat/stu") - URI uri; - - @Test - public void testWebsocketChat() throws Exception { - try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(Client.class, uri)) { - Assertions.assertEquals("CONNECT", MESSAGES.poll(10, TimeUnit.SECONDS)); - Assertions.assertEquals("User stu joined", MESSAGES.poll(10, TimeUnit.SECONDS)); - session.getAsyncRemote().sendText("hello world"); - Assertions.assertEquals(">> stu: hello world", MESSAGES.poll(10, TimeUnit.SECONDS)); - } - } - - @ClientEndpoint - public static class Client { - - @OnOpen - public void open(Session session) { - MESSAGES.add("CONNECT"); - // Send a message to indicate that we are ready, - // as the message handler may not be registered immediately after this callback. - session.getAsyncRemote().sendText("_ready_"); - } - - @OnMessage - void message(String msg) { - MESSAGES.add(msg); - } - - } - -} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/base/README.tpl.qute.md similarity index 100% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/README.tpl.qute.md rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/base/README.tpl.qute.md diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html similarity index 100% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/undertow-websockets-example/base/src/main/resources/META-INF/resources/index.entry.qute.html rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/codestart.yml new file mode 100644 index 0000000000000..db9b979db4763 --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/codestart.yml @@ -0,0 +1,12 @@ +name: websockets-codestart +ref: websockets +type: code +tags: extension-codestart +metadata: + title: WebSockets + description: WebSocket communication channel starter code + related-guide-section: https://quarkus.io/guides/websockets +language: + base: + dependencies: + - io.quarkus:quarkus-websockets diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/java/src/main/java/org/acme/StartWebSocket.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/java/src/main/java/org/acme/StartWebSocket.java new file mode 100644 index 0000000000000..6b3b9c9ef49a0 --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/java/src/main/java/org/acme/StartWebSocket.java @@ -0,0 +1,55 @@ +package ilove.quark.us; + +import javax.enterprise.context.ApplicationScoped; +import javax.websocket.EncodeException; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +import static java.util.Objects.requireNonNull; + +@ServerEndpoint("/start-websocket/{name}") +@ApplicationScoped +public class StartWebSocket { + + @OnOpen + public void onOpen(Session session, @PathParam("name") String name) { + System.out.println("onOpen> " + name); + } + + @OnClose + public void onClose(Session session, @PathParam("name") String name) { + System.out.println("onClose> " + name); + } + + @OnError + public void onError(Session session, @PathParam("name") String name, Throwable throwable) { + System.out.println("onError> " + name + ": " + throwable); + } + + @OnMessage + public void onMessage(String message, @PathParam("username") String name) { + System.out.println("onMessage> " + name + ": " + message); + } + + /** + * Send a message to a remote websocket session + * + * @param session the websocket session + * @param message the message to send + * @throws IOException if there is a communication error sending the message object. + * @throws EncodeException if there was a problem encoding the message. + * @throws IllegalArgumentException if the message parameter is {@code null} + */ + public void sendMessage(Session session, String message) throws EncodeException, IOException { + requireNonNull(session, "session is required"); + + session.getBasicRemote().sendObject(message); + } + +} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/kotlin/src/main/kotlin/org/acme/StartWebSocket.kt b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/kotlin/src/main/kotlin/org/acme/StartWebSocket.kt new file mode 100644 index 0000000000000..c498e11faf9f9 --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/websockets-codestart/kotlin/src/main/kotlin/org/acme/StartWebSocket.kt @@ -0,0 +1,45 @@ +package ilove.quark.us + +import java.io.IOException +import javax.enterprise.context.ApplicationScoped +import javax.websocket.* +import javax.websocket.server.PathParam +import javax.websocket.server.ServerEndpoint + +@ServerEndpoint("/start-websocket/{name}") +@ApplicationScoped +class StartWebSocket { + + @OnOpen + fun onOpen(session: Session?, @PathParam("name") name: String) { + println("onOpen> $name") + } + + @OnClose + fun onClose(session: Session?, @PathParam("name") name: String) { + println("onClose> $name") + } + + @OnError + fun onError(session: Session?, @PathParam("name") name: String, throwable: Throwable) { + println("onError> $name: $throwable") + } + + @OnMessage + fun onMessage(message: String, @PathParam("username") name: String) { + println("onMessage> $name: $message") + } + + /** + * Send a message to a remote websocket session + * + * @param session the websocket session + * @param message the message to send + * @throws IOException if there is a communication error sending the message object. + * @throws EncodeException if there was a problem encoding the message. + */ + @Throws(EncodeException::class, IOException::class) + fun sendMessage(session: Session, message: String) { + session.basicRemote.sendObject(message) + } +} diff --git a/extensions/websockets/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/websockets/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml index bbc2120e13289..4af707a51e6d3 100644 --- a/extensions/websockets/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/websockets/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -13,9 +13,10 @@ metadata: - "web" status: "stable" codestart: - name: "undertow-websockets" - kind: "example" - languages: "java" + name: "websockets" + languages: + - "java" + - "kotlin" artifact: "io.quarkus:quarkus-project-core-extension-codestarts" config: - "quarkus.websocket." \ No newline at end of file diff --git a/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/WebSocketsCodestartTest.java b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/WebSocketsCodestartTest.java new file mode 100644 index 0000000000000..9475be9a7a759 --- /dev/null +++ b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/WebSocketsCodestartTest.java @@ -0,0 +1,30 @@ +package io.quarkus.devtools.codestarts.quarkus; + +import static io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog.Language.JAVA; +import static io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog.Language.KOTLIN; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.devtools.testing.codestarts.QuarkusCodestartTest; + +public class WebSocketsCodestartTest { + + @RegisterExtension + public static QuarkusCodestartTest codestartTest = QuarkusCodestartTest.builder() + .codestarts("websockets") + .languages(JAVA, KOTLIN) + .build(); + + @Test + void testContent() throws Throwable { + codestartTest.checkGeneratedSource("org.acme.StartWebSocket"); + } + + @Test + @EnabledIfSystemProperty(named = "build-projects", matches = "true") + void buildAllProjectsForLocalUse() throws Throwable { + codestartTest.buildAllProjects(); + } +} diff --git a/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_java_ilove_quark_us_StartWebSocket.java b/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_java_ilove_quark_us_StartWebSocket.java new file mode 100644 index 0000000000000..6b3b9c9ef49a0 --- /dev/null +++ b/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_java_ilove_quark_us_StartWebSocket.java @@ -0,0 +1,55 @@ +package ilove.quark.us; + +import javax.enterprise.context.ApplicationScoped; +import javax.websocket.EncodeException; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +import static java.util.Objects.requireNonNull; + +@ServerEndpoint("/start-websocket/{name}") +@ApplicationScoped +public class StartWebSocket { + + @OnOpen + public void onOpen(Session session, @PathParam("name") String name) { + System.out.println("onOpen> " + name); + } + + @OnClose + public void onClose(Session session, @PathParam("name") String name) { + System.out.println("onClose> " + name); + } + + @OnError + public void onError(Session session, @PathParam("name") String name, Throwable throwable) { + System.out.println("onError> " + name + ": " + throwable); + } + + @OnMessage + public void onMessage(String message, @PathParam("username") String name) { + System.out.println("onMessage> " + name + ": " + message); + } + + /** + * Send a message to a remote websocket session + * + * @param session the websocket session + * @param message the message to send + * @throws IOException if there is a communication error sending the message object. + * @throws EncodeException if there was a problem encoding the message. + * @throws IllegalArgumentException if the message parameter is {@code null} + */ + public void sendMessage(Session session, String message) throws EncodeException, IOException { + requireNonNull(session, "session is required"); + + session.getBasicRemote().sendObject(message); + } + +} diff --git a/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_kotlin_ilove_quark_us_StartWebSocket.kt b/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_kotlin_ilove_quark_us_StartWebSocket.kt new file mode 100644 index 0000000000000..c498e11faf9f9 --- /dev/null +++ b/integration-tests/devtools/src/test/resources/__snapshots__/WebSocketsCodestartTest/testContent/src_main_kotlin_ilove_quark_us_StartWebSocket.kt @@ -0,0 +1,45 @@ +package ilove.quark.us + +import java.io.IOException +import javax.enterprise.context.ApplicationScoped +import javax.websocket.* +import javax.websocket.server.PathParam +import javax.websocket.server.ServerEndpoint + +@ServerEndpoint("/start-websocket/{name}") +@ApplicationScoped +class StartWebSocket { + + @OnOpen + fun onOpen(session: Session?, @PathParam("name") name: String) { + println("onOpen> $name") + } + + @OnClose + fun onClose(session: Session?, @PathParam("name") name: String) { + println("onClose> $name") + } + + @OnError + fun onError(session: Session?, @PathParam("name") name: String, throwable: Throwable) { + println("onError> $name: $throwable") + } + + @OnMessage + fun onMessage(message: String, @PathParam("username") name: String) { + println("onMessage> $name: $message") + } + + /** + * Send a message to a remote websocket session + * + * @param session the websocket session + * @param message the message to send + * @throws IOException if there is a communication error sending the message object. + * @throws EncodeException if there was a problem encoding the message. + */ + @Throws(EncodeException::class, IOException::class) + fun sendMessage(session: Session, message: String) { + session.basicRemote.sendObject(message) + } +}