Skip to content

Commit

Permalink
fix: make sure annotated endpoints works along Vaadin PUSH (#86)
Browse files Browse the repository at this point in the history
The workaround added to make PUSH work in Quarkus prevents the usage of custom annotated websocket endpoint. This change allows them to work along Vaadin PUSH.

Fixes #78
  • Loading branch information
mcollovati committed Dec 7, 2022
1 parent eabc925 commit 74310d8
Show file tree
Hide file tree
Showing 14 changed files with 311 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.quarkus.vertx.http.deployment.FilterBuildItem;
import io.quarkus.websockets.client.deployment.ServerWebSocketContainerBuildItem;
import io.quarkus.websockets.client.deployment.WebSocketDeploymentInfoBuildItem;
import org.atmosphere.cpr.ApplicationConfig;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
Expand Down Expand Up @@ -144,7 +145,8 @@ void mapVaadinServletPaths(final BeanArchiveIndexBuildItem beanArchiveIndex,
.builder(QuarkusVaadinServlet.class.getName(),
QuarkusVaadinServlet.class.getName())
.addMapping("/*").setAsyncSupported(true)
.setLoadOnStartup(1).build());
.setLoadOnStartup(1)
.build());
}
}

Expand Down
6 changes: 6 additions & 0 deletions integration-tests/common-test-code/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<artifactId>flow-test-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>custom-websockets</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.vaadin.flow.quarkus.it;

import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint(CustomAnnotatedEnpoint.URI)
public class CustomAnnotatedEnpoint {

public static final String URI = "/app-annotated-websocket";
public static final String PREFIX = ">> Application Annotated Endpoint: ";

@OnOpen
public void onOpen(Session session) {
session.getAsyncRemote().sendText(PREFIX + "Welcome");
}

@OnMessage
public void onMessage(String message, Session session) {
session.getAsyncRemote().sendText(PREFIX + message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.vaadin.flow.quarkus.it;

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusIntegrationTest;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.vaadin.sample.websockets.DependencyAnnotatedWS;
import org.vaadin.sample.websockets.SimpleEndpoint;

@QuarkusIntegrationTest
class CustomWebsocketsIT {

@TestHTTPResource(DependencyAnnotatedWS.URI)
URI dependencyAnnotatedWSURI;

@TestHTTPResource(SimpleEndpoint.URI)
URI dependencyNotAnnotatedWSURI;

@TestHTTPResource(CustomAnnotatedEnpoint.URI)
URI appAnnotatedWSURI;

@Test
void dependencyAnnotatedEndpointShouldWork() throws Exception {
assertWebsocketWorks(dependencyAnnotatedWSURI,
DependencyAnnotatedWS.PREFIX);
}

@Test
void applicationAnnotatedEndpointShouldWork() throws Exception {
assertWebsocketWorks(appAnnotatedWSURI, CustomAnnotatedEnpoint.PREFIX);
}

@Test
void dependencyNotAnnotatedEndpointShouldWork() throws Exception {
assertWebsocketWorks(dependencyNotAnnotatedWSURI,
SimpleEndpoint.PREFIX);
}

void assertWebsocketWorks(URI uri, String messagePrefix) throws Exception {
Client client = new Client();
try (Session session = ContainerProvider.getWebSocketContainer()
.connectToServer(client, uri)) {
Assertions.assertEquals("CONNECT", client.receivedMessage());
Assertions.assertEquals(messagePrefix + "Welcome",
client.receivedMessage());
session.getBasicRemote().sendText("hello world");
Assertions.assertEquals(messagePrefix + "hello world",
client.receivedMessage());
}
}

@ClientEndpoint
public static class Client {
final LinkedBlockingDeque<String> messages = new LinkedBlockingDeque<>();

@OnOpen
public void open(Session session) throws IOException {
messages.add("CONNECT");
}

@OnMessage
void message(String msg) {
messages.add(msg);
}

String receivedMessage() throws InterruptedException {
return messages.poll(12, TimeUnit.SECONDS);
}

}
}
57 changes: 57 additions & 0 deletions integration-tests/custom-websocket-dependency/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<parent>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-quarkus-integration-tests</artifactId>
<version>2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>custom-websockets</artifactId>
<name>Test dependency with custom websocket endpoints</name>
<packaging>jar</packaging>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>

<dependencies>
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-client-api</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.2.2</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.vaadin.sample.websockets;

import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint(DependencyAnnotatedWS.URI)
public class DependencyAnnotatedWS {

public static final String URI = "/dependency-annotated-websocket";
public static final String PREFIX = ">> Dependency Annotated Endpoint: ";

@OnOpen
public void onOpen(Session session) {
session.getAsyncRemote().sendText(PREFIX + "Welcome");
}

@OnMessage
public void onMessage(String message, Session session) {
session.getAsyncRemote().sendText(PREFIX + message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.vaadin.sample.websockets;

import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.MessageHandler;
import jakarta.websocket.RemoteEndpoint;
import jakarta.websocket.Session;

public class SimpleEndpoint extends Endpoint {

public static final String URI = "/dependency-websocket";

public static final String PREFIX = ">> Dependency Simple Endpoint: ";

@Override
public void onOpen(Session session, EndpointConfig config) {
Handler handler = new Handler(session.getAsyncRemote());
session.addMessageHandler(handler);
handler.reply("Welcome");
}

private static class Handler implements MessageHandler.Whole<String> {

private final RemoteEndpoint.Async remote;

public Handler(RemoteEndpoint.Async remote) {
this.remote = remote;
}

@Override
public void onMessage(String message) {
reply(message);
}

private void reply(String message) {
remote.sendText(PREFIX + message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.vaadin.sample.websockets;

import jakarta.websocket.Endpoint;
import jakarta.websocket.server.ServerApplicationConfig;
import jakarta.websocket.server.ServerEndpointConfig;
import java.util.Collections;
import java.util.Set;

public class SimpleEndpointConfig implements ServerApplicationConfig {
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(
Set<Class<? extends Endpoint>> endpointClasses) {
return Set.of(ServerEndpointConfig.Builder
.create(SimpleEndpoint.class, SimpleEndpoint.URI).build());
}

@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
return Collections.emptySet();
}
}
5 changes: 5 additions & 0 deletions integration-tests/development/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
<artifactId>test-addon-with-jandex</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>custom-websockets</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.vaadin</groupId>
Expand Down
24 changes: 14 additions & 10 deletions integration-tests/development/vite.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* This file will be overwritten on every run. Any custom changes should be made to vite.config.ts
*/
import path from 'path';
import { readFileSync, existsSync, writeFileSync } from 'fs';
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
import * as net from 'net';

import { processThemeResources } from './target/plugins/application-theme-plugin/theme-handle';
import { rewriteCssUrls } from './target/plugins/theme-loader/theme-loader-utils';
import { processThemeResources } from './target/plugins/application-theme-plugin/theme-handle.js';
import { rewriteCssUrls } from './target/plugins/theme-loader/theme-loader-utils.js';
import settings from './target/vaadin-dev-server-settings.json';
import { defineConfig, mergeConfig, PluginOption, ResolvedConfig, UserConfigFn, OutputOptions, AssetInfo, ChunkInfo } from 'vite';
import { getManifest } from 'workbox-build';
Expand All @@ -24,10 +24,13 @@ const appShellUrl = '.';

const frontendFolder = path.resolve(__dirname, settings.frontendFolder);
const themeFolder = path.resolve(frontendFolder, settings.themeFolder);
const statsFolder = path.resolve(__dirname, settings.statsOutput);
const frontendBundleFolder = path.resolve(__dirname, settings.frontendBundleOutput);
const addonFrontendFolder = path.resolve(__dirname, settings.addonFrontendFolder);
const jarResourcesFolder = path.resolve(__dirname, settings.jarResourcesFolder);
const generatedFlowImportsFolder = path.resolve(__dirname, settings.generatedFlowImportsFolder);
const themeResourceFolder = path.resolve(__dirname, settings.themeResourceFolder);
const statsFile = path.resolve(frontendBundleFolder, '..', 'config', 'stats.json');

const statsFile = path.resolve(statsFolder, 'stats.json');

const projectStaticAssetsFolders = [
path.resolve(__dirname, 'src', 'main', 'resources', 'META-INF', 'resources'),
Expand All @@ -40,7 +43,7 @@ const themeProjectFolders = projectStaticAssetsFolders.map((folder) => path.reso

const themeOptions = {
devMode: false,
// The following matches folder 'target/flow-frontend/themes/'
// The following matches folder 'frontend/generated/themes/'
// (not 'frontend/themes') for theme in JAR that is copied there
themeResourceFolder: path.resolve(themeResourceFolder, settings.themeFolder),
themeProjectFolders: themeProjectFolders,
Expand Down Expand Up @@ -182,6 +185,7 @@ function statsExtracterPlugin(): PluginOption {
.sort()
.filter((value, index, self) => self.indexOf(value) === index);

mkdirSync(path.dirname(statsFile), { recursive: true });
writeFileSync(statsFile, JSON.stringify({ npmModules }, null, 1));
}
};
Expand Down Expand Up @@ -460,8 +464,7 @@ let spaMiddlewareForceRemoved = false;

const allowedFrontendFolders = [
frontendFolder,
addonFrontendFolder,
path.resolve(addonFrontendFolder, '..', 'frontend'), // Contains only generated-flow-imports
path.resolve(generatedFlowImportsFolder), // Contains only generated-flow-imports
path.resolve(__dirname, 'node_modules')
];

Expand Down Expand Up @@ -500,6 +503,7 @@ export const vaadinConfig: UserConfigFn = (env) => {
base: '',
resolve: {
alias: {
'@vaadin/flow-frontend': jarResourcesFolder,
Frontend: frontendFolder
},
preserveSymlinks: true
Expand Down Expand Up @@ -563,14 +567,14 @@ export const vaadinConfig: UserConfigFn = (env) => {
]
}),
{
name: 'vaadin:force-remove-spa-middleware',
name: 'vaadin:force-remove-html-middleware',
transformIndexHtml: {
enforce: 'pre',
transform(_html, { server }) {
if (server && !spaMiddlewareForceRemoved) {
server.middlewares.stack = server.middlewares.stack.filter((mw) => {
const handleName = '' + mw.handle;
return !handleName.includes('viteSpaFallbackMiddleware');
return !handleName.includes('viteHtmlFallbackMiddleware');
});
spaMiddlewareForceRemoved = true;
}
Expand Down
1 change: 1 addition & 0 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<!-- Builds testing addons -->
<module>test-addons/addon-with-jandex</module>
<module>test-addons/addon-without-jandex</module>
<module>custom-websocket-dependency</module>
<!-- only validates that code compiles -->
<module>common-test-code</module>

Expand Down
5 changes: 5 additions & 0 deletions integration-tests/production/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>test-addon-with-jandex</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>custom-websockets</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.vaadin</groupId>
Expand Down
Loading

0 comments on commit 74310d8

Please sign in to comment.