Cleaning up Annotations

+ Removing @OnWebSocketText
+ Removing @OnWebSocketBinary
+ Starting to rework EventMethods + WebSocketEventDriver for new
  @OnWebSocketMessage annotation
+ Updating various example/demo/test sockets for this new reality.
This commit is contained in:
Joakim Erdfelt 2012-07-05 14:02:21 -07:00
parent 502731d719
commit c7fcb6e694
25 changed files with 451 additions and 364 deletions

View File

@ -1,31 +0,0 @@
package org.eclipse.jetty.websocket.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Annotation for tagging methods to receive Binary message events.
* <p>
* Acceptable method patterns.<br>
* Note: <code><u>methodName</u></code> can be any name you want to use.
* <ol>
* <li><code>public void methodName(byte payload[], int offset, int length)</code></li>
* <li><code>public void methodName({@link ByteBuffer} payload)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, byte payload[], int offset, int length)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, {@link ByteBuffer} payload)</code></li>
* </ol>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value =
{ ElementType.METHOD })
public @interface OnWebSocketBinary
{
/* no config */
}

View File

@ -1,5 +1,6 @@
package org.eclipse.jetty.websocket.annotations; package org.eclipse.jetty.websocket.annotations;
import java.io.Reader;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -9,13 +10,27 @@ import java.lang.annotation.Target;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
/** /**
* Annotation for tagging methods to receive Text message events. * Annotation for tagging methods to receive Binary or Text Message events.
* <p> * <p>
* Acceptable method patterns.<br> * Acceptable method patterns.<br>
* Note: <code>methodName</code> can be any name you want to use. * Note: <code>methodName</code> can be any name you want to use.
* <p>
* <u>Text Message Versions</u>
* <ol> * <ol>
* <li><code>public void methodName(String text)</code></li> * <li><code>public void methodName(String text)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, String text)</code></li> * <li><code>public void methodName({@link WebSocketConnection} conn, String text)</code></li>
* <li><code>public void methodName(Reader reader)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, Reader reader)</code></li>
* </ol>
* Note: that the {@link Reader} in this case will always use UTF-8 encoding/charset (this is dictated by the RFC 6455 spec for Text Messages. If you need to
* use a non-UTF-8 encoding/charset, you are instructed to use the binary messaging techniques.
* <p>
* <u>Binary Message Versions</u>
* <ol>
* <li><code>public void methodName(byte buf[], int offset, int length)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, byte buf[], int offset, int length)</code></li>
* <li><code>public void methodName(InputStream stream)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, InputStream stream)</code></li>
* </ol> * </ol>
*/ */
@Documented @Documented

View File

@ -1,28 +0,0 @@
package org.eclipse.jetty.websocket.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Annotation for tagging methods to receive Text message events.
* <p>
* Acceptable method patterns.<br>
* Note: <code>methodName</code> can be any name you want to use.
* <ol>
* <li><code>public void methodName(String text)</code></li>
* <li><code>public void methodName({@link WebSocketConnection} conn, String text)</code></li>
* </ol>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value =
{ ElementType.METHOD })
public @interface OnWebSocketText
{
/* no config */
}

View File

@ -114,16 +114,4 @@ public class EventMethod
{ {
return this.paramTypes; return this.paramTypes;
} }
public boolean isParameterPresent(Class<?> type)
{
for (Class<?> param : paramTypes)
{
if (param.equals(type))
{
return true;
}
}
return false;
}
} }

View File

@ -1,9 +1,5 @@
package org.eclipse.jetty.websocket.api; package org.eclipse.jetty.websocket.api;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.websocket.frames.BaseFrame;
/** /**
* A representation of the methods available to call for a particular class. * A representation of the methods available to call for a particular class.
@ -17,11 +13,11 @@ public class EventMethods
public EventMethod onConnect = null; public EventMethod onConnect = null;
public EventMethod onClose = null; public EventMethod onClose = null;
public EventMethod onBinary = null; public EventMethod onBinary = null;
public EventMethod onBinaryStream = null;
public EventMethod onText = null; public EventMethod onText = null;
public EventMethod onTextStream = null;
public EventMethod onException = null; public EventMethod onException = null;
public EventMethod onFrame = null;
// special case, multiple methods allowed
private Map<Class<? extends BaseFrame>, EventMethod> onFrames = new HashMap<Class<? extends BaseFrame>, EventMethod>();
public EventMethods(Class<?> pojoClass, boolean annotated) public EventMethods(Class<?> pojoClass, boolean annotated)
{ {
@ -29,19 +25,6 @@ public class EventMethods
this.isAnnotated = annotated; this.isAnnotated = annotated;
} }
public void addOnFrame(EventMethod eventMethod)
{
Class<?> paramTypes[] = eventMethod.getParamTypes();
Class<?> lastType = paramTypes[paramTypes.length - 1];
if (!BaseFrame.class.isAssignableFrom(lastType))
{
throw new InvalidWebSocketException("Unrecognized @OnWebSocketFrame frame type " + lastType);
}
@SuppressWarnings("unchecked")
Class<? extends BaseFrame> frameType = (Class<? extends BaseFrame>)lastType;
onFrames.put(frameType,eventMethod);
}
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@ -72,16 +55,6 @@ public class EventMethods
return true; return true;
} }
public EventMethod getOnFrame(Class<? extends BaseFrame> frameType)
{
return onFrames.get(frameType);
}
public Map<Class<? extends BaseFrame>, EventMethod> getOnFrames()
{
return onFrames;
}
public Class<?> getPojoClass() public Class<?> getPojoClass()
{ {
return pojoClass; return pojoClass;
@ -119,8 +92,8 @@ public class EventMethods
builder.append(onText); builder.append(onText);
builder.append(", onException="); builder.append(", onException=");
builder.append(onException); builder.append(onException);
builder.append(", onFrames="); builder.append(", onFrame=");
builder.append(onFrames); builder.append(onFrame);
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }

View File

@ -1,30 +1,70 @@
package org.eclipse.jetty.websocket.api; package org.eclipse.jetty.websocket.api;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.annotations.OnWebSocketBinary;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketFrame; import org.eclipse.jetty.websocket.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.annotations.OnWebSocketText; import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket; import org.eclipse.jetty.websocket.annotations.WebSocket;
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 public class EventMethodsCache
{ {
@SuppressWarnings("serial")
public static class InvalidSignatureException extends InvalidWebSocketException
{
public static InvalidSignatureException build(Method method, Class<? extends Annotation> annoClass, ParamList... paramlists)
{
// 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 (ParamList validParams : paramlists)
{
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(')');
}
}
return new InvalidSignatureException(err.toString());
}
public InvalidSignatureException(String message)
{
super(message);
}
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
private static class ParamList extends ArrayList<Class<?>[]> private static class ParamList extends ArrayList<Class<?>[]>
{ {
@ -33,27 +73,30 @@ public class EventMethodsCache
this.add(paramTypes); this.add(paramTypes);
} }
} }
/** /**
* Parameter list for &#064;OnWebSocketBinary * Parameter list for &#064;OnWebSocketMessage (Binary mode)
*/ */
private static final ParamList validBinaryParams; private static final ParamList validBinaryParams;
/** /**
* Parameter list for &#064;OnWebSocketConnect * Parameter list for &#064;OnWebSocketConnect
*/ */
private static final ParamList validConnectParams; private static final ParamList validConnectParams;
/**
* Parameter list for &#064;OnWebSocketClose
*/
private static final ParamList validCloseParams; private static final ParamList validCloseParams;
/**
* Parameter list for &#064;OnWebSocketFrame
*/
private static final ParamList validFrameParams; private static final ParamList validFrameParams;
/**
* Parameter list for &#064;OnWebSocketMessage (Text mode)
*/
private static final ParamList validTextParams; private static final ParamList validTextParams;
static 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 = new ParamList();
validConnectParams.addParams(WebSocketConnection.class); validConnectParams.addParams(WebSocketConnection.class);
@ -64,25 +107,18 @@ public class EventMethodsCache
validTextParams = new ParamList(); validTextParams = new ParamList();
validTextParams.addParams(String.class); validTextParams.addParams(String.class);
validTextParams.addParams(WebSocketConnection.class,String.class); validTextParams.addParams(WebSocketConnection.class,String.class);
validTextParams.addParams(Reader.class);
validTextParams.addParams(WebSocketConnection.class,Reader.class);
validBinaryParams = new ParamList();
validBinaryParams.addParams(byte[].class,int.class,int.class);
validBinaryParams.addParams(WebSocketConnection.class,byte[].class,int.class,int.class);
validBinaryParams.addParams(InputStream.class);
validBinaryParams.addParams(WebSocketConnection.class,InputStream.class);
validFrameParams = new ParamList(); validFrameParams = new ParamList();
validFrameParams.addParams(BaseFrame.class); validFrameParams.addParams(Frame.class);
validFrameParams.addParams(BinaryFrame.class); validFrameParams.addParams(WebSocketConnection.class,Frame.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; private ConcurrentHashMap<Class<?>, EventMethods> cache;
@ -92,33 +128,6 @@ public class EventMethodsCache
cache = new ConcurrentHashMap<>(); cache = new ConcurrentHashMap<>();
} }
private void assertFrameUnset(EventMethods events, Method method)
{
Class<?> paramTypes[] = method.getParameterTypes();
Class<?> lastType = paramTypes[paramTypes.length - 1];
if (!BaseFrame.class.isAssignableFrom(lastType))
{
throw new InvalidWebSocketException("Unrecognized @OnWebSocketFrame frame type " + lastType);
}
@SuppressWarnings("unchecked")
Class<? extends BaseFrame> frameType = (Class<? extends BaseFrame>)lastType;
EventMethod dup = events.getOnFrame(frameType);
if (dup != null)
{
// Attempt to add duplicate frame type (a no-no)
StringBuilder err = new StringBuilder();
err.append("Duplicate Frame Type declaration on ");
err.append(method);
err.append(StringUtil.__LINE_SEPARATOR);
err.append("Type ").append(frameType.getSimpleName()).append(" previously declared at ");
err.append(dup.getMethod());
throw new InvalidWebSocketException(err.toString());
}
}
private void assertIsPublicNonStatic(Method method) private void assertIsPublicNonStatic(Method method)
{ {
int mods = method.getModifiers(); int mods = method.getModifiers();
@ -199,39 +208,7 @@ public class EventMethodsCache
if (!valid) if (!valid)
{ {
// Build big detailed exception to help the developer throw InvalidSignatureException.build(method,annoClass,validParams);
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());
} }
} }
@ -297,6 +274,12 @@ public class EventMethodsCache
return true; return true;
} }
private boolean isSignatureMatch(Method method, ParamList validbinaryparams2)
{
// TODO Auto-generated method stub
return false;
}
/** /**
* Register a pojo with the cache. * Register a pojo with the cache.
* *
@ -328,14 +311,30 @@ public class EventMethodsCache
continue; continue;
} }
if (method.getAnnotation(OnWebSocketBinary.class) != null) if (method.getAnnotation(OnWebSocketMessage.class) != null)
{ {
assertValidSignature(method,OnWebSocketBinary.class,validBinaryParams); if (isSignatureMatch(method,validTextParams))
assertUnset(events.onBinary,OnWebSocketBinary.class,method); {
// Text mode
// TODO
assertUnset(events.onText,OnWebSocketMessage.class,method);
events.onText = new EventMethod(pojo,method);
continue;
}
if (isSignatureMatch(method,validBinaryParams))
{
// Binary Mode
// TODO
assertUnset(events.onBinary,OnWebSocketMessage.class,method);
events.onBinary = new EventMethod(pojo,method); events.onBinary = new EventMethod(pojo,method);
continue; continue;
} }
throw InvalidSignatureException.build(method,OnWebSocketMessage.class,validTextParams,validBinaryParams);
}
if (method.getAnnotation(OnWebSocketClose.class) != null) if (method.getAnnotation(OnWebSocketClose.class) != null)
{ {
assertValidSignature(method,OnWebSocketClose.class,validCloseParams); assertValidSignature(method,OnWebSocketClose.class,validCloseParams);
@ -344,19 +343,11 @@ public class EventMethodsCache
continue; continue;
} }
if (method.getAnnotation(OnWebSocketText.class) != null)
{
assertValidSignature(method,OnWebSocketText.class,validTextParams);
assertUnset(events.onText,OnWebSocketText.class,method);
events.onText = new EventMethod(pojo,method);
continue;
}
if (method.getAnnotation(OnWebSocketFrame.class) != null) if (method.getAnnotation(OnWebSocketFrame.class) != null)
{ {
assertValidSignature(method,OnWebSocketFrame.class,validFrameParams); assertValidSignature(method,OnWebSocketFrame.class,validFrameParams);
assertFrameUnset(events,method); assertUnset(events.onFrame,OnWebSocketFrame.class,method);
events.addOnFrame(new EventMethod(pojo,method)); events.onFrame = new EventMethod(pojo,method);
continue; continue;
} }

View File

@ -0,0 +1,25 @@
package org.eclipse.jetty.websocket.api;
/**
* The immutable frame details.
*/
public interface Frame
{
public byte[] getMask();
public int getOpCode();
public byte[] getPayload();
public long getPayloadLength();
public boolean isFin();
public boolean isMasked();
public boolean isRsv1();
public boolean isRsv2();
public boolean isRsv3();
}

View File

@ -0,0 +1,100 @@
package org.eclipse.jetty.websocket.api;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.frames.BinaryFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.eclipse.jetty.websocket.generator.Generator;
import org.eclipse.jetty.websocket.io.RawConnection;
/**
* For working with the {@link WebSocketConnection} in a blocking technique.
* <p>
* This is an end-user accessible class.
*/
public class WebSocketBlockingConnection
{
private final RawConnection conn;
private final ByteBufferPool bufferPool;
private final WebSocketPolicy policy;
private final Generator generator;
public WebSocketBlockingConnection(WebSocketConnection conn)
{
if (conn instanceof RawConnection)
{
this.conn = (RawConnection)conn;
}
else
{
throw new IllegalArgumentException("Unsupported implementation of WebSocketConnection");
}
this.bufferPool = this.conn.getBufferPool();
this.policy = conn.getPolicy();
this.generator = new Generator(this.policy);
}
/**
* Send a binary message.
* <p>
* Basic usage, results in a blocking write.
*/
public void write(byte[] data, int offset, int length) throws IOException
{
BinaryFrame frame = new BinaryFrame(data,offset,length);
ByteBuffer buf = bufferPool.acquire(policy.getBufferSize(),false);
try
{
generator.generate(buf,frame);
FutureCallback<Void> blocking = new FutureCallback<>();
this.conn.writeRaw(null,blocking,buf);
blocking.get(); // block till finished
}
catch (InterruptedException e)
{
throw new IOException("Blocking write failed",e);
}
catch (ExecutionException e)
{
FutureCallback.rethrow(e);
}
finally
{
bufferPool.release(buf);
}
}
/**
* Send text message.
* <p>
* Basic usage, results in a blocking write.
*/
public void write(String message) throws IOException
{
TextFrame frame = new TextFrame(message);
ByteBuffer buf = bufferPool.acquire(policy.getBufferSize(),false);
try
{
generator.generate(buf,frame);
FutureCallback<Void> blocking = new FutureCallback<>();
this.conn.writeRaw(null,blocking,buf);
blocking.get(); // block till finished
}
catch (InterruptedException e)
{
throw new IOException("Blocking write failed",e);
}
catch (ExecutionException e)
{
FutureCallback.rethrow(e);
}
finally
{
bufferPool.release(buf);
}
}
}

View File

@ -1,7 +1,6 @@
package org.eclipse.jetty.websocket.api; package org.eclipse.jetty.websocket.api;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
@ -94,6 +93,13 @@ public class WebSocketEventDriver implements Parser.Listener
LOG.debug("{}.onFrame({})",websocket.getClass().getSimpleName(),frame); LOG.debug("{}.onFrame({})",websocket.getClass().getSimpleName(),frame);
} }
// Generic Read-Only Frame version
if ((frame instanceof Frame) && (events.onFrame != null))
{
events.onFrame.call(websocket,connection,frame);
// DO NOT return; - as this is just a read-only notification.
}
// Specified Close Case // Specified Close Case
if ((frame instanceof CloseFrame) && (events.onClose != null)) if ((frame instanceof CloseFrame) && (events.onClose != null))
{ {
@ -105,51 +111,41 @@ public class WebSocketEventDriver implements Parser.Listener
try try
{ {
// Specified Text Case // Specified Text Case
if ((frame instanceof TextFrame) && (events.onText != null)) if (frame instanceof TextFrame)
{
if (events.onText != null)
{ {
TextFrame text = (TextFrame)frame; TextFrame text = (TextFrame)frame;
events.onText.call(websocket,connection,text.getPayloadUTF8()); events.onText.call(websocket,connection,text.getPayloadUTF8());
return; return;
} }
// TODO
// if (events.onTextStream != null)
// {
// }
return;
}
// Specified Binary Case // Specified Binary Case
if ((frame instanceof BinaryFrame) && (events.onBinary != null)) if (frame instanceof BinaryFrame)
{
if (events.onBinary != null)
{ {
BinaryFrame bin = (BinaryFrame)frame; BinaryFrame bin = (BinaryFrame)frame;
if (events.onBinary.isParameterPresent(ByteBuffer.class))
{
// Byte buffer approach
events.onBinary.call(websocket,connection,bin.getPayload());
}
else
{
// Byte array approach // Byte array approach
byte buf[] = BufferUtil.toArray(bin.getPayload()); byte buf[] = BufferUtil.toArray(bin.getPayload());
events.onBinary.call(websocket,connection,buf,0,buf.length); events.onBinary.call(websocket,connection,buf,0,buf.length);
} }
return; // TODO
} // if (events.onBinaryStream != null)
// {
// }
// Basic Hierarchy Case
Class<? extends BaseFrame> frameType = frame.getClass();
while (true)
{
EventMethod event = events.getOnFrame(frameType);
if (event != null)
{
event.call(websocket,connection,frame);
return; return;
} }
if (!BaseFrame.class.isAssignableFrom(frameType.getSuperclass()))
{
// not assignable
return;
}
frameType = (Class<? extends BaseFrame>)frameType.getSuperclass();
}
} }
catch (Throwable t) catch (Throwable t)
{ {

View File

@ -1,4 +1,4 @@
package org.eclipse.jetty.websocket.io; package org.eclipse.jetty.websocket.api;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;

View File

@ -1,4 +1,4 @@
package org.eclipse.jetty.websocket.io; package org.eclipse.jetty.websocket.api;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;

View File

@ -0,0 +1,20 @@
package org.eclipse.jetty.websocket.io;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.Callback;
/**
* For advanced usage of connections.
*/
public interface RawConnection
{
ByteBufferPool getBufferPool();
Executor getExecutor();
<C> void writeRaw(C context, Callback<C> callback, ByteBuffer... buf) throws IOException;
}

View File

@ -31,7 +31,7 @@ import org.eclipse.jetty.websocket.parser.Parser;
/** /**
* Provides the implementation of {@link WebSocketConnection} within the framework of the new {@link AsyncConnection} framework of jetty-io * Provides the implementation of {@link WebSocketConnection} within the framework of the new {@link AsyncConnection} framework of jetty-io
*/ */
public class WebSocketAsyncConnection extends AbstractAsyncConnection implements WebSocketConnection public class WebSocketAsyncConnection extends AbstractAsyncConnection implements RawConnection, WebSocketConnection
{ {
private static final Logger LOG = Log.getLogger(WebSocketAsyncConnection.class); private static final Logger LOG = Log.getLogger(WebSocketAsyncConnection.class);
private static final ThreadLocal<WebSocketAsyncConnection> CURRENT_CONNECTION = new ThreadLocal<WebSocketAsyncConnection>(); private static final ThreadLocal<WebSocketAsyncConnection> CURRENT_CONNECTION = new ThreadLocal<WebSocketAsyncConnection>();
@ -90,11 +90,18 @@ public class WebSocketAsyncConnection extends AbstractAsyncConnection implements
} }
} }
@Override
public ByteBufferPool getBufferPool() public ByteBufferPool getBufferPool()
{ {
return bufferPool; return bufferPool;
} }
@Override
public Executor getExecutor()
{
return getExecutor();
}
/** /**
* Get the list of extensions in use. * Get the list of extensions in use.
* <p> * <p>
@ -281,4 +288,10 @@ public class WebSocketAsyncConnection extends AbstractAsyncConnection implements
} }
// TODO write(context,callback,frames); // TODO write(context,callback,frames);
} }
@Override
public <C> void writeRaw(C context, Callback<C> callback, ByteBuffer... buf) throws IOException
{
getEndPoint().write(context,callback,buf);
}
} }

View File

@ -1,48 +0,0 @@
package org.eclipse.jetty.websocket.io;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
public class WebSocketBlockingConnection
{
private WebSocketConnection conn;
public WebSocketBlockingConnection(WebSocketConnection conn)
{
this.conn = conn;
}
/**
* Send a binary message.
* <p>
* Basic usage, results in a blocking write.
*/
public void write(byte[] data, int offset, int length) throws IOException
{
}
/**
* Send a series of binary messages.
* <p>
* Note: each buffer results in its own binary message frame.
* <p>
* Basic usage, results in a series of blocking writes.
*/
public void write(ByteBuffer... buffers) throws IOException
{
}
/**
* Send text messages.
* <p>
* Basic usage, results in a series of blocking writes.
*/
public void write(String... messages) throws IOException
{
}
}

View File

@ -1,17 +1,18 @@
package org.eclipse.jetty.websocket.annotations; package org.eclipse.jetty.websocket.annotations;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Invalid Socket: Annotate a message interest on a method with a return type.
*/
@WebSocket @WebSocket
public class BadBinarySignatureSocket public class BadBinarySignatureSocket
{ {
/** /**
* Declaring a non-void return type * Declaring a non-void return type
*/ */
@OnWebSocketBinary @OnWebSocketMessage
public boolean onBinary(WebSocketConnection conn, ByteBuffer payload) public boolean onBinary(WebSocketConnection conn, byte buf[], int offset, int len)
{ {
return false; return false;
} }

View File

@ -1,14 +1,17 @@
package org.eclipse.jetty.websocket.annotations; package org.eclipse.jetty.websocket.annotations;
import java.nio.ByteBuffer; import java.io.InputStream;
/**
* Invalid Socket: Annotate 2 methods with interest in Binary Messages.
*/
@WebSocket @WebSocket
public class BadDuplicateBinarySocket public class BadDuplicateBinarySocket
{ {
/** /**
* First method * First method
*/ */
@OnWebSocketBinary @OnWebSocketMessage
public void binMe(byte[] payload, int offset, int len) public void binMe(byte[] payload, int offset, int len)
{ {
/* ignore */ /* ignore */
@ -17,8 +20,8 @@ public class BadDuplicateBinarySocket
/** /**
* Second method * Second method
*/ */
@OnWebSocketBinary @OnWebSocketMessage
public void binMe(ByteBuffer payload) public void streamMe(InputStream stream)
{ {
/* ignore */ /* ignore */
} }

View File

@ -2,13 +2,16 @@ package org.eclipse.jetty.websocket.annotations;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Invalid Socket: Annotate a message interest on a static method
*/
@WebSocket @WebSocket
public class BadTextSignatureSocket public class BadTextSignatureSocket
{ {
/** /**
* Declaring a static method * Declaring a static method
*/ */
@OnWebSocketText @OnWebSocketMessage
public static void onText(WebSocketConnection conn, String text) public static void onText(WebSocketConnection conn, String text)
{ {
/* do nothing */ /* do nothing */

View File

@ -2,8 +2,8 @@ package org.eclipse.jetty.websocket.annotations;
import java.io.IOException; import java.io.IOException;
import org.eclipse.jetty.websocket.api.WebSocketBlockingConnection;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.io.WebSocketBlockingConnection;
/** /**
* The most common websocket implementation. * The most common websocket implementation.
@ -34,7 +34,7 @@ public class MyEchoSocket
this.blocking = new WebSocketBlockingConnection(conn); this.blocking = new WebSocketBlockingConnection(conn);
} }
@OnWebSocketText @OnWebSocketMessage
public void onText(String message) public void onText(String message)
{ {
if (conn == null) if (conn == null)

View File

@ -16,7 +16,7 @@ import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket @WebSocket
public class MyStatelessEchoSocket public class MyStatelessEchoSocket
{ {
@OnWebSocketText @OnWebSocketMessage
public void onText(WebSocketConnection conn, String text) public void onText(WebSocketConnection conn, String text)
{ {
try try

View File

@ -15,8 +15,6 @@ import org.eclipse.jetty.websocket.annotations.NotASocket;
import org.eclipse.jetty.websocket.annotations.WebSocket; import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.samples.AdapterConnectCloseSocket; import org.eclipse.jetty.websocket.api.samples.AdapterConnectCloseSocket;
import org.eclipse.jetty.websocket.api.samples.ListenerBasicSocket; import org.eclipse.jetty.websocket.api.samples.ListenerBasicSocket;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -57,7 +55,8 @@ public class EventMethodsCacheTest
assertHasEventMethod(classId + ".onException",methods.onException); assertHasEventMethod(classId + ".onException",methods.onException);
assertHasEventMethod(classId + ".onText",methods.onText); assertHasEventMethod(classId + ".onText",methods.onText);
Assert.assertThat(".getOnFrames()",methods.getOnFrames().size(),is(0)); // Advanced, only available from @OnWebSocketFrame annotation
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
} }
/** /**
@ -158,8 +157,7 @@ public class EventMethodsCacheTest
assertHasEventMethod(classId + ".onConnect",methods.onConnect); assertHasEventMethod(classId + ".onConnect",methods.onConnect);
assertNoEventMethod(classId + ".onException",methods.onException); assertNoEventMethod(classId + ".onException",methods.onException);
assertHasEventMethod(classId + ".onText",methods.onText); assertHasEventMethod(classId + ".onText",methods.onText);
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
Assert.assertThat(".getOnFrames()",methods.getOnFrames().size(),is(0));
} }
/** /**
@ -171,15 +169,16 @@ public class EventMethodsCacheTest
EventMethodsCache cache = new EventMethodsCache(); EventMethodsCache cache = new EventMethodsCache();
EventMethods methods = cache.getMethods(MyEchoSocket.class); EventMethods methods = cache.getMethods(MyEchoSocket.class);
Assert.assertThat("EventMethods for MyEchoSocket",methods,notNullValue()); String classId = MyEchoSocket.class.getSimpleName();
assertNoEventMethod("MyEchoSocket.onBinary",methods.onBinary); Assert.assertThat("EventMethods for " + classId,methods,notNullValue());
assertHasEventMethod("MyEchoSocket.onClose",methods.onClose);
assertHasEventMethod("MyEchoSocket.onConnect",methods.onConnect);
assertNoEventMethod("MyEchoSocket.onException",methods.onException);
assertHasEventMethod("MyEchoSocket.onText",methods.onText);
Assert.assertThat("MyEchoSocket.getOnFrames()",methods.getOnFrames().size(),is(0)); assertNoEventMethod(classId + ".onBinary",methods.onBinary);
assertHasEventMethod(classId + ".onClose",methods.onClose);
assertHasEventMethod(classId + ".onConnect",methods.onConnect);
assertNoEventMethod(classId + ".onException",methods.onException);
assertHasEventMethod(classId + ".onText",methods.onText);
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
} }
/** /**
@ -191,15 +190,16 @@ public class EventMethodsCacheTest
EventMethodsCache cache = new EventMethodsCache(); EventMethodsCache cache = new EventMethodsCache();
EventMethods methods = cache.getMethods(MyStatelessEchoSocket.class); EventMethods methods = cache.getMethods(MyStatelessEchoSocket.class);
Assert.assertThat("EventMethods for MyStatelessEchoSocket",methods,notNullValue()); String classId = MyStatelessEchoSocket.class.getSimpleName();
assertNoEventMethod("MyStatelessEchoSocket.onBinary",methods.onBinary); Assert.assertThat("EventMethods for " + classId,methods,notNullValue());
assertNoEventMethod("MyStatelessEchoSocket.onClose",methods.onClose);
assertNoEventMethod("MyStatelessEchoSocket.onConnect",methods.onConnect);
assertNoEventMethod("MyStatelessEchoSocket.onException",methods.onException);
assertHasEventMethod("MyStatelessEchoSocket.onText",methods.onText);
Assert.assertThat("MyEchoSocket.getOnFrames()",methods.getOnFrames().size(),is(0)); assertNoEventMethod(classId + ".onBinary",methods.onBinary);
assertNoEventMethod(classId + ".onClose",methods.onClose);
assertNoEventMethod(classId + ".onConnect",methods.onConnect);
assertNoEventMethod(classId + ".onException",methods.onException);
assertHasEventMethod(classId + ".onText",methods.onText);
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
} }
/** /**
@ -211,19 +211,20 @@ public class EventMethodsCacheTest
EventMethodsCache cache = new EventMethodsCache(); EventMethodsCache cache = new EventMethodsCache();
EventMethods methods = cache.getMethods(NoopSocket.class); EventMethods methods = cache.getMethods(NoopSocket.class);
Assert.assertThat("Methods for NoopSocket",methods,notNullValue()); String classId = NoopSocket.class.getSimpleName();
assertNoEventMethod("NoopSocket.onBinary",methods.onBinary); Assert.assertThat("Methods for " + classId,methods,notNullValue());
assertNoEventMethod("NoopSocket.onClose",methods.onClose);
assertNoEventMethod("NoopSocket.onConnect",methods.onConnect);
assertNoEventMethod("NoopSocket.onException",methods.onException);
assertNoEventMethod("NoopSocket.onText",methods.onText);
Assert.assertThat("MyEchoSocket.getOnFrames()",methods.getOnFrames().size(),is(0)); assertNoEventMethod(classId + ".onBinary",methods.onBinary);
assertNoEventMethod(classId + ".onClose",methods.onClose);
assertNoEventMethod(classId + ".onConnect",methods.onConnect);
assertNoEventMethod(classId + ".onException",methods.onException);
assertNoEventMethod(classId + ".onText",methods.onText);
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
} }
/** /**
* Test Case for no exceptions and 3 methods * Test Case for no exceptions and 1 methods
*/ */
@Test @Test
public void testAnnotatedOnFrame() public void testAnnotatedOnFrame()
@ -231,17 +232,16 @@ public class EventMethodsCacheTest
EventMethodsCache cache = new EventMethodsCache(); EventMethodsCache cache = new EventMethodsCache();
EventMethods methods = cache.getMethods(FrameSocket.class); EventMethods methods = cache.getMethods(FrameSocket.class);
Assert.assertThat("EventMethods for MyEchoSocket",methods,notNullValue()); String classId = FrameSocket.class.getSimpleName();
assertNoEventMethod("MyEchoSocket.onBinary",methods.onBinary); Assert.assertThat("EventMethods for " + classId,methods,notNullValue());
assertNoEventMethod("MyEchoSocket.onClose",methods.onClose);
assertNoEventMethod("MyEchoSocket.onConnect",methods.onConnect);
assertNoEventMethod("MyEchoSocket.onException",methods.onException);
assertNoEventMethod("MyEchoSocket.onText",methods.onText);
Assert.assertThat("MyEchoSocket.getOnFrames()",methods.getOnFrames().size(),is(2)); assertNoEventMethod(classId + ".onBinary",methods.onBinary);
assertHasEventMethod("MyEchoSocket.onFrame(BaseFrame)",methods.getOnFrame(BaseFrame.class)); assertNoEventMethod(classId + ".onClose",methods.onClose);
assertHasEventMethod("MyEchoSocket.onFrame(BaseFrame)",methods.getOnFrame(TextFrame.class)); assertNoEventMethod(classId + ".onConnect",methods.onConnect);
assertNoEventMethod(classId + ".onException",methods.onException);
assertNoEventMethod(classId + ".onText",methods.onText);
assertHasEventMethod(classId + ".onFrame",methods.onFrame);
} }
/** /**
@ -281,7 +281,6 @@ public class EventMethodsCacheTest
assertHasEventMethod(classId + ".onBinary",methods.onBinary); assertHasEventMethod(classId + ".onBinary",methods.onBinary);
assertHasEventMethod(classId + ".onException",methods.onException); assertHasEventMethod(classId + ".onException",methods.onException);
assertHasEventMethod(classId + ".onText",methods.onText); assertHasEventMethod(classId + ".onText",methods.onText);
assertNoEventMethod(classId + ".onFrame",methods.onFrame);
Assert.assertThat(".getOnFrames()",methods.getOnFrames().size(),is(0));
} }
} }

View File

@ -2,8 +2,8 @@ package org.eclipse.jetty.websocket.api;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.samples.AdapterConnectCloseSocket; import org.eclipse.jetty.websocket.api.samples.AdapterConnectCloseSocket;
import org.eclipse.jetty.websocket.api.samples.AnnotatedByteArraySocket; import org.eclipse.jetty.websocket.api.samples.AnnotatedBinaryArraySocket;
import org.eclipse.jetty.websocket.api.samples.AnnotatedByteBufferSocket; import org.eclipse.jetty.websocket.api.samples.AnnotatedBinaryStreamSocket;
import org.eclipse.jetty.websocket.api.samples.AnnotatedFramesSocket; import org.eclipse.jetty.websocket.api.samples.AnnotatedFramesSocket;
import org.eclipse.jetty.websocket.api.samples.ListenerBasicSocket; import org.eclipse.jetty.websocket.api.samples.ListenerBasicSocket;
import org.eclipse.jetty.websocket.frames.BinaryFrame; import org.eclipse.jetty.websocket.frames.BinaryFrame;
@ -46,7 +46,7 @@ public class WebSocketEventDriverTest
@Test @Test
public void testAnnotated_ByteArray() public void testAnnotated_ByteArray()
{ {
AnnotatedByteArraySocket socket = new AnnotatedByteArraySocket(); AnnotatedBinaryArraySocket socket = new AnnotatedBinaryArraySocket();
WebSocketEventDriver driver = newDriver(socket); WebSocketEventDriver driver = newDriver(socket);
LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); LocalWebSocketConnection conn = new LocalWebSocketConnection(testname);
@ -64,7 +64,7 @@ public class WebSocketEventDriverTest
@Test @Test
public void testAnnotated_ByteBuffer() public void testAnnotated_ByteBuffer()
{ {
AnnotatedByteBufferSocket socket = new AnnotatedByteBufferSocket(); AnnotatedBinaryStreamSocket socket = new AnnotatedBinaryStreamSocket();
WebSocketEventDriver driver = newDriver(socket); WebSocketEventDriver driver = newDriver(socket);
LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); LocalWebSocketConnection conn = new LocalWebSocketConnection(testname);

View File

@ -0,0 +1,33 @@
package org.eclipse.jetty.websocket.api.samples;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.EventCapture;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket
public class AnnotatedBinaryArraySocket
{
public EventCapture capture = new EventCapture();
@OnWebSocketMessage
public void onBinary(byte payload[], int offset, int length)
{
capture.add("onBinary([%d],%d,%d)",payload.length,offset,length);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
capture.add("onClose(%d, %s)",statusCode,capture.q(reason));
}
@OnWebSocketConnect
public void onConnect(WebSocketConnection conn)
{
capture.add("onConnect(%s)", conn);
}
}

View File

@ -0,0 +1,35 @@
package org.eclipse.jetty.websocket.api.samples;
import java.io.InputStream;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.EventCapture;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket
public class AnnotatedBinaryStreamSocket
{
public EventCapture capture = new EventCapture();
@OnWebSocketMessage
public void onBinary(InputStream stream)
{
capture.add("onBinary(%s)",stream);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
capture.add("onClose(%d, %s)",statusCode,capture.q(reason));
}
@OnWebSocketConnect
public void onConnect(WebSocketConnection conn)
{
capture.add("onConnect(%s)", conn);
}
}

View File

@ -1,18 +1,18 @@
package org.eclipse.jetty.websocket.api.samples; package org.eclipse.jetty.websocket.api.samples;
import org.eclipse.jetty.websocket.annotations.OnWebSocketBinary;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket; import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.EventCapture; import org.eclipse.jetty.websocket.api.EventCapture;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket @WebSocket
public class AnnotatedByteArraySocket public class AnnotatedTextSocket
{ {
public EventCapture capture = new EventCapture(); public EventCapture capture = new EventCapture();
@OnWebSocketBinary @OnWebSocketMessage
public void onBinary(byte payload[], int offset, int length) public void onBinary(byte payload[], int offset, int length)
{ {
capture.add("onBinary([%d],%d,%d)",payload.length,offset,length); capture.add("onBinary([%d],%d,%d)",payload.length,offset,length);

View File

@ -1,25 +1,19 @@
package org.eclipse.jetty.websocket.api.samples; package org.eclipse.jetty.websocket.api.samples;
import java.nio.ByteBuffer; import java.io.Reader;
import org.eclipse.jetty.websocket.annotations.OnWebSocketBinary;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.annotations.WebSocket; import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.EventCapture; import org.eclipse.jetty.websocket.api.EventCapture;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket @WebSocket
public class AnnotatedByteBufferSocket public class AnnotatedTextStreamSocket
{ {
public EventCapture capture = new EventCapture(); public EventCapture capture = new EventCapture();
@OnWebSocketBinary
public void onBinary(ByteBuffer payload)
{
capture.add("onBinary(%s)",payload);
}
@OnWebSocketClose @OnWebSocketClose
public void onClose(int statusCode, String reason) public void onClose(int statusCode, String reason)
{ {
@ -32,4 +26,9 @@ public class AnnotatedByteBufferSocket
capture.add("onConnect(%s)", conn); capture.add("onConnect(%s)", conn);
} }
@OnWebSocketMessage
public void onText(Reader reader)
{
capture.add("onText(%s)",reader);
}
} }