Issue #3428 - Initial refactor to support javax websocket decoderLists

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-05-19 22:45:37 +10:00
parent aa52d67dbf
commit 4b19c19815
26 changed files with 964 additions and 677 deletions

View File

@ -49,14 +49,10 @@ public class JavaxWebSocketClientFrameHandlerFactory extends JavaxWebSocketFrame
public JavaxWebSocketFrameHandlerMetadata getMetadata(Class<?> endpointClass, EndpointConfig endpointConfig)
{
if (javax.websocket.Endpoint.class.isAssignableFrom(endpointClass))
{
return createEndpointMetadata((Class<? extends Endpoint>)endpointClass, endpointConfig);
}
if (endpointClass.getAnnotation(ClientEndpoint.class) == null)
{
return null;
}
JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig);
return discoverJavaxFrameHandlerMetadata(endpointClass, metadata);

View File

@ -0,0 +1,79 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.common;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.util.InvokerUtils;
// The different kind of @OnMessage method parameter signatures expected.
public class JavaxWebSocketCallingArgs
{
static final InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(String.class).required()
};
static final InvokerUtils.Arg[] textPartialCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(String.class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
static final InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(ByteBuffer.class).required()
};
static final InvokerUtils.Arg[] binaryPartialBufferCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(ByteBuffer.class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
static final InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(byte[].class).required()
};
static final InvokerUtils.Arg[] binaryPartialArrayCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(byte[].class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
static final InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(InputStream.class).required()
};
static final InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(Reader.class).required()
};
static final InvokerUtils.Arg[] pongCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(PongMessage.class).required()
};
}

View File

@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -46,6 +47,7 @@ import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.exception.ProtocolException;
import org.eclipse.jetty.websocket.core.exception.WebSocketException;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryStreamMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextMessageSink;
@ -95,9 +97,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
private MethodHandle openHandle;
private MethodHandle closeHandle;
private MethodHandle errorHandle;
private JavaxWebSocketFrameHandlerMetadata.MessageMetadata textMetadata;
private JavaxWebSocketFrameHandlerMetadata.MessageMetadata binaryMetadata;
private MethodHandle pongHandle;
private JavaxWebSocketMessageMetadata textMetadata;
private JavaxWebSocketMessageMetadata binaryMetadata;
private UpgradeRequest upgradeRequest;
@ -114,8 +116,8 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
public JavaxWebSocketFrameHandler(JavaxWebSocketContainer container,
Object endpointInstance,
MethodHandle openHandle, MethodHandle closeHandle, MethodHandle errorHandle,
JavaxWebSocketFrameHandlerMetadata.MessageMetadata textMetadata,
JavaxWebSocketFrameHandlerMetadata.MessageMetadata binaryMetadata,
JavaxWebSocketMessageMetadata textMetadata,
JavaxWebSocketMessageMetadata binaryMetadata,
MethodHandle pongHandle,
EndpointConfig endpointConfig)
{
@ -170,26 +172,32 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
errorHandle = InvokerUtils.bindTo(errorHandle, session);
pongHandle = InvokerUtils.bindTo(pongHandle, session);
JavaxWebSocketFrameHandlerMetadata.MessageMetadata actualTextMetadata = JavaxWebSocketFrameHandlerMetadata.MessageMetadata.copyOf(textMetadata);
JavaxWebSocketMessageMetadata actualTextMetadata = JavaxWebSocketMessageMetadata.copyOf(textMetadata);
if (actualTextMetadata != null)
{
if (actualTextMetadata.isMaxMessageSizeSet())
session.setMaxTextMessageBufferSize(actualTextMetadata.maxMessageSize);
session.setMaxTextMessageBufferSize(actualTextMetadata.getMaxMessageSize());
MethodHandle methodHandle = actualTextMetadata.getMethodHandle();
methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session);
methodHandle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session);
actualTextMetadata.setMethodHandle(methodHandle);
actualTextMetadata.handle = InvokerUtils.bindTo(actualTextMetadata.handle, endpointInstance, endpointConfig, session);
actualTextMetadata.handle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(actualTextMetadata.handle, session);
textSink = JavaxWebSocketFrameHandlerFactory.createMessageSink(session, actualTextMetadata);
textMetadata = actualTextMetadata;
}
JavaxWebSocketFrameHandlerMetadata.MessageMetadata actualBinaryMetadata = JavaxWebSocketFrameHandlerMetadata.MessageMetadata.copyOf(binaryMetadata);
JavaxWebSocketMessageMetadata actualBinaryMetadata = JavaxWebSocketMessageMetadata.copyOf(binaryMetadata);
if (actualBinaryMetadata != null)
{
if (actualBinaryMetadata.isMaxMessageSizeSet())
session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.maxMessageSize);
session.setMaxBinaryMessageBufferSize(actualBinaryMetadata.getMaxMessageSize());
MethodHandle methodHandle = actualBinaryMetadata.getMethodHandle();
methodHandle = InvokerUtils.bindTo(methodHandle, endpointInstance, endpointConfig, session);
methodHandle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(methodHandle, session);
actualBinaryMetadata.setMethodHandle(methodHandle);
actualBinaryMetadata.handle = InvokerUtils.bindTo(actualBinaryMetadata.handle, endpointInstance, endpointConfig, session);
actualBinaryMetadata.handle = JavaxWebSocketFrameHandlerFactory.wrapNonVoidReturnType(actualBinaryMetadata.handle, session);
binarySink = JavaxWebSocketFrameHandlerFactory.createMessageSink(session, actualBinaryMetadata);
binaryMetadata = actualBinaryMetadata;
}
@ -350,12 +358,12 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
return messageHandlerMap;
}
public JavaxWebSocketFrameHandlerMetadata.MessageMetadata getBinaryMetadata()
public JavaxWebSocketMessageMetadata getBinaryMetadata()
{
return binaryMetadata;
}
public JavaxWebSocketFrameHandlerMetadata.MessageMetadata getTextMetadata()
public JavaxWebSocketMessageMetadata getTextMetadata()
{
return textMetadata;
}
@ -369,7 +377,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
}
}
public <T> void addMessageHandler(JavaxWebSocketSession session, Class<T> clazz, MessageHandler.Partial<T> handler)
public <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler)
{
try
{
@ -384,9 +392,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName());
MessageSink messageSink = new PartialByteArrayMessageSink(coreSession, partialMessageHandler);
this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink);
JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata();
metadata.handle = partialMessageHandler;
metadata.sinkClass = PartialByteArrayMessageSink.class;
JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata();
metadata.setMethodHandle(partialMessageHandler);
metadata.setSinkClass(PartialByteArrayMessageSink.class);
this.binaryMetadata = metadata;
}
else if (ByteBuffer.class.isAssignableFrom(clazz))
@ -394,9 +402,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName());
MessageSink messageSink = new PartialByteBufferMessageSink(coreSession, partialMessageHandler);
this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink);
JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata();
metadata.handle = partialMessageHandler;
metadata.sinkClass = PartialByteBufferMessageSink.class;
JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata();
metadata.setMethodHandle(partialMessageHandler);
metadata.setSinkClass(PartialByteBufferMessageSink.class);
this.binaryMetadata = metadata;
}
else if (String.class.isAssignableFrom(clazz))
@ -404,9 +412,9 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName());
MessageSink messageSink = new PartialStringMessageSink(coreSession, partialMessageHandler);
this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink);
JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata();
metadata.handle = partialMessageHandler;
metadata.sinkClass = PartialStringMessageSink.class;
JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata();
metadata.setMethodHandle(partialMessageHandler);
metadata.setSinkClass(PartialStringMessageSink.class);
this.textMetadata = metadata;
}
else
@ -426,67 +434,67 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
}
}
public <T> void addMessageHandler(JavaxWebSocketSession session, Class<T> clazz, MessageHandler.Whole<T> handler)
public <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler)
{
try
{
MethodHandles.Lookup lookup = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup();
MethodHandle wholeMsgMethodHandle = lookup.findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class));
wholeMsgMethodHandle = wholeMsgMethodHandle.bindTo(handler);
MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup()
.findVirtual(MessageHandler.Whole.class, "onMessage", MethodType.methodType(void.class, Object.class))
.bindTo(handler);
if (PongMessage.class.isAssignableFrom(clazz))
{
assertBasicTypeNotRegistered(OpCode.PONG, this.pongHandle, handler.getClass().getName());
this.pongHandle = wholeMsgMethodHandle;
this.pongHandle = methodHandle;
registerMessageHandler(OpCode.PONG, clazz, handler, null);
}
else
{
AvailableDecoders availableDecoders = session.getDecoders();
AvailableDecoders.RegisteredDecoder registeredDecoder = availableDecoders.getRegisteredDecoderFor(clazz);
RegisteredDecoder registeredDecoder = availableDecoders.getFirstRegisteredDecoder(clazz);
if (registeredDecoder == null)
{
throw new IllegalStateException("Unable to find Decoder for type: " + clazz);
}
JavaxWebSocketFrameHandlerMetadata.MessageMetadata metadata = new JavaxWebSocketFrameHandlerMetadata.MessageMetadata();
metadata.handle = wholeMsgMethodHandle;
metadata.registeredDecoder = registeredDecoder;
JavaxWebSocketMessageMetadata metadata = new JavaxWebSocketMessageMetadata();
metadata.setMethodHandle(methodHandle);
metadata.setRegisteredDecoder(registeredDecoder);
if (registeredDecoder.implementsInterface(Decoder.Binary.class))
{
assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName());
Decoder.Binary<T> decoder = availableDecoders.getInstanceOf(registeredDecoder);
MessageSink messageSink = new DecodedBinaryMessageSink(coreSession, decoder, wholeMsgMethodHandle);
metadata.sinkClass = messageSink.getClass();
List<RegisteredDecoder> binaryDecoders = availableDecoders.getBinaryDecoders(clazz);
MessageSink messageSink = new DecodedBinaryMessageSink<T>(coreSession, methodHandle, binaryDecoders);
metadata.setSinkClass(messageSink.getClass());
this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink);
this.binaryMetadata = metadata;
}
else if (registeredDecoder.implementsInterface(Decoder.BinaryStream.class))
{
assertBasicTypeNotRegistered(OpCode.BINARY, this.binaryMetadata, handler.getClass().getName());
Decoder.BinaryStream<T> decoder = availableDecoders.getInstanceOf(registeredDecoder);
MessageSink messageSink = new DecodedBinaryStreamMessageSink(coreSession, decoder, wholeMsgMethodHandle);
metadata.sinkClass = messageSink.getClass();
List<RegisteredDecoder> binaryStreamDecoders = availableDecoders.getBinaryStreamDecoders(clazz);
MessageSink messageSink = new DecodedBinaryStreamMessageSink<T>(coreSession, methodHandle, binaryStreamDecoders);
metadata.setSinkClass(messageSink.getClass());
this.binarySink = registerMessageHandler(OpCode.BINARY, clazz, handler, messageSink);
this.binaryMetadata = metadata;
}
else if (registeredDecoder.implementsInterface(Decoder.Text.class))
{
assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName());
Decoder.Text<T> decoder = availableDecoders.getInstanceOf(registeredDecoder);
MessageSink messageSink = new DecodedTextMessageSink(coreSession, decoder, wholeMsgMethodHandle);
metadata.sinkClass = messageSink.getClass();
List<RegisteredDecoder> textDecoders = availableDecoders.getTextDecoders(clazz);
MessageSink messageSink = new DecodedTextMessageSink<T>(coreSession, methodHandle, textDecoders);
metadata.setSinkClass(messageSink.getClass());
this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink);
this.textMetadata = metadata;
}
else if (registeredDecoder.implementsInterface(Decoder.TextStream.class))
{
assertBasicTypeNotRegistered(OpCode.TEXT, this.textMetadata, handler.getClass().getName());
Decoder.TextStream<T> decoder = availableDecoders.getInstanceOf(registeredDecoder);
MessageSink messageSink = new DecodedTextStreamMessageSink(coreSession, decoder, wholeMsgMethodHandle);
metadata.sinkClass = messageSink.getClass();
List<RegisteredDecoder> textStreamDecoders = availableDecoders.getTextStreamDecoders(clazz);
MessageSink messageSink = new DecodedTextStreamMessageSink<T>(coreSession, methodHandle, textStreamDecoders);
metadata.setSinkClass(messageSink.getClass());
this.textSink = registerMessageHandler(OpCode.TEXT, clazz, handler, messageSink);
this.textMetadata = metadata;
}

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.websocket.javax.common;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -27,11 +25,11 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.websocket.CloseReason;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
@ -39,15 +37,14 @@ import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.javax.common.messages.AbstractDecodedMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedBinaryStreamMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextMessageSink;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextStreamMessageSink;
import org.eclipse.jetty.websocket.util.InvalidSignatureException;
@ -65,61 +62,11 @@ import org.eclipse.jetty.websocket.util.messages.ReaderMessageSink;
import org.eclipse.jetty.websocket.util.messages.StringMessageSink;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerMetadata.MessageMetadata;
public abstract class JavaxWebSocketFrameHandlerFactory
{
private static final MethodHandle FILTER_RETURN_TYPE_METHOD;
// The different kind of @OnMessage method parameter signatures expected.
private static final InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(String.class).required()
};
private static final InvokerUtils.Arg[] textPartialCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(String.class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
private static final InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(ByteBuffer.class).required()
};
private static final InvokerUtils.Arg[] binaryPartialBufferCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(ByteBuffer.class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
private static final InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(byte[].class).required()
};
private static final InvokerUtils.Arg[] binaryPartialArrayCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(byte[].class).required(),
new InvokerUtils.Arg(boolean.class).required()
};
private static final InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(InputStream.class).required()
};
private static final InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(Reader.class).required()
};
private static final InvokerUtils.Arg[] pongCallingArgs = new InvokerUtils.Arg[]{
new InvokerUtils.Arg(Session.class),
new InvokerUtils.Arg(PongMessage.class).required()
};
static
{
try
@ -172,8 +119,8 @@ public abstract class JavaxWebSocketFrameHandlerFactory
MethodHandle errorHandle = metadata.getErrorHandle();
MethodHandle pongHandle = metadata.getPongHandle();
MessageMetadata textMetadata = MessageMetadata.copyOf(metadata.getTextMetadata());
MessageMetadata binaryMetadata = MessageMetadata.copyOf(metadata.getBinaryMetadata());
JavaxWebSocketMessageMetadata textMetadata = JavaxWebSocketMessageMetadata.copyOf(metadata.getTextMetadata());
JavaxWebSocketMessageMetadata binaryMetadata = JavaxWebSocketMessageMetadata.copyOf(metadata.getBinaryMetadata());
UriTemplatePathSpec templatePathSpec = metadata.getUriTemplatePathSpec();
if (templatePathSpec != null)
@ -188,9 +135,9 @@ public abstract class JavaxWebSocketFrameHandlerFactory
pongHandle = bindTemplateVariables(pongHandle, namedVariables, pathParams);
if (textMetadata != null)
textMetadata.handle = bindTemplateVariables(textMetadata.handle, namedVariables, pathParams);
textMetadata.setMethodHandle(bindTemplateVariables(textMetadata.getMethodHandle(), namedVariables, pathParams));
if (binaryMetadata != null)
binaryMetadata.handle = bindTemplateVariables(binaryMetadata.handle, namedVariables, pathParams);
binaryMetadata.setMethodHandle(bindTemplateVariables(binaryMetadata.getMethodHandle(), namedVariables, pathParams));
}
openHandle = InvokerUtils.bindTo(openHandle, endpoint);
@ -313,8 +260,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
return retHandle;
}
@SuppressWarnings("Duplicates")
public static MessageSink createMessageSink(JavaxWebSocketSession session, MessageMetadata msgMetadata)
public static MessageSink createMessageSink(JavaxWebSocketSession session, JavaxWebSocketMessageMetadata msgMetadata)
{
if (msgMetadata == null)
return null;
@ -322,27 +268,27 @@ public abstract class JavaxWebSocketFrameHandlerFactory
try
{
MethodHandles.Lookup lookup = getServerMethodHandleLookup();
if (DecodedMessageSink.class.isAssignableFrom(msgMetadata.sinkClass))
if (AbstractDecodedMessageSink.class.isAssignableFrom(msgMetadata.getSinkClass()))
{
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass,
MethodType.methodType(void.class, CoreSession.class, msgMetadata.registeredDecoder.interfaceType, MethodHandle.class));
Decoder decoder = session.getDecoders().getInstanceOf(msgMetadata.registeredDecoder);
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), decoder, msgMetadata.handle);
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(),
MethodType.methodType(void.class, CoreSession.class, MethodHandle.class, List.class));
List<RegisteredDecoder> registeredDecoders = msgMetadata.getRegisteredDecoders();
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle(), registeredDecoders);
}
else
{
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.sinkClass,
MethodHandle ctorHandle = lookup.findConstructor(msgMetadata.getSinkClass(),
MethodType.methodType(void.class, CoreSession.class, MethodHandle.class));
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.handle);
return (MessageSink)ctorHandle.invoke(session.getCoreSession(), msgMetadata.getMethodHandle());
}
}
catch (NoSuchMethodException e)
{
throw new RuntimeException("Missing expected MessageSink constructor found at: " + msgMetadata.sinkClass.getName(), e);
throw new RuntimeException("Missing expected MessageSink constructor found at: " + msgMetadata.getSinkClass().getName(), e);
}
catch (IllegalAccessException | InstantiationException | InvocationTargetException e)
{
throw new RuntimeException("Unable to create MessageSink: " + msgMetadata.sinkClass.getName(), e);
throw new RuntimeException("Unable to create MessageSink: " + msgMetadata.getSinkClass().getName(), e);
}
catch (RuntimeException e)
{
@ -456,15 +402,19 @@ public abstract class JavaxWebSocketFrameHandlerFactory
for (Method onMsg : onMessages)
{
assertSignatureValid(endpointClass, onMsg, OnMessage.class);
MessageMetadata msgMetadata = new MessageMetadata();
OnMessage onMessageAnno = onMsg.getAnnotation(OnMessage.class);
if (onMessageAnno.maxMessageSize() > Integer.MAX_VALUE)
long annotationMaxMessageSize = onMessageAnno.maxMessageSize();
if (annotationMaxMessageSize > Integer.MAX_VALUE)
{
throw new InvalidWebSocketException(String.format("Value too large: %s#%s - @OnMessage.maxMessageSize=%,d > Integer.MAX_VALUE",
endpointClass.getName(), onMsg.getName(), onMessageAnno.maxMessageSize()));
endpointClass.getName(), onMsg.getName(), annotationMaxMessageSize));
}
msgMetadata.maxMessageSize = (int)onMessageAnno.maxMessageSize();
// Create MessageMetadata and set annotated maxMessageSize if it is not the default value.
JavaxWebSocketMessageMetadata msgMetadata = new JavaxWebSocketMessageMetadata();
if (annotationMaxMessageSize != -1)
msgMetadata.setMaxMessageSize((int)annotationMaxMessageSize);
// Function to search for matching MethodHandle for the endpointClass given a signature.
Function<InvokerUtils.Arg[], MethodHandle> getMethodHandle = (signature) ->
@ -486,91 +436,91 @@ public abstract class JavaxWebSocketFrameHandlerFactory
return metadata;
}
private boolean matchOnMessage(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, MessageMetadata msgMetadata,
private boolean matchOnMessage(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, JavaxWebSocketMessageMetadata msgMetadata,
Function<InvokerUtils.Arg[], MethodHandle> getMethodHandle)
{
// Whole Text Message.
MethodHandle methodHandle = getMethodHandle.apply(textCallingArgs);
MethodHandle methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.textCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = StringMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(StringMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setTextMetadata(msgMetadata, onMsg);
return true;
}
// Partial Text Message.
methodHandle = getMethodHandle.apply(textPartialCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.textPartialCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = PartialStringMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(PartialStringMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setTextMetadata(msgMetadata, onMsg);
return true;
}
// Whole ByteBuffer Binary Message.
methodHandle = getMethodHandle.apply(binaryBufferCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryBufferCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = ByteBufferMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(ByteBufferMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
// Partial ByteBuffer Binary Message.
methodHandle = getMethodHandle.apply(binaryPartialBufferCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryPartialBufferCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = PartialByteBufferMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(PartialByteBufferMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
// Whole byte[] Binary Message.
methodHandle = getMethodHandle.apply(binaryArrayCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryArrayCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = ByteArrayMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(ByteArrayMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
// Partial byte[] Binary Message.
methodHandle = getMethodHandle.apply(binaryPartialArrayCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.binaryPartialArrayCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = PartialByteArrayMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(PartialByteArrayMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
// InputStream Binary Message.
methodHandle = getMethodHandle.apply(inputStreamCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.inputStreamCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = InputStreamMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(InputStreamMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
// Reader Text Message.
methodHandle = getMethodHandle.apply(readerCallingArgs);
methodHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.readerCallingArgs);
if (methodHandle != null)
{
msgMetadata.sinkClass = ReaderMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.setSinkClass(ReaderMessageSink.class);
msgMetadata.setMethodHandle(methodHandle);
metadata.setTextMetadata(msgMetadata, onMsg);
return true;
}
// Pong Message.
MethodHandle pongHandle = getMethodHandle.apply(pongCallingArgs);
MethodHandle pongHandle = getMethodHandle.apply(JavaxWebSocketCallingArgs.pongCallingArgs);
if (pongHandle != null)
{
metadata.setPongHandle(pongHandle, onMsg);
@ -580,88 +530,70 @@ public abstract class JavaxWebSocketFrameHandlerFactory
return false;
}
private boolean matchDecoders(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, MessageMetadata msgMetadata,
private boolean matchDecoders(Method onMsg, JavaxWebSocketFrameHandlerMetadata metadata, JavaxWebSocketMessageMetadata msgMetadata,
Function<InvokerUtils.Arg[], MethodHandle> getMethodHandle)
{
// TODO: we should be able to get this information directly from the AvailableDecoders in the metadata.
List<DecodedArgs> decodedTextCallingArgs = new ArrayList<>();
List<DecodedArgs> decodedTextStreamCallingArgs = new ArrayList<>();
List<DecodedArgs> decodedBinaryCallingArgs = new ArrayList<>();
List<DecodedArgs> decodedBinaryStreamCallingArgs = new ArrayList<>();
for (AvailableDecoders.RegisteredDecoder decoder : metadata.getAvailableDecoders())
// We need to get all the decoders which match not just the first.
Stream<RegisteredDecoder> matchedDecodersStream = metadata.getAvailableDecoders().stream().filter(registeredDecoder ->
{
InvokerUtils.Arg[] args = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(decoder.objectType).required()};
DecodedArgs decodedArgs = new DecodedArgs(decoder, args);
InvokerUtils.Arg[] args = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(registeredDecoder.objectType).required()};
return getMethodHandle.apply(args) != null;
});
if (decoder.implementsInterface(Decoder.Text.class))
decodedTextCallingArgs.add(decodedArgs);
if (decoder.implementsInterface(Decoder.TextStream.class))
decodedTextStreamCallingArgs.add(decodedArgs);
if (decoder.implementsInterface(Decoder.Binary.class))
decodedBinaryCallingArgs.add(decodedArgs);
if (decoder.implementsInterface(Decoder.BinaryStream.class))
decodedBinaryStreamCallingArgs.add(decodedArgs);
// Use the interface type of the first matched decoder.
RegisteredDecoder firstDecoder = matchedDecodersStream.findFirst().orElse(null);
if (firstDecoder == null)
return false;
// TODO: COMMENT
List<RegisteredDecoder> decoders = new ArrayList<>();
Class<? extends Decoder> interfaceType = firstDecoder.interfaceType;
metadata.getAvailableDecoders().stream()
.filter(registeredDecoder -> registeredDecoder.interfaceType.equals(interfaceType))
.forEach(decoders::add);
// Get the original argument type.
Class<?> type = firstDecoder.objectType;
for (Class<?> clazz : onMsg.getParameterTypes())
{
if (clazz.isAssignableFrom(firstDecoder.objectType))
type = clazz;
}
InvokerUtils.Arg[] generalArgs = {new InvokerUtils.Arg(Session.class), new InvokerUtils.Arg(type).required()};
MethodHandle generalMethodHandle = getMethodHandle.apply(generalArgs);
if (generalMethodHandle == null)
{
// TODO: warn or throw
return false;
}
MethodHandle methodHandle;
msgMetadata.setRegisteredDecoders(decoders);
msgMetadata.setMethodHandle(generalMethodHandle);
// Decoder.Text
for (DecodedArgs decodedArgs : decodedTextCallingArgs)
if (interfaceType.equals(Decoder.Text.class))
{
methodHandle = getMethodHandle.apply(decodedArgs.args);
if (methodHandle != null)
{
msgMetadata.sinkClass = DecodedTextMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.registeredDecoder = decodedArgs.registeredDecoder;
metadata.setTextMetadata(msgMetadata, onMsg);
return true;
}
msgMetadata.setSinkClass(DecodedTextMessageSink.class);
metadata.setTextMetadata(msgMetadata, onMsg);
}
else if (interfaceType.equals(Decoder.Binary.class))
{
msgMetadata.setSinkClass(DecodedBinaryMessageSink.class);
metadata.setBinaryMetadata(msgMetadata, onMsg);
}
else if (interfaceType.equals(Decoder.TextStream.class))
{
msgMetadata.setSinkClass(DecodedTextStreamMessageSink.class);
metadata.setTextMetadata(msgMetadata, onMsg);
}
else if (interfaceType.equals(Decoder.BinaryStream.class))
{
msgMetadata.setSinkClass(DecodedBinaryStreamMessageSink.class);
metadata.setBinaryMetadata(msgMetadata, onMsg);
}
// Decoder.Binary
for (DecodedArgs decodedArgs : decodedBinaryCallingArgs)
{
methodHandle = getMethodHandle.apply(decodedArgs.args);
if (methodHandle != null)
{
msgMetadata.sinkClass = DecodedBinaryMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.registeredDecoder = decodedArgs.registeredDecoder;
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
}
// Try to match Text Stream decoders.
for (DecodedArgs decodedArgs : decodedTextStreamCallingArgs)
{
methodHandle = getMethodHandle.apply(decodedArgs.args);
if (methodHandle != null)
{
msgMetadata.sinkClass = DecodedTextStreamMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.registeredDecoder = decodedArgs.registeredDecoder;
metadata.setTextMetadata(msgMetadata, onMsg);
return true;
}
}
// Decoder.BinaryStream
for (DecodedArgs decodedArgs : decodedBinaryStreamCallingArgs)
{
methodHandle = getMethodHandle.apply(decodedArgs.args);
if (methodHandle != null)
{
msgMetadata.sinkClass = DecodedBinaryStreamMessageSink.class;
msgMetadata.handle = methodHandle;
msgMetadata.registeredDecoder = decodedArgs.registeredDecoder;
metadata.setBinaryMetadata(msgMetadata, onMsg);
return true;
}
}
return false;
return true;
}
private void assertSignatureValid(Class<?> endpointClass, Method method, Class<? extends Annotation> annotationClass)
@ -752,16 +684,4 @@ public abstract class JavaxWebSocketFrameHandlerFactory
{
return MethodHandles.publicLookup().in(lookupClass);
}
private static class DecodedArgs
{
public final AvailableDecoders.RegisteredDecoder registeredDecoder;
public final InvokerUtils.Arg[] args;
public DecodedArgs(AvailableDecoders.RegisteredDecoder registeredDecoder, InvokerUtils.Arg... args)
{
this.registeredDecoder = registeredDecoder;
this.args = args;
}
}
}

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.javax.common;
import java.lang.invoke.MethodHandle;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
@ -27,25 +26,21 @@ import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders;
import org.eclipse.jetty.websocket.util.InvalidWebSocketException;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
public class JavaxWebSocketFrameHandlerMetadata
{
private static final String[] NO_VARIABLES = new String[0];
// EndpointConfig entries
private final EndpointConfig endpointConfig;
private final AvailableDecoders availableDecoders;
private final AvailableEncoders availableEncoders;
private MethodHandle openHandle;
private MethodHandle closeHandle;
private MethodHandle errorHandle;
private MessageMetadata textMetadata;
private MessageMetadata binaryMetadata;
private MethodHandle pongHandle;
private JavaxWebSocketMessageMetadata textMetadata;
private JavaxWebSocketMessageMetadata binaryMetadata;
/**
* For {@code @ServerEndpoint} or {@code ServerEndpointConfig} based endpoints, this
@ -76,7 +71,6 @@ public class JavaxWebSocketFrameHandlerMetadata
public JavaxWebSocketFrameHandlerMetadata(EndpointConfig endpointConfig)
{
this.endpointConfig = endpointConfig;
this.availableDecoders = new AvailableDecoders(endpointConfig);
this.availableEncoders = new AvailableEncoders(endpointConfig);
}
@ -91,7 +85,7 @@ public class JavaxWebSocketFrameHandlerMetadata
return availableEncoders;
}
public MessageMetadata getBinaryMetadata()
public JavaxWebSocketMessageMetadata getBinaryMetadata()
{
return binaryMetadata;
}
@ -133,7 +127,7 @@ public class JavaxWebSocketFrameHandlerMetadata
return pongHandle;
}
public MessageMetadata getTextMetadata()
public JavaxWebSocketMessageMetadata getTextMetadata()
{
return textMetadata;
}
@ -148,7 +142,7 @@ public class JavaxWebSocketFrameHandlerMetadata
return (textMetadata != null);
}
public void setBinaryMetadata(MessageMetadata metadata, Object origin)
public void setBinaryMetadata(JavaxWebSocketMessageMetadata metadata, Object origin)
{
assertNotSet(this.binaryMetadata, "BINARY Message Metadata", origin);
this.binaryMetadata = metadata;
@ -160,11 +154,6 @@ public class JavaxWebSocketFrameHandlerMetadata
this.closeHandle = close;
}
public void setDecoders(Class<? extends Decoder>[] decoders)
{
this.availableDecoders.registerAll(decoders);
}
public void setEncoders(Class<? extends Encoder>[] encoders)
{
this.availableEncoders.registerAll(encoders);
@ -188,7 +177,7 @@ public class JavaxWebSocketFrameHandlerMetadata
this.pongHandle = pong;
}
public void setTextMetadata(MessageMetadata metadata, Object origin)
public void setTextMetadata(JavaxWebSocketMessageMetadata metadata, Object origin)
{
assertNotSet(this.textMetadata, "TEXT Messsage Metadata", origin);
this.textMetadata = metadata;
@ -219,33 +208,4 @@ public class JavaxWebSocketFrameHandlerMetadata
return obj.toString();
}
public static class MessageMetadata
{
private static final int UNSET = -1;
public MethodHandle handle;
public Class<? extends MessageSink> sinkClass;
public AvailableDecoders.RegisteredDecoder registeredDecoder;
public int maxMessageSize = UNSET;
public static MessageMetadata copyOf(MessageMetadata metadata)
{
if (metadata == null)
return null;
MessageMetadata copy = new MessageMetadata();
copy.handle = metadata.handle;
copy.sinkClass = metadata.sinkClass;
copy.registeredDecoder = metadata.registeredDecoder;
copy.maxMessageSize = metadata.maxMessageSize;
return copy;
}
public boolean isMaxMessageSizeSet()
{
return maxMessageSize != UNSET;
}
}
}

View File

@ -0,0 +1,115 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.common;
import java.lang.invoke.MethodHandle;
import java.util.List;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
public class JavaxWebSocketMessageMetadata
{
public static enum Type
{
TEXT,
BINARY,
TEXT_STREAM,
BINARY_STREAM
}
private MethodHandle methodHandle;
private Class<? extends MessageSink> sinkClass;
private List<RegisteredDecoder> registeredDecoders;
private int maxMessageSize = -1;
private boolean maxMessageSizeSet = false;
public static JavaxWebSocketMessageMetadata copyOf(JavaxWebSocketMessageMetadata metadata)
{
if (metadata == null)
return null;
JavaxWebSocketMessageMetadata copy = new JavaxWebSocketMessageMetadata();
copy.methodHandle = metadata.methodHandle;
copy.sinkClass = metadata.sinkClass;
copy.registeredDecoders = metadata.registeredDecoders;
copy.maxMessageSize = metadata.maxMessageSize;
copy.maxMessageSizeSet = metadata.maxMessageSizeSet;
return copy;
}
public boolean isMaxMessageSizeSet()
{
return maxMessageSizeSet;
}
public int getMaxMessageSize()
{
return maxMessageSize;
}
public void setMaxMessageSize(int maxMessageSize)
{
this.maxMessageSize = maxMessageSize;
this.maxMessageSizeSet = true;
}
public MethodHandle getMethodHandle()
{
return methodHandle;
}
public void setMethodHandle(MethodHandle methodHandle)
{
this.methodHandle = methodHandle;
}
public Class<? extends MessageSink> getSinkClass()
{
return sinkClass;
}
public void setSinkClass(Class<? extends MessageSink> sinkClass)
{
this.sinkClass = sinkClass;
}
public RegisteredDecoder getRegisteredDecoder()
{
if (registeredDecoders == null || registeredDecoders.isEmpty())
return null;
return registeredDecoders.get(0);
}
public void setRegisteredDecoder(RegisteredDecoder registeredDecoder)
{
this.registeredDecoders = List.of(registeredDecoder);
}
public List<RegisteredDecoder> getRegisteredDecoders()
{
return registeredDecoders;
}
public void setRegisteredDecoders(List<RegisteredDecoder> decoders)
{
this.registeredDecoders = decoders;
}
}

View File

@ -59,12 +59,11 @@ public class JavaxWebSocketSession implements javax.websocket.Session
private final JavaxWebSocketContainer container;
private final CoreSession coreSession;
private final JavaxWebSocketFrameHandler frameHandler;
private final EndpointConfig config;
private final AvailableDecoders availableDecoders;
private final AvailableEncoders availableEncoders;
private final Map<String, String> pathParameters;
private final String sessionId;
private Map<String, Object> userProperties;
private final Map<String, Object> userProperties;
private List<Extension> negotiatedExtensions;
private JavaxWebSocketAsyncRemote asyncRemote;
@ -75,17 +74,17 @@ public class JavaxWebSocketSession implements javax.websocket.Session
JavaxWebSocketFrameHandler frameHandler,
EndpointConfig endpointConfig)
{
Objects.requireNonNull(endpointConfig);
this.container = container;
this.coreSession = coreSession;
this.frameHandler = frameHandler;
this.sessionId = UUID.randomUUID().toString();
this.config = Objects.requireNonNull(endpointConfig);
this.availableDecoders = new AvailableDecoders(this.config);
this.availableEncoders = new AvailableEncoders(this.config);
this.availableDecoders = new AvailableDecoders(endpointConfig);
this.availableEncoders = new AvailableEncoders(endpointConfig);
if (this.config instanceof PathParamProvider)
if (endpointConfig instanceof PathParamProvider)
{
PathParamProvider pathParamProvider = (PathParamProvider)this.config;
PathParamProvider pathParamProvider = (PathParamProvider)endpointConfig;
this.pathParameters = new HashMap<>(pathParamProvider.getPathParams());
}
else
@ -93,7 +92,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
this.pathParameters = Collections.emptyMap();
}
this.userProperties = this.config.getUserProperties();
this.userProperties = endpointConfig.getUserProperties();
}
public CoreSession getCoreSession()
@ -116,7 +115,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
LOG.debug("Add MessageHandler.Partial: {}", handler);
}
frameHandler.addMessageHandler(this, clazz, handler);
frameHandler.addMessageHandler(clazz, handler);
}
/**
@ -134,7 +133,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
LOG.debug("Add MessageHandler.Whole: {}", handler);
}
frameHandler.addMessageHandler(this, clazz, handler);
frameHandler.addMessageHandler(clazz, handler);
}
/**

View File

@ -20,82 +20,28 @@ package org.eclipse.jetty.websocket.javax.common.decoders;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.javax.common.InitException;
import org.eclipse.jetty.websocket.util.InvalidSignatureException;
import org.eclipse.jetty.websocket.util.InvalidWebSocketException;
import org.eclipse.jetty.websocket.util.ReflectUtils;
public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredDecoder>
public class AvailableDecoders implements Iterable<RegisteredDecoder>
{
public static class RegisteredDecoder
{
// The user supplied Decoder class
public final Class<? extends Decoder> decoder;
// The javax.websocket.Decoder.* type (eg: Decoder.Binary, Decoder.BinaryStream, Decoder.Text, Decoder.TextStream)
public final Class<? extends Decoder> interfaceType;
public final Class<?> objectType;
public final boolean primitive;
public Decoder instance;
public RegisteredDecoder(Class<? extends Decoder> decoder, Class<? extends Decoder> interfaceType, Class<?> objectType)
{
this(decoder, interfaceType, objectType, false);
}
public RegisteredDecoder(Class<? extends Decoder> decoder, Class<? extends Decoder> interfaceType, Class<?> objectType, boolean primitive)
{
this.decoder = decoder;
this.interfaceType = interfaceType;
this.objectType = objectType;
this.primitive = primitive;
}
public boolean implementsInterface(Class<? extends Decoder> type)
{
return interfaceType.isAssignableFrom(type);
}
public boolean isType(Class<?> type)
{
return objectType.isAssignableFrom(type);
}
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
str.append(RegisteredDecoder.class.getSimpleName());
str.append('[').append(decoder.getName());
str.append(',').append(interfaceType.getName());
str.append(',').append(objectType.getName());
if (primitive)
{
str.append(",PRIMITIVE");
}
str.append(']');
return str.toString();
}
}
private final List<RegisteredDecoder> registeredDecoders = new LinkedList<>();
private final EndpointConfig config;
private LinkedList<RegisteredDecoder> registeredDecoders;
public AvailableDecoders(EndpointConfig config)
{
Objects.requireNonNull(config);
this.config = config;
registeredDecoders = new LinkedList<>();
this.config = Objects.requireNonNull(config);
// TEXT based [via Class reference]
registerPrimitive(BooleanDecoder.class, Decoder.Text.class, Boolean.class);
@ -132,7 +78,7 @@ public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredD
private void registerPrimitive(Class<? extends Decoder> decoderClass, Class<? extends Decoder> interfaceType, Class<?> type)
{
registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, true));
registeredDecoders.add(new RegisteredDecoder(decoderClass, interfaceType, type, config, true));
}
public void register(Class<? extends Decoder> decoder)
@ -175,23 +121,10 @@ public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredD
}
}
// TODO: consider removing (if not used)
public void registerAll(Class<? extends Decoder>[] decoders)
{
if (decoders == null)
return;
for (Class<? extends Decoder> decoder : decoders)
{
register(decoder);
}
}
public void registerAll(List<Class<? extends Decoder>> decoders)
{
if (decoders == null)
return;
decoders.forEach(this::register);
}
@ -200,52 +133,20 @@ public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredD
Class<?> objectType = ReflectUtils.findGenericClassFor(decoder, interfaceClass);
if (objectType == null)
{
StringBuilder err = new StringBuilder();
err.append("Unknown Decoder Object type declared for interface ");
err.append(interfaceClass.getName());
err.append(" on class ");
err.append(decoder);
throw new InvalidWebSocketException(err.toString());
String err = "Unknown Decoder Object type declared for interface " +
interfaceClass.getName() + " on class " + decoder;
throw new InvalidWebSocketException(err);
}
try
{
RegisteredDecoder conflicts = registeredDecoders.stream()
.filter(registered -> registered.isType(objectType))
.filter(registered -> !registered.primitive)
.findFirst()
.get();
boolean alreadyRegistered = registeredDecoders.stream().anyMatch(registered ->
registered.decoder.equals(decoder) && registered.interfaceType.equals(interfaceClass));
if (conflicts.decoder.equals(decoder) && conflicts.implementsInterface(interfaceClass))
{
// Same decoder as what is there already, don't bother adding it again.
return;
}
StringBuilder err = new StringBuilder();
err.append("Duplicate Decoder Object type ");
err.append(objectType.getName());
err.append(" in ");
err.append(decoder.getName());
err.append(", previously declared in ");
err.append(conflicts.decoder.getName());
throw new InvalidWebSocketException(err.toString());
}
catch (NoSuchElementException e)
{
registeredDecoders.addFirst(new RegisteredDecoder(decoder, interfaceClass, objectType));
}
// If decoder is already registered for this interfaceType, don't bother adding it again.
if (!alreadyRegistered)
registeredDecoders.add(0, new RegisteredDecoder(decoder, interfaceClass, objectType, config));
}
// TODO: consider removing (if not used)
public List<RegisteredDecoder> supporting(Class<? extends Decoder> interfaceType)
{
return registeredDecoders.stream()
.filter(registered -> registered.implementsInterface(interfaceType))
.collect(Collectors.toList());
}
public RegisteredDecoder getRegisteredDecoderFor(Class<?> type)
public RegisteredDecoder getFirstRegisteredDecoder(Class<?> type)
{
return registeredDecoders.stream()
.filter(registered -> registered.isType(type))
@ -253,49 +154,38 @@ public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredD
.orElse(null);
}
// TODO: consider removing (if not used)
public Class<? extends Decoder> getDecoderFor(Class<?> type)
public List<RegisteredDecoder> getRegisteredDecoders(Class<?> returnType)
{
try
{
return getRegisteredDecoderFor(type).decoder;
}
catch (NoSuchElementException e)
{
throw new InvalidWebSocketException("No Decoder found for type " + type);
}
return registeredDecoders.stream()
.filter(registered -> registered.isType(returnType))
.collect(Collectors.toList());
}
public <T extends Decoder> T getInstanceOf(RegisteredDecoder registeredDecoder)
public List<RegisteredDecoder> getRegisteredDecoders(Class<? extends Decoder> interfaceType, Class<?> returnType)
{
if (registeredDecoder.instance != null)
{
return (T)registeredDecoder.instance;
}
try
{
registeredDecoder.instance = registeredDecoder.decoder.getConstructor().newInstance();
registeredDecoder.instance.init(this.config);
return (T)registeredDecoder.instance;
}
catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e)
{
throw new InitException("Unable to init Decoder for type:" + registeredDecoder.decoder.getName(), e);
}
return registeredDecoders.stream()
.filter(registered -> registered.interfaceType.equals(interfaceType) && registered.isType(returnType))
.collect(Collectors.toList());
}
public <T extends Decoder> T getInstanceFor(Class<?> type)
public List<RegisteredDecoder> getTextDecoders(Class<?> returnType)
{
try
{
RegisteredDecoder registeredDecoder = getRegisteredDecoderFor(type);
return getInstanceOf(registeredDecoder);
}
catch (NoSuchElementException e)
{
throw new InvalidWebSocketException("No Decoder found for type " + type);
}
return getRegisteredDecoders(Decoder.Text.class, returnType);
}
public List<RegisteredDecoder> getBinaryDecoders(Class<?> returnType)
{
return getRegisteredDecoders(Decoder.Binary.class, returnType);
}
public List<RegisteredDecoder> getTextStreamDecoders(Class<?> returnType)
{
return getRegisteredDecoders(Decoder.TextStream.class, returnType);
}
public List<RegisteredDecoder> getBinaryStreamDecoders(Class<?> returnType)
{
return getRegisteredDecoders(Decoder.BinaryStream.class, returnType);
}
@Override
@ -303,4 +193,9 @@ public class AvailableDecoders implements Iterable<AvailableDecoders.RegisteredD
{
return registeredDecoders.iterator();
}
public Stream<RegisteredDecoder> stream()
{
return registeredDecoders.stream();
}
}

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.common.decoders;
import java.lang.reflect.InvocationTargetException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.javax.common.InitException;
public class RegisteredDecoder
{
// The user supplied Decoder class
public final Class<? extends Decoder> decoder;
// The javax.websocket.Decoder.* type (eg: Decoder.Binary, Decoder.BinaryStream, Decoder.Text, Decoder.TextStream)
public final Class<? extends Decoder> interfaceType;
public final Class<?> objectType;
public final boolean primitive;
public final EndpointConfig config;
private Decoder instance;
public RegisteredDecoder(Class<? extends Decoder> decoder, Class<? extends Decoder> interfaceType, Class<?> objectType, EndpointConfig endpointConfig)
{
this(decoder, interfaceType, objectType, endpointConfig, false);
}
public RegisteredDecoder(Class<? extends Decoder> decoder, Class<? extends Decoder> interfaceType, Class<?> objectType, EndpointConfig endpointConfig, boolean primitive)
{
this.decoder = decoder;
this.interfaceType = interfaceType;
this.objectType = objectType;
this.primitive = primitive;
this.config = endpointConfig;
}
public boolean implementsInterface(Class<? extends Decoder> type)
{
return interfaceType.isAssignableFrom(type);
}
public boolean isType(Class<?> type)
{
return objectType.isAssignableFrom(type);
}
public <T extends Decoder> T getInstance()
{
if (instance == null)
{
try
{
instance = decoder.getConstructor().newInstance();
instance.init(config);
return (T)instance;
}
catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e)
{
throw new InitException("Unable to init Decoder for type:" + decoder.getName(), e);
}
}
return (T)instance;
}
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
str.append(RegisteredDecoder.class.getSimpleName());
str.append('[').append(decoder.getName());
str.append(',').append(interfaceType.getName());
str.append(',').append(objectType.getName());
if (primitive)
{
str.append(",PRIMITIVE");
}
str.append(']');
return str.toString();
}
}

View File

@ -0,0 +1,75 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.util.List;
import java.util.stream.Collectors;
import javax.websocket.Decoder;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractDecodedMessageSink<T extends Decoder> implements MessageSink
{
protected final Logger _logger;
protected final CoreSession _coreSession;
protected final MethodHandle _methodHandle;
protected final MessageSink _messageSink;
protected final List<T> _decoders;
public AbstractDecodedMessageSink(CoreSession coreSession, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
{
_logger = LoggerFactory.getLogger(getClass());
_coreSession = coreSession;
_methodHandle = methodHandle;
_decoders = decoders.stream()
.map(RegisteredDecoder::<T>getInstance)
.collect(Collectors.toList());
try
{
_messageSink = getMessageSink();
}
catch (Exception e)
{
// Throwing from here is an error implementation of the DecodedMessageSink.
throw new RuntimeException(e);
}
}
/**
* @return a message sink which will first decode the message then pass it to {@link #_methodHandle}.
* @throws Exception for any error in creating the message sink.
*/
abstract MessageSink getMessageSink() throws Exception;
@Override
public void accept(Frame frame, Callback callback)
{
if (_logger.isDebugEnabled())
_logger.debug("accepting frame {} for {}", frame, _messageSink);
_messageSink.accept(frame, callback);
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import java.util.List;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
@ -28,54 +29,48 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.ByteBufferMessageSink;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
public class DecodedBinaryMessageSink<T> extends DecodedMessageSink<Decoder.Binary<T>>
public class DecodedBinaryMessageSink<T> extends AbstractDecodedMessageSink<Decoder.Binary<T>>
{
public DecodedBinaryMessageSink(CoreSession session,
Decoder.Binary<T> decoder,
MethodHandle methodHandle)
throws NoSuchMethodException, IllegalAccessException
public DecodedBinaryMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders)
{
super(session, decoder, methodHandle);
super(session, methodHandle, decoders);
}
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
MessageSink getMessageSink() throws Exception
{
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryMessageSink.class,
MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryMessageSink.class,
"onWholeMessage", MethodType.methodType(void.class, ByteBuffer.class))
.bindTo(this);
}
@Override
protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle)
{
return new ByteBufferMessageSink(session, rawMethodHandle);
return new ByteBufferMessageSink(_coreSession, methodHandle);
}
@SuppressWarnings("Duplicates")
public void onWholeMessage(ByteBuffer wholeMessage)
{
if (!getDecoder().willDecode(wholeMessage))
for (Decoder.Binary<T> decoder : _decoders)
{
logger.warn("Message lost, decoder " + getDecoder().getClass().getName() + "#willDecode() has rejected it.");
return;
}
try
{
T obj = getDecoder().decode(wholeMessage);
methodHandle.invoke(obj);
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
if (decoder.willDecode(wholeMessage))
{
try
{
T obj = decoder.decode(wholeMessage);
_methodHandle.invoke(obj);
return;
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
}
}
}
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
@ -28,48 +29,45 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.InputStreamMessageSink;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
public class DecodedBinaryStreamMessageSink<T> extends DecodedMessageSink<Decoder.BinaryStream<T>>
public class DecodedBinaryStreamMessageSink<T> extends AbstractDecodedMessageSink<Decoder.BinaryStream<T>>
{
public DecodedBinaryStreamMessageSink(CoreSession session,
Decoder.BinaryStream<T> decoder,
MethodHandle methodHandle)
throws NoSuchMethodException, IllegalAccessException
public DecodedBinaryStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders) throws NoSuchMethodException, IllegalAccessException
{
super(session, decoder, methodHandle);
super(session, methodHandle, decoders);
}
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
MessageSink getMessageSink() throws Exception
{
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryStreamMessageSink.class,
MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedBinaryStreamMessageSink.class,
"onStreamStart", MethodType.methodType(void.class, InputStream.class))
.bindTo(this);
}
@Override
protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle)
{
return new InputStreamMessageSink(session, rawMethodHandle);
return new InputStreamMessageSink(_coreSession, methodHandle);
}
@SuppressWarnings("Duplicates")
public void onStreamStart(InputStream stream)
{
try
for (Decoder.BinaryStream<T> decoder : _decoders)
{
T obj = getDecoder().decode(stream);
methodHandle.invoke(obj);
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
try
{
T obj = decoder.decode(stream);
_methodHandle.invoke(obj);
return;
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
}
}
}
}

View File

@ -1,64 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import javax.websocket.Decoder;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.util.messages.AbstractMessageSink;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class DecodedMessageSink<T extends Decoder> extends AbstractMessageSink
{
protected final Logger logger;
private final T decoder;
private final MethodHandle rawMethodHandle;
private final MessageSink rawMessageSink;
public DecodedMessageSink(CoreSession session, T decoder, MethodHandle methodHandle)
throws NoSuchMethodException, IllegalAccessException
{
super(session, methodHandle);
this.logger = LoggerFactory.getLogger(this.getClass());
this.decoder = decoder;
this.rawMethodHandle = newRawMethodHandle();
this.rawMessageSink = newRawMessageSink(session, rawMethodHandle);
}
protected abstract MethodHandle newRawMethodHandle()
throws NoSuchMethodException, IllegalAccessException;
protected abstract MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle);
public T getDecoder()
{
return decoder;
}
@Override
public void accept(Frame frame, Callback callback)
{
this.rawMessageSink.accept(frame, callback);
}
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
@ -27,54 +28,47 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.eclipse.jetty.websocket.util.messages.StringMessageSink;
public class DecodedTextMessageSink<T> extends DecodedMessageSink<Decoder.Text<T>>
public class DecodedTextMessageSink<T> extends AbstractDecodedMessageSink<Decoder.Text<T>>
{
public DecodedTextMessageSink(CoreSession session,
Decoder.Text<T> decoder,
MethodHandle methodHandle)
throws NoSuchMethodException, IllegalAccessException
public DecodedTextMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders) throws NoSuchMethodException, IllegalAccessException
{
super(session, decoder, methodHandle);
super(session, methodHandle, decoders);
}
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
MessageSink getMessageSink() throws NoSuchMethodException, IllegalAccessException
{
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextMessageSink.class,
"onWholeMessage", MethodType.methodType(void.class, String.class))
MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup()
.findVirtual(getClass(), "onMessage", MethodType.methodType(void.class, String.class))
.bindTo(this);
return new StringMessageSink(_coreSession, methodHandle);
}
@Override
protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle)
public void onMessage(String wholeMessage)
{
return new StringMessageSink(session, rawMethodHandle);
}
@SuppressWarnings("Duplicates")
public void onWholeMessage(String wholeMessage)
{
if (!getDecoder().willDecode(wholeMessage))
for (Decoder.Text<T> decoder : _decoders)
{
logger.warn("Message lost, decoder " + getDecoder().getClass().getName() + "#willDecode() has rejected it.");
return;
}
try
{
T obj = getDecoder().decode(wholeMessage);
methodHandle.invoke(obj);
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
if (decoder.willDecode(wholeMessage))
{
try
{
T obj = decoder.decode(wholeMessage);
_methodHandle.invoke(obj);
return;
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
}
}
}
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
@ -28,48 +29,44 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.exception.CloseException;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.messages.MessageSink;
import org.eclipse.jetty.websocket.util.messages.ReaderMessageSink;
public class DecodedTextStreamMessageSink<T> extends DecodedMessageSink<Decoder.TextStream<T>>
public class DecodedTextStreamMessageSink<T> extends AbstractDecodedMessageSink<Decoder.TextStream<T>>
{
public DecodedTextStreamMessageSink(CoreSession session,
Decoder.TextStream<T> decoder,
MethodHandle methodHandle)
throws NoSuchMethodException, IllegalAccessException
public DecodedTextStreamMessageSink(CoreSession session, MethodHandle methodHandle, List<RegisteredDecoder> decoders) throws NoSuchMethodException, IllegalAccessException
{
super(session, decoder, methodHandle);
super(session, methodHandle, decoders);
}
@Override
protected MethodHandle newRawMethodHandle() throws NoSuchMethodException, IllegalAccessException
MessageSink getMessageSink() throws Exception
{
return JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextStreamMessageSink.class,
MethodHandle methodHandle = JavaxWebSocketFrameHandlerFactory.getServerMethodHandleLookup().findVirtual(DecodedTextStreamMessageSink.class,
"onStreamStart", MethodType.methodType(void.class, Reader.class))
.bindTo(this);
return new ReaderMessageSink(_coreSession, methodHandle);
}
@Override
protected MessageSink newRawMessageSink(CoreSession session, MethodHandle rawMethodHandle)
{
return new ReaderMessageSink(session, rawMethodHandle);
}
@SuppressWarnings("Duplicates")
public void onStreamStart(Reader reader)
{
try
for (Decoder.TextStream<T> decoder : _decoders)
{
T obj = getDecoder().decode(reader);
methodHandle.invoke(obj);
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
try
{
T obj = decoder.decode(reader);
_methodHandle.invoke(obj);
return;
}
catch (DecodeException e)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Unable to decode", e);
}
catch (Throwable t)
{
throw new CloseException(CloseReason.CloseCodes.CANNOT_ACCEPT.getCode(), "Endpoint notification error", t);
}
}
}
}

View File

@ -20,13 +20,34 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.function.Consumer;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.javax.common.AbstractSessionTest;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
public abstract class AbstractMessageSinkTest extends AbstractSessionTest
{
public List<RegisteredDecoder> toRegisteredDecoderList(Class<? extends Decoder> clazz, Class<?> objectType)
{
Class<? extends Decoder> interfaceType;
if (Decoder.Text.class.isAssignableFrom(clazz))
interfaceType = Decoder.Text.class;
else if (Decoder.Binary.class.isAssignableFrom(clazz))
interfaceType = Decoder.Binary.class;
else if (Decoder.TextStream.class.isAssignableFrom(clazz))
interfaceType = Decoder.TextStream.class;
else if (Decoder.BinaryStream.class.isAssignableFrom(clazz))
interfaceType = Decoder.BinaryStream.class;
else
throw new IllegalStateException();
return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build()));
}
public <T> MethodHandle getAcceptHandle(Consumer<T> copy, Class<T> type)
{
try

View File

@ -22,6 +22,7 @@ import java.lang.invoke.MethodHandle;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -34,6 +35,7 @@ import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.common.AbstractSessionTest;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -49,8 +51,8 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
Decoder.Binary<Calendar> decoder = new GmtDecoder();
DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink(AbstractSessionTest.session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders);
FutureCallback finCallback = new FutureCallback();
ByteBuffer data = ByteBuffer.allocate(16);
@ -72,8 +74,8 @@ public class DecodedBinaryMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
Decoder.Binary<Calendar> decoder = new GmtDecoder();
DecodedBinaryMessageSink sink = new DecodedBinaryMessageSink(AbstractSessionTest.session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryMessageSink<Calendar> sink = new DecodedBinaryMessageSink<>(AbstractSessionTest.session.getCoreSession(), copyHandle, decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -24,6 +24,7 @@ import java.lang.invoke.MethodHandle;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -36,6 +37,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -51,8 +53,8 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
Decoder.BinaryStream<Calendar> decoder = new GmtDecoder();
DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback finCallback = new FutureCallback();
ByteBuffer data = ByteBuffer.allocate(16);
@ -74,8 +76,8 @@ public class DecodedBinaryStreamMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Calendar> copyFuture = new CompletableFuture<>();
DecodedCalendarCopy copy = new DecodedCalendarCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Calendar.class);
Decoder.BinaryStream<Calendar> decoder = new GmtDecoder();
DecodedBinaryStreamMessageSink sink = new DecodedBinaryStreamMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(GmtDecoder.class, Calendar.class);
DecodedBinaryStreamMessageSink<Calendar> sink = new DecodedBinaryStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -21,7 +21,9 @@ package org.eclipse.jetty.websocket.javax.common.messages;
import java.lang.invoke.MethodHandle;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -33,6 +35,7 @@ import javax.websocket.EndpointConfig;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -48,8 +51,8 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
Decoder.Text<Date> decoder = new GmtDecoder();
DecodedTextMessageSink sink = new DecodedTextMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback finCallback = new FutureCallback();
sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback);
@ -66,8 +69,8 @@ public class DecodedTextMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
Decoder.Text<Date> decoder = new GmtDecoder();
DecodedTextMessageSink sink = new DecodedTextMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class);
DecodedTextMessageSink<Calendar> sink = new DecodedTextMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -23,7 +23,9 @@ import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -36,6 +38,7 @@ import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -51,8 +54,8 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
Decoder.TextStream<Date> decoder = new GmtDecoder();
DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback finCallback = new FutureCallback();
sink.accept(new Frame(OpCode.TEXT).setPayload("2018.02.13").setFin(true), finCallback);
@ -69,8 +72,8 @@ public class DecodedTextStreamMessageSinkTest extends AbstractMessageSinkTest
CompletableFuture<Date> copyFuture = new CompletableFuture<>();
DecodedDateCopy copy = new DecodedDateCopy(copyFuture);
MethodHandle copyHandle = getAcceptHandle(copy, Date.class);
Decoder.TextStream<Date> decoder = new GmtDecoder();
DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, copyHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(DecodedBinaryStreamMessageSinkTest.GmtDecoder.class, Calendar.class);
DecodedTextStreamMessageSink<Calendar> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), copyHandle, decoders);
FutureCallback callback1 = new FutureCallback();
FutureCallback callback2 = new FutureCallback();

View File

@ -42,15 +42,11 @@ public class JavaxWebSocketServerFrameHandlerFactory extends JavaxWebSocketClien
public JavaxWebSocketFrameHandlerMetadata getMetadata(Class<?> endpointClass, EndpointConfig endpointConfig)
{
if (javax.websocket.Endpoint.class.isAssignableFrom(endpointClass))
{
return createEndpointMetadata((Class<? extends Endpoint>)endpointClass, endpointConfig);
}
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
if (anno == null)
{
return super.getMetadata(endpointClass, endpointConfig);
}
UriTemplatePathSpec templatePathSpec = new UriTemplatePathSpec(anno.value());
JavaxWebSocketFrameHandlerMetadata metadata = new JavaxWebSocketFrameHandlerMetadata(endpointConfig);

View File

@ -23,7 +23,7 @@ import javax.websocket.MessageHandler;
import javax.websocket.PongMessage;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketSession;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.javax.tests.MessageType;
import org.eclipse.jetty.websocket.util.ReflectUtils;
import org.hamcrest.Description;
@ -78,7 +78,7 @@ public class IsMessageHandlerType extends TypeSafeMatcher<MessageHandler>
return false;
}
AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageClass);
RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageClass);
if (registeredDecoder == null)
{
return false;

View File

@ -25,7 +25,7 @@ import javax.websocket.PongMessage;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketSession;
import org.eclipse.jetty.websocket.javax.common.RegisteredMessageHandler;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.javax.tests.MessageType;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@ -60,7 +60,7 @@ public class IsMessageHandlerTypeRegistered extends TypeSafeMatcher<JavaxWebSock
{
Class<?> onMessageType = registeredMessageHandler.getHandlerType();
AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageType);
RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageType);
if (registeredDecoder == null)
{
continue;
@ -130,7 +130,7 @@ public class IsMessageHandlerTypeRegistered extends TypeSafeMatcher<JavaxWebSock
mismatchDescription.appendText("<" + onMessageType.getName() + ">");
AvailableDecoders.RegisteredDecoder registeredDecoder = session.getDecoders().getRegisteredDecoderFor(onMessageType);
RegisteredDecoder registeredDecoder = session.getDecoders().getFirstRegisteredDecoder(onMessageType);
if (registeredDecoder == null)
{
mismatchDescription.appendText("(!NO-DECODER!)");

View File

@ -0,0 +1,162 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.tests;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.javax.client.internal.JavaxWebSocketClientContainer;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class DecoderListTest
{
private Server server;
private URI serverUri;
private JavaxWebSocketClientContainer client;
@BeforeEach
public void before() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler();
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
container.addEndpoint(DecoderListEndpoint.class));
server.start();
serverUri = WSURI.toWebsocket(server.getURI());
client = new JavaxWebSocketClientContainer();
client.start();
}
@AfterEach
public void after() throws Exception
{
server.stop();
client.stop();
}
public static Stream<Arguments> getArguments()
{
return Stream.of(
Arguments.of("=DecodeEquals", "DecodeEquals="),
Arguments.of("+DecodePlus", "DecodePlus+"),
Arguments.of("-DecodeMinus", "DecodeMinus-"),
Arguments.of("DecodeNoMatch", null)
);
}
@ParameterizedTest
@MethodSource("getArguments")
public void testDecoderList(String request, String expected) throws Exception
{
EventSocket clientEndpoint = new EventSocket();
Session session = client.connectToServer(clientEndpoint, serverUri);
session.getBasicRemote().sendText(request);
String response = clientEndpoint.textMessages.poll(3, TimeUnit.SECONDS);
assertThat(response, is(expected));
}
@ServerEndpoint(value = "/", decoders = {EqualsDecoder.class, PlusDecoder.class, MinusDecoder.class})
public static class DecoderListEndpoint
{
@OnMessage
public String echo(String message)
{
return message;
}
}
public static class EqualsDecoder extends PrefixStringDecoder
{
public EqualsDecoder()
{
super("=");
}
}
public static class PlusDecoder extends PrefixStringDecoder
{
public PlusDecoder()
{
super("+");
}
}
public static class MinusDecoder extends PrefixStringDecoder
{
public MinusDecoder()
{
super("-");
}
}
public static class PrefixStringDecoder implements Decoder.Text<String>
{
private final String prefix;
public PrefixStringDecoder(String prefix)
{
this.prefix = prefix;
}
@Override
public String decode(String s)
{
return s.substring(prefix.length()) + prefix;
}
@Override
public boolean willDecode(String s)
{
return s.startsWith(prefix);
}
@Override
public void init(EndpointConfig config)
{
}
@Override
public void destroy()
{
}
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.NoSuchElementException;
import java.util.TimeZone;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
@ -31,6 +32,7 @@ import org.eclipse.jetty.toolchain.test.Hex;
import org.eclipse.jetty.websocket.javax.client.internal.BasicClientEndpointConfig;
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.javax.common.decoders.IntegerDecoder;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.util.InvalidWebSocketException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -52,132 +54,145 @@ public class AvailableDecodersTest
testConfig = new BasicClientEndpointConfig();
}
private AvailableDecoders decoders = new AvailableDecoders(testConfig);
private final AvailableDecoders decoders = new AvailableDecoders(testConfig);
private <T> void assertTextDecoder(Class<T> type, String value, T expectedDecoded) throws IllegalAccessException, InstantiationException, DecodeException
public <T extends Decoder> T getInstanceFor(Class<?> type)
{
Decoder.Text<T> decoder = (Decoder.Text<T>)decoders.getInstanceFor(type);
try
{
RegisteredDecoder registeredDecoder = decoders.getFirstRegisteredDecoder(type);
return registeredDecoder.getInstance();
}
catch (NoSuchElementException e)
{
throw new InvalidWebSocketException("No Decoder found for type " + type);
}
}
private <T> void assertTextDecoder(Class<T> type, String value, T expectedDecoded) throws DecodeException
{
Decoder.Text<T> decoder = getInstanceFor(type);
assertThat("Decoder instance", decoder, notNullValue());
T decoded = decoder.decode(value);
assertThat("Decoded", decoded, is(expectedDecoded));
}
private <T> void assertBinaryDecoder(Class<T> type, ByteBuffer value, T expectedDecoded)
throws IllegalAccessException, InstantiationException, DecodeException
throws DecodeException
{
Decoder.Binary<T> decoder = (Decoder.Binary<T>)decoders.getInstanceFor(type);
Decoder.Binary<T> decoder = getInstanceFor(type);
assertThat("Decoder Class", decoder, notNullValue());
T decoded = decoder.decode(value);
assertThat("Decoded", decoded, equalTo(expectedDecoded));
}
@Test
public void testCoreDecodeBoolean() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeBoolean() throws DecodeException
{
Boolean expected = Boolean.TRUE;
assertTextDecoder(Boolean.class, "true", expected);
}
@Test
public void testCoreDecodeboolean() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeboolean() throws DecodeException
{
boolean expected = false;
assertTextDecoder(Boolean.TYPE, "false", expected);
}
@Test
public void testCoreDecodeByte() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeByte() throws DecodeException
{
Byte expected = (byte)0x21;
assertTextDecoder(Byte.class, "33", expected);
}
@Test
public void testCoreDecodebyte() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodebyte() throws DecodeException
{
byte expected = 0x21;
assertTextDecoder(Byte.TYPE, "33", expected);
}
@Test
public void testCoreDecodeCharacter() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeCharacter() throws DecodeException
{
Character expected = '!';
assertTextDecoder(Character.class, "!", expected);
}
@Test
public void testCoreDecodechar() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodechar() throws DecodeException
{
char expected = '!';
assertTextDecoder(Character.TYPE, "!", expected);
}
@Test
public void testCoreDecodeDouble() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeDouble() throws DecodeException
{
Double expected = 123.45D;
assertTextDecoder(Double.class, "123.45", expected);
}
@Test
public void testCoreDecodedouble() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodedouble() throws DecodeException
{
double expected = 123.45D;
assertTextDecoder(Double.TYPE, "123.45", expected);
}
@Test
public void testCoreDecodeFloat() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeFloat() throws DecodeException
{
Float expected = 123.4567F;
assertTextDecoder(Float.class, "123.4567", expected);
}
@Test
public void testCoreDecodefloat() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodefloat() throws DecodeException
{
float expected = 123.4567F;
assertTextDecoder(Float.TYPE, "123.4567", expected);
}
@Test
public void testCoreDecodeInteger() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeInteger() throws DecodeException
{
Integer expected = 1234;
assertTextDecoder(Integer.class, "1234", expected);
}
@Test
public void testCoreDecodeint() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeint() throws DecodeException
{
int expected = 1234;
assertTextDecoder(Integer.TYPE, "1234", expected);
}
@Test
public void testCoreDecodeLong() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeLong() throws DecodeException
{
Long expected = 123_456_789L;
assertTextDecoder(Long.class, "123456789", expected);
}
@Test
public void testCoreDecodelong() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodelong() throws DecodeException
{
long expected = 123_456_789L;
assertTextDecoder(Long.TYPE, "123456789", expected);
}
@Test
public void testCoreDecodeString() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeString() throws DecodeException
{
String expected = "Hello World";
assertTextDecoder(String.class, "Hello World", expected);
}
@Test
public void testCoreDecodeByteBuffer() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeByteBuffer() throws DecodeException
{
ByteBuffer val = Hex.asByteBuffer("112233445566778899");
ByteBuffer expected = Hex.asByteBuffer("112233445566778899");
@ -185,7 +200,7 @@ public class AvailableDecodersTest
}
@Test
public void testCoreDecodeByteArray() throws IllegalAccessException, InstantiationException, DecodeException
public void testCoreDecodeByteArray() throws DecodeException
{
ByteBuffer val = Hex.asByteBuffer("112233445566778899");
byte[] expected = Hex.asByteArray("112233445566778899");
@ -193,7 +208,7 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderInteger() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderInteger() throws DecodeException
{
decoders.register(IntegerDecoder.class);
@ -203,7 +218,7 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderTime() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderTime() throws DecodeException
{
decoders.register(TimeDecoder.class);
@ -222,7 +237,7 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderDate() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderDate() throws DecodeException
{
decoders.register(DateDecoder.class);
@ -241,7 +256,7 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderDateTime() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderDateTime() throws DecodeException
{
decoders.register(DateTimeDecoder.class);
@ -264,11 +279,11 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderValidDualText() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderValidDualText() throws DecodeException
{
decoders.register(ValidDualDecoder.class);
AvailableDecoders.RegisteredDecoder registered = decoders.getRegisteredDecoderFor(Integer.class);
RegisteredDecoder registered = decoders.getFirstRegisteredDecoder(Integer.class);
assertThat("Registered Decoder for Integer", registered.decoder.getName(), is(ValidDualDecoder.class.getName()));
String val = "[1,234,567]";
@ -278,11 +293,11 @@ public class AvailableDecodersTest
}
@Test
public void testCustomDecoderValidDualBinary() throws IllegalAccessException, InstantiationException, DecodeException
public void testCustomDecoderValidDualBinary() throws DecodeException
{
decoders.register(ValidDualDecoder.class);
AvailableDecoders.RegisteredDecoder registered = decoders.getRegisteredDecoderFor(Long.class);
RegisteredDecoder registered = decoders.getFirstRegisteredDecoder(Long.class);
assertThat("Registered Decoder for Long", registered.decoder.getName(), is(ValidDualDecoder.class.getName()));
ByteBuffer val = ByteBuffer.allocate(16);

View File

@ -27,11 +27,13 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.javax.common.decoders.RegisteredDecoder;
import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextStreamMessageSink;
import org.eclipse.jetty.websocket.javax.tests.FunctionMethod;
import org.eclipse.jetty.websocket.javax.tests.client.AbstractClientSessionTest;
@ -64,7 +66,6 @@ public class DecoderTextStreamTest extends AbstractClientSessionTest
@Test
public void testQuotesDecodedReaderMessageSink() throws Exception
{
Decoder.TextStream<Quotes> decoder = new QuotesDecoder();
CompletableFuture<Quotes> futureQuotes = new CompletableFuture<>();
MethodHandle functionHandle = FunctionMethod.getFunctionApplyMethodHandle();
MethodHandle quoteHandle = functionHandle.bindTo((Function<Quotes, Void>)(quotes) ->
@ -80,7 +81,8 @@ public class DecoderTextStreamTest extends AbstractClientSessionTest
return null;
});
DecodedTextStreamMessageSink sink = new DecodedTextStreamMessageSink(session.getCoreSession(), decoder, quoteHandle);
List<RegisteredDecoder> decoders = toRegisteredDecoderList(QuotesDecoder.class, Quotes.class);
DecodedTextStreamMessageSink<Quotes> sink = new DecodedTextStreamMessageSink<>(session.getCoreSession(), quoteHandle, decoders);
List<FutureCallback> callbacks = new ArrayList<>();
FutureCallback finCallback = null;
@ -107,4 +109,21 @@ public class DecoderTextStreamTest extends AbstractClientSessionTest
assertThat("Quotes.author", quotes.getAuthor(), is("Benjamin Franklin"));
assertThat("Quotes.count", quotes.getQuotes().size(), is(3));
}
public List<RegisteredDecoder> toRegisteredDecoderList(Class<? extends Decoder> clazz, Class<?> objectType)
{
Class<? extends Decoder> interfaceType;
if (Decoder.Text.class.isAssignableFrom(clazz))
interfaceType = Decoder.Text.class;
else if (Decoder.Binary.class.isAssignableFrom(clazz))
interfaceType = Decoder.Binary.class;
else if (Decoder.TextStream.class.isAssignableFrom(clazz))
interfaceType = Decoder.TextStream.class;
else if (Decoder.BinaryStream.class.isAssignableFrom(clazz))
interfaceType = Decoder.BinaryStream.class;
else
throw new IllegalStateException();
return List.of(new RegisteredDecoder(clazz, interfaceType, objectType, ClientEndpointConfig.Builder.create().build()));
}
}