diff options
Diffstat (limited to 'jetty-websocket/javax-websocket-client-impl')
18 files changed, 1025 insertions, 114 deletions
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JavaxPojoAnnotationCache.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JavaxPojoAnnotationCache.java deleted file mode 100644 index d8524faae6..0000000000 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JavaxPojoAnnotationCache.java +++ /dev/null @@ -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 - } -} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientEventDriver.java index 52c72a165c..c3768dde5e 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientEventDriver.java @@ -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) diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientImpl.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientImpl.java new file mode 100644 index 0000000000..dff9be3dd9 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientImpl.java @@ -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 @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); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScanner.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScanner.java new file mode 100644 index 0000000000..cfb6086e61 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScanner.java @@ -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 @WebSocketEndpoint} and {@link WebSocketClient @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; + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedImpl.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedImpl.java deleted file mode 100644 index 9653b5f7ca..0000000000 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedImpl.java +++ /dev/null @@ -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; - } - -} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedMetadata.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedMetadata.java new file mode 100644 index 0000000000..0a7e588a24 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedMetadata.java @@ -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 @{@link WebSocketOpen} annotation + */ + public CallableMethod onOpen; + /** + * Callable for @{@link WebSocketClose} annotation + */ + public CallableMethod onClose; + /** + * Callable for @{@link WebSocketError} annotation + */ + public CallableMethod onError; + /** + * Callable for @{@link WebSocketMessage} annotation dealing with Text Message Format + */ + public CallableMethod onText; + /** + * Callable for @{@link WebSocketMessage} annotation dealing with Binary Message Format + */ + public CallableMethod onBinary; + /** + * Callable for @{@link WebSocketMessage} annotation dealing with Pong Message Format + */ + public CallableMethod onPong; +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMessageCallableMethod.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMessageCallableMethod.java new file mode 100644 index 0000000000..2e996b5d39 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMessageCallableMethod.java @@ -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) + { + + } + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMethodParameters.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMethodParameters.java new file mode 100644 index 0000000000..94b3a1e22b --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrMethodParameters.java @@ -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); + } + } + } + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScannerTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScannerTest.java new file mode 100644 index 0000000000..5576cd7950 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedClientScannerTest.java @@ -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); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java new file mode 100644 index 0000000000..64db37e578 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/TrackingSocket.java @@ -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)); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSessionSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSessionSocket.java new file mode 100644 index 0000000000..617bb36240 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSessionSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSocket.java new file mode 100644 index 0000000000..f9cd8235f0 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenCloseSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenSessionSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenSessionSocket.java new file mode 100644 index 0000000000..776eba05ea --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenSessionSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JavaxPojoMetadata.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenSocket.java index 4504177d2b..c1ec92e2ff 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JavaxPojoMetadata.java +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/BasicOpenSocket.java @@ -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(); + } } diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java new file mode 100644 index 0000000000..8dd3290ffd --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenCloseReasonSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java new file mode 100644 index 0000000000..5535c74a44 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenIntSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java new file mode 100644 index 0000000000..a8d8bfeda7 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/endpoints/samples/InvalidOpenSessionIntSocket.java @@ -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(); + } +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties new file mode 100644 index 0000000000..8d55129899 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/resources/jetty-logging.properties @@ -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 |