Working EventMethod/EventMethods/EventMethodsCache with tests
This commit is contained in:
parent
270a04ebd0
commit
08796a7b37
|
@ -0,0 +1,106 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class EventMethod
|
||||
{
|
||||
public static final EventMethod NOOP = new EventMethod();
|
||||
private static final Logger LOG = Log.getLogger(EventMethod.class);
|
||||
|
||||
private static Object[] dropFirstArg(Object[] args)
|
||||
{
|
||||
if (args.length == 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Object ret[] = new Object[args.length - 1];
|
||||
System.arraycopy(args,1,ret,0,ret.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static EventMethod findAnnotatedMethod(Object pojo, Class<? extends Annotation> annoClass, Class<?>... paramTypes)
|
||||
{
|
||||
Class<?>[] possibleParams = new Class<?>[paramTypes.length];
|
||||
System.arraycopy(paramTypes,0,possibleParams,0,possibleParams.length);
|
||||
|
||||
for (Method method : pojo.getClass().getDeclaredMethods())
|
||||
{
|
||||
if (method.getAnnotation(annoClass) == null)
|
||||
{
|
||||
// skip, not interested
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
return NOOP;
|
||||
}
|
||||
|
||||
private Class<?> pojo;
|
||||
|
||||
private Method method;
|
||||
|
||||
private Class<?>[] paramTypes;
|
||||
|
||||
private EventMethod()
|
||||
{
|
||||
this.method = null;
|
||||
}
|
||||
|
||||
public EventMethod(Class<?> pojo, Method method)
|
||||
{
|
||||
this.pojo = pojo;
|
||||
this.paramTypes = method.getParameterTypes();
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public EventMethod(Class<?> pojo, String methodName, Class<?>... paramTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.pojo = pojo;
|
||||
this.paramTypes = paramTypes;
|
||||
this.method = pojo.getClass().getMethod(methodName,paramTypes);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e)
|
||||
{
|
||||
LOG.debug("Cannot use method {}({}): {}",methodName,paramTypes,e.getMessage(),e);
|
||||
this.method = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void call(Object obj, Object... args)
|
||||
{
|
||||
if ((this.pojo == null) || (this.method == null))
|
||||
{
|
||||
return; // no call event method determined
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
LOG.warn("Cannot call {} on null object",this.method);
|
||||
return;
|
||||
}
|
||||
if (args.length > paramTypes.length)
|
||||
{
|
||||
Object trimArgs[] = dropFirstArg(args);
|
||||
call(trimArgs);
|
||||
return;
|
||||
}
|
||||
if (args.length < paramTypes.length)
|
||||
{
|
||||
throw new IllegalArgumentException("Call arguments length must always be greater than or equal to captured args length");
|
||||
}
|
||||
try
|
||||
{
|
||||
this.method.invoke(obj,args);
|
||||
}
|
||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
||||
{
|
||||
LOG.warn("Cannot call method {} on {} with {}",method,pojo,args,e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
/**
|
||||
* A representation of the methods available to call for a particular class.
|
||||
* <p>
|
||||
* This class used to cache the method lookups via the {@link EventMethodsCache}
|
||||
*/
|
||||
public class EventMethods
|
||||
{
|
||||
private Class<?> pojoClass;
|
||||
private boolean isAnnotated = false;
|
||||
public EventMethod onConnect = EventMethod.NOOP;
|
||||
public EventMethod onClose = EventMethod.NOOP;
|
||||
public EventMethod onBinary = EventMethod.NOOP;
|
||||
public EventMethod onText = EventMethod.NOOP;
|
||||
public EventMethod onFrame = EventMethod.NOOP;
|
||||
public EventMethod onException = EventMethod.NOOP;
|
||||
|
||||
public EventMethods(Class<?> pojoClass, boolean annotated)
|
||||
{
|
||||
this.pojoClass = pojoClass;
|
||||
this.isAnnotated = annotated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EventMethods other = (EventMethods)obj;
|
||||
if (pojoClass == null)
|
||||
{
|
||||
if (other.pojoClass != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!pojoClass.getName().equals(other.pojoClass.getName()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Class<?> getPojoClass()
|
||||
{
|
||||
return pojoClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((pojoClass == null)?0:pojoClass.getName().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isAnnotated()
|
||||
{
|
||||
return isAnnotated;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||
import org.eclipse.jetty.websocket.frames.BaseFrame;
|
||||
import org.eclipse.jetty.websocket.frames.BinaryFrame;
|
||||
import org.eclipse.jetty.websocket.frames.CloseFrame;
|
||||
import org.eclipse.jetty.websocket.frames.ControlFrame;
|
||||
import org.eclipse.jetty.websocket.frames.DataFrame;
|
||||
import org.eclipse.jetty.websocket.frames.PingFrame;
|
||||
import org.eclipse.jetty.websocket.frames.PongFrame;
|
||||
import org.eclipse.jetty.websocket.frames.TextFrame;
|
||||
|
||||
public class EventMethodsCache
|
||||
{
|
||||
@SuppressWarnings("serial")
|
||||
private static class ParamList extends ArrayList<Class<?>[]>
|
||||
{
|
||||
public void addParams(Class<?>... paramTypes)
|
||||
{
|
||||
this.add(paramTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter list for @OnWebSocketBinary
|
||||
*/
|
||||
private static final ParamList validBinaryParams;
|
||||
/**
|
||||
* Parameter list for @OnWebSocketConnect
|
||||
*/
|
||||
private static final ParamList validConnectParams;
|
||||
private static final ParamList validCloseParams;
|
||||
private static final ParamList validFrameParams;
|
||||
private static final ParamList validTextParams;
|
||||
|
||||
static
|
||||
{
|
||||
validBinaryParams = new ParamList();
|
||||
validBinaryParams.addParams(ByteBuffer.class);
|
||||
validBinaryParams.addParams(byte[].class,int.class,int.class);
|
||||
validBinaryParams.addParams(WebSocketConnection.class,ByteBuffer.class);
|
||||
validBinaryParams.addParams(WebSocketConnection.class,byte[].class,int.class,int.class);
|
||||
|
||||
validConnectParams = new ParamList();
|
||||
validConnectParams.addParams(WebSocketConnection.class);
|
||||
|
||||
validCloseParams = new ParamList();
|
||||
validCloseParams.addParams(int.class,String.class);
|
||||
validCloseParams.addParams(WebSocketConnection.class,int.class,String.class);
|
||||
|
||||
validTextParams = new ParamList();
|
||||
validTextParams.addParams(String.class);
|
||||
validTextParams.addParams(WebSocketConnection.class,String.class);
|
||||
|
||||
validFrameParams = new ParamList();
|
||||
validFrameParams.addParams(BaseFrame.class);
|
||||
validFrameParams.addParams(BinaryFrame.class);
|
||||
validFrameParams.addParams(CloseFrame.class);
|
||||
validFrameParams.addParams(ControlFrame.class);
|
||||
validFrameParams.addParams(DataFrame.class);
|
||||
validFrameParams.addParams(PingFrame.class);
|
||||
validFrameParams.addParams(PongFrame.class);
|
||||
validFrameParams.addParams(TextFrame.class);
|
||||
|
||||
validFrameParams.addParams(WebSocketConnection.class,BaseFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,BinaryFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,CloseFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,ControlFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,DataFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,PingFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,PongFrame.class);
|
||||
validFrameParams.addParams(WebSocketConnection.class,TextFrame.class);
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<Class<?>, EventMethods> cache;
|
||||
|
||||
public EventMethodsCache()
|
||||
{
|
||||
cache = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private void assertUnset(EventMethod event, Class<? extends Annotation> annoClass, Class<?> pojo, Method method)
|
||||
{
|
||||
if (event == EventMethod.NOOP)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidParams(Class<?> pojo, Method method, Class<? extends Annotation> annoClass, ParamList validParams)
|
||||
{
|
||||
boolean valid = false;
|
||||
|
||||
Class<?> actual[] = method.getParameterTypes();
|
||||
for (Class<?> params[] : validParams)
|
||||
{
|
||||
if (isSameParameters(actual,params))
|
||||
{
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
// Build big detailed exception to help the developer
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Invalid declaration of ");
|
||||
err.append(method);
|
||||
err.append(StringUtil.__LINE_SEPARATOR);
|
||||
|
||||
err.append("Acceptable method declarations for @");
|
||||
err.append(annoClass.getSimpleName());
|
||||
err.append(" are:");
|
||||
for (Class<?> params[] : validParams)
|
||||
{
|
||||
err.append(StringUtil.__LINE_SEPARATOR);
|
||||
err.append("public void ").append(method.getName());
|
||||
err.append('(');
|
||||
boolean delim = false;
|
||||
for (Class<?> type : params)
|
||||
{
|
||||
if (delim)
|
||||
{
|
||||
err.append(',');
|
||||
}
|
||||
err.append(' ');
|
||||
err.append(type.getName());
|
||||
if (type.isArray())
|
||||
{
|
||||
err.append("[]");
|
||||
}
|
||||
delim = true;
|
||||
}
|
||||
err.append(')');
|
||||
}
|
||||
|
||||
throw new InvalidWebSocketException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the basic discovery mechanism for WebSocket events from the provided pojo.
|
||||
*
|
||||
* @param pojo
|
||||
* the pojo to scan
|
||||
* @return the discovered event methods
|
||||
* @throws InvalidWebSocketException
|
||||
*/
|
||||
private EventMethods discoverMethods(Class<?> pojo) throws InvalidWebSocketException
|
||||
{
|
||||
if (WebSocketListener.class.isInstance(pojo))
|
||||
{
|
||||
return scanListenerMethods(pojo);
|
||||
}
|
||||
|
||||
WebSocket anno = pojo.getAnnotation(WebSocket.class);
|
||||
if (anno == null)
|
||||
{
|
||||
throw new InvalidWebSocketException(pojo.getName() + " does not implement " + WebSocketListener.class.getName() + " or use the @"
|
||||
+ WebSocket.class.getName() + " annotation");
|
||||
}
|
||||
|
||||
return scanAnnotatedMethods(pojo);
|
||||
}
|
||||
|
||||
public EventMethods getMethods(Class<?> pojo) throws InvalidWebSocketException
|
||||
{
|
||||
EventMethods methods = cache.get(pojo);
|
||||
if (methods == null)
|
||||
{
|
||||
methods = discoverMethods(pojo);
|
||||
cache.put(pojo,methods);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
private boolean isSameParameters(Class<?>[] actual, Class<?>[] params)
|
||||
{
|
||||
if(actual.length != params.length) {
|
||||
// skip
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = params.length;
|
||||
for(int i=0; i<len; i++) {
|
||||
if(!actual[i].equals(params[i])) {
|
||||
return false; // not valid
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a pojo with the cache.
|
||||
*
|
||||
* @param pojo
|
||||
* the pojo to register with the cache.
|
||||
* @throws InvalidWebSocketException
|
||||
* if the pojo does not conform to a WebSocket implementation.
|
||||
*/
|
||||
public void register(Class<?> pojo) throws InvalidWebSocketException
|
||||
{
|
||||
discoverMethods(pojo);
|
||||
}
|
||||
|
||||
private EventMethods scanAnnotatedMethods(Class<?> pojo)
|
||||
{
|
||||
EventMethods events = new EventMethods(pojo,true);
|
||||
|
||||
for (Method method : pojo.getDeclaredMethods())
|
||||
{
|
||||
if (method.getAnnotation(OnWebSocketConnect.class) != null)
|
||||
{
|
||||
assertUnset(events.onConnect,OnWebSocketConnect.class,pojo,method);
|
||||
assertValidParams(pojo,method,OnWebSocketConnect.class,validConnectParams);
|
||||
events.onConnect = new EventMethod(pojo,method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.getAnnotation(OnWebSocketBinary.class) != null)
|
||||
{
|
||||
assertUnset(events.onBinary,OnWebSocketBinary.class,pojo,method);
|
||||
assertValidParams(pojo,method,OnWebSocketBinary.class,validBinaryParams);
|
||||
events.onBinary = new EventMethod(pojo,method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.getAnnotation(OnWebSocketClose.class) != null)
|
||||
{
|
||||
assertUnset(events.onClose,OnWebSocketClose.class,pojo,method);
|
||||
assertValidParams(pojo,method,OnWebSocketClose.class,validCloseParams);
|
||||
events.onClose = new EventMethod(pojo,method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.getAnnotation(OnWebSocketText.class) != null)
|
||||
{
|
||||
assertUnset(events.onText,OnWebSocketText.class,pojo,method);
|
||||
assertValidParams(pojo,method,OnWebSocketText.class,validTextParams);
|
||||
events.onText = new EventMethod(pojo,method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.getAnnotation(OnWebSocketFrame.class) != null)
|
||||
{
|
||||
assertUnset(events.onFrame,OnWebSocketFrame.class,pojo,method);
|
||||
assertValidParams(pojo,method,OnWebSocketFrame.class,validFrameParams);
|
||||
events.onFrame = new EventMethod(pojo,method);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not a tagged method we are interested in, ignore
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
private EventMethods scanListenerMethods(Class<?> pojo)
|
||||
{
|
||||
EventMethods events = new EventMethods(pojo,false);
|
||||
// This is a WebSocketListener object
|
||||
events.onConnect = new EventMethod(pojo,"onWebSocketConnect",WebSocketConnection.class);
|
||||
events.onClose = new EventMethod(pojo,"onWebSocketClose",Integer.TYPE,String.class);
|
||||
events.onBinary = new EventMethod(pojo,"onWebSocketBinary",byte[].class,Integer.TYPE,Integer.TYPE);
|
||||
events.onText = new EventMethod(pojo,"onWebSocketText",WebSocketConnection.class);
|
||||
events.onException = new EventMethod(pojo,"onWebSocketException",WebSocketException.class);
|
||||
|
||||
return events;
|
||||
}
|
||||
}
|
|
@ -19,5 +19,7 @@ public @interface WebSocket
|
|||
|
||||
int maxBufferSize() default 8192;
|
||||
|
||||
int maxIdleTime() default 300000;
|
||||
|
||||
int maxTextSize() default 8192;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package org.eclipse.jetty.websocket.api;
|
||||
|
||||
import org.eclipse.jetty.websocket.annotations.WebSocket;
|
||||
|
||||
/**
|
||||
* Indicating that the provided Class is not a valid WebSocket as defined by the API.
|
||||
* <p>
|
||||
* A valid WebSocket should do one of the following:
|
||||
* <ul>
|
||||
* <li>Implement {@link WebSocketListener}</li>
|
||||
* <li>Extend {@link WebSocketAdapter}</li>
|
||||
* <li>Declare the {@link WebSocket @WebSocket} annotation on the type</li>
|
||||
* </ul>
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class InvalidWebSocketException extends WebSocketException
|
||||
{
|
||||
public InvalidWebSocketException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidWebSocketException(String message, Throwable cause)
|
||||
{
|
||||
super(message,cause);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package org.eclipse.jetty.websocket.api;
|
||||
|
||||
import org.eclipse.jetty.websocket.annotations.EventMethods;
|
||||
import org.eclipse.jetty.websocket.annotations.EventMethodsCache;
|
||||
import org.eclipse.jetty.websocket.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.frames.BaseFrame;
|
||||
import org.eclipse.jetty.websocket.parser.Parser;
|
||||
|
||||
/**
|
||||
* Responsible for routing the internally generated events destined for a specific WebSocket instance to whatever choice of development style the developer has
|
||||
|
@ -11,20 +14,38 @@ import org.eclipse.jetty.websocket.frames.BaseFrame;
|
|||
* <p>
|
||||
* There will be an instance of the WebSocketEventDriver per connection.
|
||||
*/
|
||||
public class WebSocketEventDriver
|
||||
public class WebSocketEventDriver implements Parser.Listener
|
||||
{
|
||||
private Object websocket;
|
||||
private WebSocketPolicy policy;
|
||||
private WebSocketConnection connection;
|
||||
private EventMethods events;
|
||||
|
||||
/**
|
||||
* Establish the driver for the Websocket POJO
|
||||
*
|
||||
* @param websocket
|
||||
*/
|
||||
public WebSocketEventDriver(Object websocket)
|
||||
public WebSocketEventDriver(EventMethodsCache methodsCache, WebSocketPolicy policy, Object websocket)
|
||||
{
|
||||
this.policy = policy;
|
||||
this.websocket = websocket;
|
||||
// TODO Discover and bind what routing is available in the POJO
|
||||
this.events = methodsCache.getMethods(websocket.getClass());
|
||||
|
||||
if (events.isAnnotated())
|
||||
{
|
||||
WebSocket anno = websocket.getClass().getAnnotation(WebSocket.class);
|
||||
// Setup the policy
|
||||
policy.setBufferSize(anno.maxBufferSize());
|
||||
policy.setMaxBinaryMessageSize(anno.maxBinarySize());
|
||||
policy.setMaxTextMessageSize(anno.maxTextSize());
|
||||
policy.setMaxIdleTime(anno.maxIdleTime());
|
||||
}
|
||||
}
|
||||
|
||||
public WebSocketPolicy getPolicy()
|
||||
{
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,15 +63,7 @@ public class WebSocketEventDriver
|
|||
*/
|
||||
public void onConnect()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal entry point for connection disconnected
|
||||
*/
|
||||
public void onDisconnect()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
events.onConnect.call(websocket,connection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,9 +72,18 @@ public class WebSocketEventDriver
|
|||
* @param frame
|
||||
* the frame that appeared
|
||||
*/
|
||||
@Override
|
||||
public void onFrame(BaseFrame frame)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketException(WebSocketException e)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,6 +84,17 @@ public class WebSocketPolicy
|
|||
}
|
||||
}
|
||||
|
||||
public WebSocketPolicy clonePolicy()
|
||||
{
|
||||
WebSocketPolicy clone = new WebSocketPolicy(this.behavior);
|
||||
clone.bufferSize = this.bufferSize;
|
||||
clone.masker = this.masker;
|
||||
clone.maxBinaryMessageSize = this.maxBinaryMessageSize;
|
||||
clone.maxIdleTime = this.maxIdleTime;
|
||||
clone.maxTextMessageSize = this.maxTextMessageSize;
|
||||
return clone;
|
||||
}
|
||||
|
||||
public WebSocketBehavior getBehavior()
|
||||
{
|
||||
return behavior;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class EventMethodsCacheTest
|
||||
{
|
||||
private void assertHasEventMethod(String message, EventMethod actual)
|
||||
{
|
||||
Assert.assertNotSame(message + "Event method should have been discovered",actual,EventMethod.NOOP);
|
||||
}
|
||||
|
||||
private void assertNoEventMethod(String message, EventMethod actual)
|
||||
{
|
||||
Assert.assertEquals(message + "Event method should have been NOOP",actual,EventMethod.NOOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case for no exceptions and 3 methods
|
||||
*/
|
||||
@Test
|
||||
public void testDiscoverMyEchoSocket()
|
||||
{
|
||||
EventMethodsCache cache = new EventMethodsCache();
|
||||
EventMethods methods = cache.getMethods(MyEchoSocket.class);
|
||||
|
||||
Assert.assertThat("EventMethods for MyEchoSocket",methods,notNullValue());
|
||||
|
||||
assertNoEventMethod("MyEchoSocket.onBinary",methods.onBinary);
|
||||
assertHasEventMethod("MyEchoSocket.onClose",methods.onClose);
|
||||
assertHasEventMethod("MyEchoSocket.onConnect",methods.onConnect);
|
||||
assertNoEventMethod("MyEchoSocket.onException",methods.onException);
|
||||
assertNoEventMethod("MyEchoSocket.onFrame",methods.onFrame);
|
||||
assertHasEventMethod("MyEchoSocket.onText",methods.onText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case for no exceptions and 1 method
|
||||
*/
|
||||
@Test
|
||||
public void testDiscoverMyStatelessEchoSocket()
|
||||
{
|
||||
EventMethodsCache cache = new EventMethodsCache();
|
||||
EventMethods methods = cache.getMethods(MyStatelessEchoSocket.class);
|
||||
|
||||
Assert.assertThat("EventMethods for MyStatelessEchoSocket",methods,notNullValue());
|
||||
|
||||
assertNoEventMethod("MyStatelessEchoSocket.onBinary",methods.onBinary);
|
||||
assertNoEventMethod("MyStatelessEchoSocket.onClose",methods.onClose);
|
||||
assertNoEventMethod("MyStatelessEchoSocket.onConnect",methods.onConnect);
|
||||
assertNoEventMethod("MyStatelessEchoSocket.onException",methods.onException);
|
||||
assertNoEventMethod("MyStatelessEchoSocket.onFrame",methods.onFrame);
|
||||
assertHasEventMethod("MyStatelessEchoSocket.onText",methods.onText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case for no exceptions and no methods
|
||||
*/
|
||||
@Test
|
||||
public void testDiscoverNoop()
|
||||
{
|
||||
EventMethodsCache cache = new EventMethodsCache();
|
||||
EventMethods methods = cache.getMethods(NoopSocket.class);
|
||||
|
||||
Assert.assertThat("Methods for NoopSocket",methods,notNullValue());
|
||||
|
||||
assertNoEventMethod("NoopSocket.onBinary",methods.onBinary);
|
||||
assertNoEventMethod("NoopSocket.onClose",methods.onClose);
|
||||
assertNoEventMethod("NoopSocket.onConnect", methods.onConnect);
|
||||
assertNoEventMethod("NoopSocket.onException",methods.onException);
|
||||
assertNoEventMethod("NoopSocket.onFrame",methods.onFrame);
|
||||
assertNoEventMethod("NoopSocket.onText",methods.onText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case for no exceptions and 3 methods
|
||||
*/
|
||||
@Test
|
||||
public void testDiscoverOnFrame()
|
||||
{
|
||||
EventMethodsCache cache = new EventMethodsCache();
|
||||
EventMethods methods = cache.getMethods(FrameSocket.class);
|
||||
|
||||
Assert.assertThat("EventMethods for MyEchoSocket",methods,notNullValue());
|
||||
|
||||
assertNoEventMethod("MyEchoSocket.onBinary",methods.onBinary);
|
||||
assertNoEventMethod("MyEchoSocket.onClose",methods.onClose);
|
||||
assertNoEventMethod("MyEchoSocket.onConnect",methods.onConnect);
|
||||
assertNoEventMethod("MyEchoSocket.onException",methods.onException);
|
||||
assertHasEventMethod("MyEchoSocket.onFrame",methods.onFrame);
|
||||
assertNoEventMethod("MyEchoSocket.onText",methods.onText);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
import org.eclipse.jetty.websocket.frames.BaseFrame;
|
||||
|
||||
@WebSocket
|
||||
public class FrameSocket
|
||||
{
|
||||
@OnWebSocketFrame
|
||||
public void frameMe(BaseFrame frame)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package org.eclipse.jetty.websocket.annotations;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.LocalWebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketEventDriver;
|
||||
import org.eclipse.jetty.websocket.frames.CloseFrame;
|
||||
import org.eclipse.jetty.websocket.frames.TextFrame;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WebSocketAnnotationTest
|
||||
{
|
||||
/**
|
||||
* Test Case for no exceptions
|
||||
*/
|
||||
@Test
|
||||
public void testCapture()
|
||||
{
|
||||
WebSocketEventDriver driver = new WebSocketEventDriver(new NoopSocket());
|
||||
WebSocketConnection conn = new LocalWebSocketConnection();
|
||||
|
||||
driver.setConnection(conn);
|
||||
driver.onConnect();
|
||||
driver.onFrame(new TextFrame("Hello World"));
|
||||
driver.onFrame(new CloseFrame(StatusCode.NORMAL));
|
||||
driver.onDisconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Case for no exceptions
|
||||
*/
|
||||
@Test
|
||||
public void testNoop()
|
||||
{
|
||||
WebSocketEventDriver driver = new WebSocketEventDriver(new NoopSocket());
|
||||
WebSocketConnection conn = new LocalWebSocketConnection();
|
||||
|
||||
driver.setConnection(conn);
|
||||
driver.onConnect();
|
||||
driver.onFrame(new TextFrame("Hello World"));
|
||||
driver.onFrame(new CloseFrame(StatusCode.NORMAL));
|
||||
driver.onDisconnect();
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ import org.eclipse.jetty.server.HttpConnection;
|
|||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.annotations.EventMethodsCache;
|
||||
import org.eclipse.jetty.websocket.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.api.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketEventDriver;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
|
@ -65,11 +67,14 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
}
|
||||
|
||||
private final String supportedVersions;
|
||||
private WebSocketPolicy policy;
|
||||
private WebSocketPolicy basePolicy;
|
||||
private WebSocketCreator creator;
|
||||
private EventMethodsCache methodsCache;
|
||||
|
||||
public WebSocketServerFactory(WebSocketPolicy policy)
|
||||
{
|
||||
this.policy = policy;
|
||||
this.basePolicy = policy;
|
||||
this.methodsCache = new EventMethodsCache();
|
||||
|
||||
// Create supportedVersions
|
||||
List<Integer> versions = new ArrayList<>();
|
||||
|
@ -109,7 +114,8 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
// TODO: discover type, create proxy
|
||||
|
||||
// Send the upgrade
|
||||
WebSocketEventDriver websocket = new WebSocketEventDriver(websocketPojo);
|
||||
WebSocketPolicy objPolicy = this.basePolicy.clonePolicy();
|
||||
WebSocketEventDriver websocket = new WebSocketEventDriver(methodsCache,objPolicy,websocketPojo);
|
||||
return upgrade(sockreq,sockresp,websocket);
|
||||
}
|
||||
|
||||
|
@ -134,8 +140,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
|
||||
public WebSocketCreator getCreator()
|
||||
{
|
||||
// TODO: implement
|
||||
return null;
|
||||
return this.creator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,13 +152,15 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the policy in use for WebSockets.
|
||||
* Get the base policy in use for WebSockets.
|
||||
* <p>
|
||||
* Note: individual WebSocket implementations can override some of the values in here by using the {@link WebSocket @WebSocket} annotation.
|
||||
*
|
||||
* @return
|
||||
* @return the base policy
|
||||
*/
|
||||
public WebSocketPolicy getPolicy()
|
||||
{
|
||||
return policy;
|
||||
return basePolicy;
|
||||
}
|
||||
|
||||
public List<Extension> initExtensions(List<ExtensionConfig> requested)
|
||||
|
@ -179,12 +186,26 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
|
||||
public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
// TODO: other checks against the spec?
|
||||
String upgrade = request.getHeader("Upgrade");
|
||||
if (upgrade == null)
|
||||
{
|
||||
// Quietly fail
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"websocket".equalsIgnoreCase(upgrade))
|
||||
{
|
||||
LOG.warn("Not a 'Upgrade: WebSocket' (was [Upgrade: " + upgrade + "])");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"HTTP/1.1".equals(request.getProtocol()))
|
||||
{
|
||||
throw new IllegalStateException("Not a 'HTTP/1.1' request");
|
||||
LOG.warn("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])");
|
||||
return false;
|
||||
}
|
||||
return ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Extension newExtension(String name)
|
||||
|
@ -226,7 +247,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
|
||||
public void register(Class<?> websocketClass)
|
||||
{
|
||||
// TODO: implement
|
||||
methodsCache.register(websocketClass);
|
||||
}
|
||||
|
||||
protected boolean removeConnection(AsyncWebSocketConnection connection)
|
||||
|
@ -236,7 +257,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
|
||||
public void setCreator(WebSocketCreator creator)
|
||||
{
|
||||
// TODO: implement
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,7 +308,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
|
|||
HttpConnection http = HttpConnection.getCurrentConnection();
|
||||
AsyncEndPoint endp = http.getEndPoint();
|
||||
Executor executor = http.getConnector().findExecutor();
|
||||
final AsyncWebSocketConnection connection = new AsyncWebSocketConnection(endp,executor,policy);
|
||||
final AsyncWebSocketConnection connection = new AsyncWebSocketConnection(endp,executor,websocket.getPolicy());
|
||||
endp.setAsyncConnection(connection);
|
||||
|
||||
// Notify POJO of connection
|
||||
|
|
Loading…
Reference in New Issue