JSR-356 - cleaning up Decoder and Encoder lifecycle.

This commit is contained in:
Joakim Erdfelt 2013-07-09 14:15:16 -07:00
parent 46e9493c85
commit 33c11dffaa
73 changed files with 2758 additions and 1223 deletions

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.websocket.jsr356;
import java.io.IOException;
import java.net.URI;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@ -32,70 +34,80 @@ import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.Extension;
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.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.AnnotatedClientEndpointMetadata;
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.client.SimpleEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.decoders.PrimitiveDecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.encoders.PrimitiveEncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
/**
* 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
public class ClientContainer implements WebSocketContainer
{
private static final Logger LOG = Log.getLogger(ClientContainer.class);
/** Tracking all primitive decoders for the container */
private final DecoderFactory decoderFactory;
/** Tracking all primitive encoders for the container */
private final EncoderFactory encoderFactory;
/** Tracking for all declared Client endpoints */
private final ConcurrentHashMap<Class<?>, JsrClientMetadata> endpointClientMetadataCache;
private final Map<Class<?>, EndpointMetadata> endpointClientMetadataCache;
/** The jetty websocket client in use for this container */
private WebSocketClient client;
public ClientContainer()
{
super();
endpointClientMetadataCache = new ConcurrentHashMap<>();
decoderFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
encoderFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE);
EmptyClientEndpointConfig empty = new EmptyClientEndpointConfig();
decoderFactory.init(empty);
encoderFactory.init(empty);
}
private Session connect(Object websocket, ClientEndpointConfig config, URI path) throws IOException
private Session connect(EndpointInstance instance, URI path) throws IOException
{
ClientEndpointConfig cec = config;
if (cec == null)
{
// Create default config
cec = ClientEndpointConfig.Builder.create().build();
}
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(websocket,cec);
Objects.requireNonNull(instance,"EndpointInstance cannot be null");
Objects.requireNonNull(path,"Path cannot be null");
ClientEndpointConfig config = (ClientEndpointConfig)instance.getConfig();
ClientUpgradeRequest req = new ClientUpgradeRequest();
UpgradeListener upgradeListener = null;
if (cec != null)
for (Extension ext : config.getExtensions())
{
for (Extension ext : cec.getExtensions())
{
req.addExtensions(new JsrExtensionConfig(ext));
}
if (cec.getPreferredSubprotocols().size() > 0)
{
req.setSubProtocols(config.getPreferredSubprotocols());
}
if (cec.getConfigurator() != null)
{
upgradeListener = new JsrUpgradeListener(cec.getConfigurator());
}
req.addExtensions(new JsrExtensionConfig(ext));
}
Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(endpoint,path,req,upgradeListener);
if (config.getPreferredSubprotocols().size() > 0)
{
req.setSubProtocols(config.getPreferredSubprotocols());
}
if (config.getConfigurator() != null)
{
upgradeListener = new JsrUpgradeListener(config.getConfigurator());
}
Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(instance,path,req,upgradeListener);
try
{
return (JsrSession)futSess.get();
@ -107,79 +119,80 @@ public class ClientContainer extends CommonContainer
}
@Override
public Session connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException
public Session connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
try
{
Object websocket = endpointClass.newInstance();
return connect(websocket,cec,path);
}
catch (InstantiationException | IllegalAccessException e)
{
throw new DeploymentException("Unable to instantiate websocket: " + endpointClass,e);
}
EndpointInstance instance = newClientEndpointInstance(endpointClass,config);
return connect(instance,path);
}
@Override
public Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException, IOException
{
try
EndpointInstance instance = newClientEndpointInstance(annotatedEndpointClass,null);
return connect(instance,path);
}
@Override
public Session connectToServer(Endpoint endpoint, ClientEndpointConfig config, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(endpoint,config);
return connect(instance,path);
}
@Override
public Session connectToServer(Object endpoint, URI path) throws DeploymentException, IOException
{
EndpointInstance instance = newClientEndpointInstance(endpoint,null);
return connect(instance,path);
}
public EndpointMetadata getClientEndpointMetadata(Class<?> endpoint)
{
EndpointMetadata metadata = null;
synchronized (endpointClientMetadataCache)
{
ClientEndpoint anno = annotatedEndpointClass.getAnnotation(ClientEndpoint.class);
metadata = endpointClientMetadataCache.get(endpoint);
if (metadata != null)
{
return metadata;
}
ClientEndpoint anno = endpoint.getAnnotation(ClientEndpoint.class);
if (anno != null)
{
// Annotated takes precedence here
JsrClientMetadata metadata = new JsrClientMetadata(this,annotatedEndpointClass);
Object websocket = annotatedEndpointClass.newInstance();
return connect(websocket,metadata.getConfig(),path);
AnnotatedClientEndpointMetadata annoMetadata = new AnnotatedClientEndpointMetadata(this,endpoint);
AnnotatedEndpointScanner<ClientEndpoint, ClientEndpointConfig> scanner = new AnnotatedEndpointScanner<>(annoMetadata);
scanner.scan();
metadata = annoMetadata;
}
else if (Endpoint.class.isAssignableFrom(annotatedEndpointClass))
else if (Endpoint.class.isAssignableFrom(endpoint))
{
// Try if extends Endpoint (alternate use)
Object websocket = annotatedEndpointClass.newInstance();
ClientEndpointConfig cec = new EmptyClientEndpointConfig();
return connect(websocket,cec,path);
// extends Endpoint
@SuppressWarnings("unchecked")
Class<? extends Endpoint> eendpoint = (Class<? extends Endpoint>)endpoint;
metadata = new SimpleEndpointMetadata(eendpoint);
}
else
{
StringBuilder err = new StringBuilder();
err.append("Not a recognized websocket [");
err.append(annotatedEndpointClass.getName());
err.append(endpoint.getName());
err.append("] does not extend @").append(ClientEndpoint.class.getName());
err.append(" or extend from ").append(Endpoint.class.getName());
throw new DeploymentException(err.toString());
throw new InvalidWebSocketException("Unable to identify as valid Endpoint: " + endpoint);
}
}
catch (InstantiationException | IllegalAccessException e)
{
throw new DeploymentException("Unable to instantiate websocket: " + annotatedEndpointClass,e);
endpointClientMetadataCache.put(endpoint,metadata);
return metadata;
}
}
@Override
public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException
public DecoderFactory getDecoderFactory()
{
return connect(endpointInstance,cec,path);
}
@Override
public Session connectToServer(Object annotatedEndpointInstance, URI path) throws DeploymentException, IOException
{
return connect(annotatedEndpointInstance,null,path);
}
public JsrClientMetadata getClientEndpointMetadata(Class<?> endpointClass) throws DeploymentException
{
JsrClientMetadata basemetadata = endpointClientMetadataCache.get(endpointClass);
if (basemetadata == null)
{
basemetadata = new JsrClientMetadata(this,endpointClass);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(basemetadata);
scanner.scan();
endpointClientMetadataCache.put(endpointClass,basemetadata);
}
return basemetadata;
return decoderFactory;
}
@Override
@ -206,6 +219,11 @@ public class ClientContainer extends CommonContainer
return client.getMaxTextMessageBufferSize();
}
public EncoderFactory getEncoderFactory()
{
return encoderFactory;
}
@Override
public Set<Extension> getInstalledExtensions()
{
@ -220,13 +238,47 @@ public class ClientContainer extends CommonContainer
return ret;
}
@Override
/**
* Used in {@link Session#getOpenSessions()}
*
* @return
*/
public Set<Session> getOpenSessions()
{
// TODO Auto-generated method stub
return null;
}
private EndpointInstance newClientEndpointInstance(Class<?> endpointClass, ClientEndpointConfig config)
{
try
{
return newClientEndpointInstance(endpointClass.newInstance(),config);
}
catch (InstantiationException | IllegalAccessException e)
{
throw new InvalidWebSocketException("Unable to instantiate websocket: " + endpointClass.getClass());
}
}
public EndpointInstance newClientEndpointInstance(Object endpoint, ClientEndpointConfig config)
{
EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass());
ClientEndpointConfig cec = config;
if (config == null)
{
if (metadata instanceof AnnotatedClientEndpointMetadata)
{
cec = ((AnnotatedClientEndpointMetadata)metadata).getConfig();
}
else
{
cec = new EmptyClientEndpointConfig();
}
}
return new EndpointInstance(endpoint,cec,metadata);
}
@Override
public void setAsyncSendTimeout(long timeoutmillis)
{
@ -253,11 +305,13 @@ public class ClientContainer extends CommonContainer
client.setMaxTextMessageBufferSize(max);
}
@Override
/**
* Start the container
*/
public void start()
{
client = new WebSocketClient();
client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy(),this));
client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy()));
client.setSessionFactory(new JsrSessionFactory(this));
try
@ -270,7 +324,9 @@ public class ClientContainer extends CommonContainer
}
}
@Override
/**
* Stop the container
*/
public void stop()
{
try

View File

@ -1,114 +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.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,229 +18,161 @@
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 javax.websocket.EndpointConfig;
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;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
/**
* 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>
* <li>From Static DecoderMetadataSet (based on data in annotations and static EndpointConfig)</li>
* <li>From Composite DecoderMetadataSet (based static and instance specific EndpointConfig)</li>
* <li>Container declared DecoderMetadataSet (primitives)</li>
* </ul>
*/
public class DecoderFactory
public class DecoderFactory implements Configurable
{
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()
public static class Wrapper implements Configurable
{
this.typeMap = new ConcurrentHashMap<>();
this.registered = new ConcurrentHashMap<>();
private final Decoder decoder;
private final DecoderMetadata metadata;
private Wrapper(Decoder decoder, DecoderMetadata metadata)
{
this.decoder = decoder;
this.metadata = metadata;
}
public Decoder getDecoder()
{
return decoder;
}
public DecoderMetadata getMetadata()
{
return metadata;
}
@Override
public void init(EndpointConfig config)
{
this.decoder.init(config);
}
}
public DecoderFactory(DecoderFactory parentFactory)
private static final Logger LOG = Log.getLogger(DecoderFactory.class);
private final DecoderMetadataSet metadatas;
private DecoderFactory parentFactory;
private Map<Class<?>, Wrapper> activeWrappers;
public DecoderFactory(DecoderMetadataSet metadatas)
{
this();
this.metadatas = metadatas;
this.activeWrappers = new ConcurrentHashMap<>();
}
public DecoderFactory(DecoderMetadataSet metadatas, DecoderFactory parentFactory)
{
this(metadatas);
this.parentFactory = parentFactory;
}
private Class<?> getDecoderMessageClass(Class<? extends Decoder> decoder, Class<?> interfaceClass)
public Decoder getDecoderFor(Class<?> type)
{
Class<?> decoderClass = ReflectUtils.findGenericClassFor(decoder,interfaceClass);
if (decoderClass == null)
Wrapper wrapper = getWrapperFor(type);
if (wrapper == 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 null;
}
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);
return wrapper.decoder;
}
public DecoderMetadata getMetadataFor(Class<?> type)
{
DecoderMetadata metadata = typeMap.get(type);
if (metadata == null)
{
if (parentFactory != null)
{
return parentFactory.getMetadataFor(type);
}
}
return metadata;
}
LOG.debug("getMetadataFor({})",type);
DecoderMetadata metadata = metadatas.getMetadataByType(type);
public DecoderWrapper getWrapperFor(Class<?> type)
{
DecoderMetadata metadata = getMetadataFor(type);
if (metadata != null)
{
return newWrapper(metadata);
return metadata;
}
if (parentFactory != null)
{
return parentFactory.getMetadataFor(type);
}
return null;
}
public DecoderWrapper newWrapper(DecoderMetadata metadata)
public Wrapper getWrapperFor(Class<?> type)
{
Class<? extends Decoder> decoderClass = metadata.getDecoderClass();
synchronized (activeWrappers)
{
Wrapper wrapper = activeWrappers.get(type);
// Try parent (if needed)
if ((wrapper == null) && (parentFactory != null))
{
wrapper = parentFactory.getWrapperFor(type);
}
if (wrapper == null)
{
// Attempt to create Wrapper on demand
DecoderMetadata metadata = metadatas.getMetadataByType(type);
if (metadata == null)
{
return null;
}
wrapper = newWrapper(metadata);
// track wrapper
activeWrappers.put(type,wrapper);
}
return wrapper;
}
}
@Override
public void init(EndpointConfig config)
{
LOG.debug("init({})",config);
// Instantiate all declared decoders
for (DecoderMetadata metadata : metadatas)
{
Wrapper wrapper = newWrapper(metadata);
activeWrappers.put(metadata.getObjectType(),wrapper);
}
// Initialize all decoders
for (Wrapper wrapper : activeWrappers.values())
{
wrapper.decoder.init(config);
}
}
public Wrapper newWrapper(DecoderMetadata metadata)
{
Class<? extends Decoder> decoderClass = metadata.getCoderClass();
try
{
Decoder decoder = decoderClass.newInstance();
return new DecoderWrapper(decoder,metadata);
return new Wrapper(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

@ -18,39 +18,155 @@
package org.eclipse.jetty.websocket.jsr356;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
/**
* Represents all of the declared {@link Encoder}s that the Container is aware of.
*/
public class EncoderFactory
public class EncoderFactory implements Configurable
{
public EncoderFactory()
public static class Wrapper implements Configurable
{
// TODO Auto-generated constructor stub
private final Encoder encoder;
private final EncoderMetadata metadata;
private Wrapper(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);
}
}
public EncoderFactory(EncoderFactory encoderFactory)
private static final Logger LOG = Log.getLogger(EncoderFactory.class);
private final EncoderMetadataSet metadatas;
private EncoderFactory parentFactory;
private Map<Class<?>, Wrapper> activeWrappers;
public EncoderFactory(EncoderMetadataSet metadatas)
{
// TODO Auto-generated constructor stub
this.metadatas = metadatas;
this.activeWrappers = new ConcurrentHashMap<>();
}
public Encoder getEncoder(Class<?> targetType)
public EncoderFactory(EncoderMetadataSet metadatas, EncoderFactory parentFactory)
{
// TODO Auto-generated method stub
this(metadatas);
this.parentFactory = parentFactory;
}
public Encoder getEncoderFor(Class<?> type)
{
Wrapper wrapper = getWrapperFor(type);
if (wrapper == null)
{
return null;
}
return wrapper.encoder;
}
public EncoderMetadata getMetadataFor(Class<?> type)
{
LOG.debug("getMetadataFor({})",type);
EncoderMetadata metadata = metadatas.getMetadataByType(type);
if (metadata != null)
{
return metadata;
}
if (parentFactory != null)
{
return parentFactory.getMetadataFor(type);
}
return null;
}
public List<Class<? extends Encoder>> getList()
public Wrapper getWrapperFor(Class<?> type)
{
// TODO Auto-generated method stub
return null;
synchronized (activeWrappers)
{
Wrapper wrapper = activeWrappers.get(type);
// Try parent (if needed)
if ((wrapper == null) && (parentFactory != null))
{
wrapper = parentFactory.getWrapperFor(type);
}
if (wrapper == null)
{
// Attempt to create Wrapper on demand
EncoderMetadata metadata = metadatas.getMetadataByType(type);
if (metadata == null)
{
return null;
}
wrapper = newWrapper(metadata);
// track wrapper
activeWrappers.put(type,wrapper);
}
return wrapper;
}
}
public void registerAll(Class<? extends Encoder>[] encoders)
@Override
public void init(EndpointConfig config)
{
// TODO Auto-generated method stub
LOG.debug("init({})",config);
// Instantiate all declared encoders
for (EncoderMetadata metadata : metadatas)
{
Wrapper wrapper = newWrapper(metadata);
activeWrappers.put(metadata.getObjectType(),wrapper);
}
// Initialize all encoders
for (Wrapper wrapper : activeWrappers.values())
{
wrapper.encoder.init(config);
}
}
private Wrapper newWrapper(EncoderMetadata metadata)
{
Class<? extends Encoder> encoderClass = metadata.getCoderClass();
try
{
Encoder encoder = encoderClass.newInstance();
return new Wrapper(encoder,metadata);
}
catch (InstantiationException | IllegalAccessException e)
{
throw new IllegalStateException("Unable to instantiate Encoder: " + encoderClass.getName());
}
}
}

View File

@ -22,16 +22,60 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Future;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.RemoteEndpoint;
import javax.websocket.SendHandler;
import javax.websocket.SendResult;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint;
import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
import org.eclipse.jetty.websocket.common.message.MessageOutputStream;
import org.eclipse.jetty.websocket.common.message.MessageWriter;
import org.eclipse.jetty.websocket.jsr356.encoders.EncodeFailedFuture;
import org.eclipse.jetty.websocket.jsr356.messages.SendHandlerWriteCallback;
public class JsrAsyncRemote implements RemoteEndpoint.Async
{
private final org.eclipse.jetty.websocket.api.RemoteEndpoint jettyRemote;
private static final Logger LOG = Log.getLogger(JsrAsyncRemote.class);
private final JsrSession session;
private final WebSocketRemoteEndpoint jettyRemote;
private final EncoderFactory encoders;
protected JsrAsyncRemote(JsrSession session)
{
this.jettyRemote = session.getRemote();
this.session = session;
if (!(session.getRemote() instanceof WebSocketRemoteEndpoint))
{
StringBuilder err = new StringBuilder();
err.append("Unexpected implementation [");
err.append(session.getRemote().getClass().getName());
err.append("]. Expected an instanceof [");
err.append(WebSocketRemoteEndpoint.class.getName());
err.append("]");
throw new IllegalStateException(err.toString());
}
this.jettyRemote = (WebSocketRemoteEndpoint)session.getRemote();
this.encoders = session.getEncoderFactory();
}
private void assertMessageNotNull(Object data)
{
if (data == null)
{
throw new IllegalArgumentException("message cannot be null");
}
}
private void assertSendHandlerNotNull(SendHandler handler)
{
if (handler == null)
{
throw new IllegalArgumentException("SendHandler cannot be null");
}
}
@Override
@ -57,35 +101,174 @@ public class JsrAsyncRemote implements RemoteEndpoint.Async
@Override
public Future<Void> sendBinary(ByteBuffer data)
{
assertMessageNotNull(data);
return jettyRemote.sendBytesByFuture(data);
}
@Override
public void sendBinary(ByteBuffer data, SendHandler handler)
{
// TODO: wrap the send handler?
jettyRemote.sendBytesByFuture(data);
assertMessageNotNull(data);
assertSendHandlerNotNull(handler);
WebSocketFrame frame = WebSocketFrame.binary().setPayload(data).setFin(true);
jettyRemote.sendFrame(frame,new SendHandlerWriteCallback(handler));
}
@SuppressWarnings(
{ "rawtypes", "unchecked" })
@Override
public Future<Void> sendObject(Object data)
{
// TODO Auto-generated method stub
return null;
LOG.debug("sendObject({})",data);
assertMessageNotNull(data);
Encoder encoder = encoders.getEncoderFor(data.getClass());
if (encoder == null)
{
throw new IllegalArgumentException("No encoder for type: " + data.getClass());
}
if (encoder instanceof Encoder.Text)
{
Encoder.Text etxt = (Encoder.Text)encoder;
try
{
String msg = etxt.encode(data);
return jettyRemote.sendStringByFuture(msg);
}
catch (EncodeException e)
{
return new EncodeFailedFuture(data,etxt,Encoder.Text.class,e);
}
}
else if (encoder instanceof Encoder.TextStream)
{
Encoder.TextStream etxt = (Encoder.TextStream)encoder;
FutureWriteCallback callback = new FutureWriteCallback();
try (MessageWriter writer = new MessageWriter(session))
{
writer.setCallback(callback);
etxt.encode(data,writer);
return callback;
}
catch (EncodeException | IOException e)
{
return new EncodeFailedFuture(data,etxt,Encoder.Text.class,e);
}
}
else if (encoder instanceof Encoder.Binary)
{
Encoder.Binary ebin = (Encoder.Binary)encoder;
try
{
ByteBuffer buf = ebin.encode(data);
return jettyRemote.sendBytesByFuture(buf);
}
catch (EncodeException e)
{
return new EncodeFailedFuture(data,ebin,Encoder.Binary.class,e);
}
}
else if (encoder instanceof Encoder.BinaryStream)
{
Encoder.BinaryStream ebin = (Encoder.BinaryStream)encoder;
FutureWriteCallback callback = new FutureWriteCallback();
try (MessageOutputStream out = new MessageOutputStream(session))
{
out.setCallback(callback);
ebin.encode(data,out);
return callback;
}
catch (EncodeException | IOException e)
{
return new EncodeFailedFuture(data,ebin,Encoder.Binary.class,e);
}
}
throw new IllegalArgumentException("Unknown encoder type: " + encoder);
}
@SuppressWarnings(
{ "rawtypes", "unchecked" })
@Override
public void sendObject(Object data, SendHandler handler)
{
// TODO Auto-generated method stub
LOG.debug("sendObject({},{})",data,handler);
assertMessageNotNull(data);
assertSendHandlerNotNull(handler);
Encoder encoder = encoders.getEncoderFor(data.getClass());
if (encoder == null)
{
throw new IllegalArgumentException("No encoder for type: " + data.getClass());
}
if (encoder instanceof Encoder.Text)
{
Encoder.Text etxt = (Encoder.Text)encoder;
try
{
String msg = etxt.encode(data);
sendText(msg,handler);
return;
}
catch (EncodeException e)
{
handler.onResult(new SendResult(e));
}
}
else if (encoder instanceof Encoder.TextStream)
{
Encoder.TextStream etxt = (Encoder.TextStream)encoder;
SendHandlerWriteCallback callback = new SendHandlerWriteCallback(handler);
try (MessageWriter writer = new MessageWriter(session))
{
writer.setCallback(callback);
etxt.encode(data,writer);
return;
}
catch (EncodeException | IOException e)
{
handler.onResult(new SendResult(e));
}
}
else if (encoder instanceof Encoder.Binary)
{
Encoder.Binary ebin = (Encoder.Binary)encoder;
try
{
ByteBuffer buf = ebin.encode(data);
sendBinary(buf,handler);
return;
}
catch (EncodeException e)
{
handler.onResult(new SendResult(e));
}
}
else if (encoder instanceof Encoder.BinaryStream)
{
Encoder.BinaryStream ebin = (Encoder.BinaryStream)encoder;
SendHandlerWriteCallback callback = new SendHandlerWriteCallback(handler);
try (MessageOutputStream out = new MessageOutputStream(session))
{
out.setCallback(callback);
ebin.encode(data,out);
return;
}
catch (EncodeException | IOException e)
{
handler.onResult(new SendResult(e));
}
}
throw new IllegalArgumentException("Unknown encoder type: " + encoder);
}
@Override
public void sendPing(ByteBuffer applicationData) throws IOException, IllegalArgumentException
{
jettyRemote.sendPing(applicationData);
}
@Override
@ -97,13 +280,17 @@ public class JsrAsyncRemote implements RemoteEndpoint.Async
@Override
public Future<Void> sendText(String text)
{
assertMessageNotNull(text);
return jettyRemote.sendStringByFuture(text);
}
@Override
public void sendText(String text, SendHandler handler)
{
// TODO Auto-generated method stub
assertMessageNotNull(text);
assertSendHandlerNotNull(handler);
WebSocketFrame frame = WebSocketFrame.text(text).setFin(true);
jettyRemote.sendFrame(frame,new SendHandlerWriteCallback(handler));
}
@Override

View File

@ -19,7 +19,6 @@
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;
@ -48,20 +47,21 @@ import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
/**
* Session for the JSR.
*/
public class JsrSession extends WebSocketSession implements javax.websocket.Session, Configurable
{
private static final Logger LOG = Log.getLogger(JsrSession.class);
private final CommonContainer container;
private final ClientContainer container;
private final String id;
private final EndpointConfig config;
/** Factory for Decoders */
private final EndpointMetadata metadata;
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()} */
@ -76,20 +76,17 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
public JsrSession(URI requestURI, EventDriver websocket, LogicalConnection connection, ClientContainer container, String id)
{
super(requestURI,websocket,connection);
if (websocket instanceof AbstractJsrEventDriver)
if (!(websocket instanceof AbstractJsrEventDriver))
{
this.config = ((AbstractJsrEventDriver)websocket).getConfig();
}
else
{
this.config = new BasicEndpointConfig();
throw new IllegalArgumentException("Cannot non JSR WebSocket: " + websocket);
}
AbstractJsrEventDriver jsr = (AbstractJsrEventDriver)websocket;
this.config = jsr.getConfig();
this.metadata = jsr.getMetadata();
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.decoderFactory = new DecoderFactory(metadata.getDecoders(),container.getDecoderFactory());
this.encoderFactory = new EncoderFactory(metadata.getEncoders(),container.getEncoderFactory());
this.messageHandlerFactory = new MessageHandlerFactory();
this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
this.messageHandlerSet = new HashSet<>();
@ -104,8 +101,19 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
{
for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
{
DecoderWrapper decoder = decoderFactory.getWrapperFor(metadata.getMessageClass());
MessageType key = decoder.getMetadata().getMessageType();
DecoderFactory.Wrapper wrapper = decoderFactory.getWrapperFor(metadata.getMessageClass());
if (wrapper == null)
{
StringBuilder err = new StringBuilder();
err.append("Unable to find decoder for type <");
err.append(metadata.getMessageClass().getName());
err.append("> used in <");
err.append(metadata.getHandlerClass().getName());
err.append(">");
throw new IllegalStateException(err.toString());
}
MessageType key = wrapper.getMetadata().getMessageType();
MessageHandlerWrapper other = wrappers[key.ordinal()];
if (other != null)
{
@ -124,8 +132,8 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
}
else
{
MessageHandlerWrapper wrapper = new MessageHandlerWrapper(handler,metadata,decoder);
wrappers[key.ordinal()] = wrapper;
MessageHandlerWrapper handlerWrapper = new MessageHandlerWrapper(handler,metadata,wrapper);
wrappers[key.ordinal()] = handlerWrapper;
}
}
@ -185,6 +193,11 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
return encoderFactory;
}
public EndpointMetadata getEndpointMetadata()
{
return metadata;
}
@Override
public String getId()
{
@ -298,28 +311,12 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
}
@Override
public void init(EndpointConfig endpointconfig)
public void init(EndpointConfig config)
{
// Initialize encoders
for (EncoderWrapper wrapper : activeEncoders.values())
{
wrapper.getEncoder().init(config);
}
encoderFactory.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);
}
}
decoderFactory.init(config);
}
@Override

View File

@ -32,16 +32,16 @@ public class MessageHandlerWrapper
{
private final MessageHandler handler;
private final MessageHandlerMetadata metadata;
private final DecoderWrapper decoder;
private final DecoderFactory.Wrapper decoder;
public MessageHandlerWrapper(MessageHandler handler, MessageHandlerMetadata metadata, DecoderWrapper decoder)
public MessageHandlerWrapper(MessageHandler handler, MessageHandlerMetadata metadata, DecoderFactory.Wrapper decoder)
{
this.handler = handler;
this.metadata = metadata;
this.decoder = decoder;
}
public DecoderWrapper getDecoder()
public DecoderFactory.Wrapper getDecoder()
{
return decoder;
}

View File

@ -20,14 +20,17 @@ 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.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
/**
@ -36,13 +39,8 @@ import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
* @param <T>
* the annotation this metadata is based off of
*/
public abstract class JsrMetadata<T extends Annotation>
public abstract class AnnotatedEndpointMetadata<T extends Annotation, C extends EndpointConfig> implements EndpointMetadata
{
/**
* The actual class that this metadata belongs to
*/
public final Class<?> pojo;
/**
* Callable for &#064;{@link OnOpen} annotation.
*/
@ -83,9 +81,15 @@ public abstract class JsrMetadata<T extends Annotation>
*/
public OnMessagePongCallable onPong;
protected JsrMetadata(Class<?> websocket)
private final Class<?> endpointClass;
private DecoderMetadataSet decoders;
private EncoderMetadataSet encoders;
protected AnnotatedEndpointMetadata(Class<?> endpointClass)
{
this.pojo = websocket;
this.endpointClass = endpointClass;
this.decoders = new DecoderMetadataSet();
this.encoders = new EncoderMetadataSet();
}
public void customizeParamsOnClose(LinkedList<IJsrParamId> params)
@ -100,7 +104,7 @@ public abstract class JsrMetadata<T extends Annotation>
public void customizeParamsOnMessage(LinkedList<IJsrParamId> params)
{
for (Class<? extends Decoder> decoder : getConfiguredDecoders())
for (Class<? extends Decoder> decoder : getDecoders().getList())
{
if (Decoder.Text.class.isAssignableFrom(decoder))
{
@ -141,5 +145,23 @@ public abstract class JsrMetadata<T extends Annotation>
public abstract T getAnnotation();
protected abstract List<Class<? extends Decoder>> getConfiguredDecoders();
public abstract C getConfig();
@Override
public DecoderMetadataSet getDecoders()
{
return decoders;
}
@Override
public EncoderMetadataSet getEncoders()
{
return encoders;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
}

View File

@ -23,6 +23,7 @@ import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
@ -34,7 +35,7 @@ import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotat
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<JsrMetadata<?>>
public class AnnotatedEndpointScanner<T extends Annotation, C extends EndpointConfig> extends AbstractMethodAnnotationScanner<AnnotatedEndpointMetadata<T, C>>
{
private static final Logger LOG = Log.getLogger(AnnotatedEndpointScanner.class);
@ -42,9 +43,9 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
private final LinkedList<IJsrParamId> paramsOnClose;
private final LinkedList<IJsrParamId> paramsOnError;
private final LinkedList<IJsrParamId> paramsOnMessage;
private final JsrMetadata<?> metadata;
private final AnnotatedEndpointMetadata<T, C> metadata;
public AnnotatedEndpointScanner(JsrMetadata<?> metadata)
public AnnotatedEndpointScanner(AnnotatedEndpointMetadata<T, C> metadata)
{
this.metadata = metadata;
@ -84,7 +85,7 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
}
@Override
public void onMethodAnnotation(JsrMetadata<?> metadata, Class<?> pojo, Method method, Annotation annotation)
public void onMethodAnnotation(AnnotatedEndpointMetadata<T, C> metadata, Class<?> pojo, Method method, Annotation annotation)
{
LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
@ -156,9 +157,9 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
}
}
public JsrMetadata<?> scan()
public AnnotatedEndpointMetadata<T, C> scan()
{
scanMethodAnnotations(metadata,metadata.pojo);
scanMethodAnnotations(metadata,metadata.getEndpointClass());
return metadata;
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
@ -37,9 +38,9 @@ import org.eclipse.jetty.websocket.jsr356.JsrSession;
/**
* The live event methods found for a specific Annotated Endpoint
*/
public class JsrEvents
public class JsrEvents<T extends Annotation, C extends EndpointConfig>
{
private final JsrMetadata<?> metadata;
private final AnnotatedEndpointMetadata<T, C> metadata;
/**
* Callable for &#064;{@link OnOpen} annotation.
@ -81,7 +82,7 @@ public class JsrEvents
*/
private OnMessagePongCallable onPong;
public JsrEvents(JsrMetadata<?> metadata)
public JsrEvents(AnnotatedEndpointMetadata<T, C> metadata)
{
this.metadata = metadata;
this.onOpen = (metadata.onOpen == null)?null:new OnOpenCallable(metadata.onOpen);
@ -175,7 +176,7 @@ public class JsrEvents
}
}
public JsrMetadata<?> getMetadata()
public AnnotatedEndpointMetadata<T, C> getMetadata()
{
return metadata;
}

View File

@ -24,6 +24,7 @@ import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.EncoderFactory;
import org.eclipse.jetty.websocket.jsr356.InitException;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
@ -140,7 +141,11 @@ public class OnMessageCallable extends JsrCallable
public void init(JsrSession session)
{
super.init(session);
this.returnEncoder = session.getEncoderFactory().getEncoder(returnType);
EncoderFactory.Wrapper encoderWrapper = session.getEncoderFactory().getWrapperFor(returnType);
if (encoderWrapper != null)
{
this.returnEncoder = encoderWrapper.getEncoder();
}
if (decoderClass != null)
{

View File

@ -27,10 +27,11 @@ 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 org.eclipse.jetty.websocket.api.InvalidWebSocketException;
public class AnnotatedClientEndpointConfig implements ClientEndpointConfig
{
private final List<Class<? extends Decoder>> decoders;
@ -40,7 +41,7 @@ public class AnnotatedClientEndpointConfig implements ClientEndpointConfig
private final Configurator configurator;
private Map<String, Object> userProperties;
public AnnotatedClientEndpointConfig(ClientEndpoint anno) throws DeploymentException
public AnnotatedClientEndpointConfig(ClientEndpoint anno)
{
this.decoders = Collections.unmodifiableList(Arrays.asList(anno.decoders()));
this.encoders = Collections.unmodifiableList(Arrays.asList(anno.encoders()));
@ -68,7 +69,7 @@ public class AnnotatedClientEndpointConfig implements ClientEndpointConfig
err.append(anno.configurator().getName());
err.append(" defined as annotation in ");
err.append(anno.getClass().getName());
throw new DeploymentException(err.toString(),e);
throw new InvalidWebSocketException(err.toString(),e);
}
}
}

View File

@ -18,22 +18,19 @@
package org.eclipse.jetty.websocket.jsr356.client;
import java.util.List;
import javax.websocket.ClientEndpoint;
import javax.websocket.Decoder;
import javax.websocket.DeploymentException;
import javax.websocket.ClientEndpointConfig;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrMetadata;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointMetadata;
public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
public class AnnotatedClientEndpointMetadata extends AnnotatedEndpointMetadata<ClientEndpoint, ClientEndpointConfig>
{
private final ClientEndpoint endpoint;
private final AnnotatedClientEndpointConfig config;
public JsrClientMetadata(ClientContainer container, Class<?> websocket) throws DeploymentException
public AnnotatedClientEndpointMetadata(ClientContainer container, Class<?> websocket)
{
super(websocket);
@ -54,14 +51,9 @@ public class JsrClientMetadata extends JsrMetadata<ClientEndpoint>
return endpoint;
}
public AnnotatedClientEndpointConfig getConfig()
@Override
public ClientEndpointConfig getConfig()
{
return config;
}
@Override
protected List<Class<? extends Decoder>> getConfiguredDecoders()
{
return config.getDecoders();
}
}

View File

@ -19,14 +19,14 @@
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;
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.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
@ -34,37 +34,19 @@ import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
*/
public class JsrClientEndpointImpl implements EventDriverImpl
{
private ClientContainer container;
public JsrClientEndpointImpl(ClientContainer container)
{
this.container = container;
}
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy) throws DeploymentException
{
Object endpoint = websocket;
if (websocket instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
// Classes annotated with @ClientEndpoint will have their ClientEndpointConfig
// built up from the information present in the annotations, any provided config will be ignored
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
}
Class<?> endpointClass = endpoint.getClass();
// Get the base metadata for this class
JsrClientMetadata basemetadata = container.getClientEndpointMetadata(endpointClass);
EndpointInstance ei = (EndpointInstance)websocket;
AnnotatedClientEndpointMetadata metadata = (AnnotatedClientEndpointMetadata)ei.getMetadata();
JsrEvents<ClientEndpoint, ClientEndpointConfig> events = new JsrEvents<>(metadata);
// At this point we have a base metadata, now we need to copy it for
// this specific instance of the WebSocket Endpoint (as we will be
// modifying the metadata)
JsrEvents events = new JsrEvents(basemetadata); // copy constructor.
// Create copy of base config
AnnotatedClientEndpointConfig config = basemetadata.getConfig();
return new JsrAnnotatedEventDriver(policy,endpoint,events,config);
return new JsrAnnotatedEventDriver(policy,ei,events);
}
@Override
@ -76,15 +58,14 @@ public class JsrClientEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
Object endpoint = websocket;
if (endpoint instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
// unwrap
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
Object endpoint = ei.getEndpoint();
ClientEndpoint anno = endpoint.getClass().getAnnotation(ClientEndpoint.class);
return (anno != null);
}

View File

@ -0,0 +1,60 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.client;
import javax.websocket.Endpoint;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
/**
* Basic {@link EndpointMetadata} for an WebSocket that extends from {@link Endpoint}
*/
public class SimpleEndpointMetadata implements EndpointMetadata
{
private final Class<?> endpointClass;
private DecoderMetadataSet decoders;
private EncoderMetadataSet encoders;
public SimpleEndpointMetadata(Class<? extends Endpoint> endpointClass)
{
this.endpointClass = endpointClass;
this.decoders = new DecoderMetadataSet();
this.encoders = new EncoderMetadataSet();
}
@Override
public DecoderMetadataSet getDecoders()
{
return decoders;
}
@Override
public EncoderMetadataSet getEncoders()
{
return encoders;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
}

View File

@ -0,0 +1,61 @@
//
// ========================================================================
// 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.nio.ByteBuffer;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
public class PrimitiveDecoderMetadataSet extends DecoderMetadataSet
{
public static final DecoderMetadataSet INSTANCE = new PrimitiveDecoderMetadataSet();
public PrimitiveDecoderMetadataSet()
{
boolean streamed = false;
// TEXT based - Classes Based
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 - Primitive Types
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);
}
}

View File

@ -0,0 +1,71 @@
//
// ========================================================================
// 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.encoders;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.websocket.Encoder;
/**
* A <code>Future&lt;Void&gt;</code> that is already failed as a result of an Encode error
*/
public class EncodeFailedFuture implements Future<Void>
{
private final String msg;
private final Throwable cause;
public EncodeFailedFuture(Object data, Encoder encoder, Class<?> encoderType, Throwable cause)
{
this.msg = String.format("Unable to encode %s using %s as %s",data.getClass().getName(),encoder.getClass().getName(),encoderType.getName());
this.cause = cause;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning)
{
return false;
}
@Override
public Void get() throws InterruptedException, ExecutionException
{
throw new ExecutionException(msg,cause);
}
@Override
public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{
throw new ExecutionException(msg,cause);
}
@Override
public boolean isCancelled()
{
return false;
}
@Override
public boolean isDone()
{
return true;
}
}

View File

@ -1,166 +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.encoders;
import java.util.ArrayList;
import java.util.List;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.ConfigurationException;
import org.eclipse.jetty.websocket.jsr356.utils.DeploymentTypeUtils;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
public class Encoders
{
private static class EncoderRef
{
Class<?> type;
Class<? extends Encoder> encoder;
public EncoderRef(Class<?> type, Class<? extends Encoder> encoder)
{
this.type = type;
this.encoder = encoder;
}
}
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()
{
this.encoders = new ArrayList<>();
add(new EncoderRef(Boolean.class,BooleanEncoder.class));
add(new EncoderRef(Byte.class,ByteEncoder.class));
add(new EncoderRef(Character.class,CharacterEncoder.class));
add(new EncoderRef(Double.class,DoubleEncoder.class));
add(new EncoderRef(Float.class,FloatEncoder.class));
add(new EncoderRef(Integer.class,IntegerEncoder.class));
add(new EncoderRef(Long.class,LongEncoder.class));
add(new EncoderRef(Short.class,ShortEncoder.class));
add(new EncoderRef(String.class,StringEncoder.class));
}
public Encoders(Class<? extends Encoder> encoderClasses[])
{
this();
if (encoderClasses != null)
{
// now add user provided encoders
for (Class<? extends Encoder> encoder : encoderClasses)
{
add(encoder);
}
}
}
public Encoders(List<Class<? extends Encoder>> encoderClasses)
{
this();
if (encoderClasses != null)
{
// now add user provided encoders
for (Class<? extends Encoder> encoder : encoderClasses)
{
add(encoder);
}
}
}
public void add(Class<? extends Encoder> encoder)
{
for (Class<?> type : TYPES)
{
Class<?> encoderClass = ReflectUtils.findGenericClassFor(encoder,type);
if (encoderClass != null)
{
add(encoderClass,encoder);
}
}
}
private void add(Class<?> handler, Class<? extends Encoder> encoder)
{
// verify that we are not adding a duplicate
for (EncoderRef ref : encoders)
{
if (DeploymentTypeUtils.isAssignableClass(handler,ref.type))
{
throw new ConfigurationException("Duplicate Encoder handling for type " + ref.type + ": found in " + ref.encoder + " and " + encoder);
}
}
// add entry
this.encoders.add(new EncoderRef(handler,encoder));
}
private void add(EncoderRef ref)
{
this.encoders.add(ref);
}
public void addAll(Class<? extends Encoder>[] encoderArr)
{
// TODO Auto-generated method stub
}
public Encoder getEncoder(Class<?> type)
{
Class<?> targetType = type;
if (targetType.isPrimitive())
{
targetType = DeploymentTypeUtils.getPrimitiveClass(targetType);
}
for (EncoderRef ref : encoders)
{
if (ref.type.isAssignableFrom(type))
{
return instantiate(ref.encoder);
}
}
throw new InvalidSignatureException("Unable to find appropriate Encoder for type: " + type);
}
private Encoder instantiate(Class<? extends Encoder> encoderClass)
{
try
{
return encoderClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
throw new ConfigurationException("Unable to instantiate Encoder: " + encoderClass,e);
}
}
}

View File

@ -0,0 +1,60 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.encoders;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
public class PrimitiveEncoderMetadataSet extends EncoderMetadataSet
{
public static final EncoderMetadataSet INSTANCE = new PrimitiveEncoderMetadataSet();
public PrimitiveEncoderMetadataSet()
{
boolean streamed = false;
// TEXT based - Classes Based
MessageType msgType = MessageType.TEXT;
register(Boolean.class,BooleanEncoder.class,msgType,streamed);
register(Byte.class,ByteEncoder.class,msgType,streamed);
register(Character.class,CharacterEncoder.class,msgType,streamed);
register(Double.class,DoubleEncoder.class,msgType,streamed);
register(Float.class,FloatEncoder.class,msgType,streamed);
register(Integer.class,IntegerEncoder.class,msgType,streamed);
register(Long.class,LongEncoder.class,msgType,streamed);
register(Short.class,ShortEncoder.class,msgType,streamed);
register(String.class,StringEncoder.class,msgType,streamed);
// TEXT based - Primitive Types
msgType = MessageType.TEXT;
register(Boolean.TYPE,BooleanEncoder.class,msgType,streamed);
register(Byte.TYPE,ByteEncoder.class,msgType,streamed);
register(Character.TYPE,CharacterEncoder.class,msgType,streamed);
register(Double.TYPE,DoubleEncoder.class,msgType,streamed);
register(Float.TYPE,FloatEncoder.class,msgType,streamed);
register(Integer.TYPE,IntegerEncoder.class,msgType,streamed);
register(Long.TYPE,LongEncoder.class,msgType,streamed);
register(Short.TYPE,ShortEncoder.class,msgType,streamed);
// BINARY based
msgType = MessageType.BINARY;
// FIXME register(ByteBuffer.class,ByteBufferEncoder.class,msgType,streamed);
// FIXME register(byte[].class,ByteArrayEncoder.class,msgType,streamed);
}
}

View File

@ -31,17 +31,20 @@ 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;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
public abstract class AbstractJsrEventDriver extends AbstractEventDriver implements EventDriver
{
protected final EndpointMetadata metadata;
protected final EndpointConfig config;
protected JsrSession jsrsession;
private boolean hasCloseBeenCalled = false;
public AbstractJsrEventDriver(WebSocketPolicy policy, Object websocket, EndpointConfig config)
public AbstractJsrEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance)
{
super(policy,websocket);
this.config = config;
super(policy,endpointInstance.getEndpoint());
this.config = endpointInstance.getConfig();
this.metadata = endpointInstance.getMetadata();
}
public EndpointConfig getConfig()
@ -54,6 +57,11 @@ public abstract class AbstractJsrEventDriver extends AbstractEventDriver impleme
return this.jsrsession;
}
public EndpointMetadata getMetadata()
{
return metadata;
}
protected void init(JsrSession jsrsession)
{
}

View File

@ -20,18 +20,25 @@ package org.eclipse.jetty.websocket.jsr356.endpoints;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
/**
* Associate a JSR Endpoint with its optional {@link EndpointConfig}
*/
public class ConfiguredEndpoint
public class EndpointInstance
{
private Object endpoint;
private EndpointConfig config;
/** The instance of the Endpoint */
private final Object endpoint;
/** The instance specific configuration for the Endpoint */
private final EndpointConfig config;
/** The metadata for this endpoint */
private final EndpointMetadata metadata;
public ConfiguredEndpoint(Object endpoint, EndpointConfig config)
public EndpointInstance(Object endpoint, EndpointConfig config, EndpointMetadata metadata)
{
this.endpoint = endpoint;
this.config = config;
this.metadata = metadata;
}
public EndpointConfig getConfig()
@ -43,4 +50,9 @@ public class ConfiguredEndpoint
{
return endpoint;
}
public EndpointMetadata getMetadata()
{
return metadata;
}
}

View File

@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
import javax.websocket.DecodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import org.eclipse.jetty.util.BufferUtil;
@ -49,9 +48,9 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver implements E
private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
private final JsrEvents events;
public JsrAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JsrEvents events, EndpointConfig config)
public JsrAnnotatedEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance, JsrEvents events)
{
super(policy,websocket,config);
super(policy,endpointInstance);
this.events = events;
}

View File

@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.MessageHandler.Whole;
@ -52,10 +51,10 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver implements Ev
private final Endpoint endpoint;
public JsrEndpointEventDriver(WebSocketPolicy policy, Endpoint endpoint, EndpointConfig config)
public JsrEndpointEventDriver(WebSocketPolicy policy, EndpointInstance endpointInstance)
{
super(policy,endpoint,config);
this.endpoint = endpoint;
super(policy,endpointInstance);
this.endpoint = (Endpoint)endpointInstance.getEndpoint();
}
@Override

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.websocket.jsr356.endpoints;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
@ -30,18 +27,12 @@ public class JsrEndpointImpl implements EventDriverImpl
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
Object endpoint = websocket;
EndpointConfig config = null;
if (endpoint instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
// unwrap
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
config = ce.getConfig();
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
}
return new JsrEndpointEventDriver(policy,(Endpoint)endpoint,config);
return new JsrEndpointEventDriver(policy,(EndpointInstance)websocket);
}
@Override
@ -53,15 +44,14 @@ public class JsrEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
Object endpoint = websocket;
if (endpoint instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
// unwrap
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
Object endpoint = ei.getEndpoint();
return (endpoint instanceof javax.websocket.Endpoint);
}
}

View File

@ -20,12 +20,11 @@ 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
{
public JsrEventDriverFactory(WebSocketPolicy policy, ClientContainer container)
public JsrEventDriverFactory(WebSocketPolicy policy)
{
super(policy);
@ -33,7 +32,7 @@ public class JsrEventDriverFactory extends EventDriverFactory
// Classes that extend javax.websocket.Endpoint
addImplementation(new JsrEndpointImpl());
// Classes annotated with @javax.websocket.ClientEndpoint
addImplementation(new JsrClientEndpointImpl(container));
addImplementation(new JsrClientEndpointImpl());
}
/**
@ -42,9 +41,9 @@ public class JsrEventDriverFactory extends EventDriverFactory
@Override
protected String getClassName(Object websocket)
{
if (websocket instanceof ConfiguredEndpoint)
if (websocket instanceof EndpointInstance)
{
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
EndpointInstance ce = (EndpointInstance)websocket;
return ce.getEndpoint().getClass().getName();
}

View File

@ -28,7 +28,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
import org.eclipse.jetty.websocket.jsr356.DecoderFactory;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class BinaryWholeMessage extends SimpleBinaryMessage
@ -52,7 +52,7 @@ public class BinaryWholeMessage extends SimpleBinaryMessage
byte data[] = out.toByteArray();
DecoderWrapper decoder = msgWrapper.getDecoder();
DecoderFactory.Wrapper decoder = msgWrapper.getDecoder();
Decoder.Binary<Object> binaryDecoder = (Binary<Object>)decoder.getDecoder();
try
{

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.messages;
import javax.websocket.SendHandler;
import javax.websocket.SendResult;
import org.eclipse.jetty.websocket.api.WriteCallback;
public class SendHandlerWriteCallback implements WriteCallback
{
private final SendHandler sendHandler;
public SendHandlerWriteCallback(SendHandler sendHandler)
{
this.sendHandler = sendHandler;
}
@Override
public void writeFailed(Throwable x)
{
sendHandler.onResult(new SendResult(x));
}
@Override
public void writeSuccess()
{
sendHandler.onResult(new SendResult());
}
}

View File

@ -26,7 +26,7 @@ import javax.websocket.MessageHandler.Whole;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
import org.eclipse.jetty.websocket.jsr356.DecoderWrapper;
import org.eclipse.jetty.websocket.jsr356.DecoderFactory;
import org.eclipse.jetty.websocket.jsr356.MessageHandlerWrapper;
public class TextWholeMessage extends SimpleTextMessage
@ -48,7 +48,7 @@ public class TextWholeMessage extends SimpleTextMessage
{
finished = true;
DecoderWrapper decoder = msgWrapper.getDecoder();
DecoderFactory.Wrapper decoder = msgWrapper.getDecoder();
Decoder.Text<Object> textDecoder = (Decoder.Text<Object>)decoder.getDecoder();
try
{

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// 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.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
/**
* The immutable base metadata for a coder ({@link Decoder} or {@link Encoder}
*
* @param <T>
* the specific type of coder ({@link Decoder} or {@link Encoder}
*/
public abstract class CoderMetadata<T>
{
/** The class for the Coder */
private final Class<? extends T> coderClass;
/** The Class that the Decoder declares it decodes */
private final Class<?> objType;
/** 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 CoderMetadata(Class<? extends T> coderClass, Class<?> objType, MessageType messageType, boolean streamed)
{
this.objType = objType;
this.coderClass = coderClass;
this.messageType = messageType;
this.streamed = streamed;
}
public Class<? extends T> getCoderClass()
{
return this.coderClass;
}
public MessageType getMessageType()
{
return messageType;
}
public Class<?> getObjectType()
{
return objType;
}
public boolean isStreamed()
{
return streamed;
}
}

View File

@ -0,0 +1,222 @@
//
// ========================================================================
// 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 java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
/**
* An durable collection of {@link CoderMetadata}.
* <p>
* This is a write-only collection, and cannot be modified once initialized.
*
* @param <T>
* The type of coder ({@link Decoder} or {@link Encoder}
* @param <M>
* The metadata for the coder
*/
public abstract class CoderMetadataSet<T, M extends CoderMetadata<T>> implements Iterable<M>
{
/**
* Collection of metadatas
*/
private final List<M> metadatas;
/**
* Collection of declared Coder classes
*/
private final List<Class<? extends T>> coders;
/**
* Mapping of supported Type to metadata list index
*/
private final Map<Class<?>, Integer> typeMap;
/**
* Mapping of Coder class to list of supported metadata
*/
private final Map<Class<? extends T>, List<Integer>> implMap;
protected CoderMetadataSet()
{
metadatas = new ArrayList<>();
coders = new ArrayList<>();
typeMap = new ConcurrentHashMap<>();
implMap = new ConcurrentHashMap<>();
}
public void add(Class<? extends T> coder)
{
List<M> metadatas = discover(coder);
trackMetadata(metadatas);
}
public List<M> addAll(Class<? extends T>[] coders)
{
List<M> metadatas = new ArrayList<>();
for (Class<? extends T> coder : coders)
{
metadatas.addAll(discover(coder));
}
trackMetadata(metadatas);
return metadatas;
}
/**
* Coder Specific discovery of Metadata for a specific coder.
*
* @param coder
* the coder to discover metadata in.
* @return the list of metadata discovered
* @throws InvalidWebSocketException
* if unable to discover some metadata. Sucha as: a duplicate {@link CoderMetadata#getObjectType()} encountered, , or if unable to find the
* concrete generic class reference for the coder, or if the provided coder is not valid per spec.
*/
protected abstract List<M> discover(Class<? extends T> coder);
public Class<? extends T> getCoder(Class<?> type)
{
M metadata = getMetadataByType(type);
if (metadata == null)
{
return null;
}
return metadata.getCoderClass();
}
public List<Class<? extends T>> getList()
{
return coders;
}
public List<M> getMetadataByImplementation(Class<? extends T> clazz)
{
List<Integer> indexes = implMap.get(clazz);
if (indexes == null)
{
return null;
}
List<M> ret = new ArrayList<>();
for (Integer idx : indexes)
{
ret.add(metadatas.get(idx));
}
return ret;
}
public M getMetadataByType(Class<?> type)
{
Integer idx = typeMap.get(type);
if (idx == null)
{
return null;
}
return metadatas.get(idx);
}
@Override
public Iterator<M> iterator()
{
return metadatas.iterator();
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(this.getClass().getSimpleName());
builder.append("[metadatas=");
builder.append(metadatas.size());
builder.append(",coders=");
builder.append(coders.size());
builder.append("]");
return builder.toString();
}
protected void trackMetadata(List<M> metadatas)
{
for (M metadata : metadatas)
{
trackMetadata(metadata);
}
}
protected void trackMetadata(M metadata)
{
synchronized (metadatas)
{
// Validate
boolean duplicate = false;
// Is this metadata already declared?
if (metadatas.contains(metadata))
{
duplicate = true;
}
// Is this type already declared?
Class<?> type = metadata.getObjectType();
if (typeMap.containsKey(type))
{
duplicate = true;
}
if (duplicate)
{
StringBuilder err = new StringBuilder();
err.append("Duplicate decoder for type: ");
err.append(type);
err.append(" (class ").append(metadata.getCoderClass().getName());
// Get prior one
M dup = getMetadataByType(type);
err.append(" duplicates ");
err.append(dup.getCoderClass().getName());
err.append(")");
throw new IllegalStateException(err.toString());
}
// Track
Class<? extends T> coderClass = metadata.getCoderClass();
int newidx = metadatas.size();
metadatas.add(metadata);
coders.add(coderClass);
typeMap.put(type,newidx);
List<Integer> indexes = implMap.get(coderClass);
if (indexes == null)
{
indexes = new ArrayList<>();
}
if (indexes.contains(newidx))
{
// possible duplicate, TODO: how?
}
indexes.add(newidx);
implMap.put(coderClass,indexes);
}
}
}

View File

@ -25,42 +25,10 @@ import org.eclipse.jetty.websocket.jsr356.MessageType;
/**
* Immutable Metadata for a {@link Decoder}
*/
public class DecoderMetadata
public class DecoderMetadata extends CoderMetadata<Decoder>
{
/** The Class for the Decoder itself */
private final Class<? extends Decoder> decoderClass;
/** The Class that the Decoder declares it decodes */
private final Class<?> objType;
/** 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> decoderClass, MessageType messageType, boolean streamed)
public DecoderMetadata(Class<? extends Decoder> coderClass, Class<?> objType, MessageType messageType, boolean streamed)
{
this.objType = objType;
this.decoderClass = decoderClass;
this.messageType = messageType;
this.streamed = streamed;
}
public Class<? extends Decoder> getDecoderClass()
{
return decoderClass;
}
public MessageType getMessageType()
{
return messageType;
}
public Class<?> getObjectType()
{
return objType;
}
public boolean isStreamed()
{
return streamed;
super(coderClass,objType,messageType,streamed);
}
}

View File

@ -0,0 +1,92 @@
//
// ========================================================================
// 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 java.util.ArrayList;
import java.util.List;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
public class DecoderMetadataSet extends CoderMetadataSet<Decoder, DecoderMetadata>
{
@Override
protected List<DecoderMetadata> discover(Class<? extends Decoder> decoder)
{
List<DecoderMetadata> metadatas = new ArrayList<>();
if (Decoder.Binary.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderType(decoder,Decoder.Binary.class);
metadatas.add(new DecoderMetadata(decoder,objType,MessageType.BINARY,false));
}
if (Decoder.BinaryStream.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderType(decoder,Decoder.BinaryStream.class);
metadatas.add(new DecoderMetadata(decoder,objType,MessageType.BINARY,true));
}
if (Decoder.Text.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderType(decoder,Decoder.Text.class);
metadatas.add(new DecoderMetadata(decoder,objType,MessageType.TEXT,false));
}
if (Decoder.TextStream.class.isAssignableFrom(decoder))
{
Class<?> objType = getDecoderType(decoder,Decoder.TextStream.class);
metadatas.add(new DecoderMetadata(decoder,objType,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 metadatas;
}
private Class<?> getDecoderType(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 InvalidWebSocketException(err.toString());
}
return decoderClass;
}
protected final void register(Class<?> type, Class<? extends Decoder> decoder, MessageType msgType, boolean streamed)
{
DecoderMetadata metadata = new DecoderMetadata(decoder,type,msgType,streamed);
trackMetadata(metadata);
}
}

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.metadata;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
/**
* Thrown when a duplicate coder is encountered when attempting to identify a Endpoint's metadata ( {@link Decoder} or {@link Encoder})
*/
public class DuplicateCoderException extends InvalidWebSocketException
{
private static final long serialVersionUID = -3049181444035417170L;
public DuplicateCoderException(String message)
{
super(message);
}
public DuplicateCoderException(String message, Throwable cause)
{
super(message,cause);
}
}

View File

@ -25,42 +25,10 @@ import org.eclipse.jetty.websocket.jsr356.MessageType;
/**
* Immutable Metadata for a {@link Encoder}
*/
public class EncoderMetadata
public class EncoderMetadata extends CoderMetadata<Encoder>
{
/** 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)
public EncoderMetadata(Class<? extends Encoder> coderClass, Class<?> objType, 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;
super(coderClass,objType,messageType,streamed);
}
}

View File

@ -0,0 +1,92 @@
//
// ========================================================================
// 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 java.util.ArrayList;
import java.util.List;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.utils.ReflectUtils;
public class EncoderMetadataSet extends CoderMetadataSet<Encoder, EncoderMetadata>
{
@Override
protected List<EncoderMetadata> discover(Class<? extends Encoder> encoder)
{
List<EncoderMetadata> metadatas = new ArrayList<>();
if (Encoder.Binary.class.isAssignableFrom(encoder))
{
Class<?> objType = getEncoderType(encoder,Encoder.Binary.class);
metadatas.add(new EncoderMetadata(encoder,objType,MessageType.BINARY,false));
}
if (Encoder.BinaryStream.class.isAssignableFrom(encoder))
{
Class<?> objType = getEncoderType(encoder,Encoder.BinaryStream.class);
metadatas.add(new EncoderMetadata(encoder,objType,MessageType.BINARY,true));
}
if (Encoder.Text.class.isAssignableFrom(encoder))
{
Class<?> objType = getEncoderType(encoder,Encoder.Text.class);
metadatas.add(new EncoderMetadata(encoder,objType,MessageType.TEXT,false));
}
if (Encoder.TextStream.class.isAssignableFrom(encoder))
{
Class<?> objType = getEncoderType(encoder,Encoder.TextStream.class);
metadatas.add(new EncoderMetadata(encoder,objType,MessageType.TEXT,true));
}
if (!ReflectUtils.isDefaultConstructable(encoder))
{
throw new InvalidSignatureException("Encoder must have public, no-args constructor: " + encoder.getName());
}
if (metadatas.size() <= 0)
{
throw new InvalidSignatureException("Not a valid Encoder class: " + encoder.getName() + " implements no " + Encoder.class.getName() + " interfaces");
}
return metadatas;
}
private Class<?> getEncoderType(Class<? extends Encoder> encoder, Class<?> interfaceClass)
{
Class<?> decoderClass = ReflectUtils.findGenericClassFor(encoder,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(encoder);
throw new InvalidWebSocketException(err.toString());
}
return decoderClass;
}
protected final void register(Class<?> type, Class<? extends Encoder> encoder, MessageType msgType, boolean streamed)
{
EncoderMetadata metadata = new EncoderMetadata(encoder,type,msgType,streamed);
trackMetadata(metadata);
}
}

View File

@ -0,0 +1,28 @@
//
// ========================================================================
// 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;
public interface EndpointMetadata
{
public DecoderMetadataSet getDecoders();
public EncoderMetadataSet getEncoders();
public Class<?> getEndpointClass();
}

View File

@ -110,14 +110,23 @@ public class ConfiguratorTest
{
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
EndpointEchoClient echoer = new EndpointEchoClient();
// Build Config
ClientEndpointConfig.Builder cfgbldr = ClientEndpointConfig.Builder.create();
TrackingConfigurator configurator = new TrackingConfigurator();
cfgbldr.configurator(configurator);
ClientEndpointConfig config = cfgbldr.build();
// Connect
Session session = container.connectToServer(echoer,config,serverUri);
// Send Simple Message
session.getBasicRemote().sendText("Echo");
// Wait for echo
echoer.textCapture.messageQueue.awaitMessages(1,1000,TimeUnit.MILLISECONDS);
// Validate client side configurator use
Assert.assertThat("configurator.request",configurator.request,notNullValue());
Assert.assertThat("configurator.response",configurator.response,notNullValue());
}

View File

@ -20,10 +20,8 @@ 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;
@ -32,10 +30,10 @@ 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.PrimitiveDecoderMetadataSet;
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.metadata.DecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
import org.eclipse.jetty.websocket.jsr356.samples.FruitDecoder;
import org.junit.Assert;
@ -44,21 +42,13 @@ import org.junit.Test;
public class DecoderFactoryTest
{
private DecoderMetadataSet metadatas;
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.assertEquals("metadata.coderClass",metadata.getCoderClass(),expectedDecoderClass);
Assert.assertThat("metadata.messageType",metadata.getMessageType(),is(expectedType));
Assert.assertEquals("metadata.objectType",metadata.getObjectType(),type);
}
@ -66,90 +56,52 @@ public class DecoderFactoryTest
@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);
DecoderFactory primitivesFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
metadatas = new DecoderMetadataSet();
factory = new DecoderFactory(metadatas,primitivesFactory);
}
@Test
public void testGetMetadataForByteArray()
{
factory.register(ByteArrayDecoder.class);
assertMetadataFor(byte[].class,ByteArrayDecoder.class,MessageType.BINARY);
}
@Test
public void testGetMetadataForByteBuffer()
{
assertMetadataFor(ByteBuffer.class,ByteBufferDecoder.class,MessageType.BINARY);
}
@Test
public void testGetMetadataForDate()
{
factory.register(DateDecoder.class);
metadatas.add(DateDecoder.class);
assertMetadataFor(Date.class,DateDecoder.class,MessageType.TEXT);
}
@Test
public void testGetMetadataForFruit()
{
metadatas.add(FruitDecoder.class);
assertMetadataFor(Fruit.class,FruitDecoder.class,MessageType.TEXT);
}
@Test
public void testGetMetadataForInteger()
{
assertMetadataFor(Integer.TYPE,IntegerDecoder.class,MessageType.TEXT);
}
@Test
public void testGetMetadataForLong()
{
assertMetadataFor(Long.TYPE,LongDecoder.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"));
}
assertMetadataFor(String.class,StringDecoder.class,MessageType.TEXT);
}
}

View File

@ -0,0 +1,86 @@
//
// ========================================================================
// 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.Encoder;
import org.eclipse.jetty.websocket.jsr356.encoders.IntegerEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.LongEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.PrimitiveEncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.EncoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
import org.eclipse.jetty.websocket.jsr356.samples.FruitBinaryEncoder;
import org.eclipse.jetty.websocket.jsr356.samples.FruitTextEncoder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Tests against the Encoders class
*/
public class EncoderFactoryTest
{
private EncoderMetadataSet metadatas;
private EncoderFactory factory;
private void assertMetadataFor(Class<?> type, Class<? extends Encoder> expectedEncoderClass, MessageType expectedType)
{
EncoderMetadata metadata = factory.getMetadataFor(type);
Assert.assertEquals("metadata.coderClass",metadata.getCoderClass(),expectedEncoderClass);
Assert.assertThat("metadata.messageType",metadata.getMessageType(),is(expectedType));
Assert.assertEquals("metadata.objectType",metadata.getObjectType(),type);
}
@Before
public void initEncoderFactory()
{
EncoderFactory primitivesFactory = new EncoderFactory(PrimitiveEncoderMetadataSet.INSTANCE);
metadatas = new EncoderMetadataSet();
factory = new EncoderFactory(metadatas,primitivesFactory);
}
@Test
public void testGetMetadataForFruitBinary()
{
metadatas.add(FruitBinaryEncoder.class);
assertMetadataFor(Fruit.class,FruitBinaryEncoder.class,MessageType.BINARY);
}
@Test
public void testGetMetadataForFruitText()
{
metadatas.add(FruitTextEncoder.class);
assertMetadataFor(Fruit.class,FruitTextEncoder.class,MessageType.TEXT);
}
@Test
public void testGetMetadataForInteger()
{
assertMetadataFor(Integer.TYPE,IntegerEncoder.class,MessageType.TEXT);
}
@Test
public void testGetMetadataForLong()
{
assertMetadataFor(Long.TYPE,LongEncoder.class,MessageType.TEXT);
}
}

View File

@ -30,6 +30,8 @@ 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.client.SimpleEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayWholeHandler;
import org.eclipse.jetty.websocket.jsr356.handlers.ByteBufferPartialHandler;
@ -55,7 +57,11 @@ public class JsrSessionTest
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
ClientEndpointConfig config = new EmptyClientEndpointConfig();
DummyEndpoint websocket = new DummyEndpoint();
EventDriver driver = new JsrEndpointEventDriver(policy,websocket,config);
SimpleEndpointMetadata metadata = new SimpleEndpointMetadata(websocket.getClass());
EndpointInstance ei = new EndpointInstance(websocket,config,metadata);
EventDriver driver = new JsrEndpointEventDriver(policy,ei);
DummyConnection connection = new DummyConnection();
session = new JsrSession(requestURI,driver,connection,container,id);
}

View File

@ -25,9 +25,11 @@ import java.util.List;
import javax.websocket.DeploymentException;
import org.eclipse.jetty.websocket.jsr356.decoders.PrimitiveDecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.handlers.ByteArrayPartialHandler;
import org.eclipse.jetty.websocket.jsr356.handlers.StringPartialHandler;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadataSet;
import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
import org.junit.Assert;
import org.junit.Before;
@ -36,13 +38,15 @@ import org.junit.Test;
public class MessageHandlerFactoryTest
{
private MessageHandlerFactory factory;
private DecoderMetadataSet metadatas;
private DecoderFactory decoders;
@Before
public void init() throws DeploymentException
{
ClientContainer container = new ClientContainer();
decoders = new DecoderFactory(container.getDecoderFactory());
DecoderFactory primitivesFactory = new DecoderFactory(PrimitiveDecoderMetadataSet.INSTANCE);
metadatas = new DecoderMetadataSet();
decoders = new DecoderFactory(metadatas,primitivesFactory);
factory = new MessageHandlerFactory();
}

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.samples;
package org.eclipse.jetty.websocket.jsr356.decoders;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@ -28,11 +28,13 @@ import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
import org.eclipse.jetty.websocket.jsr356.samples.FruitBinaryEncoder;
/**
* Intentionally bad example of attempting to decode the same object to different message formats.
*/
public class DualDecoder implements Decoder.Text<Fruit>, Decoder.Binary<Fruit>
public class BadDualDecoder implements Decoder.Text<Fruit>, Decoder.Binary<Fruit>
{
@Override
public Fruit decode(ByteBuffer bytes) throws DecodeException

View File

@ -0,0 +1,53 @@
//
// ========================================================================
// 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 static org.hamcrest.Matchers.*;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
import org.junit.Assert;
import org.junit.Test;
public class PrimitiveDecoderMetadataSetTest
{
private void assertClassEquals(String msg, Class<?> actual, Class<?> expected)
{
Assert.assertThat(msg,actual.getName(),is(expected.getName()));
}
private void assertDecoderType(Class<? extends Decoder> expectedDecoder, MessageType expectedMsgType, Class<?> type)
{
PrimitiveDecoderMetadataSet primitives = new PrimitiveDecoderMetadataSet();
DecoderMetadata metadata = primitives.getMetadataByType(type);
String prefix = String.format("Metadata By Type [%s]",type.getName());
Assert.assertThat(prefix,metadata,notNullValue());
assertClassEquals(prefix + ".coderClass",metadata.getCoderClass(),expectedDecoder);
Assert.assertThat(prefix + ".messageType",metadata.getMessageType(),is(expectedMsgType));
}
@Test
public void testGetByteArray()
{
assertDecoderType(ByteArrayDecoder.class,MessageType.BINARY,byte[].class);
}
}

View File

@ -16,40 +16,50 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
package org.eclipse.jetty.websocket.jsr356.decoders;
import java.nio.ByteBuffer;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
/**
* Expose a configured {@link Decoder} instance along with its associated {@link DecoderMetadata}
* Example of a valid decoder impl declaring 2 decoders.
*/
public class DecoderWrapper implements Configurable
public class ValidDualDecoder implements Decoder.Text<Integer>, Decoder.Binary<Long>
{
private final Decoder decoder;
private final DecoderMetadata metadata;
public DecoderWrapper(Decoder decoder, DecoderMetadata metadata)
@Override
public Long decode(ByteBuffer bytes) throws DecodeException
{
this.decoder = decoder;
this.metadata = metadata;
return bytes.getLong();
}
public Decoder getDecoder()
@Override
public Integer decode(String s) throws DecodeException
{
return decoder;
return Integer.parseInt(s);
}
public DecoderMetadata getMetadata()
@Override
public void destroy()
{
return metadata;
}
@Override
public void init(EndpointConfig config)
{
this.decoder.init(config);
}
@Override
public boolean willDecode(ByteBuffer bytes)
{
return true;
}
@Override
public boolean willDecode(String s)
{
return true;
}
}

View File

@ -16,40 +16,39 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
package org.eclipse.jetty.websocket.jsr356.encoders;
import java.io.IOException;
import java.io.Writer;
import javax.websocket.EncodeException;
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}
* Intentionally bad example of attempting to encode the same object for different message types.
*/
public class EncoderWrapper implements Configurable
public class BadDualEncoder implements Encoder.Text<Integer>, Encoder.TextStream<Integer>
{
private final Encoder encoder;
private final EncoderMetadata metadata;
public EncoderWrapper(Encoder encoder, EncoderMetadata metadata)
@Override
public void destroy()
{
this.encoder = encoder;
this.metadata = metadata;
}
public Encoder getEncoder()
@Override
public String encode(Integer object) throws EncodeException
{
return encoder;
return Integer.toString(object);
}
public EncoderMetadata getMetadata()
@Override
public void encode(Integer object, Writer writer) throws EncodeException, IOException
{
return metadata;
writer.write(Integer.toString(object));
}
@Override
public void init(EndpointConfig config)
{
this.encoder.init(config);
}
}

View File

@ -16,31 +16,33 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
package org.eclipse.jetty.websocket.jsr356.encoders;
import java.util.HashMap;
import java.util.Map;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
public abstract class CommonConfig implements EndpointConfig
/**
* Encode Date
*/
public class DateEncoder implements Encoder.Text<Date>
{
/** User Properties for the Endpoint */
private Map<String, Object> userProperties;
public CommonConfig(CommonConfig copy)
@Override
public void destroy()
{
userProperties = copy.userProperties;
}
protected CommonConfig(CommonContainer container)
{
userProperties = new HashMap<>();
}
@Override
public Map<String, Object> getUserProperties()
public String encode(Date object) throws EncodeException
{
return new SimpleDateFormat("yyyy.MM.dd").format(object);
}
@Override
public void init(EndpointConfig config)
{
return userProperties;
}
}

View File

@ -0,0 +1,48 @@
//
// ========================================================================
// 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.encoders;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* Encode Date
*/
public class DateTimeEncoder implements Encoder.Text<Date>
{
@Override
public void destroy()
{
}
@Override
public String encode(Date object) throws EncodeException
{
return new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z").format(object);
}
@Override
public void init(EndpointConfig config)
{
}
}

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// 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.encoders;
import java.io.IOException;
import java.io.Writer;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.websocket.jsr356.samples.Fruit;
/**
* Intentionally bad example of attempting to decode the same object to different message formats.
*/
public class DualEncoder implements Encoder.Text<Fruit>, Encoder.TextStream<Fruit>
{
@Override
public void destroy()
{
}
@Override
public String encode(Fruit fruit) throws EncodeException
{
return String.format("%s|%s",fruit.name,fruit.color);
}
@Override
public void encode(Fruit fruit, Writer writer) throws EncodeException, IOException
{
writer.write(fruit.name);
writer.write('|');
writer.write(fruit.color);
}
@Override
public void init(EndpointConfig config)
{
}
}

View File

@ -1,56 +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.encoders;
import static org.hamcrest.Matchers.*;
import org.eclipse.jetty.websocket.jsr356.ConfigurationException;
import org.eclipse.jetty.websocket.jsr356.samples.FruitBinaryEncoder;
import org.eclipse.jetty.websocket.jsr356.samples.FruitTextEncoder;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests against the Encoders class
*/
public class EncodersTest
{
@Test
public void testAddDuplicateEncoder()
{
Encoders encoders = new Encoders();
encoders.add(FruitBinaryEncoder.class);
try
{
encoders.add(FruitTextEncoder.class); // throws exception
Assert.fail("Should have thrown ConfigurationException");
}
catch (ConfigurationException e)
{
Assert.assertThat("Error Message",e.getMessage(),containsString("Duplicate"));
}
}
@Test
public void testAddEncoder()
{
Encoders encoders = new Encoders();
encoders.add(FruitBinaryEncoder.class);
}
}

View File

@ -0,0 +1,48 @@
//
// ========================================================================
// 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.encoders;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* Encode Time
*/
public class TimeEncoder implements Encoder.Text<Date>
{
@Override
public void destroy()
{
}
@Override
public String encode(Date object) throws EncodeException
{
return new SimpleDateFormat("HH:mm:ss z").format(object);
}
@Override
public void init(EndpointConfig config)
{
}
}

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.encoders;
import java.io.IOException;
import java.io.OutputStream;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* Example of a valid encoder impl declaring 2 encoders.
*/
public class ValidDualEncoder implements Encoder.Text<Integer>, Encoder.BinaryStream<Long>
{
@Override
public void destroy()
{
}
@Override
public String encode(Integer object) throws EncodeException
{
return Integer.toString(object);
}
@Override
public void encode(Long object, OutputStream os) throws EncodeException, IOException
{
byte b[] = new byte[8];
long v = object;
b[0] = (byte)(v >>> 56);
b[1] = (byte)(v >>> 48);
b[2] = (byte)(v >>> 40);
b[3] = (byte)(v >>> 32);
b[4] = (byte)(v >>> 24);
b[5] = (byte)(v >>> 16);
b[6] = (byte)(v >>> 8);
b[7] = (byte)(v >>> 0);
os.write(b,0,8);
}
@Override
public void init(EndpointConfig config)
{
}
}

View File

@ -26,7 +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.client.AnnotatedClientEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSocket;
import org.junit.Assert;
@ -52,7 +52,7 @@ public class ClientAnnotatedEndpointScannerTest
@Test
public void testScan_BasicOpenClose() throws DeploymentException
{
JsrClientMetadata metadata = new JsrClientMetadata(container,BasicOpenCloseSocket.class);
AnnotatedClientEndpointMetadata metadata = new AnnotatedClientEndpointMetadata(container,BasicOpenCloseSocket.class);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
scanner.scan();
@ -65,7 +65,7 @@ public class ClientAnnotatedEndpointScannerTest
@Test
public void testScan_BasicSessionOpenClose() throws DeploymentException
{
JsrClientMetadata metadata = new JsrClientMetadata(container,BasicOpenCloseSessionSocket.class);
AnnotatedClientEndpointMetadata metadata = new AnnotatedClientEndpointMetadata(container,BasicOpenCloseSessionSocket.class);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
scanner.scan();

View File

@ -33,8 +33,8 @@ import javax.websocket.Session;
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.annotations.AnnotatedEndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointMetadata;
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;
@ -90,12 +90,12 @@ public class ClientAnnotatedEndpointScanner_GoodSignaturesTest
public static Collection<Case[]> data() throws Exception
{
List<Case[]> data = new ArrayList<>();
Field fOpen = findFieldRef(JsrMetadata.class,"onOpen");
Field fClose = findFieldRef(JsrMetadata.class,"onClose");
Field fError = findFieldRef(JsrMetadata.class,"onError");
Field fText = findFieldRef(JsrMetadata.class,"onText");
Field fBinary = findFieldRef(JsrMetadata.class,"onBinary");
Field fPong = findFieldRef(JsrMetadata.class,"onPong");
Field fOpen = findFieldRef(AnnotatedEndpointMetadata.class,"onOpen");
Field fClose = findFieldRef(AnnotatedEndpointMetadata.class,"onClose");
Field fError = findFieldRef(AnnotatedEndpointMetadata.class,"onError");
Field fText = findFieldRef(AnnotatedEndpointMetadata.class,"onText");
Field fBinary = findFieldRef(AnnotatedEndpointMetadata.class,"onBinary");
Field fPong = findFieldRef(AnnotatedEndpointMetadata.class,"onPong");
// @formatter:off
// -- Open Events
@ -140,7 +140,7 @@ public class ClientAnnotatedEndpointScanner_GoodSignaturesTest
@Test
public void testScan_Basic() throws Exception
{
JsrClientMetadata metadata = new JsrClientMetadata(container,testcase.pojo);
AnnotatedClientEndpointMetadata metadata = new AnnotatedClientEndpointMetadata(container,testcase.pojo);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
scanner.scan();

View File

@ -35,7 +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.client.AnnotatedClientEndpointMetadata;
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;
@ -95,7 +95,7 @@ public class ClientAnnotatedEndpointScanner_InvalidSignaturesTest
@Test
public void testScan_InvalidSignature() throws DeploymentException
{
JsrClientMetadata metadata = new JsrClientMetadata(container,pojo);
AnnotatedClientEndpointMetadata metadata = new AnnotatedClientEndpointMetadata(container,pojo);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
try
{

View File

@ -0,0 +1,132 @@
//
// ========================================================================
// 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 static org.hamcrest.Matchers.*;
import java.util.List;
import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.decoders.BadDualDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.DateDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.IntegerDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.TimeDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ValidDualDecoder;
import org.junit.Assert;
import org.junit.Test;
public class DecoderMetadataSetTest
{
private void assertMetadata(CoderMetadata<?> metadata, Class<?> expectedType, Class<?> expectedCoder, MessageType expectedMessageType)
{
Assert.assertEquals("metadata.coderClass",expectedCoder,metadata.getCoderClass());
Assert.assertThat("metadata.messageType",metadata.getMessageType(),is(expectedMessageType));
Assert.assertEquals("metadata.objectType",expectedType,metadata.getObjectType());
}
@Test
public void testAddBadDualDecoders()
{
try
{
DecoderMetadataSet coders = new DecoderMetadataSet();
// has duplicated support for the same target Type
coders.add(BadDualDecoder.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 testAddDuplicate()
{
DecoderMetadataSet coders = new DecoderMetadataSet();
// Add DateDecoder (decodes java.util.Date)
coders.add(DateDecoder.class);
try
{
// Add TimeDecoder (which also wants to decode java.util.Date)
coders.add(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"));
}
}
@Test
public void testAddGetCoder()
{
DecoderMetadataSet coders = new DecoderMetadataSet();
coders.add(IntegerDecoder.class);
Class<? extends Decoder> actualClazz = coders.getCoder(Integer.class);
Assert.assertEquals("Coder Class",IntegerDecoder.class,actualClazz);
}
@Test
public void testAddGetMetadataByImpl()
{
DecoderMetadataSet coders = new DecoderMetadataSet();
coders.add(IntegerDecoder.class);
List<DecoderMetadata> metadatas = coders.getMetadataByImplementation(IntegerDecoder.class);
Assert.assertThat("Metadatas (by impl) count",metadatas.size(),is(1));
DecoderMetadata metadata = metadatas.get(0);
assertMetadata(metadata,Integer.class,IntegerDecoder.class,MessageType.TEXT);
}
@Test
public void testAddGetMetadataByType()
{
DecoderMetadataSet coders = new DecoderMetadataSet();
coders.add(IntegerDecoder.class);
DecoderMetadata metadata = coders.getMetadataByType(Integer.class);
assertMetadata(metadata,Integer.class,IntegerDecoder.class,MessageType.TEXT);
}
@Test
public void testAddValidDualDecoders()
{
DecoderMetadataSet coders = new DecoderMetadataSet();
coders.add(ValidDualDecoder.class);
List<Class<? extends Decoder>> decodersList = coders.getList();
Assert.assertThat("Decoder List",decodersList,notNullValue());
Assert.assertThat("Decoder List count",decodersList.size(),is(2));
DecoderMetadata metadata;
metadata = coders.getMetadataByType(Integer.class);
assertMetadata(metadata,Integer.class,ValidDualDecoder.class,MessageType.TEXT);
metadata = coders.getMetadataByType(Long.class);
assertMetadata(metadata,Long.class,ValidDualDecoder.class,MessageType.BINARY);
}
}

View File

@ -0,0 +1,132 @@
//
// ========================================================================
// 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 static org.hamcrest.Matchers.*;
import java.util.List;
import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.jsr356.MessageType;
import org.eclipse.jetty.websocket.jsr356.encoders.BadDualEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.DateEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.IntegerEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.TimeEncoder;
import org.eclipse.jetty.websocket.jsr356.encoders.ValidDualEncoder;
import org.junit.Assert;
import org.junit.Test;
public class EncoderMetadataSetTest
{
private void assertMetadata(CoderMetadata<?> metadata, Class<?> expectedType, Class<?> expectedCoder, MessageType expectedMessageType)
{
Assert.assertEquals("metadata.coderClass",expectedCoder,metadata.getCoderClass());
Assert.assertThat("metadata.messageType",metadata.getMessageType(),is(expectedMessageType));
Assert.assertEquals("metadata.objectType",expectedType,metadata.getObjectType());
}
@Test
public void testAddBadDualEncoders()
{
try
{
EncoderMetadataSet coders = new EncoderMetadataSet();
// has duplicated support for the same target Type
coders.add(BadDualEncoder.class);
Assert.fail("Should have thrown IllegalStateException for attempting to register Encoders with duplicate implementation");
}
catch (IllegalStateException e)
{
Assert.assertThat(e.getMessage(),containsString("Duplicate"));
}
}
@Test
public void testAddDuplicate()
{
EncoderMetadataSet coders = new EncoderMetadataSet();
// Add DateEncoder (decodes java.util.Date)
coders.add(DateEncoder.class);
try
{
// Add TimeEncoder (which also wants to decode java.util.Date)
coders.add(TimeEncoder.class);
Assert.fail("Should have thrown IllegalStateException for attempting to register Encoders with duplicate implementation");
}
catch (IllegalStateException e)
{
Assert.assertThat(e.getMessage(),containsString("Duplicate"));
}
}
@Test
public void testAddGetCoder()
{
EncoderMetadataSet coders = new EncoderMetadataSet();
coders.add(IntegerEncoder.class);
Class<? extends Encoder> actualClazz = coders.getCoder(Integer.class);
Assert.assertEquals("Coder Class",IntegerEncoder.class,actualClazz);
}
@Test
public void testAddGetMetadataByImpl()
{
EncoderMetadataSet coders = new EncoderMetadataSet();
coders.add(IntegerEncoder.class);
List<EncoderMetadata> metadatas = coders.getMetadataByImplementation(IntegerEncoder.class);
Assert.assertThat("Metadatas (by impl) count",metadatas.size(),is(1));
EncoderMetadata metadata = metadatas.get(0);
assertMetadata(metadata,Integer.class,IntegerEncoder.class,MessageType.TEXT);
}
@Test
public void testAddGetMetadataByType()
{
EncoderMetadataSet coders = new EncoderMetadataSet();
coders.add(IntegerEncoder.class);
EncoderMetadata metadata = coders.getMetadataByType(Integer.class);
assertMetadata(metadata,Integer.class,IntegerEncoder.class,MessageType.TEXT);
}
@Test
public void testAddValidDualEncoders()
{
EncoderMetadataSet coders = new EncoderMetadataSet();
coders.add(ValidDualEncoder.class);
List<Class<? extends Encoder>> EncodersList = coders.getList();
Assert.assertThat("Encoder List",EncodersList,notNullValue());
Assert.assertThat("Encoder List count",EncodersList.size(),is(2));
EncoderMetadata metadata;
metadata = coders.getMetadataByType(Integer.class);
assertMetadata(metadata,Integer.class,ValidDualEncoder.class,MessageType.TEXT);
metadata = coders.getMetadataByType(Long.class);
assertMetadata(metadata,Long.class,ValidDualEncoder.class,MessageType.BINARY);
}
}

View File

@ -25,8 +25,10 @@ import javax.websocket.EncodeException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.jsr356.decoders.BadDualDecoder;
@ClientEndpoint(decoders =
{ DualDecoder.class })
{ BadDualDecoder.class })
public class IntSocket
{
@OnMessage

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.LEVEL=DEBUG
org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG

View File

@ -126,4 +126,24 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
{
return configurator;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("AnnotatedServerEndpointConfig[endpointClass=");
builder.append(endpointClass);
builder.append(",path=");
builder.append(path);
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

@ -19,26 +19,21 @@
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.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.AnnotatedEndpointMetadata;
public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
public class AnnotatedServerEndpointMetadata extends AnnotatedEndpointMetadata<ServerEndpoint,ServerEndpointConfig> implements ServerEndpointMetadata
{
private final ServerEndpoint endpoint;
private final AnnotatedServerEndpointConfig config;
private final DecoderFactory decoders;
private final EncoderFactory encoders;
protected JsrServerMetadata(ServerContainer container, Class<?> websocket) throws DeploymentException
protected AnnotatedServerEndpointMetadata(ServerContainer container, Class<?> websocket) throws DeploymentException
{
super(websocket);
@ -50,11 +45,6 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
this.endpoint = anno;
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
@ -69,12 +59,6 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
params.addFirst(JsrParamPath.INSTANCE);
}
@Override
protected List<Class<? extends Decoder>> getConfiguredDecoders()
{
return config.getDecoders();
}
@Override
public void customizeParamsOnOpen(LinkedList<IJsrParamId> params)
{

View File

@ -16,52 +16,46 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356;
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.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Extension;
import javax.websocket.server.ServerEndpointConfig;
public class SimpleClientEndpointConfig implements ClientEndpointConfig
public class EmptyServerEndpointConfig implements ServerEndpointConfig
{
public static class DummyConfigurator extends ClientEndpointConfig.Configurator
{
public static final DummyConfigurator INSTANCE = new DummyConfigurator();
/* do nothing */
}
private Configurator configurator;
private List<Class<? extends Decoder>> decoders;
private List<Class<? extends Encoder>> encoders;
private List<Extension> extensions;
private List<String> preferredSubprotocols;
private final List<Class<? extends Decoder>> decoders;
private final List<Class<? extends Encoder>> encoders;
private final List<Extension> extensions;
private final List<String> subprotocols;
private final Configurator configurator;
private final Class<?> endpointClass;
private final String path;
private Map<String, Object> userProperties;
public SimpleClientEndpointConfig()
public EmptyServerEndpointConfig(Class<?> endpointClass, String path)
{
this.configurator = DummyConfigurator.INSTANCE;
this.endpointClass = endpointClass;
this.path = path;
this.decoders = new ArrayList<>();
this.encoders = new ArrayList<>();
this.subprotocols = new ArrayList<>();
this.extensions = new ArrayList<>();
this.preferredSubprotocols = new ArrayList<>();
this.userProperties = new HashMap<>();
this.configurator = BasicServerEndpointConfigurator.INSTANCE;
}
public void addDecoder(Class<? extends Decoder> decoderClass)
{
this.decoders.add(decoderClass);
}
@Override
public Configurator getConfigurator()
public List<Class<? extends Encoder>> getEncoders()
{
return configurator;
return encoders;
}
@Override
@ -71,9 +65,27 @@ public class SimpleClientEndpointConfig implements ClientEndpointConfig
}
@Override
public List<Class<? extends Encoder>> getEncoders()
public Map<String, Object> getUserProperties()
{
return encoders;
return userProperties;
}
@Override
public Class<?> getEndpointClass()
{
return endpointClass;
}
@Override
public String getPath()
{
return path;
}
@Override
public List<String> getSubprotocols()
{
return subprotocols;
}
@Override
@ -83,14 +95,8 @@ public class SimpleClientEndpointConfig implements ClientEndpointConfig
}
@Override
public List<String> getPreferredSubprotocols()
public Configurator getConfigurator()
{
return preferredSubprotocols;
return configurator;
}
@Override
public Map<String, Object> getUserProperties()
{
return userProperties;
}
}
}

View File

@ -27,16 +27,17 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
public class JsrCreator implements WebSocketCreator
{
private static final Logger LOG = Log.getLogger(JsrCreator.class);
private final ServerEndpointConfig config;
private final ServerEndpointMetadata metadata;
public JsrCreator(ServerEndpointConfig config)
public JsrCreator(ServerEndpointMetadata metadata)
{
this.config = config;
this.metadata = metadata;
}
@Override
@ -44,6 +45,8 @@ public class JsrCreator implements WebSocketCreator
{
JsrHandshakeRequest hsreq = new JsrHandshakeRequest(req);
JsrHandshakeResponse hsresp = new JsrHandshakeResponse(resp);
ServerEndpointConfig config = metadata.getConfig();
ServerEndpointConfig.Configurator configurator = config.getConfigurator();
@ -77,7 +80,8 @@ public class JsrCreator implements WebSocketCreator
try
{
Class<?> endpointClass = config.getEndpointClass();
return config.getConfigurator().getEndpointInstance(endpointClass);
Object endpoint = config.getConfigurator().getEndpointInstance(endpointClass);
return new EndpointInstance(endpoint,config,metadata);
}
catch (InstantiationException e)
{
@ -89,6 +93,6 @@ public class JsrCreator implements WebSocketCreator
@Override
public String toString()
{
return String.format("%s[config=%s]",this.getClass().getName(),config);
return String.format("%s[metadata=%s]",this.getClass().getName(),metadata);
}
}

View File

@ -19,13 +19,12 @@
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
import org.eclipse.jetty.websocket.jsr356.endpoints.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
/**
@ -33,42 +32,18 @@ import org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver;
*/
public class JsrServerEndpointImpl implements EventDriverImpl
{
private ServerContainer container;
public JsrServerEndpointImpl(ServerContainer container)
{
this.container = container;
}
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy) throws Throwable
{
Object endpoint = websocket;
if (websocket instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
// Classes annotated with @ServerEndpoint cannot be created with
// an external ServerEndpointConfig, this information MUST come
// from the @ServerEndpoint annotation.
if (ce.getConfig() != null)
{
throw new IllegalStateException("Cannot create @ServerEndpoint websocket with an external EndpointConfig");
}
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),EndpointInstance.class.getName()));
}
Class<?> endpointClass = endpoint.getClass();
// Get the base metadata for this class
JsrServerMetadata basemetadata = container.getServerEndpointMetadata(endpointClass);
// At this point we have a base metadata, now we need to copy it for
// this specific instance of the WebSocket Endpoint (as we will be
// modifying the metadata)
JsrEvents events = new JsrEvents(basemetadata); // copy constructor.
// Create copy of base config
ServerEndpointConfig config = basemetadata.getConfig();
return new JsrAnnotatedEventDriver(policy,endpoint,events,config);
EndpointInstance ei = (EndpointInstance)websocket;
AnnotatedServerEndpointMetadata metadata = (AnnotatedServerEndpointMetadata)ei.getMetadata();
JsrEvents events = new JsrEvents(metadata);
return new JsrAnnotatedEventDriver(policy,ei,events);
}
@Override
@ -80,14 +55,13 @@ public class JsrServerEndpointImpl implements EventDriverImpl
@Override
public boolean supports(Object websocket)
{
Object endpoint = websocket;
if (endpoint instanceof ConfiguredEndpoint)
if (!(websocket instanceof EndpointInstance))
{
// unwrap
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
return false;
}
EndpointInstance ei = (EndpointInstance)websocket;
Object endpoint = ei.getEndpoint();
ServerEndpoint anno = endpoint.getClass().getAnnotation(ServerEndpoint.class);
return (anno != null);

View File

@ -18,9 +18,12 @@
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.webapp.WebAppContext;
@ -28,7 +31,9 @@ import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointImpl;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
@ -43,7 +48,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
private final MappedWebSocketCreator mappedCreator;
private WebSocketServerFactory webSocketServletFactory;
private ConcurrentHashMap<Class<?>, JsrServerMetadata> endpointServerMetadataCache = new ConcurrentHashMap<>();
private Map<Class<?>, ServerEndpointMetadata> endpointServerMetadataCache = new ConcurrentHashMap<>();
public ServerContainer(MappedWebSocketCreator creator)
{
@ -51,37 +56,84 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
this.mappedCreator = creator;
}
public EndpointInstance newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path)
{
EndpointMetadata metadata = getClientEndpointMetadata(endpoint.getClass());
ServerEndpointConfig cec = config;
if (config == null)
{
if (metadata instanceof AnnotatedServerEndpointMetadata)
{
cec = ((AnnotatedServerEndpointMetadata)metadata).getConfig();
}
else
{
cec = new EmptyServerEndpointConfig(endpoint.getClass(),path);
}
}
return new EndpointInstance(endpoint,cec,metadata);
}
@Override
public void addEndpoint(Class<?> endpointClass) throws DeploymentException
{
JsrServerMetadata metadata = getServerEndpointMetadata(endpointClass);
ServerEndpointMetadata metadata = getServerEndpointMetadata(endpointClass,null);
addEndpoint(metadata);
}
public void addEndpoint(JsrServerMetadata metadata) throws DeploymentException
public void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
{
addEndpoint(metadata.getConfig());
JsrCreator creator = new JsrCreator(metadata);
mappedCreator.addMapping(new WebSocketPathSpec(metadata.getPath()),creator);
}
@Override
public void addEndpoint(ServerEndpointConfig config) throws DeploymentException
{
JsrCreator creator = new JsrCreator(config);
mappedCreator.addMapping(new WebSocketPathSpec(config.getPath()),creator);
ServerEndpointMetadata metadata = getServerEndpointMetadata(config.getEndpointClass(),config);
addEndpoint(metadata);
}
public JsrServerMetadata getServerEndpointMetadata(Class<?> endpointClass) throws DeploymentException
public ServerEndpointMetadata getServerEndpointMetadata(Class<?> endpoint, ServerEndpointConfig config) throws DeploymentException
{
JsrServerMetadata basemetadata = endpointServerMetadataCache.get(endpointClass);
if (basemetadata == null)
synchronized (endpointServerMetadataCache)
{
basemetadata = new JsrServerMetadata(this,endpointClass);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(basemetadata);
scanner.scan();
endpointServerMetadataCache.put(endpointClass,basemetadata);
}
ServerEndpointMetadata metadata = endpointServerMetadataCache.get(endpoint);
if (metadata != null)
{
return metadata;
}
return basemetadata;
ServerEndpoint anno = endpoint.getAnnotation(ServerEndpoint.class);
if (anno != null)
{
// Annotated takes precedence here
AnnotatedServerEndpointMetadata ametadata = new AnnotatedServerEndpointMetadata(this,endpoint);
AnnotatedEndpointScanner<ServerEndpoint,ServerEndpointConfig> scanner = new AnnotatedEndpointScanner<>(ametadata);
metadata = ametadata;
scanner.scan();
}
else if (Endpoint.class.isAssignableFrom(endpoint))
{
// extends Endpoint
@SuppressWarnings("unchecked")
Class<? extends Endpoint> eendpoint = (Class<? extends Endpoint>)endpoint;
metadata = new SimpleServerEndpointMetadata(eendpoint,config);
}
else
{
StringBuilder err = new StringBuilder();
err.append("Not a recognized websocket [");
err.append(endpoint.getName());
err.append("] does not extend @").append(ServerEndpoint.class.getName());
err.append(" or extend from ").append(Endpoint.class.getName());
throw new DeploymentException("Unable to identify as valid Endpoint: " + endpoint);
}
endpointServerMetadataCache.put(endpoint,metadata);
return metadata;
}
}
public WebSocketServletFactory getWebSocketServletFactory()
@ -94,7 +146,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
{
this.webSocketServletFactory = factory;
EventDriverFactory eventDriverFactory = this.webSocketServletFactory.getEventDriverFactory();
eventDriverFactory.addImplementation(new JsrServerEndpointImpl(this));
eventDriverFactory.addImplementation(new JsrServerEndpointImpl());
eventDriverFactory.addImplementation(new JsrEndpointImpl());
this.webSocketServletFactory.setSessionFactory(new JsrSessionFactory(this));
}

View File

@ -58,11 +58,9 @@ public class ServerEndpointAnnotation extends DiscoveredAnnotation
LOG.info("Got path: \"{}\"",path);
ServerContainer container = ServerContainer.get(_context);
try
{
JsrServerMetadata metadata = container.getServerEndpointMetadata(clazz);
container.addEndpoint(metadata);
container.addEndpoint(clazz);
}
catch (DeploymentException e)
{

View File

@ -0,0 +1,30 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
public interface ServerEndpointMetadata extends EndpointMetadata
{
ServerEndpointConfig getConfig();
public String getPath();
}

View File

@ -0,0 +1,47 @@
//
// ========================================================================
// 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 javax.websocket.Endpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.SimpleEndpointMetadata;
public class SimpleServerEndpointMetadata extends SimpleEndpointMetadata implements ServerEndpointMetadata
{
private final ServerEndpointConfig config;
public SimpleServerEndpointMetadata(Class<? extends Endpoint> endpointClass, ServerEndpointConfig config)
{
super(endpointClass);
this.config = config;
}
@Override
public ServerEndpointConfig getConfig()
{
return config;
}
@Override
public String getPath()
{
return config.getPath();
}
}

View File

@ -88,12 +88,12 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
public static Collection<Case[]> data() throws Exception
{
List<Case[]> data = new ArrayList<>();
Field fOpen = findFieldRef(JsrServerMetadata.class,"onOpen");
Field fClose = findFieldRef(JsrServerMetadata.class,"onClose");
Field fError = findFieldRef(JsrServerMetadata.class,"onError");
Field fText = findFieldRef(JsrServerMetadata.class,"onText");
Field fBinary = findFieldRef(JsrServerMetadata.class,"onBinary");
Field fPong = findFieldRef(JsrServerMetadata.class,"onPong");
Field fOpen = findFieldRef(AnnotatedServerEndpointMetadata.class,"onOpen");
Field fClose = findFieldRef(AnnotatedServerEndpointMetadata.class,"onClose");
Field fError = findFieldRef(AnnotatedServerEndpointMetadata.class,"onError");
Field fText = findFieldRef(AnnotatedServerEndpointMetadata.class,"onText");
Field fBinary = findFieldRef(AnnotatedServerEndpointMetadata.class,"onBinary");
Field fPong = findFieldRef(AnnotatedServerEndpointMetadata.class,"onPong");
// @formatter:off
// -- Open Events
@ -139,7 +139,7 @@ public class ServerAnnotatedEndpointScanner_GoodSignaturesTest
@Test
public void testScan_Basic() throws Exception
{
JsrServerMetadata metadata = new JsrServerMetadata(container,testcase.pojo);
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,testcase.pojo);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
scanner.scan();

View File

@ -94,7 +94,7 @@ public class ServerAnnotatedEndpointScanner_InvalidSignaturesTest
@Test
public void testScan_InvalidSignature() throws DeploymentException
{
JsrServerMetadata metadata = new JsrServerMetadata(container,pojo);
AnnotatedServerEndpointMetadata metadata = new AnnotatedServerEndpointMetadata(container,pojo);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata);
try

View File

@ -133,6 +133,10 @@ public class WSServer
{
contexts.addHandler(webapp);
webapp.start();
if (LOG.isDebugEnabled())
{
webapp.dump(System.err);
}
}
public void dump()
@ -171,6 +175,7 @@ public class WSServer
int port = connector.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d%s/",host,port,contextPath));
LOG.debug("Server started on {}",serverUri);
}
public void stop()

View File

@ -30,6 +30,7 @@ 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.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
@ -93,15 +94,7 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
private Future<Void> sendAsyncFrame(WebSocketFrame frame)
{
FutureWriteCallback future = new FutureWriteCallback();
try
{
connection.getIOState().assertOutputOpen();
outgoing.outgoingFrame(frame,future);
}
catch (IOException e)
{
future.writeFailed(e);
}
sendFrame(frame,future);
return future;
}
@ -139,27 +132,25 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
@Override
public Future<Void> sendBytesByFuture(ByteBuffer data)
{
if (msgLock.tryLock())
msgType.set(BINARY);
if (LOG.isDebugEnabled())
{
try
{
msgType.set(BINARY);
if (LOG.isDebugEnabled())
{
LOG.debug("sendBytesByFuture with {}",BufferUtil.toDetailString(data));
}
WebSocketFrame frame = WebSocketFrame.binary().setPayload(data);
return sendAsyncFrame(frame);
}
finally
{
msgType.set(NONE);
msgLock.unlock();
}
LOG.debug("sendBytesByFuture with {}",BufferUtil.toDetailString(data));
}
else
WebSocketFrame frame = WebSocketFrame.binary().setPayload(data);
return sendAsyncFrame(frame);
}
public void sendFrame(WebSocketFrame frame, WriteCallback callback)
{
try
{
throw new IllegalStateException(PRIORMSG_ERROR);
connection.getIOState().assertOutputOpen();
outgoing.outgoingFrame(frame,callback);
}
catch (IOException e)
{
callback.writeFailed(e);
}
}
@ -318,27 +309,12 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
@Override
public Future<Void> sendStringByFuture(String text)
{
if (msgLock.tryLock())
msgType.set(BINARY);
WebSocketFrame frame = WebSocketFrame.text(text);
if (LOG.isDebugEnabled())
{
try
{
msgType.set(BINARY);
WebSocketFrame frame = WebSocketFrame.text(text);
if (LOG.isDebugEnabled())
{
LOG.debug("sendStringByFuture with {}",BufferUtil.toDetailString(frame.getPayload()));
}
return sendAsyncFrame(frame);
}
finally
{
msgType.set(NONE);
msgLock.unlock();
}
}
else
{
throw new IllegalStateException(PRIORMSG_ERROR);
LOG.debug("sendStringByFuture with {}",BufferUtil.toDetailString(frame.getPayload()));
}
return sendAsyncFrame(frame);
}
}

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
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.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
@ -45,6 +46,7 @@ public class MessageOutputStream extends OutputStream
private WebSocketFrame frame;
private ByteBuffer buffer;
private FutureWriteCallback blocker;
private WriteCallback callback;
private boolean closed = false;
public MessageOutputStream(OutgoingFrames outgoing, int bufferSize, ByteBufferPool bufferPool)
@ -65,7 +67,9 @@ public class MessageOutputStream extends OutputStream
{
if (closed)
{
throw new IOException("Stream is closed");
IOException e = new IOException("Stream is closed");
notifyFailure(e);
throw e;
}
}
@ -81,9 +85,21 @@ public class MessageOutputStream extends OutputStream
// close stream
LOG.debug("Sent Frame Count: {}",frameCount);
closed = true;
super.close();
bufferPool.release(buffer);
LOG.debug("closed");
try
{
if (callback != null)
{
callback.writeSuccess();
}
super.close();
bufferPool.release(buffer);
LOG.debug("closed");
}
catch (IOException e)
{
notifyFailure(e);
throw e;
}
}
@Override
@ -94,8 +110,16 @@ public class MessageOutputStream extends OutputStream
// flush whatever is in the buffer with FIN=false
flush(false);
super.flush();
LOG.debug("flushed");
try
{
super.flush();
LOG.debug("flushed");
}
catch (IOException e)
{
notifyFailure(e);
throw e;
}
}
/**
@ -112,42 +136,71 @@ public class MessageOutputStream extends OutputStream
frame.setPayload(buffer);
frame.setFin(fin);
blocker = new FutureWriteCallback();
outgoing.outgoingFrame(frame,blocker);
try
{
// block on write
blocker.get();
// block success
frameCount++;
frame.setOpCode(OpCode.CONTINUATION);
}
catch (ExecutionException e)
{
Throwable cause = e.getCause();
if (cause != null)
blocker = new FutureWriteCallback();
outgoing.outgoingFrame(frame,blocker);
try
{
if (cause instanceof IOException)
{
throw (IOException)cause;
}
else
{
throw new IOException(cause);
}
// block on write
blocker.get();
// block success
frameCount++;
frame.setOpCode(OpCode.CONTINUATION);
}
catch (ExecutionException e)
{
Throwable cause = e.getCause();
if (cause != null)
{
if (cause instanceof IOException)
{
throw (IOException)cause;
}
else
{
throw new IOException(cause);
}
}
throw new IOException("Failed to flush",e);
}
catch (InterruptedException e)
{
throw new IOException("Failed to flush",e);
}
throw new IOException("Failed to flush",e);
}
catch (InterruptedException e)
catch (IOException e)
{
throw new IOException("Failed to flush",e);
notifyFailure(e);
throw e;
}
}
private void notifyFailure(IOException e)
{
if (callback != null)
{
callback.writeFailed(e);
}
}
public void setCallback(WriteCallback callback)
{
this.callback = callback;
}
@Override
public synchronized void write(byte[] b) throws IOException
{
this.write(b,0,b.length);
try
{
this.write(b,0,b.length);
}
catch (IOException e)
{
notifyFailure(e);
throw e;
}
}
@Override

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
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.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
@ -48,6 +49,7 @@ public class MessageWriter extends Writer
private ByteBuffer buffer;
private Utf8CharBuffer utf;
private FutureWriteCallback blocker;
private WriteCallback callback;
private boolean closed = false;
public MessageWriter(OutgoingFrames outgoing, int bufferSize, ByteBufferPool bufferPool)
@ -69,7 +71,9 @@ public class MessageWriter extends Writer
{
if (closed)
{
throw new IOException("Stream is closed");
IOException e = new IOException("Stream is closed");
notifyFailure(e);
throw e;
}
}
@ -83,6 +87,10 @@ public class MessageWriter extends Writer
// close stream
closed = true;
if (callback != null)
{
callback.writeSuccess();
}
bufferPool.release(buffer);
LOG.debug("closed (frame count={})",frameCount);
}
@ -109,44 +117,73 @@ public class MessageWriter extends Writer
frame.setPayload(data);
frame.setFin(fin);
blocker = new FutureWriteCallback();
outgoing.outgoingFrame(frame,blocker);
try
{
// block on write
blocker.get();
// write success
// clear utf buffer
utf.clear();
frameCount++;
frame.setOpCode(OpCode.CONTINUATION);
}
catch (ExecutionException e)
{
Throwable cause = e.getCause();
if (cause != null)
blocker = new FutureWriteCallback();
outgoing.outgoingFrame(frame,blocker);
try
{
if (cause instanceof IOException)
{
throw (IOException)cause;
}
else
{
throw new IOException(cause);
}
// block on write
blocker.get();
// write success
// clear utf buffer
utf.clear();
frameCount++;
frame.setOpCode(OpCode.CONTINUATION);
}
catch (ExecutionException e)
{
Throwable cause = e.getCause();
if (cause != null)
{
if (cause instanceof IOException)
{
throw (IOException)cause;
}
else
{
throw new IOException(cause);
}
}
throw new IOException("Failed to flush",e);
}
catch (InterruptedException e)
{
throw new IOException("Failed to flush",e);
}
throw new IOException("Failed to flush",e);
}
catch (InterruptedException e)
catch (IOException e)
{
throw new IOException("Failed to flush",e);
notifyFailure(e);
throw e;
}
}
private void notifyFailure(IOException e)
{
if (callback != null)
{
callback.writeFailed(e);
}
}
public void setCallback(WriteCallback callback)
{
this.callback = callback;
}
@Override
public void write(char[] cbuf) throws IOException
{
this.write(cbuf,0,cbuf.length);
try
{
this.write(cbuf,0,cbuf.length);
}
catch (IOException e)
{
notifyFailure(e);
throw e;
}
}
@Override