393473 - Add support for JSR-356 (javax.websocket) draft

+ Start of @WebSocketClient class/method scanning with tests
This commit is contained in:
Joakim Erdfelt 2013-02-14 16:24:07 -07:00
parent e27ad8ef5c
commit 2faba0bf4b
18 changed files with 1025 additions and 114 deletions

View File

@ -1,75 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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 java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
/**
* Cache for discovered javax.websocket {@link WebSocketEndpoint @WebSocketEndpoint} annotated websockets
*/
public class JavaxPojoAnnotationCache extends AbstractMethodAnnotationScanner<JavaxPojoMetadata>
{
private static final Logger LOG = Log.getLogger(JavaxPojoAnnotationCache.class);
public static final JavaxPojoAnnotationCache INSTANCE = new JavaxPojoAnnotationCache();
public synchronized static JavaxPojoMetadata discover(Class<?> websocket)
{
// TODO: move to server side deployer
// WebSocketEndpoint anno = websocket.getAnnotation(WebSocketEndpoint.class);
// if (anno == null)
// {
// return null;
// }
JavaxPojoMetadata metadata = INSTANCE.cache.get(websocket);
if (metadata == null)
{
metadata = new JavaxPojoMetadata();
INSTANCE.scanMethodAnnotations(metadata,websocket);
INSTANCE.cache.put(websocket,metadata);
}
return metadata;
}
public static JavaxPojoMetadata discover(Object websocket)
{
return discover(websocket.getClass());
}
private ConcurrentHashMap<Class<?>, JavaxPojoMetadata> cache;
public JavaxPojoAnnotationCache()
{
cache = new ConcurrentHashMap<>();
}
@Override
public void onMethodAnnotation(JavaxPojoMetadata metadata, Class<?> pojo, Method method, Annotation annotation)
{
LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
// TODO Auto-generated method stub
}
}

View File

@ -28,8 +28,12 @@ import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
public class JsrAnnotatedEventDriver implements EventDriver
public class JsrAnnotatedClientEventDriver implements EventDriver
{
public JsrAnnotatedClientEventDriver(WebSocketPolicy policy, Object websocket, JsrAnnotatedMetadata metadata)
{
// TODO Auto-generated constructor stub
}
@Override
public void incomingError(WebSocketException e)

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.WebSocketClient;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
/**
* {@link EventDriverImpl} for {@link WebSocketClient &#064;WebSocketClient} annotated classes
*/
public class JsrAnnotatedClientImpl implements EventDriverImpl
{
private ConcurrentHashMap<Class<?>, JsrAnnotatedMetadata> cache = new ConcurrentHashMap<>();
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
Class<?> websocketClass = websocket.getClass();
WebSocketClient wsclient = websocketClass.getAnnotation(WebSocketClient.class);
if (wsclient == null)
{
throw new InvalidWebSocketException("Cannot handle @WebSocketClient annotations here");
}
JsrAnnotatedMetadata metadata = cache.get(websocketClass);
if (metadata == null)
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(websocketClass);
metadata = scanner.scan();
cache.put(websocketClass,metadata);
}
return new JsrAnnotatedClientEventDriver(policy,websocket,metadata);
}
@Override
public String describeRule()
{
return "class annotated with @" + WebSocketClient.class.getName();
}
@Override
public boolean supports(Object websocket)
{
return (websocket.getClass().getAnnotation(WebSocketClient.class) != null);
}
}

View File

@ -0,0 +1,199 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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 java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import javax.websocket.CloseReason;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfiguration;
import javax.websocket.Session;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketClose;
import javax.websocket.WebSocketError;
import javax.websocket.WebSocketMessage;
import javax.websocket.WebSocketOpen;
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.common.events.ParamList;
import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrMethodParameters.Param;
/**
* Scanner for javax.websocket {@link WebSocketEndpoint &#064;WebSocketEndpoint} and {@link WebSocketClient &#064;WebSocketClient} annotated websockets
*/
public class JsrAnnotatedClientScanner extends AbstractMethodAnnotationScanner<JsrAnnotatedMetadata>
{
private static final Logger LOG = Log.getLogger(JsrAnnotatedClientScanner.class);
private static final ParamList validOpenParams;
private static final ParamList validCloseParams;
private static final ParamList validErrorParams;
static
{
validOpenParams = new ParamList();
validOpenParams.addParams(Session.class);
validOpenParams.addParams(EndpointConfiguration.class);
validCloseParams = new ParamList();
validCloseParams.addParams(Session.class);
validCloseParams.addParams(CloseReason.class);
validErrorParams = new ParamList();
validErrorParams.addParams(Session.class);
validErrorParams.addParams(Throwable.class);
}
protected final Class<?> pojo;
protected final Class<? extends Encoder> encoders[];
protected final Class<? extends Decoder> decoders[];
public JsrAnnotatedClientScanner(Class<?> websocket)
{
this.pojo = websocket;
WebSocketClient anno = websocket.getAnnotation(WebSocketClient.class);
if (anno == null)
{
throw new InvalidWebSocketException("Unsupported WebSocket object, missing @" + WebSocketClient.class + " annotation");
}
this.encoders = anno.encoders();
this.decoders = anno.decoders();
}
private void assertValidJsrSignature(Method method, Class<? extends Annotation> annoClass, ParamList validParams)
{
JsrMethodParameters params = new JsrMethodParameters(method);
// First, find the path-mapping parameters
for (Param param : params)
{
String varname = getPathMappingParameterVariable(param.type);
if (varname != null)
{
param.setPathParamVariable(varname);
}
}
// Next find the valid parameter sets and flag them
for (Class<?>[] paramSet : validParams)
{
// Each entry in validParams is a set of possible valid references.
// If not all parts of the set are present, that set isn't valid for the provided parameters.
if (params.containsParameterSet(paramSet))
{
// flag as valid
params.setValid(paramSet);
}
}
// Finally, ensure we identified all of the parameters
for (Param param : params)
{
if (param.isValid() == false)
{
StringBuilder err = new StringBuilder();
err.append("Encountered invalid/unhandled parameter <");
err.append(param.type.getName());
err.append("> (position ").append(param.index).append(") in method <");
err.append(method.getName());
err.append("> of object <");
err.append(pojo.getName());
err.append("> that doesn't fit the requirements for the @");
err.append(annoClass.getSimpleName());
err.append(" annotation");
throw new InvalidSignatureException(err.toString());
}
}
}
public String getPathMappingParameterVariable(Class<?> type)
{
/* override to implement */
return null;
}
@Override
public void onMethodAnnotation(JsrAnnotatedMetadata metadata, Class<?> pojo, Method method, Annotation annotation)
{
LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
if (isAnnotation(annotation,WebSocketOpen.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertValidJsrSignature(method,WebSocketOpen.class,validOpenParams);
assertUnset(metadata.onOpen,WebSocketOpen.class,method);
metadata.onOpen = new CallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,WebSocketClose.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertValidJsrSignature(method,WebSocketClose.class,validCloseParams);
assertUnset(metadata.onClose,WebSocketClose.class,method);
metadata.onClose = new CallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,WebSocketError.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertValidJsrSignature(method,WebSocketError.class,validErrorParams);
assertUnset(metadata.onError,WebSocketError.class,method);
metadata.onError = new CallableMethod(pojo,method);
return;
}
if (isAnnotation(annotation,WebSocketMessage.class))
{
assertIsPublicNonStatic(method);
JsrMessageCallableMethod callable = new JsrMessageCallableMethod(pojo,method);
callable.setReturnType(method.getReturnType(),encoders);
// TODO: create MessageHandler wrapper for methods
// TODO: ensure conflicting parameters not present
// assertUnset(metadata.onMessage,WebSocketMessage.class,method);
// metadata.onMessage = new CallableMethod(pojo,method);
return;
}
}
public JsrAnnotatedMetadata scan()
{
JsrAnnotatedMetadata metadata = new JsrAnnotatedMetadata();
scanMethodAnnotations(metadata,pojo);
return metadata;
}
}

View File

@ -1,31 +0,0 @@
package org.eclipse.jetty.websocket.jsr356.endpoints;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
public class JsrAnnotatedImpl implements EventDriverImpl
{
@Override
public EventDriver create(Object websocket, WebSocketPolicy policy)
{
// TODO Auto-generated method stub
return null;
}
@Override
public String describeRule()
{
// TODO Auto-generated method stub
return null;
}
@Override
public boolean supports(Object websocket)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,57 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.WebSocketClose;
import javax.websocket.WebSocketError;
import javax.websocket.WebSocketMessage;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
/**
* Represents the metadata associated with Annotation discovery of a specific class.
*/
public class JsrAnnotatedMetadata
{
/**
* Callable for &#064;{@link WebSocketOpen} annotation
*/
public CallableMethod onOpen;
/**
* Callable for &#064;{@link WebSocketClose} annotation
*/
public CallableMethod onClose;
/**
* Callable for &#064;{@link WebSocketError} annotation
*/
public CallableMethod onError;
/**
* Callable for &#064;{@link WebSocketMessage} annotation dealing with Text Message Format
*/
public CallableMethod onText;
/**
* Callable for &#064;{@link WebSocketMessage} annotation dealing with Binary Message Format
*/
public CallableMethod onBinary;
/**
* Callable for &#064;{@link WebSocketMessage} annotation dealing with Pong Message Format
*/
public CallableMethod onPong;
}

View File

@ -0,0 +1,76 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.List;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
public class JsrMessageCallableMethod extends CallableMethod
{
private Class<?> returnType;
public JsrMessageCallableMethod(Class<?> pojo, Method method)
{
super(pojo,method);
}
public void setReturnType(Class<?> returnType, Class<? extends Encoder> encoders[])
{
if (Void.TYPE.equals(returnType))
{
// Void type
this.returnType = returnType;
return;
}
if (returnType.isArray() && Byte.TYPE.equals(returnType))
{
// A byte array
this.returnType = returnType;
return;
}
if (TypeUtil.toName(returnType) != null)
{
// A primitive (including String)
this.returnType = returnType;
return;
}
if (ByteBuffer.class.isAssignableFrom(returnType))
{
// A nio ByteBuffer
this.returnType = returnType;
return;
}
// Determine if encoder exists for this return type
for (Class<? extends Encoder> encoder : encoders)
{
}
}
}

View File

@ -0,0 +1,107 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrMethodParameters.Param;
public class JsrMethodParameters extends ArrayList<Param>
{
public static class Param
{
public int index;
public Class<?> type;
private boolean valid = false;
private String pathParamVariable = null;
public Param(int idx, Class<?> type)
{
this.index = idx;
this.type = type;
}
public String getPathParamVariable()
{
return this.pathParamVariable;
}
public boolean isValid()
{
return valid;
}
public void setPathParamVariable(String name)
{
this.pathParamVariable = name;
}
public void setValid(boolean flag)
{
this.valid = flag;
}
}
private static final long serialVersionUID = -181761176209945279L;
public JsrMethodParameters(Method method)
{
Class<?> ptypes[] = method.getParameterTypes();
int len = ptypes.length;
for (int i = 0; i < len; i++)
{
add(new Param(i,ptypes[i]));
}
}
public boolean containsParameterSet(Class<?>[] paramSet)
{
for (Class<?> entry : paramSet)
{
boolean found = false;
for (Param param : this)
{
if (param.type.isAssignableFrom(entry))
{
found = true;
}
}
if (!found)
{
return false;
}
}
return true;
}
public void setValid(Class<?>[] paramSet)
{
for (Class<?> entry : paramSet)
{
for (Param param : this)
{
if (param.type.isAssignableFrom(entry))
{
param.setValid(true);
}
}
}
}
}

View File

@ -0,0 +1,134 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import static org.hamcrest.Matchers.*;
import java.lang.annotation.Annotation;
import javax.websocket.CloseReason;
import javax.websocket.Session;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.CallableMethod;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenCloseSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenSessionSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.BasicOpenSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenCloseReasonSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenIntSocket;
import org.eclipse.jetty.websocket.jsr356.endpoints.samples.InvalidOpenSessionIntSocket;
import org.junit.Assert;
import org.junit.Test;
public class JsrAnnotatedClientScannerTest
{
private static final Logger LOG = Log.getLogger(JsrAnnotatedClientScannerTest.class);
private void assertHasCallable(String msg, CallableMethod callable, Class<?>... expectedParameters)
{
Assert.assertThat(msg,notNullValue());
int len = expectedParameters.length;
for (int i = 0; i < len; i++)
{
Class<?> expectedParam = expectedParameters[i];
Class<?> actualParam = callable.getParamTypes()[i];
Assert.assertTrue("Parameter[" + i + "] - expected:[" + expectedParam + "], actual:[" + actualParam + "]",actualParam.equals(expectedParam));
}
}
private void assertInvalidAnnotationSignature(Class<?> pojo, Class<? extends Annotation> expectedAnnoClass)
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(pojo);
try
{
scanner.scan();
Assert.fail("Expected " + InvalidSignatureException.class + " with message that references " + expectedAnnoClass + " annotation");
}
catch (InvalidSignatureException e)
{
LOG.debug("{}:{}",e.getClass(),e.getMessage());
Assert.assertThat("Message",e.getMessage(),containsString(expectedAnnoClass.getSimpleName()));
}
}
@Test
public void testScan_BasicOpen()
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(BasicOpenSocket.class);
JsrAnnotatedMetadata metadata = scanner.scan();
Assert.assertThat("Metadata",metadata,notNullValue());
assertHasCallable("Metadata.onOpen",metadata.onOpen);
}
@Test
public void testScan_BasicOpenClose()
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(BasicOpenCloseSocket.class);
JsrAnnotatedMetadata metadata = scanner.scan();
Assert.assertThat("Metadata",metadata,notNullValue());
assertHasCallable("Metadata.onOpen",metadata.onOpen);
assertHasCallable("Metadata.onClose",metadata.onClose,CloseReason.class);
}
@Test
public void testScan_BasicOpenSession()
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(BasicOpenSessionSocket.class);
JsrAnnotatedMetadata metadata = scanner.scan();
Assert.assertThat("Metadata",metadata,notNullValue());
assertHasCallable("Metadata.onOpen",metadata.onOpen,Session.class);
}
@Test
public void testScan_BasicSessionOpenClose()
{
JsrAnnotatedClientScanner scanner = new JsrAnnotatedClientScanner(BasicOpenCloseSessionSocket.class);
JsrAnnotatedMetadata metadata = scanner.scan();
Assert.assertThat("Metadata",metadata,notNullValue());
assertHasCallable("Metadata.onOpen",metadata.onOpen);
assertHasCallable("Metadata.onClose",metadata.onClose,CloseReason.class);
}
@Test
public void testScan_InvalidOpenCloseReason()
{
assertInvalidAnnotationSignature(InvalidOpenCloseReasonSocket.class,WebSocketOpen.class);
}
@Test
public void testScan_InvalidOpenInt()
{
assertInvalidAnnotationSignature(InvalidOpenIntSocket.class,WebSocketOpen.class);
}
@Test
public void testScan_InvalidOpenSessionInt()
{
assertInvalidAnnotationSignature(InvalidOpenSessionIntSocket.class,WebSocketOpen.class);
}
}

View File

@ -0,0 +1,125 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
import static org.hamcrest.Matchers.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert;
/**
* Abstract base socket used for tracking state and events within the socket for testing reasons.
*/
public abstract class TrackingSocket
{
private static final Logger LOG = Log.getLogger(TrackingSocket.class);
public CloseReason closeReason;
public BlockingQueue<String> eventQueue = new BlockingArrayQueue<String>();
public BlockingQueue<Throwable> errorQueue = new BlockingArrayQueue<>();
public CountDownLatch openLatch = new CountDownLatch(1);
public CountDownLatch closeLatch = new CountDownLatch(1);
public CountDownLatch dataLatch = new CountDownLatch(1);
public void assertClose(CloseCode expectedCode, String expectedReason) throws InterruptedException
{
assertCloseCode(expectedCode);
assertCloseReason(expectedReason);
}
public void assertCloseCode(CloseCode expectedCode) throws InterruptedException
{
Assert.assertThat("Was Closed",closeLatch.await(50,TimeUnit.MILLISECONDS),is(true));
Assert.assertThat("CloseReason",closeReason,notNullValue());
Assert.assertThat("Close Code",closeReason.getCloseCode(),is(expectedCode));
}
private void assertCloseReason(String expectedReason)
{
Assert.assertThat("Close Reason",closeReason.getReasonPhrase(),is(expectedReason));
}
protected void addEvent(String format, Object... args)
{
eventQueue.add(String.format(format,args));
}
protected void addError(Throwable t)
{
errorQueue.add(t);
}
public void assertIsOpen() throws InterruptedException
{
assertWasOpened();
assertNotClosed();
}
public void assertEvent(String expected)
{
String actual = eventQueue.poll();
Assert.assertEquals("Event",expected,actual);
}
public void assertNotClosed()
{
Assert.assertThat("Closed Latch",closeLatch.getCount(),greaterThanOrEqualTo(1L));
}
public void assertNotOpened()
{
Assert.assertThat("Open Latch",openLatch.getCount(),greaterThanOrEqualTo(1L));
}
public void assertWasOpened() throws InterruptedException
{
Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
}
public void clear()
{
eventQueue.clear();
errorQueue.clear();
}
public void waitForClose(int timeoutDuration, TimeUnit timeoutUnit) throws InterruptedException
{
Assert.assertThat("Client Socket Closed",closeLatch.await(timeoutDuration,timeoutUnit),is(true));
}
public void waitForConnected(int timeoutDuration, TimeUnit timeoutUnit) throws InterruptedException
{
Assert.assertThat("Client Socket Connected",openLatch.await(timeoutDuration,timeoutUnit),is(true));
}
public void waitForData(int timeoutDuration, TimeUnit timeoutUnit) throws InterruptedException
{
LOG.debug("Waiting for message");
Assert.assertThat("Data Received",dataLatch.await(timeoutDuration,timeoutUnit),is(true));
}
}

View File

@ -0,0 +1,46 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.CloseReason;
import javax.websocket.Session;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketClose;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class BasicOpenCloseSessionSocket extends TrackingSocket
{
@WebSocketClose
public void onClose(CloseReason close, Session session)
{
addEvent("onClose(%s, %s)",close,session);
this.closeReason = close;
closeLatch.countDown();
}
@WebSocketOpen
public void onOpen(Session session)
{
addEvent("onOpen(%s)",session);
openLatch.countDown();
}
}

View File

@ -0,0 +1,41 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.CloseReason;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketClose;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class BasicOpenCloseSocket extends TrackingSocket
{
@WebSocketOpen
public void onOpen() {
openLatch.countDown();
}
@WebSocketClose
public void onClose(CloseReason close) {
this.closeReason = close;
closeLatch.countDown();
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.Session;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class BasicOpenSessionSocket extends TrackingSocket
{
@WebSocketOpen
public void onOpen(Session session)
{
openLatch.countDown();
}
}

View File

@ -1,6 +1,6 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// 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
@ -16,12 +16,19 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints;
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
/**
* Represents the metadata associated with Annotation discovery of a specific class.
*/
public class JavaxPojoMetadata
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class BasicOpenSocket extends TrackingSocket
{
@WebSocketOpen
public void onOpen()
{
openLatch.countDown();
}
}

View File

@ -0,0 +1,37 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class InvalidOpenCloseReasonSocket extends TrackingSocket
{
/**
* Invalid Open Method Declaration (parameter type int)
*/
@WebSocketOpen
public void onOpen(int count)
{
openLatch.countDown();
}
}

View File

@ -0,0 +1,38 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.CloseReason;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class InvalidOpenIntSocket extends TrackingSocket
{
/**
* Invalid Open Method Declaration (parameter type CloseReason)
*/
@WebSocketOpen
public void onOpen(CloseReason close)
{
openLatch.countDown();
}
}

View File

@ -0,0 +1,38 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.endpoints.samples;
import javax.websocket.Session;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketOpen;
import org.eclipse.jetty.websocket.jsr356.endpoints.TrackingSocket;
@WebSocketClient
public class InvalidOpenSessionIntSocket extends TrackingSocket
{
/**
* Invalid Open Method Declaration (parameter of type int)
*/
@WebSocketOpen
public void onOpen(Session session, int count)
{
openLatch.countDown();
}
}

View File

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