JSR-356 - cleaning up Decoder and Encoder lifecycle.
This commit is contained in:
parent
46e9493c85
commit
33c11dffaa
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 @{@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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @{@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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<Void></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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue