diff --git a/dependencies/pom.xml b/dependencies/pom.xml
index 88828639d9d..9c9295f072f 100644
--- a/dependencies/pom.xml
+++ b/dependencies/pom.xml
@@ -110,7 +110,7 @@
1.24
1.3.3
1.4.0
- 1.15
+ 1.17
${version.lib.ojdbc8}
2.0.2
1.1.2
diff --git a/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketApplication.java b/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketApplication.java
index a4c3c818ea2..77c5e8d476f 100644
--- a/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketApplication.java
+++ b/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketApplication.java
@@ -22,6 +22,7 @@
import java.util.logging.Logger;
import javax.websocket.Endpoint;
+import javax.websocket.Extension;
import javax.websocket.server.ServerApplicationConfig;
/**
@@ -32,11 +33,13 @@ public final class WebSocketApplication {
private Class extends ServerApplicationConfig> applicationClass;
private Set> annotatedEndpoints;
private Set> programmaticEndpoints;
+ private Set extensions;
private WebSocketApplication(Builder builder) {
this.applicationClass = builder.applicationClass;
this.annotatedEndpoints = builder.annotatedEndpoints;
this.programmaticEndpoints = builder.programmaticEndpoints;
+ this.extensions = builder.extensions;
}
/**
@@ -75,6 +78,15 @@ public Set> annotatedEndpoints() {
return annotatedEndpoints;
}
+ /**
+ * Get list of installed extensions.
+ *
+ * @return List of installed extensions.
+ */
+ public Set extensions() {
+ return extensions;
+ }
+
/**
* Fluent API builder to create {@link WebSocketApplication} instances.
*/
@@ -84,6 +96,7 @@ public static class Builder {
private Class extends ServerApplicationConfig> applicationClass;
private Set> annotatedEndpoints = new HashSet<>();
private Set> programmaticEndpoints = new HashSet<>();
+ private Set extensions = new HashSet<>();
/**
* Updates an application class in the builder. Clears all results from scanning.
@@ -135,6 +148,17 @@ public Builder annotatedEndpoint(Class> annotatedEndpoint) {
return this;
}
+ /**
+ * Add single extension.
+ *
+ * @param extension Extension.
+ * @return The builder.
+ */
+ public Builder extension(Extension extension) {
+ extensions.add(extension);
+ return this;
+ }
+
/**
* Builds application.
*
diff --git a/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketCdiExtension.java b/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketCdiExtension.java
index ade84f85c8c..a6ef58b645c 100644
--- a/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketCdiExtension.java
+++ b/microprofile/websocket/src/main/java/io/helidon/microprofile/tyrus/WebSocketCdiExtension.java
@@ -100,7 +100,7 @@ private void endpointClasses(@Observes @WithAnnotations(ServerEndpoint.class) Pr
}
/**
- * Collects programmatic endpoints .
+ * Collects programmatic endpoints.
*
* @param endpoint The endpoint.
*/
@@ -109,6 +109,26 @@ private void endpointConfig(@Observes ProcessAnnotatedType extends Endpoint> e
appBuilder.programmaticEndpoint(endpoint.getAnnotatedType().getJavaClass());
}
+ /**
+ * Collects extensions.
+ *
+ * @param extension The extension.
+ */
+ private void extension(@Observes ProcessAnnotatedType extends javax.websocket.Extension> extension) {
+ LOGGER.finest(() -> "Extension found " + extension.getAnnotatedType().getJavaClass());
+
+ Class extends javax.websocket.Extension> cls = extension.getAnnotatedType().getJavaClass();
+ try {
+ javax.websocket.Extension instance = cls.getConstructor().newInstance();
+ appBuilder.extension(instance);
+ } catch (NoSuchMethodException e) {
+ LOGGER.warning(() -> "Extension does not have no-args constructor for "
+ + extension.getAnnotatedType().getJavaClass() + "! Skppping.");
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException("Unable to load WebSocket extension", e);
+ }
+ }
+
/**
* Provides access to websocket application.
*
@@ -165,6 +185,7 @@ private void registerWebSockets() {
// Direct registration without calling application class
app.annotatedEndpoints().forEach(builder::register);
app.programmaticEndpoints().forEach(builder::register);
+ app.extensions().forEach(builder::register);
// Create routing builder
routing = serverCdiExtension.serverRoutingBuilder();
diff --git a/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/EchoClient.java b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/EchoClient.java
index 95a99aa9f56..08de902a941 100644
--- a/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/EchoClient.java
+++ b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/EchoClient.java
@@ -20,10 +20,13 @@
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
+import javax.websocket.Extension;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import java.io.IOException;
import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -46,14 +49,20 @@ class EchoClient {
private final URI uri;
private final BiFunction equals;
+ private final List extensions;
public EchoClient(URI uri) {
this(uri, String::equals);
}
- public EchoClient(URI uri, BiFunction equals) {
+ public EchoClient(URI uri, Extension... extensions) {
+ this(uri, String::equals, extensions);
+ }
+
+ public EchoClient(URI uri, BiFunction equals, Extension... extensions) {
this.uri = uri;
this.equals = equals;
+ this.extensions = Arrays.asList(extensions);
}
/**
@@ -66,7 +75,7 @@ public void echo(String... messages) throws Exception {
CountDownLatch messageLatch = new CountDownLatch(messages.length);
CompletableFuture openFuture = new CompletableFuture<>();
CompletableFuture closeFuture = new CompletableFuture<>();
- ClientEndpointConfig config = ClientEndpointConfig.Builder.create().build();
+ ClientEndpointConfig config = ClientEndpointConfig.Builder.create().extensions(extensions).build();
client.connectToServer(new Endpoint() {
@Override
diff --git a/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketEndpointAppTest.java b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketEndpointAppTest.java
index 1447d529eab..12f611e11c7 100644
--- a/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketEndpointAppTest.java
+++ b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketEndpointAppTest.java
@@ -22,14 +22,12 @@
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.se.SeContainerInitializer;
-import javax.enterprise.inject.spi.CDI;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
import io.helidon.microprofile.server.RoutingPath;
-import io.helidon.microprofile.server.ServerCdiExtension;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
diff --git a/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketExtensionEndpointTest.java b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketExtensionEndpointTest.java
new file mode 100644
index 00000000000..d5fc8d978d5
--- /dev/null
+++ b/microprofile/websocket/src/test/java/io/helidon/microprofile/tyrus/WebSocketExtensionEndpointTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.helidon.microprofile.tyrus;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.enterprise.inject.se.SeContainer;
+import javax.enterprise.inject.se.SeContainerInitializer;
+import javax.enterprise.inject.spi.CDI;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import io.helidon.microprofile.server.ServerCdiExtension;
+import javax.websocket.Extension;
+import javax.websocket.OnMessage;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+/**
+ * A test that mixes Websocket endpoints and extensions in the same application.
+ */
+public class WebSocketExtensionEndpointTest {
+
+ static SeContainer container;
+
+ @BeforeAll
+ static void initClass() {
+ container = SeContainerInitializer.newInstance()
+ .addBeanClasses(ExtensionEndpointAnnot.class, TestExtension.class)
+ .initialize();
+ }
+
+ @AfterAll
+ static void destroyClass() {
+ container.close();
+ }
+
+ public int port() {
+ ServerCdiExtension cdiExtension = CDI.current().getBeanManager().getExtension(ServerCdiExtension.class);
+ return cdiExtension.port();
+ }
+
+ @Test
+ public void test() throws Exception {
+ URI echoUri = URI.create("ws://localhost:" + port() + "/extAnnot");
+ EchoClient echoClient = new EchoClient(echoUri, new TestExtension());
+ echoClient.echo("hi", "how are you?");
+ }
+
+ @ServerEndpoint("/extAnnot")
+ public static class ExtensionEndpointAnnot {
+ private static final Logger LOGGER = Logger.getLogger(ExtensionEndpointAnnot.class.getName());
+
+ @OnMessage
+ public void echo(Session session, String message) throws Exception {
+ LOGGER.info("OnMessage called '" + message + "'");
+ if (session.getNegotiatedExtensions().isEmpty()) {
+ throw new IllegalStateException();
+ }
+ session.getBasicRemote().sendObject(message);
+ }
+ }
+
+ public static class TestExtension implements Extension {
+ @Override
+ public String getName() {
+ return "testExtension";
+ }
+
+ @Override
+ public List getParameters() {
+ return Collections.emptyList();
+ }
+ }
+}
diff --git a/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java b/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java
index b85bdd318fb..9994b783fab 100644
--- a/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java
+++ b/webserver/tyrus/src/main/java/io/helidon/webserver/tyrus/TyrusSupport.java
@@ -27,6 +27,7 @@
import java.util.logging.Logger;
import javax.websocket.DeploymentException;
+import javax.websocket.Extension;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
@@ -60,6 +61,7 @@ public class TyrusSupport implements Service {
private final TyrusHandler handler = new TyrusHandler();
private Set> endpointClasses;
private Set endpointConfigs;
+ private Set extensions;
/**
* Create from another instance.
@@ -70,12 +72,18 @@ protected TyrusSupport(TyrusSupport other) {
this.engine = other.engine;
this.endpointClasses = other.endpointClasses;
this.endpointConfigs = other.endpointConfigs;
+ this.extensions = other.extensions;
}
- TyrusSupport(WebSocketEngine engine, Set> endpointClasses, Set endpointConfigs) {
+ TyrusSupport(
+ WebSocketEngine engine,
+ Set> endpointClasses,
+ Set endpointConfigs,
+ Set extensions) {
this.engine = engine;
this.endpointClasses = endpointClasses;
this.endpointConfigs = endpointConfigs;
+ this.extensions = extensions;
}
/**
@@ -108,6 +116,15 @@ public Set endpointConfigs() {
return Collections.unmodifiableSet(endpointConfigs);
}
+ /**
+ * Access to extensions.
+ *
+ * @return Immutable set of extensions.
+ */
+ public Set extensions() {
+ return Collections.unmodifiableSet(extensions);
+ }
+
/**
* Returns executor service, can be overridden.
*
@@ -133,6 +150,7 @@ public static class Builder implements io.helidon.common.Builder {
private Set> endpointClasses = new HashSet<>();
private Set endpointConfigs = new HashSet<>();
+ private Set extensions = new HashSet<>();
private Builder() {
}
@@ -159,8 +177,21 @@ public Builder register(ServerEndpointConfig endpointConfig) {
return this;
}
+ /**
+ * Register an extension.
+ *
+ * @param extension The extension.
+ * @return The builder.
+ */
+ public Builder register(Extension extension) {
+ extensions.add(extension);
+ return this;
+ }
+
@Override
public TyrusSupport build() {
+ // a purposefully mutable extensions
+ Set installedExtensions = new HashSet<>(extensions);
// Create container and WebSocket engine
TyrusServerContainer serverContainer = new TyrusServerContainer(endpointClasses) {
private final WebSocketEngine engine =
@@ -176,6 +207,11 @@ public void register(ServerEndpointConfig serverEndpointConfig) {
throw new UnsupportedOperationException("Use TyrusWebSocketEngine for registration");
}
+ @Override
+ public Set getInstalledExtensions() {
+ return installedExtensions;
+ }
+
@Override
public WebSocketEngine getWebSocketEngine() {
return engine;
@@ -202,7 +238,7 @@ public WebSocketEngine getWebSocketEngine() {
});
// Create TyrusSupport using WebSocket engine
- return new TyrusSupport(serverContainer.getWebSocketEngine(), endpointClasses, endpointConfigs);
+ return new TyrusSupport(serverContainer.getWebSocketEngine(), endpointClasses, endpointConfigs, extensions);
}
}