Issue #207 - Support javax.websocket version 1.1

+ Removing more EventDriver references
+ Fixing bad implementation exposed by testcases
This commit is contained in:
Joakim Erdfelt 2016-08-19 17:59:07 -07:00
parent 1d341398da
commit 1cf61d6df7
19 changed files with 223 additions and 911 deletions

View File

@ -222,6 +222,7 @@ public class ConfigurationAssert
}
@SuppressWarnings("Duplicates")
public static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)
{
try

View File

@ -23,6 +23,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@ -53,10 +54,14 @@ 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.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.function.EndpointFunctions;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
import org.eclipse.jetty.websocket.jsr356.function.JsrEndpointFunctions;
/**
* Container for Client use of the javax.websocket API.
@ -95,6 +100,21 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
ShutdownThread.register(this);
}
public EndpointFunctions newJsrEndpointFunction(Object endpoint,
AvailableEncoders availableEncoders,
AvailableDecoders availableDecoders,
Map<String, String> pathParameters,
EndpointConfig config)
{
return new JsrEndpointFunctions(endpoint,
getPolicy(),
getExecutor(),
availableEncoders,
availableDecoders,
pathParameters,
config);
}
private Session connect(ConfiguredEndpoint instance, URI path) throws IOException
{
Objects.requireNonNull(instance, "EndpointInstance cannot be null");

View File

@ -96,9 +96,9 @@ public class JsrSession extends WebSocketSession implements javax.websocket.Sess
@Override
public EndpointFunctions newEndpointFunctions(Object endpoint)
{
return new JsrEndpointFunctions(endpoint,
getPolicy(),
getExecutor(),
// Delegate to container to obtain correct version of JsrEndpointFunctions
// Could be a Client version, or a Server version
return container.newJsrEndpointFunction(endpoint,
availableEncoders,
availableDecoders,
pathParameters,

View File

@ -85,6 +85,7 @@ public class AvailableDecoders implements Predicate<Class<?>>
registerPrimitive(CharacterDecoder.class, Decoder.Text.class, Character.class);
registerPrimitive(DoubleDecoder.class, Decoder.Text.class, Double.class);
registerPrimitive(FloatDecoder.class, Decoder.Text.class, Float.class);
registerPrimitive(ShortDecoder.class, Decoder.Text.class, Short.class);
registerPrimitive(IntegerDecoder.class, Decoder.Text.class, Integer.class);
registerPrimitive(LongDecoder.class, Decoder.Text.class, Long.class);
registerPrimitive(StringDecoder.class, Decoder.Text.class, String.class);
@ -95,6 +96,7 @@ public class AvailableDecoders implements Predicate<Class<?>>
registerPrimitive(CharacterDecoder.class, Decoder.Text.class, Character.TYPE);
registerPrimitive(DoubleDecoder.class, Decoder.Text.class, Double.TYPE);
registerPrimitive(FloatDecoder.class, Decoder.Text.class, Float.TYPE);
registerPrimitive(ShortDecoder.class, Decoder.Text.class, Short.TYPE);
registerPrimitive(IntegerDecoder.class, Decoder.Text.class, Integer.TYPE);
registerPrimitive(LongDecoder.class, Decoder.Text.class, Long.TYPE);

View File

@ -83,6 +83,7 @@ public class AvailableEncoders implements Predicate<Class<?>>
registerPrimitive(CharacterEncoder.class, Encoder.Text.class, Character.class);
registerPrimitive(DoubleEncoder.class, Encoder.Text.class, Double.class);
registerPrimitive(FloatEncoder.class, Encoder.Text.class, Float.class);
registerPrimitive(ShortEncoder.class, Encoder.Text.class, Short.class);
registerPrimitive(IntegerEncoder.class, Encoder.Text.class, Integer.class);
registerPrimitive(LongEncoder.class, Encoder.Text.class, Long.class);
registerPrimitive(StringEncoder.class, Encoder.Text.class, String.class);
@ -93,6 +94,7 @@ public class AvailableEncoders implements Predicate<Class<?>>
registerPrimitive(CharacterEncoder.class, Encoder.Text.class, Character.TYPE);
registerPrimitive(DoubleEncoder.class, Encoder.Text.class, Double.TYPE);
registerPrimitive(FloatEncoder.class, Encoder.Text.class, Float.TYPE);
registerPrimitive(ShortEncoder.class, Encoder.Text.class, Short.TYPE);
registerPrimitive(IntegerEncoder.class, Encoder.Text.class, Integer.TYPE);
registerPrimitive(LongEncoder.class, Encoder.Text.class, Long.TYPE);

View File

@ -1,106 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
@Deprecated
public abstract class AbstractJsrEventDriver /*extends AbstractEventDriver*/
{
/* protected final EndpointMetadata metadata;
protected final Executor executor;
protected final EndpointConfig config;
protected JsrSession jsrsession;
private boolean hasCloseBeenCalled = false;
public AbstractJsrEventDriver(WebSocketPolicy policy, ConfiguredEndpoint endpointInstance, Executor executor)
{
super(policy,endpointInstance.getEndpoint());
this.config = endpointInstance.getConfig();
this.metadata = endpointInstance.getMetadata();
this.executor = executor;
}
public EndpointConfig getConfig()
{
return config;
}
public Session getJsrSession()
{
return this.jsrsession;
}
public EndpointMetadata getMetadata()
{
return metadata;
}
public abstract void init(JsrSession jsrsession);
@Override
public final void onClose(CloseInfo close)
{
if (hasCloseBeenCalled)
{
// avoid duplicate close events (possible when using harsh Session.disconnect())
return;
}
hasCloseBeenCalled = true;
CloseCode closecode = CloseCodes.getCloseCode(close.getStatusCode());
CloseReason closereason = new CloseReason(closecode,close.getReason());
onClose(closereason);
}
protected abstract void onClose(CloseReason closereason);
@Override
public void onFrame(Frame frame)
{
*//* Ignored, not supported by JSR-356 *//*
}
@Override
public final void openSession(WebSocketSession session)
{
// Cast should be safe, as it was created by JsrSessionFactory
this.jsrsession = (JsrSession)session;
// Allow jsr session to init
this.jsrsession.init(config);
// Allow event driver to init itself
init(jsrsession);
// Allow end-user socket to adjust configuration
super.openSession(session);
}
public void setEndpointconfig(EndpointConfig endpointconfig)
{
throw new RuntimeException("Why are you reconfiguring the endpoint?");
// this.config = endpointconfig;
}
public abstract void setPathParameters(Map<String, String> pathParameters);
public void dispatch(Runnable runnable)
{
executor.execute(runnable);
}*/
}

View File

@ -1,382 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import javax.websocket.MessageHandler.Whole;
/**
* Base implementation for JSR-356 Annotated event drivers.
*/
@Deprecated
public class JsrAnnotatedEventDriver /*extends AbstractJsrEventDriver*/
{
/* private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
private final JsrEvents<?, ?> events;
public JsrAnnotatedEventDriver(WebSocketPolicy policy, ConfiguredEndpoint endpointInstance, JsrEvents<?, ?> events, Executor executor)
{
super(policy,endpointInstance,executor);
this.events = events;
}
@Override
public void init(JsrSession jsrsession)
{
this.events.init(jsrsession);
}
*//**
* Entry point for all incoming binary frames.
*//*
@Override
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (LOG.isDebugEnabled())
{
LOG.debug("onBinaryFrame({}, {})",BufferUtil.toDetailString(buffer),fin);
LOG.debug("events.onBinary={}",events.hasBinary());
LOG.debug("events.onBinaryStream={}",events.hasBinaryStream());
}
boolean handled = false;
if (events.hasBinary())
{
handled = true;
if (activeMessage == null)
{
if (events.isBinaryPartialSupported())
{
// Partial Message Support (does not use messageAppender)
if (LOG.isDebugEnabled())
{
LOG.debug("Partial Binary Message: fin={}",fin);
}
activeMessage = new BinaryPartialOnMessage(this);
}
else
{
// Whole Message Support
if (LOG.isDebugEnabled())
{
LOG.debug("Whole Binary Message");
}
activeMessage = new ByteArrayMessageSink(this);
}
}
}
if (events.hasBinaryStream())
{
handled = true;
// Streaming Message Support
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Binary Message InputStream");
}
final MessageInputStream stream = new MessageInputStream();
activeMessage = stream;
// Always dispatch streaming read to another thread.
dispatch(new Runnable()
{
@Override
public void run()
{
try
{
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (Throwable e)
{
onFatalError(e);
}
}
});
}
}
if (LOG.isDebugEnabled())
{
LOG.debug("handled = {}",handled);
}
// Process any active MessageAppender
if (handled && (activeMessage != null))
{
appendMessage(buffer,fin);
}
}
*//**
* Entry point for binary frames destined for {@link Whole}
*//*
@Override
public void onBinaryMessage(byte[] data)
{
if (data == null)
{
return;
}
ByteBuffer buf = ByteBuffer.wrap(data);
if (LOG.isDebugEnabled())
{
LOG.debug("onBinaryMessage({})",BufferUtil.toDetailString(buf));
}
try
{
// FIN is always true here
events.callBinary(jsrsession.getAsyncRemote(),websocket,buf,true);
}
catch (Throwable e)
{
onFatalError(e);
}
}
@Override
public void onObject(Object obj)
{
// TODO Auto-generated method stub
}
@Override
protected void onClose(CloseReason closereason)
{
events.callClose(websocket,closereason);
}
@Override
public void onConnect()
{
events.callOpen(websocket,config);
}
@Override
public void onError(Throwable cause)
{
try
{
events.callError(websocket,cause);
}
catch (Throwable e)
{
LOG.warn("Unable to call onError with cause", cause);
LOG.warn("Call to onError resulted in exception", e);
}
}
private void onFatalError(Throwable t)
{
onError(t);
}
@Override
public void onFrame(Frame frame)
{
*//* Ignored in JSR-356 *//*
}
@Override
public void onInputStream(InputStream stream) throws IOException
{
try
{
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (DecodeException e)
{
throw new RuntimeException("Unable decode input stream", e);
}
}
public void onPartialBinaryMessage(ByteBuffer buffer, boolean fin)
{
try
{
events.callBinary(jsrsession.getAsyncRemote(),websocket,buffer,fin);
}
catch (DecodeException e)
{
throw new RuntimeException("Unable decode partial binary message", e);
}
}
public void onPartialTextMessage(String message, boolean fin)
{
try
{
events.callText(jsrsession.getAsyncRemote(),websocket,message,fin);
}
catch (DecodeException e)
{
throw new RuntimeException("Unable decode partial text message", e);
}
}
@Override
public void onPing(ByteBuffer buffer)
{
// Call pong, as there is no "onPing" method in the JSR
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
}
@Override
public void onPong(ByteBuffer buffer)
{
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
}
@Override
public void onReader(Reader reader) throws IOException
{
try
{
events.callTextStream(jsrsession.getAsyncRemote(),websocket,reader);
}
catch (DecodeException e)
{
throw new RuntimeException("Unable decode reader", e);
}
}
*//**
* Entry point for all incoming text frames.
*//*
@Override
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (LOG.isDebugEnabled())
{
LOG.debug("onTextFrame({}, {})",BufferUtil.toDetailString(buffer),fin);
LOG.debug("events.hasText={}",events.hasText());
LOG.debug("events.hasTextStream={}",events.hasTextStream());
}
boolean handled = false;
if (events.hasText())
{
handled = true;
if (activeMessage == null)
{
if (events.isTextPartialSupported())
{
// Partial Message Support
if (LOG.isDebugEnabled())
{
LOG.debug("Partial Text Message: fin={}",fin);
}
activeMessage = new TextPartialOnMessage(this);
}
else
{
// Whole Message Support
if (LOG.isDebugEnabled())
{
LOG.debug("Whole Text Message");
}
activeMessage = new StringMessageSink(this);
}
}
}
if (events.hasTextStream())
{
handled = true;
// Streaming Message Support
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Text Message Writer");
}
final MessageReader stream = new MessageReader(new MessageInputStream());
activeMessage = stream;
// Always dispatch streaming read to another thread.
dispatch(new Runnable()
{
@Override
public void run()
{
try
{
events.callTextStream(jsrsession.getAsyncRemote(),websocket,stream);
}
catch (Throwable e)
{
onFatalError(e);
}
}
});
}
}
if (LOG.isDebugEnabled())
{
LOG.debug("handled = {}", handled);
}
// Process any active MessageAppender
if (handled && (activeMessage != null))
{
appendMessage(buffer,fin);
}
}
*//**
* Entry point for whole text messages
*//*
@Override
public void onTextMessage(String message)
{
if (LOG.isDebugEnabled())
{
LOG.debug("onText({})",message);
}
try
{
// FIN is always true here
events.callText(jsrsession.getAsyncRemote(),websocket,message,true);
}
catch (Throwable e)
{
onFatalError(e);
}
}
@Override
public void setPathParameters(Map<String, String> pathParameters)
{
events.setPathParameters(pathParameters);
}
@Override
public String toString()
{
return String.format("%s[websocket=%s]",this.getClass().getSimpleName(),websocket);
}*/
}

View File

@ -1,270 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
/**
* EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
*/
@Deprecated
public class JsrEndpointEventDriver extends AbstractJsrEventDriver
{
/*private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class);
private final Endpoint endpoint;
private Map<String, String> pathParameters;
public JsrEndpointEventDriver(WebSocketPolicy policy, Executor executor, ConfiguredEndpoint endpointInstance)
{
super(policy,executor,endpointInstance);
this.endpoint = (Endpoint)endpointInstance.getEndpoint();
}
@Override
public void init(JsrSession jsrsession)
{
jsrsession.setPathParameters(pathParameters);
}
@Override
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (activeMessage == null)
{
activeMessage = jsrsession.newMessageAppenderFor(MessageType.BINARY);
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("No BINARY MessageHandler declared");
}
return;
}
*//*
if (wrapper.wantsPartialMessages())
{
activeMessage = new BinaryPartialMessage(wrapper);
}
else if (wrapper.wantsStreams())
{
final MessageInputStream stream = new MessageInputStream();
activeMessage = stream;
dispatch(new Runnable()
{
@SuppressWarnings("unchecked")
@Override
public void run()
{
MessageHandler.Whole<InputStream> handler = (Whole<InputStream>)wrapper.getHandler();
handler.onMessage(stream);
}
});
}
else
{
activeMessage = new BinaryWholeMessage(this,wrapper);
}
*//*
}
activeMessage.appendFrame(buffer,fin);
if (fin)
{
activeMessage.messageComplete();
activeMessage = null;
}
}
@Override
public void onBinaryMessage(byte[] data)
{
*//* Ignored, handled by BinaryWholeMessage *//*
}
@Override
public void onObject(Object o)
{
// TODO: deliver to message handler
}
@Override
protected void onClose(CloseReason closereason)
{
endpoint.onClose(this.jsrsession,closereason);
}
@Override
public void onConnect()
{
if (LOG.isDebugEnabled())
{
LOG.debug("onConnect({}, {})",jsrsession,config);
}
// Let unhandled exceptions flow out
endpoint.onOpen(jsrsession,config);
}
@Override
public void onError(Throwable cause)
{
try
{
endpoint.onError(jsrsession,cause);
}
catch (Throwable t)
{
LOG.warn("Unable to report to onError due to exception",t);
}
}
@Override
public void onFrame(Frame frame)
{
*//* Ignored, not supported by JSR-356 *//*
}
@Override
public void onInputStream(InputStream stream)
{
*//* Ignored, handled by BinaryStreamMessage *//*
}
@Override
public void onReader(Reader reader)
{
*//* Ignored, handled by TextStreamMessage *//*
}
@Override
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
{
if (activeMessage == null)
{
activeMessage = jsrsession.newMessageAppenderFor(MessageType.TEXT);
if (activeMessage == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("No TEXT MessageHandler declared");
}
return;
}
// if (wrapper.wantsPartialMessages())
// {
// activeMessage = new TextPartialMessage(wrapper);
// }
// else if (wrapper.wantsStreams())
// {
// final MessageReader stream = new MessageReader(new MessageInputStream());
// activeMessage = stream;
//
// dispatch(new Runnable()
// {
// @SuppressWarnings("unchecked")
// @Override
// public void run()
// {
// MessageHandler.Whole<Reader> handler = (Whole<Reader>)wrapper.getHandler();
// handler.onMessage(stream);
// }
// });
// }
// else
// {
// activeMessage = new TextWholeMessage(this,wrapper);
// }
}
activeMessage.appendFrame(buffer,fin);
if (fin)
{
activeMessage.messageComplete();
activeMessage = null;
}
}
@Override
public void onTextMessage(String message)
{
*//* Ignored, handled by TextWholeMessage *//*
}
@Override
public void onPing(ByteBuffer buffer)
{
onPongMessage(buffer);
}
@Override
public void onPong(ByteBuffer buffer)
{
onPongMessage(buffer);
}
private void onPongMessage(ByteBuffer buffer)
{
MessageSink appender = jsrsession.newMessageAppenderFor(MessageType.PONG);
if (appender == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("No PONG MessageHandler declared");
}
return;
}
ByteBuffer pongBuf = null;
if (BufferUtil.isEmpty(buffer))
{
pongBuf = BufferUtil.EMPTY_BUFFER;
}
else
{
pongBuf = ByteBuffer.allocate(buffer.remaining());
BufferUtil.put(buffer,pongBuf);
BufferUtil.flipToFlush(pongBuf,0);
}
try
{
appender.appendFrame(pongBuf,true);
}
catch (IOException e)
{
LOG.debug(e);
}
}
@Override
public void setPathParameters(Map<String, String> pathParameters)
{
this.pathParameters = pathParameters;
}
@Override
public String toString()
{
return String.format("%s[%s]",JsrEndpointEventDriver.class.getSimpleName(),endpoint.getClass().getName());
}*/
}

View File

@ -1,53 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
@Deprecated
public class JsrEndpointImpl /*implements EventDriverImpl*/
{
/*public EventDriver create(Object websocket, WebSocketPolicy policy)
{
if (!(websocket instanceof ConfiguredEndpoint))
{
throw new IllegalStateException(String.format("Websocket %s must be an %s",websocket.getClass().getName(),ConfiguredEndpoint.class.getName()));
}
return new JsrEndpointEventDriver(policy,(ConfiguredEndpoint)websocket);
}
@Override
public String describeRule()
{
return "class extends " + javax.websocket.Endpoint.class.getName();
}
@Override
public boolean supports(Object websocket)
{
if (!(websocket instanceof ConfiguredEndpoint))
{
return false;
}
ConfiguredEndpoint ei = (ConfiguredEndpoint)websocket;
Object endpoint = ei.getEndpoint();
return (endpoint instanceof javax.websocket.Endpoint);
}*/
}

View File

@ -1,48 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
@Deprecated
public class JsrEventDriverFactory /*extends EventDriverFactory*/
{
/* public JsrEventDriverFactory(WebSocketPolicy policy)
{
// super(policy);
*//*clearImplementations();
// Classes that extend javax.websocket.Endpoint
addImplementation(new JsrEndpointImpl());
// Classes annotated with @javax.websocket.ClientEndpoint
addImplementation(new JsrClientEndpointImpl());*//*
}
*//**
* Unwrap ConfiguredEndpoint for end-user.
*//*
protected String getClassName(Object websocket)
{
if (websocket instanceof ConfiguredEndpoint)
{
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
return ce.getEndpoint().getClass().getName();
}
return websocket.getClass().getName();
}*/
}

View File

@ -149,8 +149,8 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
}
}
private final AvailableEncoders encoders;
private final AvailableDecoders decoders;
protected final AvailableEncoders encoders;
protected final AvailableDecoders decoders;
private final EndpointConfig endpointConfig;
private List<StaticArg> staticArgs;
@ -424,6 +424,7 @@ public class JsrEndpointFunctions extends CommonEndpointFunctions<JsrSession>
*
* @param endpoint the endpoint object
*/
@SuppressWarnings("Duplicates")
protected void discoverAnnotatedEndpointFunctions(Object endpoint)
{
Class<?> endpointClass = endpoint.getClass();

View File

@ -18,17 +18,33 @@
package org.eclipse.jetty.websocket.jsr356.endpoints;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.websocket.ClientEndpointConfig;
import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseEndpointConfigSocket;
import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig;
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseReasonSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseSessionReasonSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.close.CloseSocket;
import org.eclipse.jetty.websocket.jsr356.function.JsrEndpointFunctions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@ -44,69 +60,81 @@ public class OnCloseTest
Case tcase = new Case();
tcase.closeClass = closeClass;
data.add(new Case[]
{ tcase });
{tcase});
return tcase;
}
Class<?> closeClass;
String expectedCloseEvent;
public Case expect(String expectedEvent)
{
this.expectedCloseEvent = expectedEvent;
return this;
}
@Override
public String toString()
{
return closeClass.getSimpleName();
}
}
private static ClientContainer container = new ClientContainer();
@Parameters
@Parameters(name = "{0}")
public static Collection<Case[]> data() throws Exception
{
List<Case[]> data = new ArrayList<>();
Case.add(data,CloseSocket.class).expect("onClose()");
Case.add(data,CloseReasonSocket.class).expect("onClose(CloseReason)");
Case.add(data,CloseSessionSocket.class).expect("onClose(Session)");
Case.add(data,CloseReasonSessionSocket.class).expect("onClose(CloseReason,Session)");
Case.add(data,CloseSessionReasonSocket.class).expect("onClose(Session,CloseReason)");
Case.add(data,CloseEndpointConfigSocket.class).expect("onClose(EndpointConfig)");
Case.add(data, CloseSocket.class).expect("onClose()");
Case.add(data, CloseReasonSocket.class).expect("onClose(CloseReason)");
Case.add(data, CloseSessionSocket.class).expect("onClose(Session)");
Case.add(data, CloseReasonSessionSocket.class).expect("onClose(CloseReason,Session)");
Case.add(data, CloseSessionReasonSocket.class).expect("onClose(Session,CloseReason)");
return data;
}
private final Case testcase;
public OnCloseTest(Case testcase)
{
this.testcase = testcase;
System.err.printf("Testing @OnClose for %s%n",testcase.closeClass.getName());
System.err.printf("Testing @OnClose for %s%n", testcase.closeClass.getName());
}
@Test
public void testOnCloseCall() throws Exception
{
/*// Scan annotations
AnnotatedClientEndpointMetadata metadata = new AnnotatedClientEndpointMetadata(container,testcase.closeClass);
AnnotatedEndpointScanner<ClientEndpoint, ClientEndpointConfig> scanner = new AnnotatedEndpointScanner<>(metadata);
scanner.scan();
// Build up EventDriver
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
ClientEndpointConfig config = metadata.getConfig();
TrackingSocket endpoint = (TrackingSocket)testcase.closeClass.newInstance();
ConfiguredEndpoint ei = new ConfiguredEndpoint(endpoint,config,metadata);
JsrEvents<ClientEndpoint, ClientEndpointConfig> jsrevents = new JsrEvents<>(metadata);
EventDriver driver = new JsrAnnotatedEventDriver(policy,ei,jsrevents);
// Execute onClose call
driver.onClose(new CloseInfo(StatusCode.NORMAL,"normal"));
// Test captured event
EventQueue<String> events = endpoint.eventQueue;
Assert.assertThat("Number of Events Captured",events.size(),is(1));
String closeEvent = events.poll();
Assert.assertThat("Close Event",closeEvent,is(testcase.expectedCloseEvent)); */
TrackingSocket endpoint = (TrackingSocket) testcase.closeClass.newInstance();
Executor executor = new QueuedThreadPool();
ClientEndpointConfig config = new EmptyClientEndpointConfig();
AvailableEncoders encoders = new AvailableEncoders(config);
AvailableDecoders decoders = new AvailableDecoders(config);
Map<String, String> uriParams = new HashMap<>();
JsrEndpointFunctions jsrFunctions = new JsrEndpointFunctions(endpoint, policy,
executor, encoders, decoders, uriParams, config);
try
{
jsrFunctions.start();
// Execute onClose call
jsrFunctions.onClose(new CloseInfo(StatusCode.NORMAL, "normal"));
// Test captured event
EventQueue<String> events = endpoint.eventQueue;
assertThat("Number of Events Captured", events.size(), is(1));
String closeEvent = events.poll();
assertThat("Close Event", closeEvent, is(testcase.expectedCloseEvent));
}
finally
{
jsrFunctions.stop();
}
}
}

View File

@ -27,10 +27,10 @@ import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@ClientEndpoint
public class CloseEndpointConfigSocket extends TrackingSocket
{
// Intentionally Invalid Declaration
@OnClose
public void onClose(EndpointConfig config)
{
addEvent("onClose(EndpointConfig)");
closeLatch.countDown();
throw new RuntimeException("Should not have worked. Invalid declaration: " + this.getClass().getName());
}
}

View File

@ -37,6 +37,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.jsr356.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@ -162,7 +163,7 @@ public class JsrCreator implements WebSocketCreator
Object endpoint = config.getConfigurator().getEndpointInstance(endpointClass);
// Do not decorate here (let the Connection and Session start first)
// This will allow CDI to see Session for injection into Endpoint classes.
return endpoint;
return new ConfiguredEndpoint(endpoint,config);
}
catch (InstantiationException e)
{

View File

@ -0,0 +1,78 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.server;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.websocket.DecodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
import org.eclipse.jetty.websocket.jsr356.function.JsrEndpointFunctions;
public class JsrServerEndpointFunctions extends JsrEndpointFunctions
{
public JsrServerEndpointFunctions(Object endpoint, WebSocketPolicy policy, Executor executor,
AvailableEncoders encoders, AvailableDecoders decoders,
Map<String, String> uriParams, EndpointConfig endpointConfig)
{
super(endpoint, policy, executor, encoders, decoders, uriParams, endpointConfig);
}
/**
* Generic discovery of annotated endpoint functions.
*
* @param endpoint the endpoint object
*/
@SuppressWarnings("Duplicates")
protected void discoverAnnotatedEndpointFunctions(Object endpoint)
{
Class<?> endpointClass = endpoint.getClass();
// Use the JSR/Server annotation
ServerEndpoint websocket = endpointClass.getAnnotation(ServerEndpoint.class);
if (websocket != null)
{
encoders.registerAll(websocket.encoders());
decoders.registerAll(websocket.decoders());
// From here, the discovery of endpoint method is standard across
// both JSR356/Client and JSR356/Server endpoints
try
{
discoverJsrAnnotatedEndpointFunctions(endpoint);
return;
}
catch (DecodeException e)
{
throw new InvalidWebSocketException("Cannot instantiate WebSocket", e);
}
}
// Not a ServerEndpoint, let the ClientEndpoint test proceed
super.discoverAnnotatedEndpointFunctions(endpoint);
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.jsr356.server;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@ -34,8 +35,11 @@ import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.function.EndpointFunctions;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
import org.eclipse.jetty.websocket.jsr356.decoders.AvailableDecoders;
import org.eclipse.jetty.websocket.jsr356.encoders.AvailableEncoders;
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
@ -152,6 +156,22 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
mappedCreator.addMapping(new UriTemplatePathSpec(config.getPath()), creator);
}
@Override
public EndpointFunctions newJsrEndpointFunction(Object endpoint,
AvailableEncoders availableEncoders,
AvailableDecoders availableDecoders,
Map<String, String> pathParameters,
EndpointConfig config)
{
return new JsrServerEndpointFunctions(endpoint,
getPolicy(),
getExecutor(),
availableEncoders,
availableDecoders,
pathParameters,
config);
}
@Override
protected void doStart() throws Exception
{

View File

@ -124,7 +124,7 @@ public class EchoCase
StringBuilder str = new StringBuilder();
str.append("EchoCase['");
str.append(path);
str.append("',").append(serverPojo.getName());
str.append("',").append(serverPojo.getSimpleName());
str.append(",messages[").append(messages.size());
str.append("]=");
boolean delim = false;

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.websocket.jsr356.server;
import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered;
import static org.junit.Assert.assertThat;
import java.net.URI;
@ -77,7 +76,9 @@ import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.InputStreamSo
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.ReaderParamSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.ReaderSocket;
import org.eclipse.jetty.websocket.jsr356.server.samples.streaming.StringReturnReaderParamSocket;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -280,7 +281,7 @@ public class EchoTest
server.stop();
}
@Parameters
@Parameters(name = "{0}")
public static Collection<EchoCase[]> data() throws Exception
{
return TESTCASES;
@ -339,7 +340,7 @@ public class EchoTest
EventQueue<String> received = socket.eventQueue;
// Validate Responses
assertThat("Received Events", received, ordered(testcase.expectedStrings));
assertOrdered("Received Events", testcase.expectedStrings, received);
}
finally
{
@ -347,4 +348,21 @@ public class EchoTest
socket.close();
}
}
@SuppressWarnings("Duplicates")
public static void assertOrdered(String msg, List<String> expectedList, EventQueue<String> actualList)
{
try
{
Assert.assertEquals(msg, expectedList.size(), actualList.size());
if (!expectedList.isEmpty())
assertThat(msg, actualList, Matchers.contains(expectedList.toArray()));
}
catch (AssertionError e)
{
System.err.println("Expected: " + expectedList);
System.err.println("Actual : " + actualList);
throw e;
}
}
}

View File

@ -264,7 +264,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
}
}
throw new InvalidWebSocketException("Unable to create Session: unrecognized internal EventDriver type: " + websocket.getClass().getName());
throw new InvalidWebSocketException("Unable to create Session: unrecognized endpoint type: " + websocket.getClass().getName());
}
/**