JSR-356 - reworked Config to maintain user provided config

+ If a user provided config is supplied, then that config should
  be used for init(EndpointConfig) and other various accesses to
  the configuration object.  This refactor stops using an internal
  EndpointConfig object always and moves the internal config
  fields into the JsrSession object instead.
This commit is contained in:
Joakim Erdfelt 2013-07-02 12:24:55 -07:00
parent e35914e400
commit 46e9493c85
85 changed files with 2688 additions and 1744 deletions

View File

@ -0,0 +1,63 @@
//
// ========================================================================
// 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.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* Basic EndpointConfig (used when no EndpointConfig is provided or discovered)
*/
public class BasicEndpointConfig implements EndpointConfig
{
private List<Class<? extends Decoder>> decoders;
private List<Class<? extends Encoder>> encoders;
private Map<String, Object> userProperties;
public BasicEndpointConfig()
{
decoders = Collections.emptyList();
encoders = Collections.emptyList();
userProperties = new HashMap<>();
}
@Override
public List<Class<? extends Decoder>> getDecoders()
{
return decoders;
}
@Override
public List<Class<? extends Encoder>> getEncoders()
{
return encoders;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
}

View File

@ -35,26 +35,34 @@ import javax.websocket.Session;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.client.io.UpgradeListener;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrClientMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEventDriverFactory;
public class ClientContainer implements ContainerService
/**
* Container for Client use of the javax.websocket API.
* <p>
* This should be specific to a JVM if run in a standalone mode. or specific to a WebAppContext if running on the Jetty server.
*/
public class ClientContainer extends CommonContainer
{
private static final Logger LOG = Log.getLogger(ClientContainer.class);
private final DecoderMetadataFactory decoderMetadataFactory;
private ConcurrentHashMap<Class<?>, JsrClientMetadata> endpointClientMetadataCache = new ConcurrentHashMap<>();
/** Tracking for all declared Client endpoints */
private final ConcurrentHashMap<Class<?>, JsrClientMetadata> endpointClientMetadataCache;
/** The jetty websocket client in use for this container */
private WebSocketClient client;
public ClientContainer()
{
decoderMetadataFactory = new DecoderMetadataFactory();
super();
endpointClientMetadataCache = new ConcurrentHashMap<>();
}
private Session connect(Object websocket, ClientEndpointConfig config, URI path) throws IOException
@ -123,13 +131,13 @@ public class ClientContainer implements ContainerService
// Annotated takes precedence here
JsrClientMetadata metadata = new JsrClientMetadata(this,annotatedEndpointClass);
Object websocket = annotatedEndpointClass.newInstance();
return connect(websocket,metadata.getEndpointConfigCopy(),path);
return connect(websocket,metadata.getConfig(),path);
}
else if (Endpoint.class.isAssignableFrom(annotatedEndpointClass))
{
// Try if extends Endpoint (alternate use)
Object websocket = annotatedEndpointClass.newInstance();
ClientEndpointConfig cec = new JettyClientEndpointConfig();
ClientEndpointConfig cec = new EmptyClientEndpointConfig();
return connect(websocket,cec,path);
}
else
@ -174,12 +182,6 @@ public class ClientContainer implements ContainerService
return basemetadata;
}
@Override
public DecoderMetadataFactory getDecoderMetadataFactory()
{
return decoderMetadataFactory;
}
@Override
public long getDefaultAsyncSendTimeout()
{
@ -218,6 +220,7 @@ public class ClientContainer implements ContainerService
return ret;
}
@Override
public Set<Session> getOpenSessions()
{
// TODO Auto-generated method stub

View File

@ -0,0 +1,46 @@
//
// ========================================================================
// 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.HashMap;
import java.util.Map;
import javax.websocket.EndpointConfig;
public abstract class CommonConfig implements EndpointConfig
{
/** User Properties for the Endpoint */
private Map<String, Object> userProperties;
public CommonConfig(CommonConfig copy)
{
userProperties = copy.userProperties;
}
protected CommonConfig(CommonContainer container)
{
userProperties = new HashMap<>();
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
}

View File

@ -0,0 +1,114 @@
//
// ========================================================================
// 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.nio.ByteBuffer;
import java.util.Set;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
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;
/**
* Service Interface for working with Jetty Internal Container.
*/
public abstract class CommonContainer implements WebSocketContainer
{
/** Tracking all primitive decoders for the container */
private final DecoderFactory decoderFactory;
/** Tracking all primitive encoders for the container */
private final EncoderFactory encoderFactory;
public CommonContainer()
{
decoderFactory = new DecoderFactory();
encoderFactory = new EncoderFactory();
// ---------------------------------------
// Register Decoder Primitives
// ---------------------------------------
boolean streamed = false;
// TEXT based - Classes Based
MessageType msgType = MessageType.TEXT;
decoderFactory.register(Boolean.class,BooleanDecoder.class,msgType,streamed);
decoderFactory.register(Byte.class,ByteDecoder.class,msgType,streamed);
decoderFactory.register(Character.class,CharacterDecoder.class,msgType,streamed);
decoderFactory.register(Double.class,DoubleDecoder.class,msgType,streamed);
decoderFactory.register(Float.class,FloatDecoder.class,msgType,streamed);
decoderFactory.register(Integer.class,IntegerDecoder.class,msgType,streamed);
decoderFactory.register(Long.class,LongDecoder.class,msgType,streamed);
decoderFactory.register(Short.class,ShortDecoder.class,msgType,streamed);
decoderFactory.register(String.class,StringDecoder.class,msgType,streamed);
// TEXT based - Primitive Types
msgType = MessageType.TEXT;
decoderFactory.register(Boolean.TYPE,BooleanDecoder.class,msgType,streamed);
decoderFactory.register(Byte.TYPE,ByteDecoder.class,msgType,streamed);
decoderFactory.register(Character.TYPE,CharacterDecoder.class,msgType,streamed);
decoderFactory.register(Double.TYPE,DoubleDecoder.class,msgType,streamed);
decoderFactory.register(Float.TYPE,FloatDecoder.class,msgType,streamed);
decoderFactory.register(Integer.TYPE,IntegerDecoder.class,msgType,streamed);
decoderFactory.register(Long.TYPE,LongDecoder.class,msgType,streamed);
decoderFactory.register(Short.TYPE,ShortDecoder.class,msgType,streamed);
// BINARY based
msgType = MessageType.BINARY;
decoderFactory.register(ByteBuffer.class,ByteBufferDecoder.class,msgType,streamed);
decoderFactory.register(byte[].class,ByteArrayDecoder.class,msgType,streamed);
}
public DecoderFactory getDecoderFactory()
{
return decoderFactory;
}
public EncoderFactory getEncoderFactory()
{
return encoderFactory;
}
/**
* Get set of open sessions.
*
* @return the set of open sessions
*/
public abstract Set<Session> getOpenSessions();
/**
* Start the container
*/
public abstract void start();
/**
* Stop the container
*/
public abstract void stop();
}

View File

@ -18,22 +18,12 @@
package org.eclipse.jetty.websocket.jsr356;
import javax.websocket.WebSocketContainer;
import javax.websocket.EndpointConfig;
/**
* Service Interface for working with Jetty Internal Container.
* Tag indicating a component that needs to be configured.
*/
public interface ContainerService extends WebSocketContainer
public interface Configurable
{
public DecoderMetadataFactory getDecoderMetadataFactory();
/**
* Start the service
*/
public void start();
/**
* Stop the service
*/
public void stop();
public void init(EndpointConfig config);
}

View File

@ -0,0 +1,246 @@
//
// ========================================================================
// 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.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.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
/**
* Factory for {@link DecoderMetadata}
* <p>
* Relies on search order of parent {@link DecoderFactory} instances as such.
* <ul>
* <li>Endpoint declared decoders</li>
* <li>EndpointConfig declared decoders</li>
* <li>Container declared decoders (primitives)</li>
* </ul>
*/
public class DecoderFactory
{
private static final Logger LOG = Log.getLogger(DecoderFactory.class);
/** Decoders by Type */
private final Map<Class<?>, DecoderMetadata> typeMap;
/** Registered Decoders at this level */
private Map<Class<? extends Decoder>, List<DecoderMetadata>> registered;
/** Parent Factory */
private DecoderFactory parentFactory;
public DecoderFactory()
{
this.typeMap = new ConcurrentHashMap<>();
this.registered = new ConcurrentHashMap<>();
}
public DecoderFactory(DecoderFactory parentFactory)
{
this();
this.parentFactory = parentFactory;
}
private Class<?> getDecoderMessageClass(Class<? extends Decoder> decoder, Class<?> interfaceClass)
{
Class<?> decoderClass = ReflectUtils.findGenericClassFor(decoder,interfaceClass);
if (decoderClass == null)
{
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());
}
return decoderClass;
}
/**
* Get the list of registered decoder classes.
* <p>
* Includes all decoders at this level and above.
*
* @return the list of registered decoder classes.
*/
public List<Class<? extends Decoder>> getList()
{
List<Class<? extends Decoder>> decoders = new ArrayList<>();
decoders.addAll(registered.keySet());
if (parentFactory != null)
{
decoders.addAll(parentFactory.getList());
}
return decoders;
}
public List<DecoderMetadata> getMetadata(Class<? extends Decoder> decoder)
{
LOG.debug("getMetadata({})",decoder);
List<DecoderMetadata> ret = registered.get(decoder);
if (ret != null)
{
return ret;
}
// Not found, Try parent factory (if declared)
if (parentFactory != null)
{
ret = parentFactory.registered.get(decoder);
if (ret != null)
{
return ret;
}
}
return register(decoder);
}
public DecoderMetadata getMetadataFor(Class<?> type)
{
DecoderMetadata metadata = typeMap.get(type);
if (metadata == null)
{
if (parentFactory != null)
{
return parentFactory.getMetadataFor(type);
}
}
return metadata;
}
public DecoderWrapper getWrapperFor(Class<?> type)
{
DecoderMetadata metadata = getMetadataFor(type);
if (metadata != null)
{
return newWrapper(metadata);
}
return null;
}
public DecoderWrapper newWrapper(DecoderMetadata metadata)
{
Class<? extends Decoder> decoderClass = metadata.getDecoderClass();
try
{
Decoder decoder = decoderClass.newInstance();
return new DecoderWrapper(decoder,metadata);
}
catch (InstantiationException | IllegalAccessException e)
{
throw new IllegalStateException("Unable to instantiate Decoder: " + decoderClass.getName());
}
}
public List<DecoderMetadata> register(Class<? extends Decoder> decoder)
{
List<DecoderMetadata> metadatas = new ArrayList<>();
if (Decoder.Binary.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderMessageClass(decoder,Decoder.Binary.class);
metadatas.add(new DecoderMetadata(objType,decoder,MessageType.BINARY,false));
}
if (Decoder.BinaryStream.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderMessageClass(decoder,Decoder.BinaryStream.class);
metadatas.add(new DecoderMetadata(objType,decoder,MessageType.BINARY,true));
}
if (Decoder.Text.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderMessageClass(decoder,Decoder.Text.class);
metadatas.add(new DecoderMetadata(objType,decoder,MessageType.TEXT,false));
}
if (Decoder.TextStream.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderMessageClass(decoder,Decoder.TextStream.class);
metadatas.add(new DecoderMetadata(objType,decoder,MessageType.TEXT,true));
}
if (!ReflectUtils.isDefaultConstructable(decoder))
{
throw new InvalidSignatureException("Decoder must have public, no-args constructor: " + decoder.getName());
}
if (metadatas.size() <= 0)
{
throw new InvalidSignatureException("Not a valid Decoder class: " + decoder.getName());
}
return trackMetadata(decoder,metadatas);
}
public 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));
trackMetadata(decoderClass,metadatas);
}
public List<DecoderMetadata> registerAll(Class<? extends Decoder>[] decoders)
{
List<DecoderMetadata> metadatas = new ArrayList<>();
for (Class<? extends Decoder> decoder : decoders)
{
metadatas.addAll(register(decoder));
}
return metadatas;
}
private List<DecoderMetadata> trackMetadata(Class<? extends Decoder> decoder, List<DecoderMetadata> metadatas)
{
for (DecoderMetadata metadata : metadatas)
{
trackType(metadata);
}
LOG.debug("Registered {} with [{} entries]",decoder.getName(),metadatas.size());
registered.put(decoder,metadatas);
return metadatas;
}
private void trackType(DecoderMetadata metadata)
{
Class<?> type = metadata.getObjectType();
if (typeMap.containsKey(type))
{
StringBuilder err = new StringBuilder();
err.append("Duplicate decoder for type: ");
err.append(type);
err.append(" (class ").append(metadata.getDecoderClass().getName());
DecoderMetadata dup = typeMap.get(type);
err.append(" duplicates ");
err.append(dup.getDecoderClass().getName());
err.append(")");
throw new IllegalStateException(err.toString());
}
typeMap.put(type,metadata);
}
}

View File

@ -1,168 +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;
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.ReflectUtils;
/**
* 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)
{
Class<?> decoderClass = ReflectUtils.findGenericClassFor(decoder,interfaceClass);
if (decoderClass == null)
{
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());
}
return decoderClass;
}
public List<DecoderMetadata> getMetadata(Class<? extends Decoder> decoder)
{
LOG.debug("getDecoder({})",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

@ -19,11 +19,14 @@
package org.eclipse.jetty.websocket.jsr356;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
/**
* Expose a {@link Decoder} instance along with its associated {@link DecoderMetadata}
* Expose a configured {@link Decoder} instance along with its associated {@link DecoderMetadata}
*/
public class DecoderWrapper
public class DecoderWrapper implements Configurable
{
private final Decoder decoder;
private final DecoderMetadata metadata;
@ -43,4 +46,10 @@ public class DecoderWrapper
{
return metadata;
}
@Override
public void init(EndpointConfig config)
{
this.decoder.init(config);
}
}

View File

@ -1,187 +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;
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;
if (config != null)
{
for (Class<? extends Decoder> decoder : config.getDecoders())
{
try
{
addAllMetadata(decoder);
}
catch (IllegalStateException e)
{
throw new DeploymentException("Invalid configuration: " + e.getMessage());
}
}
}
}
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);
if (defaultDecoder == null)
{
throw new IllegalStateException("Unable to find decoder for type: " + 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 void init(EndpointConfig config)
{
for (DecoderWrapper decoder : decoderMap.values())
{
decoder.getDecoder().init(config);
}
}
public Set<Class<?>> keySet()
{
return decoderMap.keySet();
}
public Collection<DecoderWrapper> wrapperSet()
{
return decoderMap.values();
}
}

View File

@ -0,0 +1,56 @@
//
// ========================================================================
// 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.List;
import javax.websocket.Encoder;
/**
* Represents all of the declared {@link Encoder}s that the Container is aware of.
*/
public class EncoderFactory
{
public EncoderFactory()
{
// TODO Auto-generated constructor stub
}
public EncoderFactory(EncoderFactory encoderFactory)
{
// TODO Auto-generated constructor stub
}
public Encoder getEncoder(Class<?> targetType)
{
// TODO Auto-generated method stub
return null;
}
public List<Class<? extends Encoder>> getList()
{
// TODO Auto-generated method stub
return null;
}
public void registerAll(Class<? extends Encoder>[] encoders)
{
// TODO Auto-generated method stub
}
}

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;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadata;
/**
* Expose a configured {@link Encoder} instance along with its associated {@link EncoderMetadata}
*/
public class EncoderWrapper implements Configurable
{
private final Encoder encoder;
private final EncoderMetadata metadata;
public EncoderWrapper(Encoder encoder, EncoderMetadata metadata)
{
this.encoder = encoder;
this.metadata = metadata;
}
public Encoder getEncoder()
{
return encoder;
}
public EncoderMetadata getMetadata()
{
return metadata;
}
@Override
public void init(EndpointConfig config)
{
this.encoder.init(config);
}
}

View File

@ -0,0 +1,42 @@
//
// ========================================================================
// 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;
/**
* Exception during initialization of the Endpoint
*/
public class InitException extends IllegalStateException
{
private static final long serialVersionUID = -4691138423037387558L;
public InitException(String s)
{
super(s);
}
public InitException(String message, Throwable cause)
{
super(message,cause);
}
public InitException(Throwable cause)
{
super(cause);
}
}

View File

@ -1,182 +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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.HandshakeResponse;
public class JettyClientEndpointConfig implements ClientEndpointConfig
{
public static class NoopConfigurator extends ClientEndpointConfig.Configurator
{
public static final NoopConfigurator INSTANCE = new NoopConfigurator();
@Override
public void afterResponse(HandshakeResponse hr)
{
// do nothing
}
@Override
public void beforeRequest(Map<String, List<String>> headers)
{
// do nothing
}
}
private Configurator configurator;
private List<Class<? extends Decoder>> decoders;
private List<Class<? extends Encoder>> encoders;
private List<String> subprotocols;
private List<Extension> extensions;
private Map<String, Object> userProperties;
public JettyClientEndpointConfig()
{
decoders = new ArrayList<>();
encoders = new ArrayList<>();
subprotocols = new ArrayList<>();
extensions = new ArrayList<>();
userProperties = new HashMap<>();
}
public JettyClientEndpointConfig(ClientEndpoint anno) throws DeploymentException
{
this();
addAll(anno.decoders(),this.decoders);
addAll(anno.encoders(),this.encoders);
addAll(anno.subprotocols(),this.subprotocols);
// no extensions declared in annotation
// no userProperties in annotation
if (anno.configurator() == null)
{
this.configurator = NoopConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ClientEndpoint.configurator() of ");
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
}
}
}
/**
* Copy Constructor
*
* @param copy
* the endpoint configuration to copy
* @throws DeploymentException
*/
public JettyClientEndpointConfig(JettyClientEndpointConfig copy) throws DeploymentException
{
this();
this.decoders.addAll(copy.decoders);
this.encoders.addAll(copy.encoders);
this.subprotocols.addAll(copy.subprotocols);
this.extensions.addAll(copy.extensions);
this.userProperties.putAll(copy.userProperties);
if (copy.configurator instanceof NoopConfigurator)
{
this.configurator = NoopConfigurator.INSTANCE;
}
else
{
Class<? extends Configurator> configuratorClass = copy.configurator.getClass();
try
{
this.configurator = configuratorClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ClientEndpointConfig.Configurator of ");
err.append(configuratorClass);
throw new DeploymentException(err.toString(),e);
}
}
}
private <T> void addAll(T[] arr, List<T> lst)
{
if (arr == null)
{
return;
}
for (T t : arr)
{
lst.add(t);
}
}
@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 subprotocols;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
}

View File

@ -27,19 +27,17 @@ import javax.websocket.SendHandler;
public class JsrAsyncRemote implements RemoteEndpoint.Async
{
private final org.eclipse.jetty.websocket.api.RemoteEndpoint jettyRemote;
protected JsrAsyncRemote(org.eclipse.jetty.websocket.api.RemoteEndpoint endpoint)
protected JsrAsyncRemote(JsrSession session)
{
this.jettyRemote = endpoint;
this.jettyRemote = session.getRemote();
}
@Override
public void flushBatch() throws IOException
{
// TODO Auto-generated method stub
}
@Override
@ -65,8 +63,8 @@ public class JsrAsyncRemote implements RemoteEndpoint.Async
@Override
public void sendBinary(ByteBuffer data, SendHandler handler)
{
// TODO Auto-generated method stub
// TODO: wrap the send handler?
jettyRemote.sendBytesByFuture(data);
}
@Override

View File

@ -19,16 +19,20 @@
package org.eclipse.jetty.websocket.jsr356;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.MessageHandler;
import javax.websocket.RemoteEndpoint.Async;
@ -36,36 +40,107 @@ import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
public class JsrSession extends WebSocketSession implements javax.websocket.Session
public class JsrSession extends WebSocketSession implements javax.websocket.Session, Configurable
{
private final ClientContainer container;
private static final Logger LOG = Log.getLogger(JsrSession.class);
private final CommonContainer container;
private final String id;
private final EndpointConfig config;
/** Factory for Decoders */
private final DecoderFactory decoderFactory;
private Map<Type, DecoderWrapper> activeDecoders;
/** Factory for Encoders */
private final EncoderFactory encoderFactory;
private Map<Type, EncoderWrapper> activeEncoders;
/** Factory for MessageHandlers */
private final MessageHandlerFactory messageHandlerFactory;
/** Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()} */
private final MessageHandlerWrapper wrappers[];
private Set<MessageHandler> messageHandlerSet;
private List<Extension> negotiatedExtensions;
private Map<String, List<String>> jsrParameterMap;
private Map<String, String> pathParameters = new HashMap<>();
private Map<String, Object> userProperties;
private Decoders decoders;
private MessageHandlers messageHandlers;
private JsrAsyncRemote asyncRemote;
private JsrBasicRemote basicRemote;
public JsrSession(URI requestURI, EventDriver websocket, LogicalConnection connection, ClientContainer container, String id)
{
super(requestURI,websocket,connection);
if (websocket instanceof AbstractJsrEventDriver)
{
this.config = ((AbstractJsrEventDriver)websocket).getConfig();
}
else
{
this.config = new BasicEndpointConfig();
}
this.container = container;
this.id = id;
this.decoderFactory = new DecoderFactory(container.getDecoderFactory());
this.encoderFactory = new EncoderFactory(container.getEncoderFactory());
this.activeDecoders = new HashMap<>();
this.activeEncoders = new HashMap<>();
this.messageHandlerFactory = new MessageHandlerFactory();
this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
this.messageHandlerSet = new HashSet<>();
}
@Override
public void addMessageHandler(MessageHandler listener) throws IllegalStateException
public void addMessageHandler(MessageHandler handler) throws IllegalStateException
{
this.messageHandlers.add(listener);
Objects.requireNonNull(handler,"MessageHandler cannot be null");
synchronized (wrappers)
{
for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
{
DecoderWrapper decoder = decoderFactory.getWrapperFor(metadata.getMessageClass());
MessageType key = decoder.getMetadata().getMessageType();
MessageHandlerWrapper other = wrappers[key.ordinal()];
if (other != null)
{
StringBuilder err = new StringBuilder();
err.append("Encountered duplicate MessageHandler handling message type <");
err.append(key.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
{
MessageHandlerWrapper wrapper = new MessageHandlerWrapper(handler,metadata,decoder);
wrappers[key.ordinal()] = wrapper;
}
}
// Update handlerSet
messageHandlerSet.clear();
for (MessageHandlerWrapper wrapper : wrappers)
{
if (wrapper == null)
{
// skip empty
continue;
}
messageHandlerSet.add(wrapper.getHandler());
}
}
}
@Override
@ -79,7 +154,7 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
{
if (asyncRemote == null)
{
asyncRemote = new JsrAsyncRemote(getRemote());
asyncRemote = new JsrAsyncRemote(this);
}
return asyncRemote;
}
@ -100,9 +175,14 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
return this.container;
}
public Decoders getDecodersFacade()
public DecoderFactory getDecoderFactory()
{
return this.decoders;
return decoderFactory;
}
public EncoderFactory getEncoderFactory()
{
return encoderFactory;
}
@Override
@ -129,20 +209,23 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
return getPolicy().getMaxTextMessageSize();
}
public MessageHandlers getMessageHandlerFacade()
public MessageHandlerFactory getMessageHandlerFactory()
{
return messageHandlers;
return messageHandlerFactory;
}
@Override
public Set<MessageHandler> getMessageHandlers()
{
return messageHandlers.getUnmodifiableHandlerSet();
return Collections.unmodifiableSet(messageHandlerSet);
}
public MessageHandlerWrapper getMessageHandlerWrapper(MessageType msgType)
public MessageHandlerWrapper getMessageHandlerWrapper(MessageType type)
{
return messageHandlers.getWrapper(msgType);
synchronized (wrappers)
{
return wrappers[type.ordinal()];
}
}
@Override
@ -211,18 +294,50 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
return config.getUserProperties();
}
@Override
public void init(EndpointConfig endpointconfig)
{
// Initialize encoders
for (EncoderWrapper wrapper : activeEncoders.values())
{
wrapper.getEncoder().init(config);
}
// Initialize decoders
for (DecoderWrapper wrapper : activeDecoders.values())
{
wrapper.getDecoder().init(config);
}
// Init message handlers
for (MessageHandlerWrapper wrapper : wrappers)
{
if (wrapper != null)
{
// TODO wrapper.init(config);
}
}
}
@Override
public void removeMessageHandler(MessageHandler handler)
{
messageHandlers.remove(handler);
}
public void setDecodersFacade(Decoders decoders)
{
this.decoders = decoders;
try
{
for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
{
DecoderMetadata decoder = decoderFactory.getMetadataFor(metadata.getMessageClass());
MessageType key = decoder.getMessageType();
wrappers[key.ordinal()] = null;
}
}
catch (IllegalStateException e)
{
LOG.warn("Unable to identify MessageHandler: " + handler.getClass().getName(),e);
}
}
@Override
@ -242,9 +357,4 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
{
getPolicy().setMaxTextMessageBufferSize(length);
}
public void setMessageHandlerFacade(MessageHandlers messageHandlers)
{
this.messageHandlers = messageHandlers;
}
}

View File

@ -16,69 +16,71 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.messages;
package org.eclipse.jetty.websocket.jsr356;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.MessageHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
import org.eclipse.jetty.websocket.jsr356.Decoders;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
/**
* Creates {@link MessageHandlerMetadata} objects from a provided {@link MessageHandler} classes.
* Factory for {@link MessageHandlerMetadata}
*/
public class MessageHandlerMetadataFactory
public class MessageHandlerFactory
{
private static final Logger LOG = Log.getLogger(MessageHandlerMetadataFactory.class);
private final Decoders decoders;
private static final Logger LOG = Log.getLogger(MessageHandlerFactory.class);
/** Registered MessageHandlers at this level */
private Map<Class<? extends MessageHandler>, List<MessageHandlerMetadata>> registered;
public MessageHandlerMetadataFactory(Decoders decoders)
public MessageHandlerFactory()
{
this.decoders = decoders;
}
public DecoderWrapper getDecoderWrapper(Class<?> onMessageClass)
{
return decoders.getDecoderWrapper(onMessageClass);
registered = new ConcurrentHashMap<>();
}
public List<MessageHandlerMetadata> getMetadata(Class<? extends MessageHandler> handler) throws IllegalStateException
{
List<MessageHandlerMetadata> ret = new ArrayList<>();
LOG.debug("getMetadata({})",handler);
List<MessageHandlerMetadata> ret = registered.get(handler);
if (ret != null)
{
return ret;
}
return register(handler);
}
public List<MessageHandlerMetadata> register(Class<? extends MessageHandler> handler)
{
List<MessageHandlerMetadata> metadatas = new ArrayList<>();
boolean partial = false;
if (MessageHandler.Partial.class.isAssignableFrom(handler))
{
LOG.debug("supports Partial: {}",handler);
partial = true;
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handler,MessageHandler.Partial.class);
LOG.debug("Partial message class: {}",onMessageClass);
MessageType onMessageType = identifyMessageType(onMessageClass);
LOG.debug("Partial message type: {}",onMessageType);
ret.add(new MessageHandlerMetadata(handler,onMessageType,onMessageClass,partial));
metadatas.add(new MessageHandlerMetadata(handler,onMessageClass,partial));
}
if (MessageHandler.Whole.class.isAssignableFrom(handler))
{
LOG.debug("supports Whole: {}",handler.getName());
partial = false;
Class<?> onMessageClass = ReflectUtils.findGenericClassFor(handler,MessageHandler.Whole.class);
LOG.debug("Whole message class: {}",onMessageClass);
MessageType onMessageType = identifyMessageType(onMessageClass);
LOG.debug("Whole message type: {}",onMessageType);
MessageHandlerMetadata metadata = new MessageHandlerMetadata(handler,onMessageType,onMessageClass,partial);
ret.add(metadata);
metadatas.add(new MessageHandlerMetadata(handler,onMessageClass,partial));
}
return ret;
}
private MessageType identifyMessageType(Class<?> onMessageClass) throws IllegalStateException
{
DecoderWrapper wrapper = getDecoderWrapper(onMessageClass);
return wrapper.getMetadata().getMessageType();
registered.put(handler,metadatas);
return metadatas;
}
}

View File

@ -16,15 +16,17 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.messages;
package org.eclipse.jetty.websocket.jsr356;
import javax.websocket.Decoder;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Partial;
import javax.websocket.MessageHandler.Whole;
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
/**
* Expose a {@link MessageHandler} instance along with its associated {@link MessageHandlerMetadata}
* Expose a {@link MessageHandler} instance along with its associated {@link MessageHandlerMetadata} and {@link DecoderWrapper}
*/
public class MessageHandlerWrapper
{

View File

@ -1,130 +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;
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 final MessageHandlerMetadataFactory factory;
/**
* Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()}
*/
private final MessageHandlerWrapper wrappers[];
public MessageHandlers(MessageHandlerMetadataFactory factory)
{
Objects.requireNonNull(factory,"MessageHandlerMetadataFactory cannot be null");
this.factory = factory;
this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
}
public void add(MessageHandler handler)
{
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;
}
}
}
}
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)
{
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);
}
}
}

View File

@ -124,7 +124,7 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
if (isAnnotation(annotation,OnMessage.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
// assertIsReturn(method,Void.TYPE); // no validation, it can be any return type
OnMessageCallable onmessage = new OnMessageCallable(pojo,method);
visitMethod(onmessage,pojo,method,paramsOnMessage,OnMessage.class);
@ -187,6 +187,7 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
{
for (IJsrParamId paramId : paramIds)
{
LOG.debug("{}.process()",paramId);
if (paramId.process(param,callable))
{
// Successfully identified

View File

@ -22,9 +22,9 @@ import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
public abstract class JsrCallable extends CallableMethod
@ -32,8 +32,6 @@ public abstract class JsrCallable extends CallableMethod
protected final Param[] params;
protected final Object[] args;
protected int idxSession = -1;
// Optional decoder (used for OnMessage)
protected Decoder decoder;
public JsrCallable(Class<?> pojo, Method method)
{
@ -56,7 +54,6 @@ public abstract class JsrCallable extends CallableMethod
public JsrCallable(JsrCallable copy)
{
this(copy.getPojo(),copy.getMethod());
this.decoder = copy.decoder;
this.idxSession = copy.idxSession;
System.arraycopy(copy.params,0,this.params,0,params.length);
System.arraycopy(copy.args,0,this.args,0,args.length);
@ -98,17 +95,12 @@ public abstract class JsrCallable extends CallableMethod
return null;
}
public Decoder getDecoder()
{
return decoder;
}
public Param[] getParams()
{
return params;
}
public void init(Session session)
public void init(JsrSession session)
{
// Default the session.
// Session is an optional parameter (always)
@ -135,8 +127,5 @@ public abstract class JsrCallable extends CallableMethod
}
}
public void setDecoder(Decoder decoder)
{
this.decoder = decoder;
}
public abstract void setDecoderClass(Class<? extends Decoder> decoderClass);
}

View File

@ -22,17 +22,17 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.util.Map;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.RemoteEndpoint;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
/**
* The live event methods found for a specific Annotated Endpoint
@ -94,25 +94,35 @@ public class JsrEvents
this.onPong = (metadata.onPong == null)?null:new OnMessagePongCallable(metadata.onPong);
}
public void callBinary(Object websocket, ByteBuffer buf, boolean fin) throws DecodeException
public void callBinary(RemoteEndpoint.Async endpoint, Object websocket, ByteBuffer buf, boolean fin) throws DecodeException
{
if (onBinary == null)
{
return;
}
onBinary.call(websocket,buf,fin);
Object ret = onBinary.call(websocket,buf,fin);
if (ret != null)
{
endpoint.sendObject(ret);
}
}
public void callBinaryStream(Object websocket, InputStream stream) throws DecodeException, IOException
public void callBinaryStream(RemoteEndpoint.Async endpoint, Object websocket, InputStream stream) throws DecodeException, IOException
{
if (onBinaryStream == null)
{
return;
}
onBinaryStream.call(websocket,stream);
Object ret = onBinaryStream.call(websocket,stream);
if (ret != null)
{
endpoint.sendObject(ret);
}
}
public void callClose(Object websocket, CloseInfo close)
public void callClose(Object websocket, CloseReason close)
{
if (onClose == null)
{
@ -139,22 +149,35 @@ public class JsrEvents
onOpen.call(websocket,config);
}
public void callText(Object websocket, String text, boolean fin) throws DecodeException
public void callText(RemoteEndpoint.Async endpoint, Object websocket, String text, boolean fin) throws DecodeException
{
if (onText == null)
{
return;
}
onText.call(websocket,text,fin);
Object ret = onText.call(websocket,text,fin);
if (ret != null)
{
endpoint.sendObject(ret);
}
}
public void callTextStream(Object websocket, Reader reader) throws DecodeException, IOException
public void callTextStream(RemoteEndpoint.Async endpoint, Object websocket, Reader reader) throws DecodeException, IOException
{
if (onTextStream == null)
{
return;
}
onTextStream.call(websocket,reader);
Object ret = onTextStream.call(websocket,reader);
if (ret != null)
{
endpoint.sendObject(ret);
}
}
public JsrMetadata<?> getMetadata()
{
return metadata;
}
public boolean hasBinary()
@ -177,10 +200,8 @@ public class JsrEvents
return (onTextStream != null);
}
public void init(Session session)
public void init(JsrSession session)
{
Map<String, String> pathParams = session.getPathParameters();
if (onOpen != null)
{
onOpen.init(session);

View File

@ -20,12 +20,16 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.annotation.Annotation;
import java.util.LinkedList;
import java.util.List;
import javax.websocket.Decoder;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
/**
* Static reference to a specific annotated classes metadata.
*
@ -96,7 +100,38 @@ public abstract class JsrMetadata<T extends Annotation>
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
{
/* do nothing */
for (Class<? extends Decoder> decoder : getConfiguredDecoders())
{
if (Decoder.Text.class.isAssignableFrom(decoder))
{
Class<?> type = ReflectUtils.findGenericClassFor(decoder,Decoder.Text.class);
params.add(new JsrParamIdTextDecoder(decoder,type));
continue;
}
if (Decoder.TextStream.class.isAssignableFrom(decoder))
{
Class<?> type = ReflectUtils.findGenericClassFor(decoder,Decoder.TextStream.class);
params.add(new JsrParamIdTextDecoder(decoder,type));
continue;
}
if (Decoder.Binary.class.isAssignableFrom(decoder))
{
Class<?> type = ReflectUtils.findGenericClassFor(decoder,Decoder.Binary.class);
params.add(new JsrParamIdBinaryDecoder(decoder,type));
continue;
}
if (Decoder.BinaryStream.class.isAssignableFrom(decoder))
{
Class<?> type = ReflectUtils.findGenericClassFor(decoder,Decoder.BinaryStream.class);
params.add(new JsrParamIdBinaryDecoder(decoder,type));
continue;
}
throw new IllegalStateException("Invalid Decoder: " + decoder);
}
}
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
@ -105,4 +140,6 @@ public abstract class JsrMetadata<T extends Annotation>
}
public abstract T getAnnotation();
protected abstract List<Class<? extends Decoder>> getConfiguredDecoders();
}

View File

@ -48,14 +48,14 @@ public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
if (param.type.isAssignableFrom(ByteBuffer.class))
{
param.bind(Role.MESSAGE_BINARY);
callable.setDecoder(ByteBufferDecoder.INSTANCE);
callable.setDecoderClass(ByteBufferDecoder.class);
return true;
}
if (param.type.isAssignableFrom(byte[].class))
{
param.bind(Role.MESSAGE_BINARY);
callable.setDecoder(ByteArrayDecoder.INSTANCE);
callable.setDecoderClass(ByteArrayDecoder.class);
return true;
}

View File

@ -22,7 +22,6 @@ import javax.websocket.Decoder;
import javax.websocket.OnMessage;
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;
/**
@ -30,12 +29,13 @@ import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
*/
public class JsrParamIdBinaryDecoder extends JsrParamIdOnMessage implements IJsrParamId
{
private final DecoderWrapper ref;
private Class<?> supportedType;
private final Class<? extends Decoder> decoder;
private final Class<?> supportedType;
public JsrParamIdBinaryDecoder(DecoderWrapper ref)
public JsrParamIdBinaryDecoder(Class<? extends Decoder> decoder, Class<?> supportedType)
{
this.ref = ref;
this.decoder = decoder;
this.supportedType = supportedType;
}
@Override
@ -45,7 +45,7 @@ public class JsrParamIdBinaryDecoder extends JsrParamIdOnMessage implements IJsr
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_BINARY);
callable.setDecoder(ref.getDecoder());
callable.setDecoderClass(decoder);
return true;
}
return false;

View File

@ -43,7 +43,7 @@ public class JsrParamIdPong extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_PONG);
callable.setDecoder(new PongMessageDecoder());
callable.setDecoderClass(PongMessageDecoder.class);
return true;
}
return false;

View File

@ -54,7 +54,7 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
if (param.type.isAssignableFrom(String.class))
{
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(StringDecoder.INSTANCE);
callable.setDecoderClass(StringDecoder.class);
return true;
}
@ -63,56 +63,56 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(BooleanDecoder.INSTANCE);
callable.setDecoderClass(BooleanDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Byte.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(ByteDecoder.INSTANCE);
callable.setDecoderClass(ByteDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Character.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(CharacterDecoder.INSTANCE);
callable.setDecoderClass(CharacterDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Double.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(DoubleDecoder.INSTANCE);
callable.setDecoderClass(DoubleDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Float.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(FloatDecoder.INSTANCE);
callable.setDecoderClass(FloatDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Integer.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(IntegerDecoder.INSTANCE);
callable.setDecoderClass(IntegerDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Long.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(LongDecoder.INSTANCE);
callable.setDecoderClass(LongDecoder.class);
return true;
}
if (param.type.isAssignableFrom(Short.class))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoder(ShortDecoder.INSTANCE);
callable.setDecoderClass(ShortDecoder.class);
return true;
}

View File

@ -22,7 +22,6 @@ import javax.websocket.Decoder;
import javax.websocket.OnMessage;
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;
/**
@ -30,13 +29,13 @@ import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
*/
public class JsrParamIdTextDecoder extends JsrParamIdOnMessage implements IJsrParamId
{
private final DecoderWrapper ref;
private Class<?> supportedType;
private final Class<? extends Decoder> decoder;
private final Class<?> supportedType;
public JsrParamIdTextDecoder(DecoderWrapper ref)
public JsrParamIdTextDecoder(Class<? extends Decoder> decoder, Class<?> supportedType)
{
this.ref = ref;
this.supportedType = ref.getMetadata().getObjectType();
this.decoder = decoder;
this.supportedType = supportedType;
}
@Override
@ -46,7 +45,7 @@ public class JsrParamIdTextDecoder extends JsrParamIdOnMessage implements IJsrPa
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT_STREAM); // TODO: is this sane? for Text & TextStream ?
callable.setDecoder(ref.getDecoder());
callable.setDecoderClass(decoder);
return true;
}
return false;

View File

@ -22,10 +22,11 @@ import java.lang.reflect.Method;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.Decoder;
import javax.websocket.OnClose;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -64,9 +65,15 @@ public class OnCloseCallable extends JsrCallable
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxCloseReason = findIndexForRole(Role.CLOSE_REASON);
super.init(session);
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
{
/* ignore, not relevant for onClose */
}
}

View File

@ -20,8 +20,10 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method;
import javax.websocket.Decoder;
import javax.websocket.OnError;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
/**
* Callable for {@link OnError} annotated methods
@ -49,9 +51,16 @@ public class OnErrorCallable extends JsrCallable
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxThrowable = findIndexForRole(Param.Role.ERROR_CAUSE);
super.init(session);
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
{
/* ignore, not relevant for onClose */
}
}

View File

@ -24,8 +24,8 @@ import java.nio.ByteBuffer;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -52,23 +52,23 @@ public class OnMessageBinaryCallable extends OnMessageCallable
super(copy);
}
public void call(Object endpoint, ByteBuffer buf, boolean partialFlag) throws DecodeException
public Object call(Object endpoint, ByteBuffer buf, boolean partialFlag) throws DecodeException
{
super.args[idxMessageObject] = binaryDecoder.decode(buf);
if (idxPartialMessageFlag >= 0)
{
super.args[idxPartialMessageFlag] = partialFlag;
}
super.call(endpoint,super.args);
return super.call(endpoint,super.args);
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY);
assertRoleRequired(idxMessageObject,"Binary Message Object");
super.init(session);
assertDecoderRequired();
binaryDecoder = (Decoder.Binary<?>)getDecoder();
super.init(session);
}
}

View File

@ -25,8 +25,8 @@ import java.lang.reflect.Method;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -51,19 +51,19 @@ public class OnMessageBinaryStreamCallable extends OnMessageCallable
super(copy);
}
public void call(Object endpoint, InputStream stream) throws DecodeException, IOException
public Object call(Object endpoint, InputStream stream) throws DecodeException, IOException
{
super.args[idxMessageObject] = binaryDecoder.decode(stream);
super.call(endpoint,super.args);
return super.call(endpoint,super.args);
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY_STREAM);
assertRoleRequired(idxMessageObject,"Binary InputStream Message Object");
super.init(session);
assertDecoderRequired();
binaryDecoder = (Decoder.BinaryStream<?>)getDecoder();
super.init(session);
}
}

View File

@ -21,12 +21,19 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.InitException;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
public class OnMessageCallable extends JsrCallable
{
protected final Class<?> returnType;
protected Encoder returnEncoder;
protected Class<? extends Decoder> decoderClass;
protected Decoder decoder;
protected int idxPartialMessageFlag = -1;
protected int idxMessageObject = -1;
protected Param.Role messageRole;
@ -34,11 +41,15 @@ public class OnMessageCallable extends JsrCallable
public OnMessageCallable(Class<?> pojo, Method method)
{
super(pojo,method);
this.returnType = method.getReturnType();
}
public OnMessageCallable(OnMessageCallable copy)
{
super(copy);
this.returnType = copy.returnType;
this.decoderClass = copy.decoderClass;
this.decoder = copy.decoder;
this.idxPartialMessageFlag = copy.idxPartialMessageFlag;
this.idxMessageObject = copy.idxMessageObject;
this.messageRole = copy.messageRole;
@ -88,6 +99,16 @@ public class OnMessageCallable extends JsrCallable
return -1;
}
public Decoder getDecoder()
{
return decoder;
}
public Class<? extends Decoder> getDecoderClass()
{
return decoderClass;
}
public Param getMessageObjectParam()
{
if (idxMessageObject < 0)
@ -105,11 +126,46 @@ public class OnMessageCallable extends JsrCallable
return super.params[idxMessageObject];
}
public Encoder getReturnEncoder()
{
return returnEncoder;
}
public Class<?> getReturnType()
{
return returnType;
}
@Override
public void init(JsrSession session)
{
super.init(session);
this.returnEncoder = session.getEncoderFactory().getEncoder(returnType);
if (decoderClass != null)
{
try
{
this.decoder = decoderClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
throw new InitException("Unable to create decoder: " + decoderClass.getName(),e);
}
}
}
public boolean isPartialMessageSupported()
{
return (idxPartialMessageFlag >= 0);
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
{
this.decoderClass = decoderClass;
}
public void setPartialMessageFlag(Param param)
{
idxPartialMessageFlag = param.index;

View File

@ -24,9 +24,9 @@ import java.nio.ByteBuffer;
import javax.websocket.DecodeException;
import javax.websocket.OnMessage;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -47,14 +47,14 @@ public class OnMessagePongCallable extends OnMessageCallable
super(copy);
}
public void call(Object endpoint, ByteBuffer buf) throws DecodeException
public Object call(Object endpoint, ByteBuffer buf) throws DecodeException
{
super.args[idxMessageObject] = new JsrPongMessage(buf);
super.call(endpoint,super.args);
return super.call(endpoint,super.args);
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxMessageObject = findIndexForRole(Role.MESSAGE_PONG);
assertRoleRequired(idxMessageObject,"Pong Message Object");

View File

@ -24,8 +24,8 @@ import java.lang.reflect.Method;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -52,23 +52,23 @@ public class OnMessageTextCallable extends OnMessageCallable
super(copy);
}
public void call(Object endpoint, String str, boolean partialFlag) throws DecodeException
public Object call(Object endpoint, String str, boolean partialFlag) throws DecodeException
{
super.args[idxMessageObject] = textDecoder.decode(str);
if (idxPartialMessageFlag >= 0)
{
super.args[idxPartialMessageFlag] = partialFlag;
}
super.call(endpoint,super.args);
return super.call(endpoint,super.args);
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT);
assertRoleRequired(idxMessageObject,"Text Message Object");
super.init(session);
assertDecoderRequired();
textDecoder = (Decoder.Text<?>)getDecoder();
super.init(session);
}
}

View File

@ -25,8 +25,8 @@ import java.lang.reflect.Method;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -51,19 +51,19 @@ public class OnMessageTextStreamCallable extends OnMessageCallable
super(copy);
}
public void call(Object endpoint, Reader reader) throws DecodeException, IOException
public Object call(Object endpoint, Reader reader) throws DecodeException, IOException
{
super.args[idxMessageObject] = textDecoder.decode(reader);
super.call(endpoint,super.args);
return super.call(endpoint,super.args);
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT_STREAM);
assertRoleRequired(idxMessageObject,"Text Reader Message Object");
super.init(session);
assertDecoderRequired();
textDecoder = (Decoder.TextStream<?>)getDecoder();
super.init(session);
}
}

View File

@ -20,10 +20,11 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
@ -55,9 +56,15 @@ public class OnOpenCallable extends JsrCallable
}
@Override
public void init(Session session)
public void init(JsrSession session)
{
idxEndpointConfig = findIndexForRole(Role.ENDPOINT_CONFIG);
super.init(session);
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
{
/* ignore, not relevant for onClose */
}
}

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.websocket.jsr356.annotations;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
public class Param
{
@ -93,7 +93,7 @@ public class Param
StringBuilder str = new StringBuilder();
str.append("Param[");
str.append("index=").append(index);
str.append(",type=").append(MethodUtils.toString(type));
str.append(",type=").append(ReflectUtils.toShortName(type));
str.append(",role=").append(role);
if (pathParamName != null)
{

View File

@ -0,0 +1,111 @@
//
// ========================================================================
// 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.client;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.Extension;
public class AnnotatedClientEndpointConfig implements ClientEndpointConfig
{
private final List<Class<? extends Decoder>> decoders;
private final List<Class<? extends Encoder>> encoders;
private final List<Extension> extensions;
private final List<String> preferredSubprotocols;
private final Configurator configurator;
private Map<String, Object> userProperties;
public AnnotatedClientEndpointConfig(ClientEndpoint anno) throws DeploymentException
{
this.decoders = Collections.unmodifiableList(Arrays.asList(anno.decoders()));
this.encoders = Collections.unmodifiableList(Arrays.asList(anno.encoders()));
this.preferredSubprotocols = Collections.unmodifiableList(Arrays.asList(anno.subprotocols()));
// no extensions declared in annotation
this.extensions = Collections.emptyList();
// no userProperties in annotation
this.userProperties = new HashMap<>();
if (anno.configurator() == null)
{
this.configurator = EmptyConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ClientEndpoint.configurator() of ");
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
}
}
}
@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

@ -0,0 +1,85 @@
//
// ========================================================================
// 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.client;
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 EmptyClientEndpointConfig implements ClientEndpointConfig
{
private final List<Class<? extends Decoder>> decoders;
private final List<Class<? extends Encoder>> encoders;
private final List<Extension> extensions;
private final List<String> preferredSubprotocols;
private final Configurator configurator;
private Map<String, Object> userProperties;
public EmptyClientEndpointConfig()
{
this.decoders = new ArrayList<>();
this.encoders = new ArrayList<>();
this.preferredSubprotocols = new ArrayList<>();
this.extensions = new ArrayList<>();
this.userProperties = new HashMap<>();
this.configurator = EmptyConfigurator.INSTANCE;
}
@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

@ -0,0 +1,42 @@
//
// ========================================================================
// 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.client;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.HandshakeResponse;
public class EmptyConfigurator extends ClientEndpointConfig.Configurator
{
public static final EmptyConfigurator INSTANCE = new EmptyConfigurator();
@Override
public void afterResponse(HandshakeResponse hr)
{
// do nothing
}
@Override
public void beforeRequest(Map<String, List<String>> headers)
{
// do nothing
}
}

View File

@ -16,10 +16,9 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
package org.eclipse.jetty.websocket.jsr356.client;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.DeploymentException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@ -27,6 +26,8 @@ import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.jsr356.endpoints.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
* Event Driver for classes annotated with &#064;{@link ClientEndpoint}
@ -62,7 +63,7 @@ public class JsrClientEndpointImpl implements EventDriverImpl
JsrEvents events = new JsrEvents(basemetadata); // copy constructor.
// Create copy of base config
ClientEndpointConfig config = basemetadata.getEndpointConfigCopy();
AnnotatedClientEndpointConfig config = basemetadata.getConfig();
return new JsrAnnotatedEventDriver(policy,endpoint,events,config);
}

View File

@ -16,30 +16,22 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
package org.eclipse.jetty.websocket.jsr356.client;
import java.util.LinkedList;
import java.util.List;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
import org.eclipse.jetty.websocket.jsr356.Decoders;
import org.eclipse.jetty.websocket.jsr356.JettyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
{
private final ClientEndpoint endpoint;
private final JettyClientEndpointConfig config;
private final Decoders decoders;
private final AnnotatedClientEndpointConfig config;
public JsrClientMetadata(ClientContainer container, Class<?> websocket) throws DeploymentException
{
@ -53,31 +45,7 @@ public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
}
this.endpoint = anno;
this.config = new JettyClientEndpointConfig(anno);
this.decoders = new Decoders(container.getDecoderMetadataFactory(),config);
}
@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);
}
this.config = new AnnotatedClientEndpointConfig(anno);
}
@Override
@ -86,9 +54,14 @@ public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
return endpoint;
}
public ClientEndpointConfig getEndpointConfigCopy() throws DeploymentException
public AnnotatedClientEndpointConfig getConfig()
{
// Copy constructor
return new JettyClientEndpointConfig(config);
return config;
}
@Override
protected List<Class<? extends Decoder>> getConfiguredDecoders()
{
return config.getDecoders();
}
}

View File

@ -30,16 +30,6 @@ import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
public class Encoders
{
private static final List<Class<?>> TYPES = new ArrayList<>();
static
{
TYPES.add(Encoder.Text.class);
TYPES.add(Encoder.TextStream.class);
TYPES.add(Encoder.Binary.class);
TYPES.add(Encoder.BinaryStream.class);
}
private static class EncoderRef
{
Class<?> type;
@ -52,6 +42,16 @@ public class Encoders
}
}
private static final List<Class<?>> TYPES = new ArrayList<>();
static
{
TYPES.add(Encoder.Text.class);
TYPES.add(Encoder.TextStream.class);
TYPES.add(Encoder.Binary.class);
TYPES.add(Encoder.BinaryStream.class);
}
private final List<EncoderRef> encoders;
public Encoders()
@ -127,6 +127,12 @@ public class Encoders
this.encoders.add(ref);
}
public void addAll(Class<? extends Encoder>[] encoderArr)
{
// TODO Auto-generated method stub
}
public Encoder getEncoder(Class<?> type)
{
Class<?> targetType = type;

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.endpoints;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo;
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.jsr356.JsrSession;
public abstract class AbstractJsrEventDriver extends AbstractEventDriver implements EventDriver
{
protected final EndpointConfig config;
protected JsrSession jsrsession;
private boolean hasCloseBeenCalled = false;
public AbstractJsrEventDriver(WebSocketPolicy policy, Object websocket, EndpointConfig config)
{
super(policy,websocket);
this.config = config;
}
public EndpointConfig getConfig()
{
return config;
}
public Session getJsrSession()
{
return this.jsrsession;
}
protected void init(JsrSession jsrsession)
{
}
@Override
public final void onClose(CloseInfo close)
{
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());
onClose(closereason);
}
protected abstract void onClose(CloseReason closereason);
@Override
public void onFrame(Frame frame)
{
/* Ignored, not supported by JSR-356 */
}
@Override
public final void openSession(WebSocketSession session)
{
// Cast should be safe, as it was created by JsrSessionFactory
this.jsrsession = (JsrSession)session;
// Allow jsr session to init
this.jsrsession.init(config);
// Allow event driver to init itself
init(jsrsession);
// Allow end-user socket to adjust configuration
super.openSession(session);
}
public void setEndpointconfig(EndpointConfig endpointconfig)
{
throw new RuntimeException("Why are you reconfiguring the endpoint?");
// this.config = endpointconfig;
}
}

View File

@ -23,19 +23,16 @@ import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo;
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.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
@ -47,24 +44,21 @@ import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
/**
* Base implementation for JSR-356 Annotated event drivers.
*/
public class JsrAnnotatedEventDriver extends AbstractEventDriver implements EventDriver
public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver implements EventDriver
{
private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
private final JsrEvents events;
private final EndpointConfig endpointconfig;
private boolean hasCloseBeenCalled = false;
private JsrSession jsrsession;
public JsrAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JsrEvents events, EndpointConfig endpointconfig)
public JsrAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JsrEvents events, EndpointConfig config)
{
super(policy,websocket);
super(policy,websocket,config);
this.events = events;
this.endpointconfig = endpointconfig;
}
public Session getJsrSession()
@Override
protected void init(JsrSession jsrsession)
{
return this.jsrsession;
this.events.init(jsrsession);
}
/**
@ -90,7 +84,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
// Partial Message Support (does not use messageAppender)
try
{
events.callBinary(websocket,buffer,fin);
events.callBinary(jsrsession.getAsyncRemote(),websocket,buffer,fin);
}
catch (DecodeException e)
{
@ -125,7 +119,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
{
try
{
events.callBinaryStream(websocket,stream);
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (DecodeException | IOException e)
{
@ -159,7 +153,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
try
{
// FIN is always true here
events.callBinary(websocket,ByteBuffer.wrap(data),true);
events.callBinary(jsrsession.getAsyncRemote(),websocket,ByteBuffer.wrap(data),true);
}
catch (DecodeException e)
{
@ -168,21 +162,15 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
}
@Override
public void onClose(CloseInfo close)
protected void onClose(CloseReason closereason)
{
if (hasCloseBeenCalled)
{
// avoid duplicate close events (possible when using harsh Session.disconnect())
return;
}
hasCloseBeenCalled = true;
events.callClose(websocket,close);
events.callClose(websocket,closereason);
}
@Override
public void onConnect()
{
events.callOpen(websocket,endpointconfig);
events.callOpen(websocket,config);
}
@Override
@ -208,7 +196,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
{
try
{
events.callBinaryStream(websocket,stream);
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (DecodeException | IOException e)
{
@ -221,7 +209,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
{
try
{
events.callTextStream(websocket,reader);
events.callTextStream(jsrsession.getAsyncRemote(),websocket,reader);
}
catch (DecodeException | IOException e)
{
@ -254,7 +242,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
try
{
String text = BufferUtil.toUTF8String(buffer);
events.callText(websocket,text,fin);
events.callText(jsrsession.getAsyncRemote(),websocket,text,fin);
}
catch (DecodeException e)
{
@ -291,7 +279,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
{
try
{
events.callTextStream(websocket,stream);
events.callTextStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (DecodeException | IOException e)
{
@ -322,7 +310,7 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
try
{
// FIN is always true here
events.callText(websocket,message,true);
events.callText(jsrsession.getAsyncRemote(),websocket,message,true);
}
catch (DecodeException e)
{
@ -330,17 +318,6 @@ public class JsrAnnotatedEventDriver extends AbstractEventDriver implements Even
}
}
@Override
public void openSession(WebSocketSession session)
{
// Cast should be safe, as it was created by JsrSessionFactory
this.jsrsession = (JsrSession)session;
// Initialize the events
this.events.init(jsrsession);
// TODO: Initialize the decoders
super.openSession(session);
}
@Override
public String toString()
{

View File

@ -24,65 +24,38 @@ import java.io.Reader;
import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Whole;
import javax.websocket.Session;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo;
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.message.MessageInputStream;
import org.eclipse.jetty.websocket.common.message.MessageReader;
import org.eclipse.jetty.websocket.jsr356.ContainerService;
import org.eclipse.jetty.websocket.jsr356.Decoders;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.MessageHandlers;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.BinaryWholeMessage;
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerMetadataFactory;
import org.eclipse.jetty.websocket.jsr356.messages.MessageHandlerWrapper;
import org.eclipse.jetty.websocket.jsr356.messages.TextPartialMessage;
import org.eclipse.jetty.websocket.jsr356.messages.TextWholeMessage;
/**
* EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
*/
public class JsrEndpointEventDriver extends AbstractEventDriver implements EventDriver
public class JsrEndpointEventDriver extends AbstractJsrEventDriver implements EventDriver
{
private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class);
private final Endpoint endpoint;
private JsrSession jsrsession;
private EndpointConfig endpointconfig;
private boolean hasCloseBeenCalled = false;
public JsrEndpointEventDriver(WebSocketPolicy policy, Endpoint endpoint, EndpointConfig config)
{
super(policy,endpoint);
super(policy,endpoint,config);
this.endpoint = endpoint;
this.endpointconfig = config;
}
public EndpointConfig getEndpointconfig()
{
return endpointconfig;
}
public Session getJsrSession()
{
return this.jsrsession;
}
@Override
@ -137,27 +110,18 @@ public class JsrEndpointEventDriver extends AbstractEventDriver implements Event
}
@Override
public void onClose(CloseInfo close)
protected void onClose(CloseReason closereason)
{
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
public void onConnect()
{
LOG.debug("onConnect({}, {})",jsrsession,endpointconfig);
LOG.debug("onConnect({}, {})",jsrsession,config);
try
{
endpoint.onOpen(jsrsession,endpointconfig);
endpoint.onOpen(jsrsession,config);
}
catch (Throwable t)
{
@ -241,41 +205,6 @@ public class JsrEndpointEventDriver extends AbstractEventDriver implements Event
/* Ignored, handled by TextWholeMessage */
}
@Override
public void openSession(WebSocketSession session)
{
// Cast should be safe, as it was created by JsrSessionFactory
this.jsrsession = (JsrSession)session;
try
{
// Create Decoders
ContainerService container = (ContainerService)jsrsession.getContainer();
Decoders decoders = new Decoders(container.getDecoderMetadataFactory(),endpointconfig);
jsrsession.setDecodersFacade(decoders);
// Create MessageHandlers
MessageHandlerMetadataFactory metadataFactory = new MessageHandlerMetadataFactory(decoders);
MessageHandlers messageHandlers = new MessageHandlers(metadataFactory);
jsrsession.setMessageHandlerFacade(messageHandlers);
}
catch (DeploymentException e)
{
throw new WebSocketException(e);
}
// Allow end-user socket to adjust configuration
super.openSession(session);
// Initialize Decoders
jsrsession.getDecodersFacade().init(endpointconfig);
}
public void setEndpointconfig(EndpointConfig endpointconfig)
{
this.endpointconfig = endpointconfig;
}
@Override
public String toString()
{

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientEndpointImpl;
public class JsrEventDriverFactory extends EventDriverFactory
{

View File

@ -26,6 +26,7 @@ import javax.websocket.MessageHandler.Partial;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class BinaryPartialMessage implements MessageAppender
{

View File

@ -29,6 +29,7 @@ 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;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class BinaryWholeMessage extends SimpleBinaryMessage
{

View File

@ -26,6 +26,7 @@ import javax.websocket.MessageHandler.Partial;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class TextPartialMessage implements MessageAppender
{

View File

@ -27,6 +27,7 @@ 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;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class TextWholeMessage extends SimpleTextMessage
{

View File

@ -16,31 +16,37 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
package org.eclipse.jetty.websocket.jsr356.metadata;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
/**
* Metadata for a {@link Decoder}
* Immutable Metadata for a {@link Decoder}
*/
public class DecoderMetadata
{
/** The Class for the Decoder itself */
private final Class<? extends Decoder> decoderClass;
/** The Class that the Decoder declares it decodes */
private final Class<?> objType;
private final Class<? extends Decoder> decoder;
/** The Basic type of message the decoder handles */
private final MessageType messageType;
/** Flag indicating if Decoder is for streaming (or not) */
private final boolean streamed;
public DecoderMetadata(Class<?> objType, Class<? extends Decoder> decoder, MessageType messageType, boolean streamed)
public DecoderMetadata(Class<?> objType, Class<? extends Decoder> decoderClass, MessageType messageType, boolean streamed)
{
this.objType = objType;
this.decoder = decoder;
this.decoderClass = decoderClass;
this.messageType = messageType;
this.streamed = streamed;
}
public Class<? extends Decoder> getDecoder()
public Class<? extends Decoder> getDecoderClass()
{
return decoder;
return decoderClass;
}
public MessageType getMessageType()

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.metadata;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
/**
* Immutable Metadata for a {@link Encoder}
*/
public class EncoderMetadata
{
/** The Class for the Encoder itself */
private final Class<? extends Encoder> encoderClass;
/** The Class that the Encoder declares it encodes */
private final Class<?> objType;
/** The Basic type of message the encoder handles */
private final MessageType messageType;
/** Flag indicating if Encoder is for streaming (or not) */
private final boolean streamed;
public EncoderMetadata(Class<?> objType, Class<? extends Encoder> encoderClass, MessageType messageType, boolean streamed)
{
this.objType = objType;
this.encoderClass = encoderClass;
this.messageType = messageType;
this.streamed = streamed;
}
public Class<? extends Encoder> getEncoderClass()
{
return encoderClass;
}
public MessageType getMessageType()
{
return messageType;
}
public Class<?> getObjectType()
{
return objType;
}
public boolean isStreamed()
{
return streamed;
}
}

View File

@ -16,15 +16,12 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.messages;
package org.eclipse.jetty.websocket.jsr356.metadata;
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.
* An immutable metadata for a {@link MessageHandler}, 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.
@ -49,43 +46,12 @@ public class MessageHandlerMetadata
* 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)
public MessageHandlerMetadata(Class<? extends MessageHandler> handlerClass, 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()
@ -98,23 +64,6 @@ public class MessageHandlerMetadata
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

@ -19,9 +19,6 @@
package org.eclipse.jetty.websocket.jsr356.utils;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
@ -29,49 +26,19 @@ import java.util.Set;
*/
public class DeploymentTypeUtils
{
private static final Map<Class<?>, Class<?>> PRIMITIVE_CLASS_MAP;
private static final Map<Class<?>, Class<?>> CLASS_PRIMITIVE_MAP;
static
{
Map<Class<?>, Class<?>> primitives = new HashMap<>();
// Map of classes to primitive types
primitives.put(Boolean.class,Boolean.TYPE);
primitives.put(Byte.class,Byte.TYPE);
primitives.put(Character.class,Character.TYPE);
primitives.put(Double.class,Double.TYPE);
primitives.put(Float.class,Float.TYPE);
primitives.put(Integer.class,Integer.TYPE);
primitives.put(Long.class,Long.TYPE);
primitives.put(Short.class,Short.TYPE);
primitives.put(Void.class,Void.TYPE);
CLASS_PRIMITIVE_MAP = Collections.unmodifiableMap(primitives);
// Map of primitive types to classes
Map<Class<?>, Class<?>> types = new HashMap<>();
for (Map.Entry<Class<?>, Class<?>> classEntry : primitives.entrySet())
{
types.put(classEntry.getValue(),classEntry.getKey());
}
PRIMITIVE_CLASS_MAP = Collections.unmodifiableMap(types);
}
public static Class<?> getPrimitiveClass(Class<?> primitiveType)
{
return PRIMITIVE_CLASS_MAP.get(primitiveType);
return Primitives.getPrimitiveClass(primitiveType);
}
public static Set<Class<?>> getPrimitiveClasses()
{
return CLASS_PRIMITIVE_MAP.keySet();
return Primitives.getPrimitiveClasses();
}
public static Set<Class<?>> getPrimitives()
{
return PRIMITIVE_CLASS_MAP.keySet();
return Primitives.getPrimitives();
}
public static boolean isAssignableClass(Class<?> type, Class<?> targetClass)
@ -88,7 +55,7 @@ public class DeploymentTypeUtils
if (!targetClass.isPrimitive())
{
// going from primitive to class, make sure it matches
Class<?> primitive = CLASS_PRIMITIVE_MAP.get(targetClass);
Class<?> primitive = Primitives.getPrimitiveType(targetClass);
return primitive.equals(type);
}
@ -130,7 +97,7 @@ public class DeploymentTypeUtils
if (targetClass.isPrimitive())
{
// Class to Primitive (autoboxing)
Class<?> targetPrimitive = CLASS_PRIMITIVE_MAP.get(type);
Class<?> targetPrimitive = Primitives.getPrimitiveType(type);
return targetClass.equals(targetPrimitive);
}
}

View File

@ -19,97 +19,11 @@
package org.eclipse.jetty.websocket.jsr356.utils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
public final class MethodUtils
{
private static StringBuilder appendTypeName(StringBuilder sb, Type type, boolean ellipses)
{
if (type instanceof Class<?>)
{
Class<?> ctype = (Class<?>)type;
if (ctype.isArray())
{
try
{
int dimensions = 0;
while (ctype.isArray())
{
dimensions++;
ctype = ctype.getComponentType();
}
sb.append(ctype.getName());
for (int i = 0; i < dimensions; i++)
{
if (ellipses)
{
sb.append("...");
}
else
{
sb.append("[]");
}
}
return sb;
}
catch (Throwable ignore)
{
// ignore
}
}
sb.append(ctype.getName());
}
else
{
sb.append(type.toString());
}
return sb;
}
public static String toString(Class<?> pojo, Method method)
{
StringBuilder str = new StringBuilder();
str.append(pojo.getName());
str.append(" ");
// method modifiers
int mod = method.getModifiers() & Modifier.methodModifiers();
if (mod != 0)
{
str.append(Modifier.toString(mod)).append(' ');
}
// return type
Type retType = method.getGenericReturnType();
appendTypeName(str,retType,false).append(' ');
// method name
str.append(method.getName());
// method parameters
str.append('(');
Type[] params = method.getGenericParameterTypes();
for (int j = 0; j < params.length; j++)
{
boolean ellipses = method.isVarArgs() && (j == (params.length - 1));
appendTypeName(str,params[j],ellipses);
if (j < (params.length - 1))
{
str.append(", ");
}
}
str.append(')');
// TODO: show exceptions?
return str.toString();
}
public static String toString(Type type)
{
StringBuilder str = new StringBuilder();
appendTypeName(str,type,true);
return str.toString();
return ReflectUtils.toString(pojo,method);
}
}

View File

@ -0,0 +1,77 @@
//
// ========================================================================
// 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.utils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Primitives
{
private static final Map<Class<?>, Class<?>> PRIMITIVE_CLASS_MAP;
private static final Map<Class<?>, Class<?>> CLASS_PRIMITIVE_MAP;
static
{
Map<Class<?>, Class<?>> primitives = new HashMap<>();
// Map of classes to primitive types
primitives.put(Boolean.class,Boolean.TYPE);
primitives.put(Byte.class,Byte.TYPE);
primitives.put(Character.class,Character.TYPE);
primitives.put(Double.class,Double.TYPE);
primitives.put(Float.class,Float.TYPE);
primitives.put(Integer.class,Integer.TYPE);
primitives.put(Long.class,Long.TYPE);
primitives.put(Short.class,Short.TYPE);
primitives.put(Void.class,Void.TYPE);
CLASS_PRIMITIVE_MAP = Collections.unmodifiableMap(primitives);
// Map of primitive types to classes
Map<Class<?>, Class<?>> types = new HashMap<>();
for (Map.Entry<Class<?>, Class<?>> classEntry : primitives.entrySet())
{
types.put(classEntry.getValue(),classEntry.getKey());
}
PRIMITIVE_CLASS_MAP = Collections.unmodifiableMap(types);
}
public static Class<?> getPrimitiveClass(Class<?> primitiveType)
{
return PRIMITIVE_CLASS_MAP.get(primitiveType);
}
public static Set<Class<?>> getPrimitiveClasses()
{
return CLASS_PRIMITIVE_MAP.keySet();
}
public static Set<Class<?>> getPrimitives()
{
return PRIMITIVE_CLASS_MAP.keySet();
}
public static Class<?> getPrimitiveType(Class<?> primitiveClass)
{
return CLASS_PRIMITIVE_MAP.get(primitiveClass);
}
}

View File

@ -18,6 +18,9 @@
package org.eclipse.jetty.websocket.jsr356.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@ -49,6 +52,17 @@ public class ReflectUtils
return (genericClass == null) && (genericType != null) && (genericType instanceof TypeVariable<?>);
}
public void setGenericFromType(Type type, int index)
{
// debug("setGenericFromType(%s,%d)",toShortName(type),index);
this.genericType = type;
this.genericIndex = index;
if (type instanceof Class)
{
this.genericClass = (Class<?>)type;
}
}
@Override
public String toString()
{
@ -64,17 +78,51 @@ public class ReflectUtils
builder.append("]");
return builder.toString();
}
}
public void setGenericFromType(Type type, int index)
private static StringBuilder appendTypeName(StringBuilder sb, Type type, boolean ellipses)
{
if (type instanceof Class<?>)
{
// debug("setGenericFromType(%s,%d)",toShortName(type),index);
this.genericType = type;
this.genericIndex = index;
if (type instanceof Class)
Class<?> ctype = (Class<?>)type;
if (ctype.isArray())
{
this.genericClass = (Class<?>)type;
try
{
int dimensions = 0;
while (ctype.isArray())
{
dimensions++;
ctype = ctype.getComponentType();
}
sb.append(ctype.getName());
for (int i = 0; i < dimensions; i++)
{
if (ellipses)
{
sb.append("...");
}
else
{
sb.append("[]");
}
}
return sb;
}
catch (Throwable ignore)
{
// ignore
}
}
sb.append(ctype.getName());
}
else
{
sb.append(type.toString());
}
return sb;
}
/**
@ -99,6 +147,83 @@ public class ReflectUtils
return null;
}
private static int findTypeParameterIndex(Class<?> clazz, TypeVariable<?> needVar)
{
// debug("findTypeParameterIndex(%s, [%s])",toShortName(clazz),toShortName(needVar));
TypeVariable<?> params[] = clazz.getTypeParameters();
for (int i = 0; i < params.length; i++)
{
if (params[i].getName().equals(needVar.getName()))
{
// debug("Type Parameter found at index: [%d]",i);
return i;
}
}
// debug("Type Parameter NOT found");
return -1;
}
public static boolean isDefaultConstructable(Class<?> clazz)
{
int mods = clazz.getModifiers();
if (Modifier.isAbstract(mods) || !Modifier.isPublic(mods))
{
// Needs to be public, non-abstract
return false;
}
Class<?>[] noargs = new Class<?>[0];
try
{
// Needs to have a no-args constructor
Constructor<?> constructor = clazz.getConstructor(noargs);
// Constructor needs to be public
return Modifier.isPublic(constructor.getModifiers());
}
catch (NoSuchMethodException | SecurityException e)
{
return false;
}
}
private static boolean resolveGenericRef(GenericRef ref, Class<?> clazz, Type type)
{
if (type instanceof Class)
{
if (type == ref.ifaceClass)
{
// is this a straight ref or a TypeVariable?
// debug("Found ref (as class): %s",toShortName(type));
ref.setGenericFromType(type,0);
return true;
}
else
{
// Keep digging
return resolveGenericRef(ref,type);
}
}
if (type instanceof ParameterizedType)
{
ParameterizedType ptype = (ParameterizedType)type;
Type rawType = ptype.getRawType();
if (rawType == ref.ifaceClass)
{
// debug("Found ref on [%s] as ParameterizedType [%s]",toShortName(clazz),toShortName(ptype));
// Always get the raw type parameter, let unwrap() solve for what it is
ref.setGenericFromType(ptype.getActualTypeArguments()[0],0);
return true;
}
else
{
// Keep digging
return resolveGenericRef(ref,rawType);
}
}
return false;
}
private static boolean resolveGenericRef(GenericRef ref, Type type)
{
if ((type == null) || (type == Object.class))
@ -181,72 +306,6 @@ public class ReflectUtils
return false;
}
private static int findTypeParameterIndex(Class<?> clazz, TypeVariable<?> needVar)
{
// debug("findTypeParameterIndex(%s, [%s])",toShortName(clazz),toShortName(needVar));
TypeVariable<?> params[] = clazz.getTypeParameters();
for (int i = 0; i < params.length; i++)
{
if (params[i].getName().equals(needVar.getName()))
{
// debug("Type Parameter found at index: [%d]",i);
return i;
}
}
// debug("Type Parameter NOT found");
return -1;
}
private static boolean resolveGenericRef(GenericRef ref, Class<?> clazz, Type type)
{
if (type instanceof Class)
{
if (type == ref.ifaceClass)
{
// is this a straight ref or a TypeVariable?
// debug("Found ref (as class): %s",toShortName(type));
ref.setGenericFromType(type,0);
return true;
}
else
{
// Keep digging
return resolveGenericRef(ref,(Class<?>)type);
}
}
if (type instanceof ParameterizedType)
{
ParameterizedType ptype = (ParameterizedType)type;
Type rawType = ptype.getRawType();
if (rawType == ref.ifaceClass)
{
// debug("Found ref on [%s] as ParameterizedType [%s]",toShortName(clazz),toShortName(ptype));
// Always get the raw type parameter, let unwrap() solve for what it is
ref.setGenericFromType(ptype.getActualTypeArguments()[0],0);
return true;
}
else
{
// Keep digging
return resolveGenericRef(ref,(Class<?>)rawType);
}
}
return false;
}
public static String trimClassName(String name)
{
int idx = name.lastIndexOf('.');
name = name.substring(idx + 1);
idx = name.lastIndexOf('$');
if (idx >= 0)
{
name = name.substring(idx + 1);
}
return name;
}
public static String toShortName(Type type)
{
if (type == null)
@ -270,7 +329,9 @@ public class ReflectUtils
for (int i = 0; i < args.length; i++)
{
if (i > 0)
{
str.append(",");
}
str.append(args[i]);
}
str.append(">");
@ -279,4 +340,53 @@ public class ReflectUtils
return type.toString();
}
public static String toString(Class<?> pojo, Method method)
{
StringBuilder str = new StringBuilder();
str.append(pojo.getName());
str.append(" ");
// method modifiers
int mod = method.getModifiers() & Modifier.methodModifiers();
if (mod != 0)
{
str.append(Modifier.toString(mod)).append(' ');
}
// return type
Type retType = method.getGenericReturnType();
appendTypeName(str,retType,false).append(' ');
// method name
str.append(method.getName());
// method parameters
str.append('(');
Type[] params = method.getGenericParameterTypes();
for (int j = 0; j < params.length; j++)
{
boolean ellipses = method.isVarArgs() && (j == (params.length - 1));
appendTypeName(str,params[j],ellipses);
if (j < (params.length - 1))
{
str.append(", ");
}
}
str.append(')');
// TODO: show exceptions?
return str.toString();
}
public static String trimClassName(String name)
{
int idx = name.lastIndexOf('.');
name = name.substring(idx + 1);
idx = name.lastIndexOf('$');
if (idx >= 0)
{
name = name.substring(idx + 1);
}
return name;
}
}

View File

@ -0,0 +1,155 @@
//
// ========================================================================
// 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.Date;
import java.util.List;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ByteArrayDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ByteBufferDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.DateDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.IntegerDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.LongDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.StringDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.TimeDecoder;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.samples.DualDecoder;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
import org.eclipse.jetty.websocket.jsr356.samples.FruitDecoder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class DecoderFactoryTest
{
private DecoderFactory factory;
private void assertDecoderType(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));
}
private void assertMetadataFor(Class<?> type, Class<? extends Decoder> expectedDecoderClass, MessageType expectedType)
{
DecoderMetadata metadata = factory.getMetadataFor(type);
Assert.assertEquals("metadata.decoderClass",metadata.getDecoderClass(),expectedDecoderClass);
Assert.assertThat("metadata.messageType",metadata.getMessageType(),is(expectedType));
Assert.assertEquals("metadata.objectType",metadata.getObjectType(),type);
}
@Before
public void initDecoderFactory()
{
CommonContainer container = new ClientContainer();
// create factory based on parent factory with primitives.
factory = new DecoderFactory(container.getDecoderFactory());
}
@Test
public void testGetByteArrayDecoder()
{
assertDecoderType(ByteArrayDecoder.class,MessageType.BINARY,byte[].class);
}
@Test
public void testGetByteBufferDecoder()
{
assertDecoderType(ByteBufferDecoder.class,MessageType.BINARY,ByteBuffer.class);
}
@Test
public void testGetFruitDecoder()
{
assertDecoderType(FruitDecoder.class,MessageType.TEXT,Fruit.class);
}
@Test
public void testGetIntegerDecoder()
{
assertDecoderType(IntegerDecoder.class,MessageType.TEXT,Integer.TYPE);
}
@Test
public void testGetLongDecoder()
{
assertDecoderType(LongDecoder.class,MessageType.TEXT,Long.TYPE);
}
@Test
public void testGetMetadataForByteArray()
{
factory.register(ByteArrayDecoder.class);
assertMetadataFor(byte[].class,ByteArrayDecoder.class,MessageType.BINARY);
}
@Test
public void testGetMetadataForDate()
{
factory.register(DateDecoder.class);
assertMetadataFor(Date.class,DateDecoder.class,MessageType.TEXT);
}
@Test
public void testGetStringDecoder()
{
assertDecoderType(StringDecoder.class,MessageType.TEXT,String.class);
}
@Test
public void testGetTextDecoder_Dual()
{
try
{
// has duplicated support for the same target Type
factory.getMetadata(DualDecoder.class);
Assert.fail("Should have thrown IllegalStateException for attempting to register Decoders with duplicate implementation");
}
catch (IllegalStateException e)
{
Assert.assertThat(e.getMessage(),containsString("Duplicate"));
}
}
@Test
public void testRegisterDuplicate()
{
// Register the DateDecoder (decodes java.util.Date)
factory.register(DateDecoder.class);
try
{
// Register the TimeDecoder (which also wants to decode java.util.Date)
factory.register(TimeDecoder.class);
Assert.fail("Should have thrown IllegalStateException for attempting to register Decoders with duplicate implementation");
}
catch (IllegalStateException e)
{
Assert.assertThat(e.getMessage(),containsString("Duplicate"));
}
}
}

View File

@ -1,77 +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;
import static org.hamcrest.Matchers.*;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import org.eclipse.jetty.websocket.jsr356.decoders.CharacterDecoder;
import org.eclipse.jetty.websocket.jsr356.samples.DualDecoder;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
import org.eclipse.jetty.websocket.jsr356.samples.FruitDecoder;
import org.junit.Assert;
import org.junit.Test;
public class DecodersTest
{
private DecoderMetadataFactory factory = new DecoderMetadataFactory();
@Test
public void testGetTextDecoder_Character() throws DeploymentException
{
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
config.addDecoder(FruitDecoder.class);
Decoders decoders = new Decoders(factory,config);
Decoder txtDecoder = decoders.getDecoder(Character.class);
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
Assert.assertThat("Text Decoder",txtDecoder,instanceOf(CharacterDecoder.class));
}
@Test
public void testGetTextDecoder_Dual()
{
try
{
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
config.addDecoder(DualDecoder.class); // has duplicated support for the same target Type
@SuppressWarnings("unused")
Decoders decoders = new Decoders(factory,config);
Assert.fail("Should have thrown DeploymentException");
}
catch (DeploymentException e)
{
Assert.assertThat("Error Message",e.getMessage(),containsString("duplicate"));
}
}
@Test
public void testGetTextDecoder_Fruit() throws DeploymentException
{
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
config.addDecoder(FruitDecoder.class);
Decoders decoders = new Decoders(factory,config);
Decoder txtDecoder = decoders.getDecoder(Fruit.class);
Assert.assertThat("Text Decoder",txtDecoder,notNullValue());
Assert.assertThat("Text Decoder",txtDecoder,instanceOf(FruitDecoder.class));
}
}

View File

@ -1,73 +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;
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

@ -20,83 +20,90 @@ package org.eclipse.jetty.websocket.jsr356;
import static org.hamcrest.Matchers.*;
import java.net.URI;
import java.nio.ByteBuffer;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.DeploymentException;
import javax.websocket.MessageHandler;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
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.eclipse.jetty.websocket.jsr356.samples.DummyConnection;
import org.eclipse.jetty.websocket.jsr356.samples.DummyEndpoint;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MessageHandlersTest
public class JsrSessionTest
{
private MessageHandlerMetadataFactory factory;
private Decoders decoders;
private ClientContainer container;
private JsrSession session;
@Before
public void init() throws DeploymentException
public void initSession()
{
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
DecoderMetadataFactory metadataFactory = new DecoderMetadataFactory();
decoders = new Decoders(metadataFactory,config);
factory = new MessageHandlerMetadataFactory(decoders);
container = new ClientContainer();
String id = JsrSessionTest.class.getSimpleName();
URI requestURI = URI.create("ws://localhost/" + id);
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
ClientEndpointConfig config = new EmptyClientEndpointConfig();
DummyEndpoint websocket = new DummyEndpoint();
EventDriver driver = new JsrEndpointEventDriver(policy,websocket,config);
DummyConnection connection = new DummyConnection();
session = new JsrSession(requestURI,driver,connection,container,id);
}
@Test
public void testGetBinaryHandler() throws DeploymentException
public void testMessageHandlerBinary() throws DeploymentException
{
MessageHandlers mhs = new MessageHandlers(factory);
mhs.add(new ByteBufferPartialHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.BINARY);
session.addMessageHandler(new ByteBufferPartialHandler());
MessageHandlerWrapper wrapper = session.getMessageHandlerWrapper(MessageType.BINARY);
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteBufferPartialHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),ByteBuffer.class);
}
@Test
public void testGetBothHandler() throws DeploymentException
public void testMessageHandlerBoth() throws DeploymentException
{
MessageHandlers mhs = new MessageHandlers(factory);
mhs.add(new StringWholeHandler());
mhs.add(new ByteArrayWholeHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
session.addMessageHandler(new StringWholeHandler());
session.addMessageHandler(new ByteArrayWholeHandler());
MessageHandlerWrapper wrapper = session.getMessageHandlerWrapper(MessageType.TEXT);
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),String.class);
wrapper = mhs.getWrapper(MessageType.BINARY);
wrapper = session.getMessageHandlerWrapper(MessageType.BINARY);
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteArrayWholeHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),byte[].class);
}
@Test
public void testGetTextHandler() throws DeploymentException
public void testMessageHandlerReplaceTextHandler() throws DeploymentException
{
MessageHandlers mhs = new MessageHandlers(factory);
mhs.add(new StringWholeHandler());
MessageHandlerWrapper wrapper = mhs.getWrapper(MessageType.TEXT);
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),String.class);
}
@Test
public void testReplaceTextHandler() throws DeploymentException
{
MessageHandlers mhs = new MessageHandlers(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);
session.addMessageHandler(oldText); // add a TEXT handler
session.addMessageHandler(new ByteArrayWholeHandler()); // add BINARY handler
session.removeMessageHandler(oldText); // remove original TEXT handler
session.addMessageHandler(new LongMessageHandler()); // add new TEXT handler
MessageHandlerWrapper wrapper = session.getMessageHandlerWrapper(MessageType.BINARY);
Assert.assertThat("Binary Handler",wrapper.getHandler(),instanceOf(ByteArrayWholeHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),byte[].class);
wrapper = mhs.getWrapper(MessageType.TEXT);
wrapper = session.getMessageHandlerWrapper(MessageType.TEXT);
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(LongMessageHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),Long.class);
}
@Test
public void testMessageHandlerText() throws DeploymentException
{
session.addMessageHandler(new StringWholeHandler());
MessageHandlerWrapper wrapper = session.getMessageHandlerWrapper(MessageType.TEXT);
Assert.assertThat("Text Handler",wrapper.getHandler(),instanceOf(StringWholeHandler.class));
Assert.assertEquals("Message Class",wrapper.getMetadata().getMessageClass(),String.class);
}
}

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.messages;
package org.eclipse.jetty.websocket.jsr356;
import static org.hamcrest.Matchers.*;
@ -25,30 +25,25 @@ 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.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MessageHandlerMetadataFactoryTest
public class MessageHandlerFactoryTest
{
private MessageHandlerMetadataFactory factory;
private Decoders decoders;
private MessageHandlerFactory factory;
private DecoderFactory decoders;
@Before
public void init() throws DeploymentException
{
SimpleClientEndpointConfig config = new SimpleClientEndpointConfig();
DecoderMetadataFactory metadataFactory = new DecoderMetadataFactory();
decoders = new Decoders(metadataFactory,config);
factory = new MessageHandlerMetadataFactory(decoders);
ClientContainer container = new ClientContainer();
decoders = new DecoderFactory(container.getDecoderFactory());
factory = new MessageHandlerFactory();
}
@Test
@ -57,9 +52,10 @@ public class MessageHandlerMetadataFactoryTest
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));
MessageHandlerMetadata handlerMetadata = metadatas.get(0);
DecoderMetadata decoderMetadata = decoders.getMetadataFor(handlerMetadata.getMessageClass());
Assert.assertThat("Message Type",decoderMetadata.getMessageType(),is(MessageType.BINARY));
Assert.assertThat("Message Class",handlerMetadata.getMessageClass(),is((Type)byte[].class));
}
@Test
@ -68,8 +64,9 @@ public class MessageHandlerMetadataFactoryTest
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));
MessageHandlerMetadata handlerMetadata = metadatas.get(0);
DecoderMetadata decoderMetadata = decoders.getMetadataFor(handlerMetadata.getMessageClass());
Assert.assertThat("Message Type",decoderMetadata.getMessageType(),is(MessageType.TEXT));
Assert.assertThat("Message Class",handlerMetadata.getMessageClass(),is((Type)String.class));
}
}

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.decoders;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* Decode Date
*/
public class DateDecoder implements Decoder.Text<Date>
{
@Override
public Date decode(String s) throws DecodeException
{
try
{
return new SimpleDateFormat("yyyy.MM.dd").parse(s);
}
catch (ParseException e)
{
throw new DecodeException(s,e.getMessage(),e);
}
}
@Override
public void destroy()
{
}
@Override
public void init(EndpointConfig config)
{
}
@Override
public boolean willDecode(String s)
{
return true;
}
}

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.decoders;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* Decode Date and Time
*/
public class DateTimeDecoder implements Decoder.Text<Date>
{
@Override
public Date decode(String s) throws DecodeException
{
try
{
return new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z").parse(s);
}
catch (ParseException e)
{
throw new DecodeException(s,e.getMessage(),e);
}
}
@Override
public void destroy()
{
}
@Override
public void init(EndpointConfig config)
{
}
@Override
public boolean willDecode(String s)
{
return true;
}
}

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.decoders;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* Decode Time
*/
public class TimeDecoder implements Decoder.Text<Date>
{
@Override
public Date decode(String s) throws DecodeException
{
try
{
return new SimpleDateFormat("HH:mm:ss z").parse(s);
}
catch (ParseException e)
{
throw new DecodeException(s,e.getMessage(),e);
}
}
@Override
public void destroy()
{
}
@Override
public void init(EndpointConfig config)
{
}
@Override
public boolean willDecode(String s)
{
return true;
}
}

View File

@ -26,6 +26,7 @@ import javax.websocket.DeploymentException;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSocket;
import org.junit.Assert;

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrCallable;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicBinaryMessageByteBufferSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicCloseReasonSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicCloseReasonSocket;

View File

@ -35,6 +35,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.client.JsrClientMetadata;
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.InvalidErrorExceptionSocket;

View File

@ -0,0 +1,148 @@
//
// ========================================================================
// 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.samples;
import java.net.InetSocketAddress;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.io.IOState;
public class DummyConnection implements LogicalConnection
{
private IOState iostate;
public DummyConnection()
{
this.iostate = new IOState();
}
@Override
public void close()
{
}
@Override
public void close(int statusCode, String reason)
{
}
@Override
public void disconnect()
{
}
@Override
public ByteBufferPool getBufferPool()
{
return null;
}
@Override
public long getIdleTimeout()
{
return 0;
}
@Override
public IOState getIOState()
{
return this.iostate;
}
@Override
public InetSocketAddress getLocalAddress()
{
return null;
}
@Override
public long getMaxIdleTimeout()
{
return 0;
}
@Override
public WebSocketPolicy getPolicy()
{
return null;
}
@Override
public InetSocketAddress getRemoteAddress()
{
// TODO Auto-generated method stub
return null;
}
@Override
public WebSocketSession getSession()
{
return null;
}
@Override
public boolean isOpen()
{
return false;
}
@Override
public boolean isReading()
{
return false;
}
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
}
@Override
public void resume()
{
}
@Override
public void setMaxIdleTimeout(long ms)
{
}
@Override
public void setNextIncomingFrames(IncomingFrames incoming)
{
}
@Override
public void setSession(WebSocketSession session)
{
}
@Override
public SuspendToken suspend()
{
return null;
}
}

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.samples;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
public class DummyEndpoint extends Endpoint
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
/* do nothing */
}
}

View File

@ -1,4 +1,4 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
# org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG
org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG

View File

@ -0,0 +1,129 @@
//
// ========================================================================
// 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.server;
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.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
{
private final Class<?> endpointClass;
private final String path;
private final List<Class<? extends Decoder>> decoders;
private final List<Class<? extends Encoder>> encoders;
private final Configurator configurator;
private final List<String> subprotocols;
private Map<String, Object> userProperties;
private List<Extension> extensions;
public AnnotatedServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
{
this.decoders = Collections.unmodifiableList(Arrays.asList(anno.decoders()));
this.encoders = Collections.unmodifiableList(Arrays.asList(anno.encoders()));
this.subprotocols = Collections.unmodifiableList(Arrays.asList(anno.subprotocols()));
// supplied by init lifecycle
this.extensions = new ArrayList<>();
this.path = anno.value();
this.endpointClass = endpointClass;
// no userProperties in annotation
this.userProperties = new HashMap<>();
if (anno.configurator() == null)
{
this.configurator = BasicServerEndpointConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ClientEndpoint.configurator() of ");
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
}
}
}
@Override
public List<Class<? extends Encoder>> getEncoders()
{
return encoders;
}
@Override
public List<Class<? extends Decoder>> getDecoders()
{
return decoders;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
@Override
public String getPath()
{
return path;
}
@Override
public List<String> getSubprotocols()
{
return subprotocols;
}
@Override
public List<Extension> getExtensions()
{
return extensions;
}
@Override
public Configurator getConfigurator()
{
return configurator;
}
}

View File

@ -29,9 +29,10 @@ import javax.websocket.server.ServerEndpointConfig.Configurator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class JettyServerEndpointConfigurator extends Configurator
public class BasicServerEndpointConfigurator extends Configurator
{
private static final Logger LOG = Log.getLogger(JettyServerEndpointConfigurator.class);
private static final Logger LOG = Log.getLogger(BasicServerEndpointConfigurator.class);
public static final Configurator INSTANCE = new BasicServerEndpointConfigurator();
@Override
public boolean checkOrigin(String originHeaderValue)

View File

@ -1,209 +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.server;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
public class JettyServerEndpointConfig implements ServerEndpointConfig
{
public static class BaseConfigurator extends ServerEndpointConfig.Configurator
{
public static final BaseConfigurator INSTANCE = new BaseConfigurator();
}
private final Class<?> endpointClass;
private final String path;
private Configurator configurator;
private List<Class<? extends Decoder>> decoders;
private List<Class<? extends Encoder>> encoders;
private List<String> subprotocols;
private List<Extension> extensions;
private Map<String, Object> userProperties;
public JettyServerEndpointConfig(Class<?> endpointClass, ServerEndpoint anno) throws DeploymentException
{
this(endpointClass,anno.value());
addAll(anno.decoders(),this.decoders);
addAll(anno.encoders(),this.encoders);
addAll(anno.subprotocols(),this.subprotocols);
// no extensions declared in annotation
// no userProperties in annotation
if (anno.configurator() == null)
{
this.configurator = BaseConfigurator.INSTANCE;
}
else
{
try
{
this.configurator = anno.configurator().newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ServerEndpoint.configurator() of ");
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
}
}
}
public JettyServerEndpointConfig(Class<?> endpointClass, String path)
{
this.endpointClass = endpointClass;
this.path = path;
this.configurator = new BaseConfigurator();
this.decoders = new ArrayList<>();
this.encoders = new ArrayList<>();
this.subprotocols = new ArrayList<>();
this.extensions = new ArrayList<>();
this.userProperties = new HashMap<>();
}
/**
* Copy Constructor
*
* @param copy
* the endpoint configuration to copy
* @throws DeploymentException
*/
public JettyServerEndpointConfig(JettyServerEndpointConfig copy) throws DeploymentException
{
this(copy.endpointClass,copy.path);
this.decoders.addAll(copy.decoders);
this.encoders.addAll(copy.encoders);
this.subprotocols.addAll(copy.subprotocols);
this.extensions.addAll(copy.extensions);
this.userProperties.putAll(copy.userProperties);
if (copy.configurator instanceof BaseConfigurator)
{
this.configurator = BaseConfigurator.INSTANCE;
}
else
{
Class<? extends Configurator> configuratorClass = copy.configurator.getClass();
try
{
this.configurator = configuratorClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
StringBuilder err = new StringBuilder();
err.append("Unable to instantiate ServerEndpointConfig.Configurator of ");
err.append(configuratorClass);
throw new DeploymentException(err.toString(),e);
}
}
}
private <T> void addAll(T[] arr, List<T> lst)
{
if (arr == null)
{
return;
}
for (T t : arr)
{
lst.add(t);
}
}
@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 Class<?> getEndpointClass()
{
return endpointClass;
}
@Override
public List<Extension> getExtensions()
{
return extensions;
}
@Override
public String getPath()
{
return path;
}
@Override
public List<String> getSubprotocols()
{
return subprotocols;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("JettyServerEndpointConfig [endpointClass=");
builder.append(endpointClass);
builder.append(", path=");
builder.append(path);
builder.append(", configurator=");
builder.append(configurator);
builder.append(", decoders=");
builder.append(decoders);
builder.append(", encoders=");
builder.append(encoders);
builder.append(", subprotocols=");
builder.append(subprotocols);
builder.append(", extensions=");
builder.append(extensions);
builder.append("]");
return builder.toString();
}
}

View File

@ -67,7 +67,7 @@ public class JsrServerEndpointImpl implements EventDriverImpl
JsrEvents events = new JsrEvents(basemetadata); // copy constructor.
// Create copy of base config
ServerEndpointConfig config = basemetadata.getEndpointConfigCopy();
ServerEndpointConfig config = basemetadata.getConfig();
return new JsrAnnotatedEventDriver(policy,endpoint,events,config);
}

View File

@ -19,25 +19,24 @@
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.LinkedList;
import java.util.List;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
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.DecoderFactory;
import org.eclipse.jetty.websocket.jsr356.EncoderFactory;
import org.eclipse.jetty.websocket.jsr356.annotations.IJsrParamId;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdBinaryDecoder;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrParamIdTextDecoder;
public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
{
private final ServerEndpoint endpoint;
private final JettyServerEndpointConfig config;
private final Decoders decoders;
private final AnnotatedServerEndpointConfig config;
private final DecoderFactory decoders;
private final EncoderFactory encoders;
protected JsrServerMetadata(ServerContainer container, Class<?> websocket) throws DeploymentException
{
@ -50,8 +49,12 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
}
this.endpoint = anno;
this.config = new JettyServerEndpointConfig(websocket,anno);
this.decoders = new Decoders(container.getDecoderMetadataFactory(),config);
this.config = new AnnotatedServerEndpointConfig(websocket,anno);
this.decoders = new DecoderFactory(container.getDecoderFactory());
this.encoders = new EncoderFactory(container.getEncoderFactory());
this.decoders.registerAll(anno.decoders());
this.encoders.registerAll(anno.encoders());
}
@Override
@ -65,28 +68,11 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
{
params.addFirst(JsrParamPath.INSTANCE);
}
@Override
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
protected List<Class<? extends Decoder>> getConfiguredDecoders()
{
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);
}
return config.getDecoders();
}
@Override
@ -101,10 +87,9 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
return endpoint;
}
public ServerEndpointConfig getEndpointConfigCopy() throws DeploymentException
public AnnotatedServerEndpointConfig getConfig()
{
// Copy constructor
return new JettyServerEndpointConfig(config);
return config;
}
public String getPath()

View File

@ -60,7 +60,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
public void addEndpoint(JsrServerMetadata metadata) throws DeploymentException
{
addEndpoint(metadata.getEndpointConfigCopy());
addEndpoint(metadata.getConfig());
}
@Override

View File

@ -1 +1 @@
org.eclipse.jetty.websocket.jsr356.server.JettyServerEndpointConfigurator
org.eclipse.jetty.websocket.jsr356.server.BasicServerEndpointConfigurator

View File

@ -46,6 +46,6 @@ public class JettyServerEndpointConfiguratorTest
ServerEndpointConfig.Configurator configr = iter.next();
assertThat("Configurator",configr,notNullValue());
assertThat("COnfigurator type",configr,instanceOf(JettyServerEndpointConfigurator.class));
assertThat("COnfigurator type",configr,instanceOf(BasicServerEndpointConfigurator.class));
}
}

View File

@ -0,0 +1,79 @@
//
// ========================================================================
// 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.server;
import java.net.URI;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoReturnEndpoint;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
public class OnMessageReturnTest
{
@Rule
public TestingDir testdir = new TestingDir();
@Test
public void testEchoReturn() throws Exception
{
WSServer wsb = new WSServer(testdir,"app");
wsb.copyWebInf("empty-web.xml");
wsb.copyClass(EchoReturnEndpoint.class);
try
{
wsb.start();
URI uri = wsb.getServerBaseURI();
WebAppContext webapp = wsb.createWebAppContext();
wsb.deployWebapp(webapp);
wsb.dump();
WebSocketClient client = new WebSocketClient();
try
{
client.start();
JettyEchoSocket clientEcho = new JettyEchoSocket();
Future<Session> future = client.connect(clientEcho,uri.resolve("echoreturn"));
// wait for connect
future.get(1,TimeUnit.SECONDS);
clientEcho.sendMessage("Hello World");
Queue<String> msgs = clientEcho.awaitMessages(1);
Assert.assertEquals("Expected message","Hello World",msgs.poll());
}
finally
{
client.stop();
}
}
finally
{
wsb.stop();
}
}
}

View File

@ -20,14 +20,13 @@ package org.eclipse.jetty.websocket.jsr356.server;
import static org.hamcrest.Matchers.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert;
@ -40,8 +39,8 @@ public abstract class TrackingSocket
private static final Logger LOG = Log.getLogger(TrackingSocket.class);
public CloseReason closeReason;
public BlockingQueue<String> eventQueue = new BlockingArrayQueue<String>();
public BlockingQueue<Throwable> errorQueue = new BlockingArrayQueue<>();
public EventQueue<String> eventQueue = new EventQueue<String>();
public EventQueue<Throwable> errorQueue = new EventQueue<>();
public CountDownLatch openLatch = new CountDownLatch(1);
public CountDownLatch closeLatch = new CountDownLatch(1);
public CountDownLatch dataLatch = new CountDownLatch(1);

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// 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.server.samples.echo;
import java.io.IOException;
import javax.websocket.CloseReason;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.toolchain.test.EventQueue;
@ServerEndpoint(value = "/echoreturn")
public class EchoReturnEndpoint
{
private Session session = null;
public CloseReason close = null;
public EventQueue<String> messageQueue = new EventQueue<>();
public void onClose(CloseReason close)
{
this.close = close;
}
@OnMessage
public String onMessage(String message)
{
this.messageQueue.offer(message);
// Return the message
return message;
}
@OnOpen
public void onOpen(Session session)
{
this.session = session;
}
public void sendText(String text) throws IOException
{
if (session != null)
{
session.getBasicRemote().sendText(text);
}
}
}

View File

@ -1,7 +1,7 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=WARN
# org.eclipse.jetty.websocket.LEVEL=DEBUG
org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.LEVEL=WARN
# org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG

View File

@ -42,18 +42,18 @@ public class CallableMethod
this.paramTypes = method.getParameterTypes();
}
public void call(Object obj, Object... args)
public Object call(Object obj, Object... args)
{
if ((this.pojo == null) || (this.method == null))
{
LOG.warn("Cannot execute call: pojo={}, method={}",pojo,method);
return; // no call event method determined
return null; // no call event method determined
}
if (obj == null)
{
LOG.warn("Cannot call {} on null object",this.method);
return;
return null;
}
if (args.length < paramTypes.length)
@ -64,7 +64,7 @@ public class CallableMethod
try
{
this.method.invoke(obj,args);
return this.method.invoke(obj,args);
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{