Skip to content

Commit

Permalink
register websocket client encoders and decoders for reflection
Browse files Browse the repository at this point in the history
fixes #2745
  • Loading branch information
michalszynkiewicz committed Jun 7, 2019
1 parent d92a58c commit 0be3024
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.util.List;
import java.util.Map;

import javax.servlet.Servlet;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
Expand Down Expand Up @@ -82,4 +80,4 @@ public String getEffectiveServletName(final String key) {

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.Set;

import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpoint;
Expand All @@ -43,11 +45,13 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.substrate.ServiceProviderBuildItem;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.undertow.deployment.ServletContextAttributeBuildItem;
import io.quarkus.undertow.deployment.UndertowBuildItem;
import io.quarkus.undertow.websockets.runtime.UndertowWebsocketTemplate;
import io.undertow.websockets.jsr.JsrWebSocketFilter;
import io.undertow.websockets.jsr.UndertowContainerProvider;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;

public class UndertowWebsocketProcessor {
Expand Down Expand Up @@ -126,8 +130,19 @@ public ServletContextAttributeBuildItem deploy(final CombinedIndexBuildItem inde
new ReflectiveClassBuildItem(true, false, annotated.toArray(new String[annotated.size()])));
reflection.produce(new ReflectiveClassBuildItem(false, false, JsrWebSocketFilter.class.getName()));

final Collection<AnnotationInstance> serverEndpoints = index.getAnnotations(SERVER_ENDPOINT);
for (AnnotationInstance endpoint : serverEndpoints) {
registerCodersForReflection(reflection, index.getAnnotations(SERVER_ENDPOINT));
registerCodersForReflection(reflection, index.getAnnotations(CLIENT_ENDPOINT));

reflection.produce(
new ReflectiveClassBuildItem(true, true, ClientEndpointConfig.Configurator.class.getName()));

return new ServletContextAttributeBuildItem(WebSocketDeploymentInfo.ATTRIBUTE_NAME,
template.createDeploymentInfo(annotated, endpoints, config));
}

private void registerCodersForReflection(BuildProducer<ReflectiveClassBuildItem> reflection,
Collection<AnnotationInstance> endpoints) {
for (AnnotationInstance endpoint : endpoints) {
if (endpoint.target() instanceof ClassInfo) {
ClassInfo clazz = (ClassInfo) endpoint.target();
if (!Modifier.isAbstract(clazz.flags())) {
Expand All @@ -136,15 +151,12 @@ public ServletContextAttributeBuildItem deploy(final CombinedIndexBuildItem inde
}
}
}

return new ServletContextAttributeBuildItem(WebSocketDeploymentInfo.ATTRIBUTE_NAME,
template.createDeploymentInfo(annotated, endpoints, config));
}

private void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflection, AnnotationValue types) {
if (types != null && types.asClassArray() != null) {
for (Type type : types.asClassArray()) {
reflection.produce(new ReflectiveClassBuildItem(false, false, type.name().toString()));
reflection.produce(new ReflectiveClassBuildItem(true, false, type.name().toString()));
}
}
}
Expand All @@ -156,6 +168,12 @@ ServiceStartBuildItem setupWorker(UndertowWebsocketTemplate template, UndertowBu
return new ServiceStartBuildItem("Websockets");
}

@BuildStep
ServiceProviderBuildItem registerContainerProviderService() {
return new ServiceProviderBuildItem(ContainerProvider.class.getName(),
UndertowContainerProvider.class.getName());
}

@BuildStep
void beanDefiningAnnotations(BuildProducer<BeanDefiningAnnotationBuildItem> annotations) {
annotations.produce(new BeanDefiningAnnotationBuildItem(SERVER_ENDPOINT));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

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

import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("/ws-client-coding-test")
public class ClientCodingResource {

@GET
public String codeBothWays(@QueryParam("echoServerUri") String echoServerUri) throws IOException, DeploymentException {
ContainerProvider.getWebSocketContainer()
.connectToServer(CodingClient.class, URI.create(echoServerUri));

try {
Dto response = CodingClient.messageQueue.poll(20, TimeUnit.SECONDS);
return response.getContent();
} catch (InterruptedException e) {
CodingClient.close();
return "Failed to get response in time";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

public class ClientDtoDecoder implements Decoder.TextStream<Dto> {
@Override
public Dto decode(Reader reader) throws IOException {
try (BufferedReader bufferedReader = new BufferedReader(reader)) {
String input = bufferedReader.readLine(); // expecting one line input
Dto result = new Dto();
result.setContent("[decoded]" + input);
return result;
}
}

@Override
public void init(EndpointConfig config) {
}

@Override
public void destroy() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

import java.io.IOException;
import java.io.Writer;

import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public class ClientDtoEncoder implements Encoder.TextStream<Dto> {
@Override
public void encode(Dto object, Writer writer) throws IOException {
writer.append("[encoded]" + object.getContent());
}

@Override
public void init(EndpointConfig config) {

}

@Override
public void destroy() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

import javax.websocket.ClientEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;

@ClientEndpoint(decoders = ClientDtoDecoder.class, encoders = ClientDtoEncoder.class)
public class CodingClient {
private static List<Session> sessions = Collections.synchronizedList(new ArrayList<>());

static LinkedBlockingDeque<Dto> messageQueue = new LinkedBlockingDeque<>();

@OnOpen
public void onOpen(Session session) {
sessions.add(session);

Dto data = new Dto();
data.setContent("initial data");
session.getAsyncRemote().sendObject(data);
}

@OnMessage
public void onMessage(Dto message) {
messageQueue.add(message);
close();
}

static void close() {
for (Session session : sessions) {
try {
session.close();
} catch (IOException ignored) {
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

public class Dto {
private String content;

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

@Override
public String toString() {
return "Dto{" +
"content='" + content + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/recoder", encoders = ServerDtoEncoder.class, decoders = ServerDtoDecoder.class)
public class RecodingSocket {
@OnMessage
public Dto recode(Dto input) {
Dto result = new Dto();
result.setContent("[recoded]" + input.getContent());
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 Red Hat, Inc.
*
* 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.quarkus.it.websocket;

import java.io.Reader;

import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

public class ServerDtoDecoder implements Decoder.TextStream<Dto> {
@Override
public Dto decode(Reader reader) {
JsonObject jsonObject = Json.createReader(reader).readObject();
Dto result = new Dto();
result.setContent(jsonObject.getString("content"));
return result;
}

@Override
public void init(EndpointConfig config) {
}

@Override
public void destroy() {
}
}
Loading

0 comments on commit 0be3024

Please sign in to comment.