Issue #3428 - Initial refactor to support javax websocket decoderLists
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
aa52d67dbf
commit
4b19c19815
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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!)");
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue