JSR-356: MessageHandler(s) layer
This commit is contained in:
parent
0232216e65
commit
d1692733f5
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for a {@link Decoder}
|
||||||
|
*/
|
||||||
|
public class DecoderMetadata
|
||||||
|
{
|
||||||
|
private final Class<?> objType;
|
||||||
|
private final Class<? extends Decoder> decoder;
|
||||||
|
private final MessageType messageType;
|
||||||
|
private final boolean streamed;
|
||||||
|
|
||||||
|
public DecoderMetadata(Class<?> objType, Class<? extends Decoder> decoder, MessageType messageType, boolean streamed)
|
||||||
|
{
|
||||||
|
this.objType = objType;
|
||||||
|
this.decoder = decoder;
|
||||||
|
this.messageType = messageType;
|
||||||
|
this.streamed = streamed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Decoder> getDecoder()
|
||||||
|
{
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageType getMessageType()
|
||||||
|
{
|
||||||
|
return messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getObjectType()
|
||||||
|
{
|
||||||
|
return objType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStreamed()
|
||||||
|
{
|
||||||
|
return streamed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.BooleanDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ByteArrayDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ByteBufferDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ByteDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.CharacterDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.DoubleDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.FloatDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.IntegerDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.LongDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ShortDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.StringDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.utils.DeploymentTypeUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global Factory for all declared Decoders in all endpoints.
|
||||||
|
*/
|
||||||
|
public class DecoderMetadataFactory
|
||||||
|
{
|
||||||
|
public static class DefaultsDecoderFactory extends DecoderMetadataFactory
|
||||||
|
{
|
||||||
|
public static final DefaultsDecoderFactory INSTANCE = new DefaultsDecoderFactory();
|
||||||
|
|
||||||
|
private Map<Type, Class<? extends Decoder>> typeMap = new HashMap<>();
|
||||||
|
|
||||||
|
public DefaultsDecoderFactory()
|
||||||
|
{
|
||||||
|
boolean streamed = false;
|
||||||
|
// TEXT based - Classes
|
||||||
|
MessageType msgType = MessageType.TEXT;
|
||||||
|
register(Boolean.class,BooleanDecoder.class,msgType,streamed);
|
||||||
|
register(Byte.class,ByteDecoder.class,msgType,streamed);
|
||||||
|
register(Character.class,CharacterDecoder.class,msgType,streamed);
|
||||||
|
register(Double.class,DoubleDecoder.class,msgType,streamed);
|
||||||
|
register(Float.class,FloatDecoder.class,msgType,streamed);
|
||||||
|
register(Integer.class,IntegerDecoder.class,msgType,streamed);
|
||||||
|
register(Long.class,LongDecoder.class,msgType,streamed);
|
||||||
|
register(Short.class,ShortDecoder.class,msgType,streamed);
|
||||||
|
register(String.class,StringDecoder.class,msgType,streamed);
|
||||||
|
|
||||||
|
// TEXT based - Primitives
|
||||||
|
msgType = MessageType.TEXT;
|
||||||
|
register(Boolean.TYPE,BooleanDecoder.class,msgType,streamed);
|
||||||
|
register(Byte.TYPE,ByteDecoder.class,msgType,streamed);
|
||||||
|
register(Character.TYPE,CharacterDecoder.class,msgType,streamed);
|
||||||
|
register(Double.TYPE,DoubleDecoder.class,msgType,streamed);
|
||||||
|
register(Float.TYPE,FloatDecoder.class,msgType,streamed);
|
||||||
|
register(Integer.TYPE,IntegerDecoder.class,msgType,streamed);
|
||||||
|
register(Long.TYPE,LongDecoder.class,msgType,streamed);
|
||||||
|
register(Short.TYPE,ShortDecoder.class,msgType,streamed);
|
||||||
|
|
||||||
|
// BINARY based
|
||||||
|
msgType = MessageType.BINARY;
|
||||||
|
register(ByteBuffer.class,ByteBufferDecoder.class,msgType,streamed);
|
||||||
|
register(byte[].class,ByteArrayDecoder.class,msgType,streamed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Decoder> getDecoder(Class<?> type)
|
||||||
|
{
|
||||||
|
return typeMap.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(Class<?> typeClass, Class<? extends Decoder> decoderClass, MessageType msgType, boolean streamed)
|
||||||
|
{
|
||||||
|
List<DecoderMetadata> metadatas = new ArrayList<>();
|
||||||
|
metadatas.add(new DecoderMetadata(typeClass,decoderClass,msgType,streamed));
|
||||||
|
cache.put(decoderClass,metadatas);
|
||||||
|
typeMap.put(typeClass,decoderClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG = Log.getLogger(DecoderMetadataFactory.class);
|
||||||
|
protected Map<Class<? extends Decoder>, List<DecoderMetadata>> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Class<?> getDecoderMessageClass(Class<? extends Decoder> decoder, Class<?> interfaceClass)
|
||||||
|
{
|
||||||
|
Type genericType = DeploymentTypeUtils.getGenericType(decoder,interfaceClass);
|
||||||
|
if (genericType instanceof Class<?>)
|
||||||
|
{
|
||||||
|
return (Class<?>)genericType;
|
||||||
|
}
|
||||||
|
StringBuilder err = new StringBuilder();
|
||||||
|
err.append("Invalid type declared for interface ");
|
||||||
|
err.append(interfaceClass.getName());
|
||||||
|
err.append(" on class ");
|
||||||
|
err.append(decoder);
|
||||||
|
throw new IllegalArgumentException(err.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DecoderMetadata> getMetadata(Class<? extends Decoder> decoder)
|
||||||
|
{
|
||||||
|
List<DecoderMetadata> ret = cache.get(decoder);
|
||||||
|
|
||||||
|
if (ret == null)
|
||||||
|
{
|
||||||
|
ret = new ArrayList<>();
|
||||||
|
|
||||||
|
if (Decoder.Binary.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
Class<?> objType = getDecoderMessageClass(decoder,Decoder.Binary.class);
|
||||||
|
ret.add(new DecoderMetadata(objType,decoder,MessageType.BINARY,false));
|
||||||
|
}
|
||||||
|
if (Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
Class<?> objType = getDecoderMessageClass(decoder,Decoder.BinaryStream.class);
|
||||||
|
ret.add(new DecoderMetadata(objType,decoder,MessageType.BINARY,true));
|
||||||
|
}
|
||||||
|
if (Decoder.Text.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
Class<?> objType = getDecoderMessageClass(decoder,Decoder.Text.class);
|
||||||
|
ret.add(new DecoderMetadata(objType,decoder,MessageType.TEXT,false));
|
||||||
|
}
|
||||||
|
if (Decoder.TextStream.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
Class<?> objType = getDecoderMessageClass(decoder,Decoder.TextStream.class);
|
||||||
|
ret.add(new DecoderMetadata(objType,decoder,MessageType.TEXT,true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret.size() <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidSignatureException("Not a valid Decoder class: " + decoder.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("New Hit [{} entries]",ret.size());
|
||||||
|
cache.put(decoder,ret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.debug("From Cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,33 +16,31 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356.decoders;
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to a Decoder.
|
* Expose a {@link Decoder} instance along with its associated {@link DecoderMetadata}
|
||||||
* <p>
|
|
||||||
* This represents a potential decoder, no instance exists (yet)
|
|
||||||
*/
|
*/
|
||||||
public class DecoderRef
|
public class DecoderWrapper
|
||||||
{
|
{
|
||||||
private Class<?> type;
|
private final Decoder decoder;
|
||||||
private Class<? extends Decoder> decoder;
|
private final DecoderMetadata metadata;
|
||||||
|
|
||||||
public DecoderRef(Class<?> type, Class<? extends Decoder> decoder)
|
public DecoderWrapper(Decoder decoder, DecoderMetadata metadata)
|
||||||
{
|
{
|
||||||
this.type = type;
|
|
||||||
this.decoder = decoder;
|
this.decoder = decoder;
|
||||||
|
this.metadata = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<? extends Decoder> getDecoder()
|
public Decoder getDecoder()
|
||||||
{
|
{
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType()
|
public DecoderMetadata getMetadata()
|
||||||
{
|
{
|
||||||
return type;
|
return metadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
import javax.websocket.EndpointConfig;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderMetadataFactory.DefaultsDecoderFactory;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collection of decoder instances declared for an Endpoint.
|
||||||
|
* <p>
|
||||||
|
* Decoder classes can arrive from:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link EndpointConfig#getDecoders()}</li>
|
||||||
|
* <li>@ClientEndpoint.decoders()</li>
|
||||||
|
* <li>@ServerEndpoint.decoders()</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* This class is also responsible for tracking the lifecycle of all the decoders.
|
||||||
|
*/
|
||||||
|
public class Decoders
|
||||||
|
{
|
||||||
|
private final DecoderMetadataFactory metadataFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of Object Type to Decoder
|
||||||
|
*/
|
||||||
|
private final Map<Class<?>, DecoderWrapper> decoderMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoder Classes from {@link EndpointConfig#getDecoders()}
|
||||||
|
*
|
||||||
|
* @param metadataFactory
|
||||||
|
* the factory to create {@link DecoderMetadata} references
|
||||||
|
* @param config
|
||||||
|
* the endpoint config with the decoder configuration
|
||||||
|
*
|
||||||
|
* @throws DeploymentException
|
||||||
|
* if unable to instantiate decoders
|
||||||
|
*/
|
||||||
|
public Decoders(DecoderMetadataFactory metadataFactory, EndpointConfig config) throws DeploymentException
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(metadataFactory,"DecoderMetadataFactory cannot be null");
|
||||||
|
this.metadataFactory = metadataFactory;
|
||||||
|
|
||||||
|
for (Class<? extends Decoder> decoder : config.getDecoders())
|
||||||
|
{
|
||||||
|
addAllMetadata(decoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(DecoderWrapper wrapper) throws IllegalStateException
|
||||||
|
{
|
||||||
|
// Check for duplicate object types
|
||||||
|
Class<?> key = wrapper.getMetadata().getObjectType();
|
||||||
|
if (decoderMap.containsKey(key))
|
||||||
|
{
|
||||||
|
DecoderWrapper other = decoderMap.get(key);
|
||||||
|
StringBuilder err = new StringBuilder();
|
||||||
|
err.append("Encountered duplicate Decoder handling type <");
|
||||||
|
err.append(MethodUtils.toString(key));
|
||||||
|
err.append(">, ").append(wrapper.getMetadata().getDecoder().getName());
|
||||||
|
err.append(" and ").append(other.getMetadata().getDecoder().getName());
|
||||||
|
err.append(" both implement this type");
|
||||||
|
throw new IllegalStateException(err.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
decoderMap.put(key,wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DecoderWrapper addAllMetadata(Class<? extends Decoder> decoder) throws IllegalStateException
|
||||||
|
{
|
||||||
|
DecoderWrapper wrapper = null;
|
||||||
|
|
||||||
|
for (DecoderMetadata metadata : metadataFactory.getMetadata(decoder))
|
||||||
|
{
|
||||||
|
Decoder decoderImpl;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
decoderImpl = decoder.newInstance();
|
||||||
|
wrapper = new DecoderWrapper(decoderImpl,metadata);
|
||||||
|
add(wrapper);
|
||||||
|
}
|
||||||
|
catch (InstantiationException | IllegalAccessException cause)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unable to instantiate Decoder: " + decoder.getName(),cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Decoder getDecoder(Class<?> type) throws DeploymentException
|
||||||
|
{
|
||||||
|
return getDecoderWrapper(type).getDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecoderWrapper getDecoderWrapper(Class<?> type) throws IllegalStateException
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(type,"Type cannot be null");
|
||||||
|
DecoderWrapper wrapper = decoderMap.get(type);
|
||||||
|
if (wrapper == null)
|
||||||
|
{
|
||||||
|
// try DEFAULT implementations
|
||||||
|
Class<? extends Decoder> defaultDecoder = DefaultsDecoderFactory.INSTANCE.getDecoder(type);
|
||||||
|
wrapper = addAllMetadata(defaultDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple lookup, return it
|
||||||
|
if (wrapper != null)
|
||||||
|
{
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow mode, test isAssignable on each key
|
||||||
|
for (Entry<Class<?>, DecoderWrapper> entry : decoderMap.entrySet())
|
||||||
|
{
|
||||||
|
Class<?> key = entry.getKey();
|
||||||
|
if (key.isAssignableFrom(type))
|
||||||
|
{
|
||||||
|
// we found a hit, return it
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidSignatureException("Unable to find appropriate Decoder for type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Class<?>> keySet()
|
||||||
|
{
|
||||||
|
return decoderMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<DecoderWrapper> wrapperSet()
|
||||||
|
{
|
||||||
|
return decoderMap.values();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.websocket.ClientEndpointConfig;
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.Encoder;
|
||||||
|
import javax.websocket.Extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DefaultClientEndpointConfig to use.
|
||||||
|
*/
|
||||||
|
public class DefaultClientEndpointConfig implements ClientEndpointConfig
|
||||||
|
{
|
||||||
|
public static class DefaultClientEndpointConfigurator extends ClientEndpointConfig.Configurator
|
||||||
|
{
|
||||||
|
public static final DefaultClientEndpointConfigurator INSTANCE = new DefaultClientEndpointConfigurator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Class<? extends Decoder>> decoders;
|
||||||
|
private List<Class<? extends Encoder>> encoders;
|
||||||
|
private List<Extension> extensions;
|
||||||
|
private Map<String, Object> userProperties;
|
||||||
|
private List<String> preferredSubprotocols;
|
||||||
|
|
||||||
|
private DefaultClientEndpointConfig()
|
||||||
|
{
|
||||||
|
this.extensions = new ArrayList<>();
|
||||||
|
this.userProperties = new HashMap<>();
|
||||||
|
this.preferredSubprotocols = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor from annotation.
|
||||||
|
*
|
||||||
|
* @param decoders
|
||||||
|
* the array of decoder classes on the annotation
|
||||||
|
* @param encoders
|
||||||
|
* the array of encoder classes on the annotation
|
||||||
|
*/
|
||||||
|
public DefaultClientEndpointConfig(Class<? extends Decoder>[] decoders, Class<? extends Encoder>[] encoders)
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
this.decoders = Collections.unmodifiableList(Arrays.asList(decoders));
|
||||||
|
this.encoders = Collections.unmodifiableList(Arrays.asList(encoders));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configurator getConfigurator()
|
||||||
|
{
|
||||||
|
return DefaultClientEndpointConfigurator.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Decoder>> getDecoders()
|
||||||
|
{
|
||||||
|
return decoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Encoder>> getEncoders()
|
||||||
|
{
|
||||||
|
return encoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Extension> getExtensions()
|
||||||
|
{
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getPreferredSubprotocols()
|
||||||
|
{
|
||||||
|
return preferredSubprotocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getUserProperties()
|
||||||
|
{
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,11 +48,14 @@ import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEventDriverFactory;
|
||||||
public class JettyWebSocketContainer implements WebSocketContainer
|
public class JettyWebSocketContainer implements WebSocketContainer
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(JettyWebSocketContainer.class);
|
private static final Logger LOG = Log.getLogger(JettyWebSocketContainer.class);
|
||||||
|
private final DecoderMetadataFactory decoderMetadataFactory;
|
||||||
private WebSocketClient client;
|
private WebSocketClient client;
|
||||||
private AtomicLong idgen = new AtomicLong(0);
|
private AtomicLong idgen = new AtomicLong(0);
|
||||||
|
|
||||||
public JettyWebSocketContainer()
|
public JettyWebSocketContainer()
|
||||||
{
|
{
|
||||||
|
decoderMetadataFactory = new DecoderMetadataFactory();
|
||||||
|
|
||||||
client = new WebSocketClient();
|
client = new WebSocketClient();
|
||||||
client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy(),this));
|
client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy(),this));
|
||||||
|
|
||||||
|
@ -134,6 +137,11 @@ public class JettyWebSocketContainer implements WebSocketContainer
|
||||||
return connect(annotatedEndpointInstance,null,path);
|
return connect(annotatedEndpointInstance,null,path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DecoderMetadataFactory getDecoderMetadataFactory()
|
||||||
|
{
|
||||||
|
return decoderMetadataFactory;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDefaultAsyncSendTimeout()
|
public long getDefaultAsyncSendTimeout()
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,7 @@ import javax.websocket.WebSocketContainer;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||||
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
|
||||||
|
|
||||||
public class JsrSession implements Session
|
public class JsrSession implements Session
|
||||||
{
|
{
|
||||||
|
@ -49,7 +50,8 @@ public class JsrSession implements Session
|
||||||
private Map<String, List<String>> jsrParameterMap;
|
private Map<String, List<String>> jsrParameterMap;
|
||||||
private Map<String, String> pathParameters = new HashMap<>();
|
private Map<String, String> pathParameters = new HashMap<>();
|
||||||
private Map<String, Object> userProperties;
|
private Map<String, Object> userProperties;
|
||||||
private Set<MessageHandler> messageHandlers;
|
private Decoders decoders;
|
||||||
|
private MessageHandlers messageHandlers;
|
||||||
private JsrAsyncRemote asyncRemote;
|
private JsrAsyncRemote asyncRemote;
|
||||||
private JsrBasicRemote basicRemote;
|
private JsrBasicRemote basicRemote;
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ public class JsrSession implements Session
|
||||||
@Override
|
@Override
|
||||||
public void addMessageHandler(MessageHandler listener) throws IllegalStateException
|
public void addMessageHandler(MessageHandler listener) throws IllegalStateException
|
||||||
{
|
{
|
||||||
messageHandlers.add(listener);
|
this.messageHandlers.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -131,7 +133,12 @@ public class JsrSession implements Session
|
||||||
@Override
|
@Override
|
||||||
public Set<MessageHandler> getMessageHandlers()
|
public Set<MessageHandler> getMessageHandlers()
|
||||||
{
|
{
|
||||||
return messageHandlers;
|
return messageHandlers.getUnmodifiableHandlerSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageHandlerWrapper getMessageHandlerWrapper(MessageType msgType)
|
||||||
|
{
|
||||||
|
return messageHandlers.getWrapper(msgType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadata;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadataFactory;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Facade around {@link MessageHandlerMetadataFactory} with {@link MessageType} tracking and enforced JSR-356 PFD1 rules and limits around websocket message
|
||||||
|
* type
|
||||||
|
*/
|
||||||
|
public class MessageHandlers
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(MessageHandlers.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for MessageHandlerMetadata instances.
|
||||||
|
*/
|
||||||
|
private MessageHandlerMetadataFactory factory;
|
||||||
|
/**
|
||||||
|
* Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()}
|
||||||
|
*/
|
||||||
|
private final MessageHandlerWrapper wrappers[];
|
||||||
|
|
||||||
|
public MessageHandlers()
|
||||||
|
{
|
||||||
|
this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(MessageHandler handler)
|
||||||
|
{
|
||||||
|
assertFactoryDefined();
|
||||||
|
Objects.requireNonNull(handler,"MessageHandler cannot be null");
|
||||||
|
|
||||||
|
synchronized (wrappers)
|
||||||
|
{
|
||||||
|
for (MessageHandlerMetadata metadata : factory.getMetadata(handler.getClass()))
|
||||||
|
{
|
||||||
|
MessageType key = metadata.getMessageType();
|
||||||
|
MessageHandlerWrapper other = wrappers[key.ordinal()];
|
||||||
|
if (other != null)
|
||||||
|
{
|
||||||
|
StringBuilder err = new StringBuilder();
|
||||||
|
err.append("Encountered duplicate MessageHandler handling message type <");
|
||||||
|
err.append(metadata.getMessageType().name());
|
||||||
|
err.append(">, ").append(metadata.getHandlerClass().getName());
|
||||||
|
err.append("<");
|
||||||
|
err.append(metadata.getMessageClass().getName());
|
||||||
|
err.append("> and ");
|
||||||
|
err.append(other.getMetadata().getHandlerClass().getName());
|
||||||
|
err.append("<");
|
||||||
|
err.append(other.getMetadata().getMessageClass().getName());
|
||||||
|
err.append("> both implement this message type");
|
||||||
|
throw new IllegalStateException(err.toString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DecoderWrapper decoder = factory.getDecoderWrapper(metadata.getMessageClass());
|
||||||
|
MessageHandlerWrapper wrapper = new MessageHandlerWrapper(handler,metadata,decoder);
|
||||||
|
wrappers[key.ordinal()] = wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFactoryDefined()
|
||||||
|
{
|
||||||
|
if (this.factory == null)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("MessageHandlerMetadataFactory has not been set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageHandlerMetadataFactory getFactory()
|
||||||
|
{
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<MessageHandler> getUnmodifiableHandlerSet()
|
||||||
|
{
|
||||||
|
Set<MessageHandler> ret = new HashSet<>();
|
||||||
|
for (MessageHandlerWrapper wrapper : wrappers)
|
||||||
|
{
|
||||||
|
if (wrapper == null)
|
||||||
|
{
|
||||||
|
// skip empty
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret.add(wrapper.getHandler());
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageHandlerWrapper getWrapper(MessageType msgType)
|
||||||
|
{
|
||||||
|
synchronized (wrappers)
|
||||||
|
{
|
||||||
|
return wrappers[msgType.ordinal()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(MessageHandler handler)
|
||||||
|
{
|
||||||
|
assertFactoryDefined();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (MessageHandlerMetadata metadata : factory.getMetadata(handler.getClass()))
|
||||||
|
{
|
||||||
|
wrappers[metadata.getMessageType().ordinal()] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalStateException e)
|
||||||
|
{
|
||||||
|
LOG.warn("Unable to identify MessageHandler: " + handler.getClass().getName(),e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFactory(MessageHandlerMetadataFactory factory)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic Message Type enum.
|
||||||
|
* <p>
|
||||||
|
* The list of options mirrors the registration limits for "websocket message type" defined in JSR-356 / PFD1 section 2.1.3 "Receiving Messages".
|
||||||
|
*/
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
TEXT,
|
||||||
|
BINARY,
|
||||||
|
PONG;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.websocket.ClientEndpointConfig;
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.Encoder;
|
||||||
|
import javax.websocket.Extension;
|
||||||
|
|
||||||
|
public class SimpleClientEndpointConfig implements ClientEndpointConfig
|
||||||
|
{
|
||||||
|
public static class DummyConfigurator extends ClientEndpointConfig.Configurator
|
||||||
|
{
|
||||||
|
public static final DummyConfigurator INSTANCE = new DummyConfigurator();
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
private Configurator configurator;
|
||||||
|
private List<Class<? extends Decoder>> decoders;
|
||||||
|
private List<Class<? extends Encoder>> encoders;
|
||||||
|
private List<Extension> extensions;
|
||||||
|
private List<String> preferredSubprotocols;
|
||||||
|
private Map<String, Object> userProperties;
|
||||||
|
|
||||||
|
public SimpleClientEndpointConfig()
|
||||||
|
{
|
||||||
|
this.configurator = DummyConfigurator.INSTANCE;
|
||||||
|
this.decoders = new ArrayList<>();
|
||||||
|
this.encoders = new ArrayList<>();
|
||||||
|
this.extensions = new ArrayList<>();
|
||||||
|
this.preferredSubprotocols = new ArrayList<>();
|
||||||
|
this.userProperties = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDecoder(Class<? extends Decoder> decoderClass)
|
||||||
|
{
|
||||||
|
this.decoders.add(decoderClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configurator getConfigurator()
|
||||||
|
{
|
||||||
|
return configurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Decoder>> getDecoders()
|
||||||
|
{
|
||||||
|
return decoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<? extends Encoder>> getEncoders()
|
||||||
|
{
|
||||||
|
return encoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Extension> getExtensions()
|
||||||
|
{
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getPreferredSubprotocols()
|
||||||
|
{
|
||||||
|
return preferredSubprotocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getUserProperties()
|
||||||
|
{
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,19 +23,10 @@ import java.lang.reflect.Method;
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.OnMessage;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||||
|
|
||||||
public interface IJsrMethod
|
public interface IJsrMethod
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* There can only be 1 of each kind of MessageType in a class
|
|
||||||
*/
|
|
||||||
public static enum MessageType
|
|
||||||
{
|
|
||||||
UNKNOWN,
|
|
||||||
TEXT,
|
|
||||||
BINARY,
|
|
||||||
PONG
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate that partial message support is desired
|
* Indicate that partial message support is desired
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -25,7 +25,6 @@ import javax.websocket.Decoder;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
|
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
||||||
|
|
||||||
public abstract class JsrCallable extends CallableMethod
|
public abstract class JsrCallable extends CallableMethod
|
||||||
|
@ -136,18 +135,6 @@ public abstract class JsrCallable extends CallableMethod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecoder(Class<? extends Decoder> decoderClass) throws InvalidSignatureException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.decoder = decoderClass.newInstance();
|
|
||||||
}
|
|
||||||
catch (InstantiationException | IllegalAccessException e)
|
|
||||||
{
|
|
||||||
throw new InvalidSignatureException("Unable to instantiate Decoder: " + decoderClass,e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDecoder(Decoder decoder)
|
public void setDecoder(Decoder decoder)
|
||||||
{
|
{
|
||||||
this.decoder = decoder;
|
this.decoder = decoder;
|
||||||
|
|
|
@ -26,9 +26,6 @@ import javax.websocket.OnError;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.OnMessage;
|
||||||
import javax.websocket.OnOpen;
|
import javax.websocket.OnOpen;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.Decoders;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.encoders.Encoders;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static reference to a specific annotated classes metadata.
|
* Static reference to a specific annotated classes metadata.
|
||||||
*
|
*
|
||||||
|
@ -42,16 +39,6 @@ public abstract class JsrMetadata<T extends Annotation>
|
||||||
*/
|
*/
|
||||||
public final Class<?> pojo;
|
public final Class<?> pojo;
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoders declared as part of annotations
|
|
||||||
*/
|
|
||||||
public Decoders decoders;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoders declared as part of annotations
|
|
||||||
*/
|
|
||||||
public Encoders encoders;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callable for @{@link OnOpen} annotation.
|
* Callable for @{@link OnOpen} annotation.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,17 +22,18 @@ import javax.websocket.Decoder;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.OnMessage;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.DecoderRef;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Param handling for Binary @{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Binary} or {@link Decoder.BinaryStream}
|
* Param handling for Binary @{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Binary} or {@link Decoder.BinaryStream}
|
||||||
*/
|
*/
|
||||||
public class JsrParamIdBinaryDecoder extends JsrParamIdOnMessage implements IJsrParamId
|
public class JsrParamIdBinaryDecoder extends JsrParamIdOnMessage implements IJsrParamId
|
||||||
{
|
{
|
||||||
private final DecoderRef ref;
|
private final DecoderWrapper ref;
|
||||||
|
private Class<?> supportedType;
|
||||||
|
|
||||||
public JsrParamIdBinaryDecoder(DecoderRef ref)
|
public JsrParamIdBinaryDecoder(DecoderWrapper ref)
|
||||||
{
|
{
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ public class JsrParamIdBinaryDecoder extends JsrParamIdOnMessage implements IJsr
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
|
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
|
||||||
{
|
{
|
||||||
if (param.type.isAssignableFrom(ref.getType()))
|
if (param.type.isAssignableFrom(supportedType))
|
||||||
{
|
{
|
||||||
assertPartialMessageSupportDisabled(param,callable);
|
assertPartialMessageSupportDisabled(param,callable);
|
||||||
param.bind(Role.MESSAGE_BINARY);
|
param.bind(Role.MESSAGE_BINARY);
|
||||||
|
|
|
@ -22,28 +22,30 @@ import javax.websocket.Decoder;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.OnMessage;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.DecoderRef;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Param handling for Text @{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Text} or {@link Decoder.TextStream}
|
* Param handling for Text @{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Text} or {@link Decoder.TextStream}
|
||||||
*/
|
*/
|
||||||
public class JsrParamIdTextDecoder extends JsrParamIdOnMessage implements IJsrParamId
|
public class JsrParamIdTextDecoder extends JsrParamIdOnMessage implements IJsrParamId
|
||||||
{
|
{
|
||||||
private final DecoderRef ref;
|
private final DecoderWrapper ref;
|
||||||
|
private Class<?> supportedType;
|
||||||
|
|
||||||
public JsrParamIdTextDecoder(DecoderRef ref)
|
public JsrParamIdTextDecoder(DecoderWrapper ref)
|
||||||
{
|
{
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
|
this.supportedType = ref.getMetadata().getObjectType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
|
public boolean process(Param param, JsrCallable callable) throws InvalidSignatureException
|
||||||
{
|
{
|
||||||
if (param.type.isAssignableFrom(ref.getType()))
|
if (param.type.isAssignableFrom(supportedType))
|
||||||
{
|
{
|
||||||
assertPartialMessageSupportDisabled(param,callable);
|
assertPartialMessageSupportDisabled(param,callable);
|
||||||
param.bind(Role.MESSAGE_TEXT_STREAM);
|
param.bind(Role.MESSAGE_TEXT_STREAM); // TODO: is this sane? for Text & TextStream ?
|
||||||
callable.setDecoder(ref.getDecoder());
|
callable.setDecoder(ref.getDecoder());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.decoders;
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
import javax.websocket.EndpointConfig;
|
import javax.websocket.EndpointConfig;
|
||||||
|
|
||||||
public class AbstractDecoder implements Decoder
|
public abstract class AbstractDecoder implements Decoder
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void destroy()
|
public void destroy()
|
||||||
|
|
|
@ -1,192 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// All rights reserved. This program and the accompanying materials
|
|
||||||
// are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
// and Apache License v2.0 which accompanies this distribution.
|
|
||||||
//
|
|
||||||
// The Eclipse Public License is available at
|
|
||||||
// http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
//
|
|
||||||
// The Apache License v2.0 is available at
|
|
||||||
// http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
//
|
|
||||||
// You may elect to redistribute this code under either of these licenses.
|
|
||||||
// ========================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356.decoders;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.websocket.Decoder;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.ConfigurationException;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.utils.DeploymentTypeUtils;
|
|
||||||
|
|
||||||
public class Decoders implements Iterable<DecoderRef>
|
|
||||||
{
|
|
||||||
public static List<ParameterizedType> getDecoderInterfaces(Class<? extends Decoder> decoder)
|
|
||||||
{
|
|
||||||
List<ParameterizedType> ret = new ArrayList<>();
|
|
||||||
for (Type type : decoder.getGenericInterfaces())
|
|
||||||
{
|
|
||||||
if (!(type instanceof ParameterizedType))
|
|
||||||
{
|
|
||||||
continue; // skip
|
|
||||||
}
|
|
||||||
|
|
||||||
ParameterizedType ptype = (ParameterizedType)type;
|
|
||||||
if (DeploymentTypeUtils.isAssignable(type,Decoder.Text.class) || DeploymentTypeUtils.isAssignable(type,Decoder.TextStream.class)
|
|
||||||
|| DeploymentTypeUtils.isAssignable(type,Decoder.Binary.class) || DeploymentTypeUtils.isAssignable(type,Decoder.BinaryStream.class))
|
|
||||||
{
|
|
||||||
ret.add(ptype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<DecoderRef> decoders;
|
|
||||||
|
|
||||||
public Decoders()
|
|
||||||
{
|
|
||||||
this.decoders = new ArrayList<>();
|
|
||||||
|
|
||||||
// Default TEXT Message Decoders
|
|
||||||
add(new DecoderRef(Boolean.class,BooleanDecoder.class));
|
|
||||||
add(new DecoderRef(Byte.class,ByteDecoder.class));
|
|
||||||
add(new DecoderRef(Character.class,CharacterDecoder.class));
|
|
||||||
add(new DecoderRef(Double.class,DoubleDecoder.class));
|
|
||||||
add(new DecoderRef(Float.class,FloatDecoder.class));
|
|
||||||
add(new DecoderRef(Integer.class,IntegerDecoder.class));
|
|
||||||
add(new DecoderRef(Long.class,LongDecoder.class));
|
|
||||||
add(new DecoderRef(Short.class,ShortDecoder.class));
|
|
||||||
add(new DecoderRef(String.class,StringDecoder.class));
|
|
||||||
|
|
||||||
// Default BINARY Message Decoders
|
|
||||||
add(new DecoderRef(ByteBuffer.class,ByteBufferDecoder.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Decoders(Class<? extends Decoder>[] decoderClasses)
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
|
|
||||||
if (decoderClasses != null)
|
|
||||||
{
|
|
||||||
// now add user provided
|
|
||||||
for (Class<? extends Decoder> decoder : decoderClasses)
|
|
||||||
{
|
|
||||||
add(decoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Decoders(List<Class<? extends Decoder>> decoderClasses)
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
|
|
||||||
if (decoderClasses != null)
|
|
||||||
{
|
|
||||||
// now add user provided
|
|
||||||
for (Class<? extends Decoder> decoder : decoderClasses)
|
|
||||||
{
|
|
||||||
add(decoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(Class<? extends Decoder> decoder)
|
|
||||||
{
|
|
||||||
for (ParameterizedType idecoder : getDecoderInterfaces(decoder))
|
|
||||||
{
|
|
||||||
Type handledTypes[] = idecoder.getActualTypeArguments();
|
|
||||||
if (handledTypes == null)
|
|
||||||
{
|
|
||||||
throw new InvalidSignatureException(decoder + " has invalid signature for " + idecoder + " Generic type is null");
|
|
||||||
}
|
|
||||||
if (handledTypes.length != 1)
|
|
||||||
{
|
|
||||||
throw new InvalidSignatureException(decoder + " has invalid signature for " + idecoder + " - multi-value generic types not supported");
|
|
||||||
}
|
|
||||||
Type handledType = handledTypes[0];
|
|
||||||
if (handledType instanceof Class<?>)
|
|
||||||
{
|
|
||||||
Class<?> handler = (Class<?>)handledType;
|
|
||||||
add(handler,decoder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidSignatureException(decoder + " has invalid signature for " + idecoder + " - only java.lang.Class based generics supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(Class<?> handler, Class<? extends Decoder> decoder)
|
|
||||||
{
|
|
||||||
// verify that we are not adding a duplicate
|
|
||||||
for (DecoderRef ref : decoders)
|
|
||||||
{
|
|
||||||
if (DeploymentTypeUtils.isAssignableClass(handler,ref.getType()))
|
|
||||||
{
|
|
||||||
StringBuilder err = new StringBuilder();
|
|
||||||
err.append("Duplicate Decoder handling for type ");
|
|
||||||
err.append(ref.getType());
|
|
||||||
err.append(": found in ");
|
|
||||||
err.append(ref.getDecoder()).append(" and ");
|
|
||||||
err.append(decoder);
|
|
||||||
throw new ConfigurationException(err.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add entry
|
|
||||||
this.decoders.add(new DecoderRef(handler,decoder));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(DecoderRef ref)
|
|
||||||
{
|
|
||||||
this.decoders.add(ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Decoder getDecoder(Class<?> type)
|
|
||||||
{
|
|
||||||
Class<?> targetType = type;
|
|
||||||
if (targetType.isPrimitive())
|
|
||||||
{
|
|
||||||
targetType = DeploymentTypeUtils.getPrimitiveClass(targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DecoderRef ref : decoders)
|
|
||||||
{
|
|
||||||
if (DeploymentTypeUtils.isAssignable(targetType,ref.getType()))
|
|
||||||
{
|
|
||||||
return instantiate(ref.getDecoder());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidSignatureException("Unable to find appropriate Decoder for type: " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Decoder instantiate(Class<? extends Decoder> decoderClass)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return decoderClass.newInstance();
|
|
||||||
}
|
|
||||||
catch (InstantiationException | IllegalAccessException e)
|
|
||||||
{
|
|
||||||
throw new ConfigurationException("Unable to instantiate Decoder: " + decoderClass,e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<DecoderRef> iterator()
|
|
||||||
{
|
|
||||||
return decoders.iterator();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -128,7 +128,7 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
|
||||||
if (handled && (activeMessage != null))
|
if (handled && (activeMessage != null))
|
||||||
{
|
{
|
||||||
LOG.debug("Appending Binary Message");
|
LOG.debug("Appending Binary Message");
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
@ -284,7 +284,7 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
|
||||||
if (handled && (activeMessage != null))
|
if (handled && (activeMessage != null))
|
||||||
{
|
{
|
||||||
LOG.debug("Appending Text Message");
|
LOG.debug("Appending Text Message");
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
@ -317,10 +317,12 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
|
||||||
@Override
|
@Override
|
||||||
public void openSession(WebSocketSession session)
|
public void openSession(WebSocketSession session)
|
||||||
{
|
{
|
||||||
super.openSession(session);
|
|
||||||
String id = container.getNextId();
|
String id = container.getNextId();
|
||||||
this.jsrsession = new JsrSession(container,session,id);
|
this.jsrsession = new JsrSession(container,session,id);
|
||||||
|
// Initialize the events
|
||||||
this.events.init(jsrsession);
|
this.events.init(jsrsession);
|
||||||
|
// TODO: Initialize the decoders
|
||||||
|
super.openSession(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.websocket.ClientEndpoint;
|
import javax.websocket.ClientEndpoint;
|
||||||
import javax.websocket.ClientEndpointConfig;
|
import javax.websocket.ClientEndpointConfig;
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
@ -41,7 +42,7 @@ public class JsrClientEndpointImpl implements EventDriverImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventDriver create(Object websocket, WebSocketPolicy policy)
|
public EventDriver create(Object websocket, WebSocketPolicy policy) throws DeploymentException
|
||||||
{
|
{
|
||||||
Object endpoint = websocket;
|
Object endpoint = websocket;
|
||||||
ClientEndpointConfig config = null;
|
ClientEndpointConfig config = null;
|
||||||
|
@ -57,7 +58,7 @@ public class JsrClientEndpointImpl implements EventDriverImpl
|
||||||
JsrClientMetadata basemetadata = cache.get(endpointClass);
|
JsrClientMetadata basemetadata = cache.get(endpointClass);
|
||||||
if (basemetadata == null)
|
if (basemetadata == null)
|
||||||
{
|
{
|
||||||
basemetadata = new JsrClientMetadata(endpointClass);
|
basemetadata = new JsrClientMetadata(container,endpointClass);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(basemetadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(basemetadata);
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
cache.put(endpointClass,basemetadata);
|
cache.put(endpointClass,basemetadata);
|
||||||
|
|
|
@ -21,22 +21,27 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import javax.websocket.ClientEndpoint;
|
import javax.websocket.ClientEndpoint;
|
||||||
|
import javax.websocket.ClientEndpointConfig;
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.Decoders;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DefaultClientEndpointConfig;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
|
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.DecoderRef;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.Decoders;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.encoders.Encoders;
|
|
||||||
|
|
||||||
public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
|
public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
|
||||||
{
|
{
|
||||||
private final ClientEndpoint endpoint;
|
private final ClientEndpoint endpoint;
|
||||||
|
private final ClientEndpointConfig config;
|
||||||
|
private final Decoders decoders;
|
||||||
|
|
||||||
public JsrClientMetadata(Class<?> websocket)
|
public JsrClientMetadata(JettyWebSocketContainer container, Class<?> websocket) throws DeploymentException
|
||||||
{
|
{
|
||||||
super(websocket);
|
super(websocket);
|
||||||
|
|
||||||
|
@ -47,26 +52,26 @@ public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
|
||||||
}
|
}
|
||||||
|
|
||||||
this.endpoint = anno;
|
this.endpoint = anno;
|
||||||
this.encoders = new Encoders(anno.encoders());
|
this.config = new DefaultClientEndpointConfig(anno.decoders(),anno.encoders());
|
||||||
this.decoders = new Decoders(anno.decoders());
|
this.decoders = new Decoders(container.getDecoderMetadataFactory(),config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
|
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
|
||||||
{
|
{
|
||||||
for (DecoderRef ref : decoders)
|
for (DecoderWrapper wrapper : decoders.wrapperSet())
|
||||||
{
|
{
|
||||||
Class<? extends Decoder> decoder = ref.getDecoder();
|
Class<? extends Decoder> decoder = wrapper.getMetadata().getDecoder();
|
||||||
|
|
||||||
if (Decoder.Text.class.isAssignableFrom(decoder) || Decoder.TextStream.class.isAssignableFrom(decoder))
|
if (Decoder.Text.class.isAssignableFrom(decoder) || Decoder.TextStream.class.isAssignableFrom(decoder))
|
||||||
{
|
{
|
||||||
params.add(new JsrParamIdTextDecoder(ref));
|
params.add(new JsrParamIdTextDecoder(wrapper));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Decoder.Binary.class.isAssignableFrom(decoder) || Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
if (Decoder.Binary.class.isAssignableFrom(decoder) || Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
||||||
{
|
{
|
||||||
params.add(new JsrParamIdBinaryDecoder(ref));
|
params.add(new JsrParamIdBinaryDecoder(wrapper));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,133 +23,176 @@ import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.CloseReason;
|
||||||
|
import javax.websocket.CloseReason.CloseCode;
|
||||||
|
import javax.websocket.CloseReason.CloseCodes;
|
||||||
|
import javax.websocket.Endpoint;
|
||||||
|
import javax.websocket.EndpointConfig;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketException;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||||
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
import org.eclipse.jetty.websocket.common.WebSocketSession;
|
||||||
|
import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
|
||||||
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.MessageAppender;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JsrSession;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.BinaryStreamMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.BinaryWholeMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.TextStreamMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.TextWholeMessage;
|
||||||
|
|
||||||
public class JsrEndpointEventDriver implements EventDriver, IJsrSession
|
public class JsrEndpointEventDriver extends AbstractEventDriver implements EventDriver, IJsrSession
|
||||||
{
|
{
|
||||||
|
private final JettyWebSocketContainer container;
|
||||||
|
private final Endpoint endpoint;
|
||||||
|
private JsrSession jsrsession;
|
||||||
|
private EndpointConfig endpointconfig;
|
||||||
|
private MessageAppender activeMessage;
|
||||||
|
private boolean hasCloseBeenCalled = false;
|
||||||
|
|
||||||
|
public JsrEndpointEventDriver(JettyWebSocketContainer container, WebSocketPolicy policy, Endpoint endpoint)
|
||||||
|
{
|
||||||
|
super(policy,endpoint);
|
||||||
|
this.container = container;
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Session getJsrSession()
|
public Session getJsrSession()
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
return this.jsrsession;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebSocketPolicy getPolicy()
|
|
||||||
{
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebSocketSession getSession()
|
|
||||||
{
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void incomingError(WebSocketException e)
|
|
||||||
{
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void incomingFrame(Frame frame)
|
|
||||||
{
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
|
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
if (activeMessage == null)
|
||||||
|
{
|
||||||
|
MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.BINARY);
|
||||||
|
if (wrapper.wantsPartialMessages())
|
||||||
|
{
|
||||||
|
activeMessage = new BinaryPartialMessage(wrapper);
|
||||||
|
}
|
||||||
|
else if (wrapper.wantsStreams())
|
||||||
|
{
|
||||||
|
activeMessage = new BinaryStreamMessage(this,wrapper);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activeMessage = new BinaryWholeMessage(this,wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
|
if (fin)
|
||||||
|
{
|
||||||
|
activeMessage.messageComplete();
|
||||||
|
activeMessage = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBinaryMessage(byte[] data)
|
public void onBinaryMessage(byte[] data)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
/* Ignored, handled by BinaryWholeMessage */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(CloseInfo close)
|
public void onClose(CloseInfo close)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
if (hasCloseBeenCalled)
|
||||||
|
{
|
||||||
|
// avoid duplicate close events (possible when using harsh Session.disconnect())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasCloseBeenCalled = true;
|
||||||
|
|
||||||
|
CloseCode closecode = CloseCodes.getCloseCode(close.getStatusCode());
|
||||||
|
CloseReason closereason = new CloseReason(closecode,close.getReason());
|
||||||
|
endpoint.onClose(this.jsrsession,closereason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnect()
|
public void onConnect()
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
endpoint.onOpen(jsrsession,endpointconfig);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable t)
|
public void onError(Throwable cause)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
endpoint.onError(jsrsession,cause);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFrame(Frame frame)
|
public void onFrame(Frame frame)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
/* Ignored, not supported by JSR-356 */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputStream(InputStream stream)
|
public void onInputStream(InputStream stream)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
/* Ignored, handled by BinaryStreamMessage */
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPong(ByteBuffer buffer)
|
|
||||||
{
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReader(Reader reader)
|
public void onReader(Reader reader)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
/* Ignored, handled by TextStreamMessage */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
|
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
if (activeMessage == null)
|
||||||
|
{
|
||||||
|
MessageHandlerWrapper wrapper = jsrsession.getMessageHandlerWrapper(MessageType.TEXT);
|
||||||
|
if (wrapper.wantsPartialMessages())
|
||||||
|
{
|
||||||
|
activeMessage = new TextPartialMessage(wrapper);
|
||||||
|
}
|
||||||
|
else if (wrapper.wantsStreams())
|
||||||
|
{
|
||||||
|
activeMessage = new TextStreamMessage(this,wrapper);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activeMessage = new TextWholeMessage(this,wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
|
if (fin)
|
||||||
|
{
|
||||||
|
activeMessage.messageComplete();
|
||||||
|
activeMessage = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextMessage(String message)
|
public void onTextMessage(String message)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
/* Ignored, handled by TextWholeMessage */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openSession(WebSocketSession session)
|
public void openSession(WebSocketSession session)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
String id = container.getNextId();
|
||||||
|
this.jsrsession = new JsrSession(container,session,id);
|
||||||
|
// TODO: Initialize the Decoders
|
||||||
|
// TODO: Initialize the MessageHandlers
|
||||||
|
// TODO: Set the Configuration?
|
||||||
|
super.openSession(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,19 +37,36 @@ public class JsrEndpointImpl implements EventDriverImpl
|
||||||
@Override
|
@Override
|
||||||
public EventDriver create(Object websocket, WebSocketPolicy policy)
|
public EventDriver create(Object websocket, WebSocketPolicy policy)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
Object endpoint = websocket;
|
||||||
return null;
|
|
||||||
|
if (endpoint instanceof ConfiguredEndpoint)
|
||||||
|
{
|
||||||
|
// unwrap
|
||||||
|
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
|
||||||
|
endpoint = ce.getEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsrEndpointEventDriver(container,policy,(Endpoint)endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String describeRule()
|
public String describeRule()
|
||||||
{
|
{
|
||||||
return "class extends " + Endpoint.class.getName();
|
return "class extends " + javax.websocket.Endpoint.class.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(Object websocket)
|
public boolean supports(Object websocket)
|
||||||
{
|
{
|
||||||
return (websocket instanceof Endpoint);
|
Object endpoint = websocket;
|
||||||
|
|
||||||
|
if (endpoint instanceof ConfiguredEndpoint)
|
||||||
|
{
|
||||||
|
// unwrap
|
||||||
|
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
|
||||||
|
endpoint = ce.getEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (endpoint instanceof javax.websocket.Endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,9 @@ public class JsrEventDriverFactory extends EventDriverFactory
|
||||||
super(policy);
|
super(policy);
|
||||||
|
|
||||||
clearImplementations();
|
clearImplementations();
|
||||||
|
// Classes that extend javax.websocket.Endpoint
|
||||||
addImplementation(new JsrEndpointImpl(container));
|
addImplementation(new JsrEndpointImpl(container));
|
||||||
|
// Classes annotated with @javax.websocket.ClientEndpoint
|
||||||
addImplementation(new JsrClientEndpointImpl(container));
|
addImplementation(new JsrClientEndpointImpl(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Partial;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.MessageAppender;
|
||||||
|
|
||||||
|
public class BinaryPartialMessage implements MessageAppender
|
||||||
|
{
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Partial<Object> partialHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BinaryPartialMessage(MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.partialHandler = (Partial<Object>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
|
{
|
||||||
|
// No decoders for Partial messages per JSR-356 (PFD1 spec)
|
||||||
|
|
||||||
|
// Supported Partial<> Type #1: ByteBuffer
|
||||||
|
if (msgWrapper.isMessageType(ByteBuffer.class))
|
||||||
|
{
|
||||||
|
partialHandler.onMessage(payload.slice(),isLast);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supported Partial<> Type #2: byte[]
|
||||||
|
if (msgWrapper.isMessageType(byte[].class))
|
||||||
|
{
|
||||||
|
partialHandler.onMessage(BufferUtil.toArray(payload),isLast);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder err = new StringBuilder();
|
||||||
|
err.append(msgWrapper.getHandler().getClass());
|
||||||
|
err.append(" does not implement an expected ");
|
||||||
|
err.append(MessageHandler.Partial.class.getName());
|
||||||
|
err.append(" of type ");
|
||||||
|
err.append(ByteBuffer.class.getName());
|
||||||
|
err.append(" or byte[]");
|
||||||
|
throw new IllegalStateException(err.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageComplete()
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Whole;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.MessageInputStream;
|
||||||
|
|
||||||
|
public class BinaryStreamMessage extends MessageInputStream
|
||||||
|
{
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Whole<InputStream> streamHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BinaryStreamMessage(EventDriver driver, MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
super(driver);
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.streamHandler = (Whole<InputStream>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import javax.websocket.DecodeException;
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.Decoder.Binary;
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Whole;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||||
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
|
||||||
|
public class BinaryWholeMessage extends SimpleBinaryMessage
|
||||||
|
{
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Whole<Object> wholeHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BinaryWholeMessage(EventDriver onEvent, MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
super(onEvent);
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.wholeHandler = (Whole<Object>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void messageComplete()
|
||||||
|
{
|
||||||
|
super.finished = true;
|
||||||
|
|
||||||
|
byte data[] = out.toByteArray();
|
||||||
|
|
||||||
|
DecoderWrapper decoder = msgWrapper.getDecoder();
|
||||||
|
Decoder.Binary<Object> binaryDecoder = (Binary<Object>)decoder.getDecoder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object obj = binaryDecoder.decode(BufferUtil.toBuffer(data));
|
||||||
|
wholeHandler.onMessage(obj);
|
||||||
|
}
|
||||||
|
catch (DecodeException e)
|
||||||
|
{
|
||||||
|
throw new WebSocketException("Unable to decode binary data",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable holder for {@link MessageHandler} metadata, representing a single interface on a message handling class.
|
||||||
|
* <p>
|
||||||
|
* A message handling class can contain more than 1 valid {@link MessageHandler} interface, this will result in multiple {@link MessageHandlerMetadata}
|
||||||
|
* instances, each tracking one of the {@link MessageHandler} interfaces declared.
|
||||||
|
*/
|
||||||
|
public class MessageHandlerMetadata
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The implemented MessageHandler class.
|
||||||
|
* <p>
|
||||||
|
* Commonly a end-user provided class, with 1 or more implemented {@link MessageHandler} interfaces
|
||||||
|
*/
|
||||||
|
private final Class<? extends MessageHandler> handlerClass;
|
||||||
|
/**
|
||||||
|
* Indicator if this is a {@link MessageHandler.Partial} or {@link MessageHandler.Whole} interface.
|
||||||
|
* <p>
|
||||||
|
* True for MessageHandler.Partial, other wise its a MessageHandler.Whole
|
||||||
|
*/
|
||||||
|
private final boolean isPartialSupported;
|
||||||
|
/**
|
||||||
|
* The class type that this specific interface's generic implements.
|
||||||
|
* <p>
|
||||||
|
* Or said another way, the first parameter type on this interface's onMessage() method.
|
||||||
|
*/
|
||||||
|
private final Class<?> messageClass;
|
||||||
|
/**
|
||||||
|
* The 'websocket message type' used for registration limits per JSR-356 / PFD1 section 2.1.3 Receiving Messages
|
||||||
|
*/
|
||||||
|
private final MessageType messageType;
|
||||||
|
|
||||||
|
protected MessageHandlerMetadata(Class<? extends MessageHandler> handlerClass, MessageType messageType, Class<?> messageClass, boolean partial)
|
||||||
|
{
|
||||||
|
this.handlerClass = handlerClass;
|
||||||
|
this.isPartialSupported = partial;
|
||||||
|
this.messageClass = messageClass;
|
||||||
|
this.messageType = messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make equals/hashcode only use messageType (for best result with {@link Session#getMessageHandlers()})
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (this == obj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MessageHandlerMetadata other = (MessageHandlerMetadata)obj;
|
||||||
|
if (messageType != other.messageType)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends MessageHandler> getHandlerClass()
|
||||||
|
{
|
||||||
|
return handlerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getMessageClass()
|
||||||
|
{
|
||||||
|
return messageClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageType getMessageType()
|
||||||
|
{
|
||||||
|
return messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make equals/hashcode only use messageType (for best result with {@link Session#getMessageHandlers()})
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = (prime * result) + ((messageType == null)?0:messageType.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPartialSupported()
|
||||||
|
{
|
||||||
|
return isPartialSupported;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.Decoders;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link MessageHandlerMetadata} objects from a provided {@link MessageHandler} classes.
|
||||||
|
*/
|
||||||
|
public class MessageHandlerMetadataFactory
|
||||||
|
{
|
||||||
|
private final Decoders decoders;
|
||||||
|
|
||||||
|
public MessageHandlerMetadataFactory(Decoders decoders)
|
||||||
|
{
|
||||||
|
this.decoders = decoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> findOnMessageType(Class<? extends MessageHandler> handlerClass, int paramCount)
|
||||||
|
{
|
||||||
|
for (Method method : handlerClass.getMethods())
|
||||||
|
{
|
||||||
|
if ("onMessage".equals(method.getName()))
|
||||||
|
{
|
||||||
|
// make sure we only look for the onMessage method that is relevant
|
||||||
|
Class<?> paramTypes[] = method.getParameterTypes();
|
||||||
|
if (paramTypes == null)
|
||||||
|
{
|
||||||
|
// skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramTypes.length == paramCount)
|
||||||
|
{
|
||||||
|
// found the method we are interested in
|
||||||
|
return paramTypes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecoderWrapper getDecoderWrapper(Class<?> onMessageClass)
|
||||||
|
{
|
||||||
|
return decoders.getDecoderWrapper(onMessageClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MessageHandlerMetadata> getMetadata(Class<? extends MessageHandler> handler) throws IllegalStateException
|
||||||
|
{
|
||||||
|
List<MessageHandlerMetadata> ret = new ArrayList<>();
|
||||||
|
boolean partial = false;
|
||||||
|
if (MessageHandler.Partial.class.isAssignableFrom(handler))
|
||||||
|
{
|
||||||
|
partial = true;
|
||||||
|
Class<?> onMessageClass = getOnMessagePartialType(handler);
|
||||||
|
MessageType onMessageType = identifyMessageType(onMessageClass);
|
||||||
|
ret.add(new MessageHandlerMetadata(handler,onMessageType,onMessageClass,partial));
|
||||||
|
}
|
||||||
|
if (MessageHandler.Whole.class.isAssignableFrom(handler))
|
||||||
|
{
|
||||||
|
partial = false;
|
||||||
|
Class<?> onMessageClass = getOnMessageType(handler);
|
||||||
|
MessageType onMessageType = identifyMessageType(onMessageClass);
|
||||||
|
MessageHandlerMetadata metadata = new MessageHandlerMetadata(handler,onMessageType,onMessageClass,partial);
|
||||||
|
ret.add(metadata);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getOnMessagePartialType(Class<? extends MessageHandler> handlerClass)
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(handlerClass);
|
||||||
|
|
||||||
|
if (MessageHandler.Partial.class.isAssignableFrom(handlerClass))
|
||||||
|
{
|
||||||
|
return findOnMessageType(handlerClass,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getOnMessageType(Class<? extends MessageHandler> handlerClass)
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(handlerClass);
|
||||||
|
|
||||||
|
if (MessageHandler.Whole.class.isAssignableFrom(handlerClass))
|
||||||
|
{
|
||||||
|
return findOnMessageType(handlerClass,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageType identifyMessageType(Class<?> onMessageClass) throws IllegalStateException
|
||||||
|
{
|
||||||
|
DecoderWrapper wrapper = getDecoderWrapper(onMessageClass);
|
||||||
|
return wrapper.getMetadata().getMessageType();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose a {@link MessageHandler} instance along with its associated {@link MessageHandlerMetadata}
|
||||||
|
*/
|
||||||
|
public class MessageHandlerWrapper
|
||||||
|
{
|
||||||
|
private final MessageHandler handler;
|
||||||
|
private final MessageHandlerMetadata metadata;
|
||||||
|
private final DecoderWrapper decoder;
|
||||||
|
|
||||||
|
public MessageHandlerWrapper(MessageHandler handler, MessageHandlerMetadata metadata, DecoderWrapper decoder)
|
||||||
|
{
|
||||||
|
this.handler = handler;
|
||||||
|
this.metadata = metadata;
|
||||||
|
this.decoder = decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecoderWrapper getDecoder()
|
||||||
|
{
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageHandler getHandler()
|
||||||
|
{
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageHandlerMetadata getMetadata()
|
||||||
|
{
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMessageType(Class<?> msgType)
|
||||||
|
{
|
||||||
|
return msgType.isAssignableFrom(metadata.getMessageClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for a onMessage() that wants partial messages.
|
||||||
|
* <p>
|
||||||
|
* This indicates the use of MessageHandler.{@link Partial}.
|
||||||
|
*
|
||||||
|
* @return true for use of MessageHandler.{@link Partial}, false for use of MessageHandler.{@link Whole}
|
||||||
|
*/
|
||||||
|
public boolean wantsPartialMessages()
|
||||||
|
{
|
||||||
|
return metadata.isPartialSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for a onMessage() method that wants MessageHandler.{@link Whole} with a Decoder that is based on {@link Decoder.TextStream} or
|
||||||
|
* {@link Decoder.BinaryStream}
|
||||||
|
*
|
||||||
|
* @return true for Streaming based Decoder, false for normal decoder for whole messages.
|
||||||
|
*/
|
||||||
|
public boolean wantsStreams()
|
||||||
|
{
|
||||||
|
return decoder.getMetadata().isStreamed();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Partial;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.MessageAppender;
|
||||||
|
|
||||||
|
public class TextPartialMessage implements MessageAppender
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Partial<String> partialHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public TextPartialMessage(MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.partialHandler = (Partial<String>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
|
{
|
||||||
|
// No decoders for Partial messages per JSR-356 (PFD1 spec)
|
||||||
|
partialHandler.onMessage(BufferUtil.toUTF8String(payload.slice()),isLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageComplete()
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Whole;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.MessageReader;
|
||||||
|
|
||||||
|
public class TextStreamMessage extends MessageReader
|
||||||
|
{
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Whole<Reader> streamHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public TextStreamMessage(EventDriver driver, MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
super(driver);
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.streamHandler = (Whole<Reader>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import javax.websocket.DecodeException;
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.MessageHandler.Whole;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||||
|
import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
|
||||||
|
public class TextWholeMessage extends SimpleTextMessage
|
||||||
|
{
|
||||||
|
private final MessageHandlerWrapper msgWrapper;
|
||||||
|
private final MessageHandler.Whole<Object> wholeHandler;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public TextWholeMessage(EventDriver onEvent, MessageHandlerWrapper wrapper)
|
||||||
|
{
|
||||||
|
super(onEvent);
|
||||||
|
this.msgWrapper = wrapper;
|
||||||
|
this.wholeHandler = (Whole<Object>)wrapper.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void messageComplete()
|
||||||
|
{
|
||||||
|
finished = true;
|
||||||
|
|
||||||
|
DecoderWrapper decoder = msgWrapper.getDecoder();
|
||||||
|
Decoder.Text<Object> textDecoder = (Decoder.Text<Object>)decoder.getDecoder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object obj = textDecoder.decode(utf.toString());
|
||||||
|
wholeHandler.onMessage(obj);
|
||||||
|
}
|
||||||
|
catch (DecodeException e)
|
||||||
|
{
|
||||||
|
throw new WebSocketException("Unable to decode text data",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,14 +16,14 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356.decoders;
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
import javax.websocket.DeploymentException;
|
import javax.websocket.DeploymentException;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.ConfigurationException;
|
import org.eclipse.jetty.websocket.jsr356.decoders.CharacterDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.samples.DualDecoder;
|
import org.eclipse.jetty.websocket.jsr356.samples.DualDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
|
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
|
||||||
import org.eclipse.jetty.websocket.jsr356.samples.FruitDecoder;
|
import org.eclipse.jetty.websocket.jsr356.samples.FruitDecoder;
|
||||||
|
@ -32,11 +32,14 @@ import org.junit.Test;
|
||||||
|
|
||||||
public class DecodersTest
|
public class DecodersTest
|
||||||
{
|
{
|
||||||
|
private DecoderMetadataFactory factory = new DecoderMetadataFactory();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetTextDecoder_Character() throws DeploymentException
|
public void testGetTextDecoder_Character() throws DeploymentException
|
||||||
{
|
{
|
||||||
Decoders decoders = new Decoders();
|
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
|
||||||
decoders.add(FruitDecoder.class);
|
config.addDecoder(FruitDecoder.class);
|
||||||
|
Decoders decoders = new Decoders(factory,config);
|
||||||
|
|
||||||
Decoder txtDecoder = decoders.getDecoder(Character.class);
|
Decoder txtDecoder = decoders.getDecoder(Character.class);
|
||||||
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
|
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
|
||||||
|
@ -48,11 +51,13 @@ public class DecodersTest
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Decoders decoders = new Decoders();
|
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
|
||||||
decoders.add(DualDecoder.class); // has duplicated support for the same target Type
|
config.addDecoder(DualDecoder.class); // has duplicated support for the same target Type
|
||||||
Assert.fail("Should have thrown ConfigurationException");
|
@SuppressWarnings("unused")
|
||||||
|
Decoders decoders = new Decoders(factory,config);
|
||||||
|
Assert.fail("Should have thrown DeploymentException");
|
||||||
}
|
}
|
||||||
catch (ConfigurationException e)
|
catch (DeploymentException e)
|
||||||
{
|
{
|
||||||
Assert.assertThat("Error Message",e.getMessage(),containsString("Duplicate"));
|
Assert.assertThat("Error Message",e.getMessage(),containsString("Duplicate"));
|
||||||
}
|
}
|
||||||
|
@ -61,8 +66,9 @@ public class DecodersTest
|
||||||
@Test
|
@Test
|
||||||
public void testGetTextDecoder_Fruit() throws DeploymentException
|
public void testGetTextDecoder_Fruit() throws DeploymentException
|
||||||
{
|
{
|
||||||
Decoders decoders = new Decoders();
|
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
|
||||||
decoders.add(FruitDecoder.class);
|
config.addDecoder(FruitDecoder.class);
|
||||||
|
Decoders decoders = new Decoders(factory,config);
|
||||||
|
|
||||||
Decoder txtDecoder = decoders.getDecoder(Fruit.class);
|
Decoder txtDecoder = decoders.getDecoder(Fruit.class);
|
||||||
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
|
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.websocket.Decoder;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderMetadataFactory.DefaultsDecoderFactory;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ByteArrayDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.ByteBufferDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.LongDecoder;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.decoders.StringDecoder;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DefaultsDecoderFactoryTest
|
||||||
|
{
|
||||||
|
private static DefaultsDecoderFactory factory = new DefaultsDecoderFactory();
|
||||||
|
|
||||||
|
private void assertDefaultDecoderType(Class<? extends Decoder> decoder, MessageType expectedMsgType, Type expectedObjType)
|
||||||
|
{
|
||||||
|
List<DecoderMetadata> metadatas = factory.getMetadata(decoder);
|
||||||
|
Assert.assertThat("Metadatas.size",metadatas.size(),is(1));
|
||||||
|
DecoderMetadata metadata = metadatas.get(0);
|
||||||
|
Assert.assertThat("Metadata.messageType",metadata.getMessageType(),is(expectedMsgType));
|
||||||
|
Assert.assertThat("Metadata.objectType",metadata.getObjectType(),is(expectedObjType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetByteArrayDecoder()
|
||||||
|
{
|
||||||
|
assertDefaultDecoderType(ByteArrayDecoder.class,MessageType.BINARY,byte[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetByteBufferDecoder()
|
||||||
|
{
|
||||||
|
assertDefaultDecoderType(ByteBufferDecoder.class,MessageType.BINARY,ByteBuffer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLongDecoder()
|
||||||
|
{
|
||||||
|
assertDefaultDecoderType(LongDecoder.class,MessageType.TEXT,Long.TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStringDecoder()
|
||||||
|
{
|
||||||
|
assertDefaultDecoderType(StringDecoder.class,MessageType.TEXT,String.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356;
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.websocket.CloseReason;
|
import javax.websocket.CloseReason;
|
||||||
|
@ -25,6 +27,8 @@ import javax.websocket.Endpoint;
|
||||||
import javax.websocket.EndpointConfig;
|
import javax.websocket.EndpointConfig;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic Echo Client from extended Endpoint
|
* Basic Echo Client from extended Endpoint
|
||||||
*/
|
*/
|
||||||
|
@ -43,6 +47,8 @@ public class EndpointEchoClient extends Endpoint
|
||||||
public void onOpen(Session session, EndpointConfig config)
|
public void onOpen(Session session, EndpointConfig config)
|
||||||
{
|
{
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
Assert.assertThat("Session",session,notNullValue());
|
||||||
|
Assert.assertThat("EndpointConfig",config,notNullValue());
|
||||||
this.session.addMessageHandler(textCapture);
|
this.session.addMessageHandler(textCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356;
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -82,6 +85,7 @@ public class EndpointEchoTest
|
||||||
{
|
{
|
||||||
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
||||||
EndpointEchoClient echoer = new EndpointEchoClient();
|
EndpointEchoClient echoer = new EndpointEchoClient();
|
||||||
|
Assert.assertThat(echoer,instanceOf(javax.websocket.Endpoint.class));
|
||||||
Session session = container.connectToServer(echoer,serverUri);
|
Session session = container.connectToServer(echoer,serverUri);
|
||||||
session.getBasicRemote().sendText("Echo");
|
session.getBasicRemote().sendText("Echo");
|
||||||
echoer.textCapture.messageQueue.awaitMessages(1,1000,TimeUnit.MILLISECONDS);
|
echoer.textCapture.messageQueue.awaitMessages(1,1000,TimeUnit.MILLISECONDS);
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayWholeHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.ByteBufferPartialHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.LongMessageHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.StringWholeHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadataFactory;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MessageHandlersTest
|
||||||
|
{
|
||||||
|
private MessageHandlerMetadataFactory factory;
|
||||||
|
private Decoders decoders;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() throws DeploymentException
|
||||||
|
{
|
||||||
|
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
|
||||||
|
DecoderMetadataFactory metadataFactory = new DecoderMetadataFactory();
|
||||||
|
decoders = new Decoders(metadataFactory,config);
|
||||||
|
factory = new MessageHandlerMetadataFactory(decoders);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBinaryHandler() throws DeploymentException
|
||||||
|
{
|
||||||
|
MessageHandlers mhs = new MessageHandlers();
|
||||||
|
mhs.setFactory(factory);
|
||||||
|
mhs.add(new ByteBufferPartialHandler());
|
||||||
|
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.BINARY);
|
||||||
|
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteBufferPartialHandler.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBothHandler() throws DeploymentException
|
||||||
|
{
|
||||||
|
MessageHandlers mhs = new MessageHandlers();
|
||||||
|
mhs.setFactory(factory);
|
||||||
|
mhs.add(new StringWholeHandler());
|
||||||
|
mhs.add(new ByteArrayWholeHandler());
|
||||||
|
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
|
||||||
|
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
|
||||||
|
wrapper = mhs.getWrapper(MessageType.BINARY);
|
||||||
|
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteArrayWholeHandler.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetTextHandler() throws DeploymentException
|
||||||
|
{
|
||||||
|
MessageHandlers mhs = new MessageHandlers();
|
||||||
|
mhs.setFactory(factory);
|
||||||
|
mhs.add(new StringWholeHandler());
|
||||||
|
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
|
||||||
|
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void testNoFactoryAdd()
|
||||||
|
{
|
||||||
|
MessageHandlers mhs = new MessageHandlers();
|
||||||
|
mhs.add(new ByteBufferPartialHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceTextHandler() throws DeploymentException
|
||||||
|
{
|
||||||
|
MessageHandlers mhs = new MessageHandlers();
|
||||||
|
mhs.setFactory(factory);
|
||||||
|
MessageHandler oldText = new StringWholeHandler();
|
||||||
|
mhs.add(oldText); // add a TEXT handler
|
||||||
|
mhs.add(new ByteArrayWholeHandler()); // add BINARY handler
|
||||||
|
mhs.remove(oldText); // remove original TEXT handler
|
||||||
|
mhs.add(new LongMessageHandler()); // add new TEXT handler
|
||||||
|
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.BINARY);
|
||||||
|
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteArrayWholeHandler.class));
|
||||||
|
wrapper = mhs.getWrapper(MessageType.TEXT);
|
||||||
|
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(LongMessageHandler.class));
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,8 +21,10 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import javax.websocket.CloseReason;
|
import javax.websocket.CloseReason;
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
|
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSessionSocket;
|
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSessionSocket;
|
||||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSocket;
|
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSocket;
|
||||||
|
@ -31,6 +33,8 @@ import org.junit.Test;
|
||||||
|
|
||||||
public class ClientAnnotatedEndpointScannerTest
|
public class ClientAnnotatedEndpointScannerTest
|
||||||
{
|
{
|
||||||
|
private static JettyWebSocketContainer container = new JettyWebSocketContainer();
|
||||||
|
|
||||||
private void assertHasCallable(String msg, CallableMethod callable, Class<?>... expectedParameters)
|
private void assertHasCallable(String msg, CallableMethod callable, Class<?>... expectedParameters)
|
||||||
{
|
{
|
||||||
Assert.assertThat(msg,callable,notNullValue());
|
Assert.assertThat(msg,callable,notNullValue());
|
||||||
|
@ -45,9 +49,9 @@ public class ClientAnnotatedEndpointScannerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScan_BasicOpenClose()
|
public void testScan_BasicOpenClose() throws DeploymentException
|
||||||
{
|
{
|
||||||
JsrClientMetadata metadata = new JsrClientMetadata(BasicOpenCloseSocket.class);
|
JsrClientMetadata metadata = new JsrClientMetadata(container,BasicOpenCloseSocket.class);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
|
|
||||||
|
@ -58,9 +62,9 @@ public class ClientAnnotatedEndpointScannerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScan_BasicSessionOpenClose()
|
public void testScan_BasicSessionOpenClose() throws DeploymentException
|
||||||
{
|
{
|
||||||
JsrClientMetadata metadata = new JsrClientMetadata(BasicOpenCloseSessionSocket.class);
|
JsrClientMetadata metadata = new JsrClientMetadata(container,BasicOpenCloseSessionSocket.class);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import javax.websocket.CloseReason;
|
||||||
import javax.websocket.PongMessage;
|
import javax.websocket.PongMessage;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
||||||
|
@ -82,6 +83,8 @@ public class ClientAnnotatedEndpointScanner_GoodSignaturesTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static JettyWebSocketContainer container = new JettyWebSocketContainer();
|
||||||
|
|
||||||
@Parameters
|
@Parameters
|
||||||
public static Collection<Case[]> data() throws Exception
|
public static Collection<Case[]> data() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -136,7 +139,7 @@ public class ClientAnnotatedEndpointScanner_GoodSignaturesTest
|
||||||
@Test
|
@Test
|
||||||
public void testScan_Basic() throws Exception
|
public void testScan_Basic() throws Exception
|
||||||
{
|
{
|
||||||
JsrClientMetadata metadata = new JsrClientMetadata(testcase.pojo);
|
JsrClientMetadata metadata = new JsrClientMetadata(container,testcase.pojo);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
import javax.websocket.OnClose;
|
import javax.websocket.OnClose;
|
||||||
import javax.websocket.OnError;
|
import javax.websocket.OnError;
|
||||||
import javax.websocket.OnOpen;
|
import javax.websocket.OnOpen;
|
||||||
|
@ -32,6 +33,7 @@ import javax.websocket.OnOpen;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidCloseIntSocket;
|
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidCloseIntSocket;
|
||||||
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidErrorErrorSocket;
|
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidErrorErrorSocket;
|
||||||
|
@ -53,6 +55,7 @@ import org.junit.runners.Parameterized.Parameters;
|
||||||
public class ClientAnnotatedEndpointScanner_InvalidSignaturesTest
|
public class ClientAnnotatedEndpointScanner_InvalidSignaturesTest
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ClientAnnotatedEndpointScanner_InvalidSignaturesTest.class);
|
private static final Logger LOG = Log.getLogger(ClientAnnotatedEndpointScanner_InvalidSignaturesTest.class);
|
||||||
|
private static JettyWebSocketContainer container = new JettyWebSocketContainer();
|
||||||
|
|
||||||
@Parameters
|
@Parameters
|
||||||
public static Collection<Class<?>[]> data()
|
public static Collection<Class<?>[]> data()
|
||||||
|
@ -89,9 +92,9 @@ public class ClientAnnotatedEndpointScanner_InvalidSignaturesTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScan_InvalidSignature()
|
public void testScan_InvalidSignature() throws DeploymentException
|
||||||
{
|
{
|
||||||
JsrClientMetadata metadata = new JsrClientMetadata(pojo);
|
JsrClientMetadata metadata = new JsrClientMetadata(container,pojo);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class BaseMessageHandler implements MessageHandler.Whole<String>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(String message)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ByteArrayPartialHandler implements MessageHandler.Partial<byte[]>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(byte[] partialMessage, boolean last)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ByteArrayWholeHandler implements MessageHandler.Whole<byte[]>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(byte[] message)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ByteBufferPartialHandler implements MessageHandler.Partial<ByteBuffer>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(ByteBuffer partialMessage, boolean last)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ByteBufferWholeHandler implements MessageHandler.Whole<ByteBuffer>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(ByteBuffer message)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A particularly annoying type of MessageHandler. One defining 2 implementations.
|
||||||
|
*/
|
||||||
|
public class ComboMessageHandler implements MessageHandler.Whole<String>, MessageHandler.Partial<ByteBuffer>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(ByteBuffer partialMessage, boolean last)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(String message)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ExtendedMessageHandler extends BaseMessageHandler implements MessageHandler.Partial<ByteBuffer>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(ByteBuffer partialMessage, boolean last)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class InputStreamWholeHandler implements MessageHandler.Whole<InputStream>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(InputStream stream)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class LongMessageHandler implements MessageHandler.Whole<Long>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(Long message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class ReaderWholeHandler implements MessageHandler.Whole<Reader>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(Reader reader)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class StringPartialHandler implements MessageHandler.Partial<String>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(String partialMessage, boolean last)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.handlers;
|
||||||
|
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
|
||||||
|
public class StringWholeHandler implements MessageHandler.Whole<String>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onMessage(String message)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.websocket.jsr356.messages;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderMetadataFactory;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.Decoders;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.MessageType;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.SimpleClientEndpointConfig;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayPartialHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.handlers.StringPartialHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadata;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadataFactory;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MessageHandlerMetadataFactoryTest
|
||||||
|
{
|
||||||
|
private MessageHandlerMetadataFactory factory;
|
||||||
|
private Decoders decoders;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() throws DeploymentException
|
||||||
|
{
|
||||||
|
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
|
||||||
|
DecoderMetadataFactory metadataFactory = new DecoderMetadataFactory();
|
||||||
|
decoders = new Decoders(metadataFactory,config);
|
||||||
|
factory = new MessageHandlerMetadataFactory(decoders);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testByteArrayPartial() throws DeploymentException
|
||||||
|
{
|
||||||
|
List<MessageHandlerMetadata> metadatas = factory.getMetadata(ByteArrayPartialHandler.class);
|
||||||
|
Assert.assertThat("Metadata.list.size",metadatas.size(),is(1));
|
||||||
|
|
||||||
|
MessageHandlerMetadata metadata = metadatas.get(0);
|
||||||
|
Assert.assertThat("Message Type",metadata.getMessageType(),is(MessageType.BINARY));
|
||||||
|
Assert.assertThat("Message Class",metadata.getMessageClass(),is((Type)byte[].class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStringPartial() throws DeploymentException
|
||||||
|
{
|
||||||
|
List<MessageHandlerMetadata> metadatas = factory.getMetadata(StringPartialHandler.class);
|
||||||
|
Assert.assertThat("Metadata.list.size",metadatas.size(),is(1));
|
||||||
|
|
||||||
|
MessageHandlerMetadata metadata = metadatas.get(0);
|
||||||
|
Assert.assertThat("Message Type",metadata.getMessageType(),is(MessageType.TEXT));
|
||||||
|
Assert.assertThat("Message Class",metadata.getMessageClass(),is((Type)String.class));
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,25 +18,30 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.websocket.jsr356.server;
|
package org.eclipse.jetty.websocket.jsr356.server;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
import javax.websocket.server.ServerEndpointConfig;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.Decoders;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
|
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.DecoderRef;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.decoders.Decoders;
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.encoders.Encoders;
|
|
||||||
|
|
||||||
public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
|
public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
|
||||||
{
|
{
|
||||||
private final ServerEndpoint endpoint;
|
private final ServerEndpoint endpoint;
|
||||||
|
private final ServerEndpointConfig config;
|
||||||
|
private final Decoders decoders;
|
||||||
|
|
||||||
protected JsrServerMetadata(Class<?> websocket)
|
protected JsrServerMetadata(JettyWebSocketContainer container, Class<?> websocket) throws DeploymentException
|
||||||
{
|
{
|
||||||
super(websocket);
|
super(websocket);
|
||||||
|
|
||||||
|
@ -47,8 +52,61 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
|
||||||
}
|
}
|
||||||
|
|
||||||
this.endpoint = anno;
|
this.endpoint = anno;
|
||||||
this.encoders = new Encoders(anno.encoders());
|
ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(websocket,anno.value());
|
||||||
this.decoders = new Decoders(anno.decoders());
|
builder.decoders(Arrays.asList(anno.decoders()));
|
||||||
|
builder.encoders(Arrays.asList(anno.encoders()));
|
||||||
|
builder.subprotocols(Arrays.asList(anno.subprotocols()));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
builder.configurator(anno.configurator().newInstance());
|
||||||
|
}
|
||||||
|
catch (InstantiationException | IllegalAccessException e)
|
||||||
|
{
|
||||||
|
throw new DeploymentException("Unable to instantiate @ServerEndpoint.configurator(): " + anno.configurator(),e);
|
||||||
|
}
|
||||||
|
this.config = builder.build();
|
||||||
|
this.decoders = new Decoders(container.getDecoderMetadataFactory(),config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeParamsOnClose(LinkedList<IJsrParamId> params)
|
||||||
|
{
|
||||||
|
params.addFirst(JsrParamPath.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeParamsOnError(LinkedList<IJsrParamId> params)
|
||||||
|
{
|
||||||
|
params.addFirst(JsrParamPath.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
|
||||||
|
{
|
||||||
|
for (DecoderWrapper wrapper : decoders.wrapperSet())
|
||||||
|
{
|
||||||
|
Class<? extends Decoder> decoder = wrapper.getMetadata().getDecoder();
|
||||||
|
|
||||||
|
if (Decoder.Text.class.isAssignableFrom(decoder) || Decoder.TextStream.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
params.add(new JsrParamIdTextDecoder(wrapper));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Decoder.Binary.class.isAssignableFrom(decoder) || Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
||||||
|
{
|
||||||
|
params.add(new JsrParamIdBinaryDecoder(wrapper));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Invalid Decoder: " + decoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
|
||||||
|
{
|
||||||
|
params.addFirst(JsrParamPath.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -56,46 +114,4 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
|
||||||
{
|
{
|
||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customizeParamsOnClose(LinkedList<IJsrParamId> params)
|
|
||||||
{
|
|
||||||
params.addFirst(JsrParamPath.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customizeParamsOnError(LinkedList<IJsrParamId> params)
|
|
||||||
{
|
|
||||||
params.addFirst(JsrParamPath.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
|
|
||||||
{
|
|
||||||
params.addFirst(JsrParamPath.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
|
|
||||||
{
|
|
||||||
for (DecoderRef ref : decoders)
|
|
||||||
{
|
|
||||||
Class<? extends Decoder> decoder = ref.getDecoder();
|
|
||||||
|
|
||||||
if (Decoder.Text.class.isAssignableFrom(decoder) || Decoder.TextStream.class.isAssignableFrom(decoder))
|
|
||||||
{
|
|
||||||
params.add(new JsrParamIdTextDecoder(ref));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Decoder.Binary.class.isAssignableFrom(decoder) || Decoder.BinaryStream.class.isAssignableFrom(decoder))
|
|
||||||
{
|
|
||||||
params.add(new JsrParamIdBinaryDecoder(ref));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException("Invalid Decoder: " + decoder);
|
|
||||||
}
|
|
||||||
params.addFirst(JsrParamPath.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import javax.websocket.CloseReason;
|
||||||
import javax.websocket.PongMessage;
|
import javax.websocket.PongMessage;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
|
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
|
||||||
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicBinaryMessageByteBufferSocket;
|
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicBinaryMessageByteBufferSocket;
|
||||||
|
@ -81,6 +82,8 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static JettyWebSocketContainer container = new JettyWebSocketContainer();
|
||||||
|
|
||||||
@Parameters
|
@Parameters
|
||||||
public static Collection<Case[]> data() throws Exception
|
public static Collection<Case[]> data() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -135,7 +138,7 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
|
||||||
@Test
|
@Test
|
||||||
public void testScan_Basic() throws Exception
|
public void testScan_Basic() throws Exception
|
||||||
{
|
{
|
||||||
JsrServerMetadata metadata = new JsrServerMetadata(testcase.pojo);
|
JsrServerMetadata metadata = new JsrServerMetadata(container,testcase.pojo);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.websocket.DeploymentException;
|
||||||
import javax.websocket.OnClose;
|
import javax.websocket.OnClose;
|
||||||
import javax.websocket.OnError;
|
import javax.websocket.OnError;
|
||||||
import javax.websocket.OnOpen;
|
import javax.websocket.OnOpen;
|
||||||
|
@ -32,6 +33,7 @@ import javax.websocket.OnOpen;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||||
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidCloseIntSocket;
|
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidCloseIntSocket;
|
||||||
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorErrorSocket;
|
import org.eclipse.jetty.websocket.jsr356.server.samples.InvalidErrorErrorSocket;
|
||||||
|
@ -54,6 +56,8 @@ public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ServerAnnotatedEndpointScanner_InvalidSignaturesTest.class);
|
private static final Logger LOG = Log.getLogger(ServerAnnotatedEndpointScanner_InvalidSignaturesTest.class);
|
||||||
|
|
||||||
|
private static JettyWebSocketContainer container = new JettyWebSocketContainer();
|
||||||
|
|
||||||
@Parameters
|
@Parameters
|
||||||
public static Collection<Class<?>[]> data()
|
public static Collection<Class<?>[]> data()
|
||||||
{
|
{
|
||||||
|
@ -89,11 +93,11 @@ public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScan_InvalidSignature()
|
public void testScan_InvalidSignature() throws DeploymentException
|
||||||
{
|
{
|
||||||
JsrServerMetadata metadata = new JsrServerMetadata(pojo);
|
JsrServerMetadata metadata = new JsrServerMetadata(container,pojo);
|
||||||
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
scanner.scan();
|
scanner.scan();
|
||||||
|
|
|
@ -124,6 +124,7 @@ public abstract class AbstractEventDriver implements IncomingFrames, EventDriver
|
||||||
{
|
{
|
||||||
pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
|
pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
|
||||||
BufferUtil.put(frame.getPayload(),pongBuf);
|
BufferUtil.put(frame.getPayload(),pongBuf);
|
||||||
|
BufferUtil.flipToFlush(pongBuf,0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -115,7 +115,14 @@ public class EventDriverFactory
|
||||||
{
|
{
|
||||||
if (impl.supports(websocket))
|
if (impl.supports(websocket))
|
||||||
{
|
{
|
||||||
return impl.create(websocket,policy.clonePolicy());
|
try
|
||||||
|
{
|
||||||
|
return impl.create(websocket,policy.clonePolicy());
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
throw new InvalidWebSocketException("Unable to create websocket",e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,10 @@ public interface EventDriverImpl
|
||||||
* @param policy
|
* @param policy
|
||||||
* the policy to use
|
* the policy to use
|
||||||
* @return the created EventDriver
|
* @return the created EventDriver
|
||||||
|
* @throws Throwable
|
||||||
|
* if unable to create the EventDriver
|
||||||
*/
|
*/
|
||||||
EventDriver create(Object websocket, WebSocketPolicy policy);
|
EventDriver create(Object websocket, WebSocketPolicy policy) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* human readable string describing the rule that would support this EventDriver.
|
* human readable string describing the rule that would support this EventDriver.
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
@ -148,6 +148,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onInputStream(InputStream stream)
|
public void onInputStream(InputStream stream)
|
||||||
{
|
{
|
||||||
if (events.onBinary != null)
|
if (events.onBinary != null)
|
||||||
|
@ -156,6 +157,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onReader(Reader reader)
|
public void onReader(Reader reader)
|
||||||
{
|
{
|
||||||
if (events.onText != null)
|
if (events.onText != null)
|
||||||
|
@ -185,7 +187,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class JettyListenerEventDriver extends AbstractEventDriver
|
||||||
activeMessage = new SimpleBinaryMessage(this);
|
activeMessage = new SimpleBinaryMessage(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ public class JettyListenerEventDriver extends AbstractEventDriver
|
||||||
activeMessage = new SimpleTextMessage(this);
|
activeMessage = new SimpleTextMessage(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
activeMessage.appendMessage(buffer);
|
activeMessage.appendMessage(buffer,fin);
|
||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,13 +31,17 @@ public interface MessageAppender
|
||||||
*
|
*
|
||||||
* @param payload
|
* @param payload
|
||||||
* the payload to append.
|
* the payload to append.
|
||||||
|
* @param isLast
|
||||||
|
* flag indicating if this is the last part of the message or not.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* if unable to append the payload
|
* if unable to append the payload
|
||||||
*/
|
*/
|
||||||
abstract void appendMessage(ByteBuffer payload) throws IOException;
|
abstract void appendMessage(ByteBuffer payload, boolean isLast) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that message is to be considered complete.
|
* Notification that message is to be considered complete.
|
||||||
|
* <p>
|
||||||
|
* Any cleanup or final actions should be taken here.
|
||||||
*/
|
*/
|
||||||
abstract void messageComplete();
|
abstract void messageComplete();
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class MessageInputStream extends InputStream implements MessageAppender
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessage(ByteBuffer payload) throws IOException
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
{
|
{
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MessageReader extends Reader implements MessageAppender
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessage(ByteBuffer payload) throws IOException
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
{
|
{
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,9 +29,9 @@ public class SimpleBinaryMessage implements MessageAppender
|
||||||
{
|
{
|
||||||
private static final int BUFFER_SIZE = 65535;
|
private static final int BUFFER_SIZE = 65535;
|
||||||
private final EventDriver onEvent;
|
private final EventDriver onEvent;
|
||||||
private final ByteArrayOutputStream out;
|
protected final ByteArrayOutputStream out;
|
||||||
private int size;
|
private int size;
|
||||||
private boolean finished;
|
protected boolean finished;
|
||||||
|
|
||||||
public SimpleBinaryMessage(EventDriver onEvent)
|
public SimpleBinaryMessage(EventDriver onEvent)
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ public class SimpleBinaryMessage implements MessageAppender
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessage(ByteBuffer payload) throws IOException
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
{
|
{
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,9 +27,9 @@ import org.eclipse.jetty.websocket.common.events.EventDriver;
|
||||||
public class SimpleTextMessage implements MessageAppender
|
public class SimpleTextMessage implements MessageAppender
|
||||||
{
|
{
|
||||||
private final EventDriver onEvent;
|
private final EventDriver onEvent;
|
||||||
private final Utf8StringBuilder utf;
|
protected final Utf8StringBuilder utf;
|
||||||
private int size = 0;
|
private int size = 0;
|
||||||
private boolean finished;
|
protected boolean finished;
|
||||||
|
|
||||||
public SimpleTextMessage(EventDriver onEvent)
|
public SimpleTextMessage(EventDriver onEvent)
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@ public class SimpleTextMessage implements MessageAppender
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessage(ByteBuffer payload) throws IOException
|
public void appendMessage(ByteBuffer payload, boolean isLast) throws IOException
|
||||||
{
|
{
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
<artifactId>jetty-test-helper</artifactId>
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
|
<version>2.2-SNAPSHOT</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class AnnotatedMaxMessageSizeTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().get(0);
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class ChromeTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().get(0);
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class FragmentExtensionTest
|
||||||
IncomingFramesCapture capture = client.readFrames(parts.length,TimeUnit.MILLISECONDS,1000);
|
IncomingFramesCapture capture = client.readFrames(parts.length,TimeUnit.MILLISECONDS,1000);
|
||||||
for (int i = 0; i < parts.length; i++)
|
for (int i = 0; i < parts.length; i++)
|
||||||
{
|
{
|
||||||
WebSocketFrame frame = capture.getFrames().get(i);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("text[" + i + "].payload",frame.getPayloadAsUTF8(),is(parts[i]));
|
Assert.assertThat("text[" + i + "].payload",frame.getPayloadAsUTF8(),is(parts[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class FrameCompressionExtensionTest
|
||||||
client.write(WebSocketFrame.text(msg));
|
client.write(WebSocketFrame.text(msg));
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
||||||
|
|
||||||
// Client sends second message
|
// Client sends second message
|
||||||
|
@ -83,7 +83,7 @@ public class FrameCompressionExtensionTest
|
||||||
client.write(WebSocketFrame.text(msg));
|
client.write(WebSocketFrame.text(msg));
|
||||||
|
|
||||||
capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
||||||
frame = capture.getFrames().get(0);
|
frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class IdentityExtensionTest
|
||||||
client.write(WebSocketFrame.text("Hello"));
|
client.write(WebSocketFrame.text("Hello"));
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is("Hello"));
|
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is("Hello"));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -143,7 +143,7 @@ public class WebSocketCloseTest
|
||||||
client.expectUpgradeResponse();
|
client.expectUpgradeResponse();
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
CloseInfo close = new CloseInfo(frame);
|
CloseInfo close = new CloseInfo(frame);
|
||||||
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
|
||||||
|
|
|
@ -96,13 +96,13 @@ public class WebSocketServerSessionTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(4,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(4,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().pop();
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Parameter Map[snack]",tf.getPayloadAsUTF8(),is("[cashews]"));
|
Assert.assertThat("Parameter Map[snack]",tf.getPayloadAsUTF8(),is("[cashews]"));
|
||||||
tf = capture.getFrames().pop();
|
tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Parameter Map[amount]",tf.getPayloadAsUTF8(),is("[handful]"));
|
Assert.assertThat("Parameter Map[amount]",tf.getPayloadAsUTF8(),is("[handful]"));
|
||||||
tf = capture.getFrames().pop();
|
tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Parameter Map[brand]",tf.getPayloadAsUTF8(),is("[off]"));
|
Assert.assertThat("Parameter Map[brand]",tf.getPayloadAsUTF8(),is("[off]"));
|
||||||
tf = capture.getFrames().pop();
|
tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Parameter Map[cost]",tf.getPayloadAsUTF8(),is("<null>"));
|
Assert.assertThat("Parameter Map[cost]",tf.getPayloadAsUTF8(),is("<null>"));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -115,7 +115,7 @@ public class WebSocketServletRFCTest
|
||||||
|
|
||||||
// Read frame echo'd back (hopefully a single binary frame)
|
// Read frame echo'd back (hopefully a single binary frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
|
||||||
Frame binmsg = capture.getFrames().get(0);
|
Frame binmsg = capture.getFrames().poll();
|
||||||
int expectedSize = buf1.length + buf2.length + buf3.length;
|
int expectedSize = buf1.length + buf2.length + buf3.length;
|
||||||
Assert.assertThat("BinaryFrame.payloadLength",binmsg.getPayloadLength(),is(expectedSize));
|
Assert.assertThat("BinaryFrame.payloadLength",binmsg.getPayloadLength(),is(expectedSize));
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ public class WebSocketServletRFCTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().get(0);
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -209,7 +209,7 @@ public class WebSocketServletRFCTest
|
||||||
|
|
||||||
// Read frame (hopefully close frame)
|
// Read frame (hopefully close frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
Frame cf = capture.getFrames().get(0);
|
Frame cf = capture.getFrames().poll();
|
||||||
CloseInfo close = new CloseInfo(cf);
|
CloseInfo close = new CloseInfo(cf);
|
||||||
Assert.assertThat("Close Frame.status code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
Assert.assertThat("Close Frame.status code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ public class WebSocketServletRFCTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().get(0);
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -292,7 +292,7 @@ public class WebSocketServletRFCTest
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
CloseInfo close = new CloseInfo(frame);
|
CloseInfo close = new CloseInfo(frame);
|
||||||
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.MESSAGE_TOO_LARGE));
|
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.MESSAGE_TOO_LARGE));
|
||||||
|
@ -334,7 +334,7 @@ public class WebSocketServletRFCTest
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
CloseInfo close = new CloseInfo(frame);
|
CloseInfo close = new CloseInfo(frame);
|
||||||
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.MESSAGE_TOO_LARGE));
|
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.MESSAGE_TOO_LARGE));
|
||||||
|
@ -367,7 +367,7 @@ public class WebSocketServletRFCTest
|
||||||
client.writeRaw(bb);
|
client.writeRaw(bb);
|
||||||
|
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
|
||||||
WebSocketFrame frame = capture.getFrames().get(0);
|
WebSocketFrame frame = capture.getFrames().poll();
|
||||||
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
CloseInfo close = new CloseInfo(frame);
|
CloseInfo close = new CloseInfo(frame);
|
||||||
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.BAD_PAYLOAD));
|
Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.BAD_PAYLOAD));
|
||||||
|
@ -413,7 +413,7 @@ public class WebSocketServletRFCTest
|
||||||
|
|
||||||
// Read frame (hopefully text frame)
|
// Read frame (hopefully text frame)
|
||||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||||
WebSocketFrame tf = capture.getFrames().get(0);
|
WebSocketFrame tf = capture.getFrames().poll();
|
||||||
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
@ -152,7 +152,7 @@ public class Fuzzer
|
||||||
for (int i = 0; i < expectedCount; i++)
|
for (int i = 0; i < expectedCount; i++)
|
||||||
{
|
{
|
||||||
WebSocketFrame expected = expect.get(i);
|
WebSocketFrame expected = expect.get(i);
|
||||||
WebSocketFrame actual = capture.getFrames().pop();
|
WebSocketFrame actual = capture.getFrames().poll();
|
||||||
|
|
||||||
prefix = "Frame[" + i + "]";
|
prefix = "Frame[" + i + "]";
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,9 @@ package org.eclipse.jetty.websocket.server.helper;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -35,8 +36,8 @@ import org.junit.Assert;
|
||||||
public class IncomingFramesCapture implements IncomingFrames
|
public class IncomingFramesCapture implements IncomingFrames
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(IncomingFramesCapture.class);
|
private static final Logger LOG = Log.getLogger(IncomingFramesCapture.class);
|
||||||
private LinkedList<WebSocketFrame> frames = new LinkedList<>();
|
private EventQueue<WebSocketFrame> frames = new EventQueue<>();
|
||||||
private LinkedList<WebSocketException> errors = new LinkedList<>();
|
private EventQueue<WebSocketException> errors = new EventQueue<>();
|
||||||
|
|
||||||
public void assertErrorCount(int expectedCount)
|
public void assertErrorCount(int expectedCount)
|
||||||
{
|
{
|
||||||
|
@ -81,11 +82,11 @@ public class IncomingFramesCapture implements IncomingFrames
|
||||||
public void dump()
|
public void dump()
|
||||||
{
|
{
|
||||||
System.err.printf("Captured %d incoming frames%n",frames.size());
|
System.err.printf("Captured %d incoming frames%n",frames.size());
|
||||||
for (int i = 0; i < frames.size(); i++)
|
int i = 0;
|
||||||
|
for (Frame frame : frames)
|
||||||
{
|
{
|
||||||
Frame frame = frames.get(i);
|
System.err.printf("[%3d] %s%n",i++,frame);
|
||||||
System.err.printf("[%3d] %s%n",i,frame);
|
System.err.printf(" payload: %s%n",BufferUtil.toDetailString(frame.getPayload()));
|
||||||
System.err.printf(" %s%n",BufferUtil.toDetailString(frame.getPayload()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ public class IncomingFramesCapture implements IncomingFrames
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedList<WebSocketException> getErrors()
|
public Queue<WebSocketException> getErrors()
|
||||||
{
|
{
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,7 @@ public class IncomingFramesCapture implements IncomingFrames
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedList<WebSocketFrame> getFrames()
|
public Queue<WebSocketFrame> getFrames()
|
||||||
{
|
{
|
||||||
return frames;
|
return frames;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue