JSR-356: MessageHandler(s) layer

This commit is contained in:
Joakim Erdfelt 2013-04-04 12:03:11 -07:00
parent 0232216e65
commit d1692733f5
77 changed files with 2451 additions and 446 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
} }
} }

View File

@ -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>&#064ClientEndpoint.decoders()</li>
* <li>&#064ServerEndpoint.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();
}
}

View File

@ -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;
}
}

View File

@ -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()
{ {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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
*/ */

View File

@ -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;

View File

@ -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 &#064;{@link OnOpen} annotation. * Callable for &#064;{@link OnOpen} annotation.
*/ */

View File

@ -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 &#064;{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Binary} or {@link Decoder.BinaryStream} * Param handling for Binary &#064;{@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);

View File

@ -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 &#064;{@link OnMessage} parameters declared as {@link Decoder}s of type {@link Decoder.Text} or {@link Decoder.TextStream} * Param handling for Text &#064;{@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;
} }

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -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));
} }

View File

@ -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 */
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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 */
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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);
} }

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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();

View File

@ -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
{ {

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
{
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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));
}
}

View File

@ -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,14 +52,20 @@ 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()));
@Override try
public ServerEndpoint getAnnotation() {
{ builder.configurator(anno.configurator().newInstance());
return endpoint; }
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 @Override
@ -69,6 +80,29 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
params.addFirst(JsrParamPath.INSTANCE); 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 @Override
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params) public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
{ {
@ -76,26 +110,8 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
} }
@Override @Override
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params) public ServerEndpoint getAnnotation()
{ {
for (DecoderRef ref : decoders) return endpoint;
{
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);
} }
} }

View File

@ -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();

View File

@ -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,9 +93,9 @@ 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

View File

@ -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
{ {

View File

@ -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);
}
} }
} }

View File

@ -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.

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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();
} }

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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]));
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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 + "]";

View File

@ -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;
} }