JSR-356 first working annotated @ClientEndpoint echo test

This commit is contained in:
Joakim Erdfelt 2013-03-28 10:51:45 -07:00
parent 52d97d8a06
commit 3a66b3ec3f
28 changed files with 730 additions and 242 deletions

View File

@ -38,10 +38,13 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory; import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.jsr356.endpoints.ConfiguredEndpoint; import org.eclipse.jetty.websocket.jsr356.endpoints.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrClientEndpointImpl; import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointImpl;
/**
* Main WebSocketContainer for working with client based WebSocket Endpoints.
*/
public class JettyWebSocketContainer implements WebSocketContainer public class JettyWebSocketContainer implements WebSocketContainer
{ {
private static final Logger LOG = Log.getLogger(JettyWebSocketContainer.class); private static final Logger LOG = Log.getLogger(JettyWebSocketContainer.class);
@ -51,8 +54,7 @@ public class JettyWebSocketContainer implements WebSocketContainer
public JettyWebSocketContainer() public JettyWebSocketContainer()
{ {
client = new WebSocketClient(); client = new WebSocketClient();
client.getEventDriverFactory().addImplementation(new JsrEndpointImpl(this)); client.setEventDriverFactory(new JsrEventDriverFactory(client.getPolicy(),this));
client.getEventDriverFactory().addImplementation(new JsrClientEndpointImpl(this));
try try
{ {
@ -83,7 +85,7 @@ public class JettyWebSocketContainer implements WebSocketContainer
Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(endpoint,path,req); Future<org.eclipse.jetty.websocket.api.Session> futSess = client.connect(endpoint,path,req);
try try
{ {
org.eclipse.jetty.websocket.api.Session sess = futSess.get(); WebSocketSession sess = (WebSocketSession)futSess.get();
return new JsrSession(this,sess,getNextId()); return new JsrSession(this,sess,getNextId());
} }
catch (InterruptedException | ExecutionException e) catch (InterruptedException | ExecutionException e)
@ -175,6 +177,12 @@ public class JettyWebSocketContainer implements WebSocketContainer
return String.format("websocket-%d",idgen.incrementAndGet()); return String.format("websocket-%d",idgen.incrementAndGet());
} }
public Set<Session> getOpenSessions()
{
// TODO Auto-generated method stub
return null;
}
@Override @Override
public void setAsyncSendTimeout(long timeoutmillis) public void setAsyncSendTimeout(long timeoutmillis)
{ {

View File

@ -26,13 +26,20 @@ import java.nio.ByteBuffer;
import javax.websocket.EncodeException; import javax.websocket.EncodeException;
import javax.websocket.RemoteEndpoint; import javax.websocket.RemoteEndpoint;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.message.MessageOutputStream;
import org.eclipse.jetty.websocket.common.message.MessageWriter;
public class JsrBasicRemote implements RemoteEndpoint.Basic public class JsrBasicRemote implements RemoteEndpoint.Basic
{ {
private final WebSocketSession jettySession;
private final org.eclipse.jetty.websocket.api.RemoteEndpoint jettyRemote; private final org.eclipse.jetty.websocket.api.RemoteEndpoint jettyRemote;
private boolean batchingAllowed = false;
protected JsrBasicRemote(org.eclipse.jetty.websocket.api.RemoteEndpoint endpoint) protected JsrBasicRemote(WebSocketSession session)
{ {
this.jettyRemote = endpoint; this.jettySession = session;
this.jettyRemote = jettySession.getRemote();
} }
@Override @Override
@ -44,29 +51,25 @@ public class JsrBasicRemote implements RemoteEndpoint.Basic
@Override @Override
public boolean getBatchingAllowed() public boolean getBatchingAllowed()
{ {
// TODO Auto-generated method stub return batchingAllowed;
return false;
} }
@Override @Override
public OutputStream getSendStream() throws IOException public OutputStream getSendStream() throws IOException
{ {
// TODO Auto-generated method stub return new MessageOutputStream(jettySession);
return null;
} }
@Override @Override
public Writer getSendWriter() throws IOException public Writer getSendWriter() throws IOException
{ {
// TODO Auto-generated method stub return new MessageWriter(jettySession);
return null;
} }
@Override @Override
public void sendBinary(ByteBuffer data) throws IOException public void sendBinary(ByteBuffer data) throws IOException
{ {
// TODO Auto-generated method stub jettyRemote.sendBytes(data);
} }
@Override @Override
@ -78,8 +81,7 @@ public class JsrBasicRemote implements RemoteEndpoint.Basic
@Override @Override
public void sendObject(Object o) throws IOException, EncodeException public void sendObject(Object o) throws IOException, EncodeException
{ {
// TODO Auto-generated method stub // TODO Find appropriate Encoder and encode for output
} }
@Override @Override
@ -97,8 +99,7 @@ public class JsrBasicRemote implements RemoteEndpoint.Basic
@Override @Override
public void sendText(String text) throws IOException public void sendText(String text) throws IOException
{ {
// TODO Auto-generated method stub jettyRemote.sendString(text);
} }
@Override @Override
@ -110,7 +111,6 @@ public class JsrBasicRemote implements RemoteEndpoint.Basic
@Override @Override
public void setBatchingAllowed(boolean allowed) public void setBatchingAllowed(boolean allowed)
{ {
// TODO Auto-generated method stub this.batchingAllowed = allowed;
} }
} }

View File

@ -22,6 +22,8 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.Principal; import java.security.Principal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -35,19 +37,23 @@ import javax.websocket.Session;
import javax.websocket.WebSocketContainer; import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.common.WebSocketSession;
public class JsrSession implements Session public class JsrSession implements Session
{ {
private final JettyWebSocketContainer container; private final JettyWebSocketContainer container;
/** Jetty API Session Impl */ /** Jetty API Session Impl */
private final org.eclipse.jetty.websocket.api.Session jettySession; private final WebSocketSession jettySession;
private final String id; private final String id;
private List<Extension> negotiatedExtensions; private List<Extension> negotiatedExtensions;
private Map<String, List<String>> jsrParameterMap; private Map<String, List<String>> jsrParameterMap;
private Map<String, String> pathParameters = new HashMap<>();
private Map<String, Object> userProperties;
private Set<MessageHandler> messageHandlers;
private JsrAsyncRemote asyncRemote; private JsrAsyncRemote asyncRemote;
private JsrBasicRemote basicRemote; private JsrBasicRemote basicRemote;
public JsrSession(JettyWebSocketContainer container, org.eclipse.jetty.websocket.api.Session session, String id) public JsrSession(JettyWebSocketContainer container, WebSocketSession session, String id)
{ {
this.container = container; this.container = container;
this.jettySession = session; this.jettySession = session;
@ -57,7 +63,7 @@ public class JsrSession implements Session
@Override @Override
public void addMessageHandler(MessageHandler listener) throws IllegalStateException public void addMessageHandler(MessageHandler listener) throws IllegalStateException
{ {
// TODO Auto-generated method stub messageHandlers.add(listener);
} }
@Override @Override
@ -87,7 +93,7 @@ public class JsrSession implements Session
{ {
if (basicRemote == null) if (basicRemote == null)
{ {
basicRemote = new JsrBasicRemote(jettySession.getRemote()); basicRemote = new JsrBasicRemote(jettySession);
} }
return basicRemote; return basicRemote;
} }
@ -113,8 +119,7 @@ public class JsrSession implements Session
@Override @Override
public long getMaxIdleTimeout() public long getMaxIdleTimeout()
{ {
// TODO Auto-generated method stub return jettySession.getPolicy().getIdleTimeout();
return 0;
} }
@Override @Override
@ -126,8 +131,7 @@ public class JsrSession implements Session
@Override @Override
public Set<MessageHandler> getMessageHandlers() public Set<MessageHandler> getMessageHandlers()
{ {
// TODO Auto-generated method stub return messageHandlers;
return null;
} }
@Override @Override
@ -153,15 +157,13 @@ public class JsrSession implements Session
@Override @Override
public Set<Session> getOpenSessions() public Set<Session> getOpenSessions()
{ {
// TODO Auto-generated method stub return container.getOpenSessions();
return null;
} }
@Override @Override
public Map<String, String> getPathParameters() public Map<String, String> getPathParameters()
{ {
// TODO Auto-generated method stub return Collections.unmodifiableMap(pathParameters);
return null;
} }
@Override @Override
@ -188,11 +190,6 @@ public class JsrSession implements Session
return jettySession.getUpgradeRequest().getRequestURI(); return jettySession.getUpgradeRequest().getRequestURI();
} }
public long getTimeout()
{
return jettySession.getIdleTimeout();
}
@Override @Override
public Principal getUserPrincipal() public Principal getUserPrincipal()
{ {
@ -203,8 +200,7 @@ public class JsrSession implements Session
@Override @Override
public Map<String, Object> getUserProperties() public Map<String, Object> getUserProperties()
{ {
// TODO Auto-generated method stub return userProperties;
return null;
} }
@Override @Override
@ -222,33 +218,24 @@ public class JsrSession implements Session
@Override @Override
public void removeMessageHandler(MessageHandler handler) public void removeMessageHandler(MessageHandler handler)
{ {
// TODO Auto-generated method stub messageHandlers.remove(handler);
} }
@Override @Override
public void setMaxBinaryMessageBufferSize(int length) public void setMaxBinaryMessageBufferSize(int length)
{ {
// TODO Auto-generated method stub jettySession.getPolicy().setMaxBinaryMessageBufferSize(length);
} }
@Override @Override
public void setMaxIdleTimeout(long milliseconds) public void setMaxIdleTimeout(long milliseconds)
{ {
// TODO Auto-generated method stub jettySession.getPolicy().setIdleTimeout(milliseconds);
} }
@Override @Override
public void setMaxTextMessageBufferSize(int length) public void setMaxTextMessageBufferSize(int length)
{ {
// TODO Auto-generated method stub jettySession.getPolicy().setMaxTextMessageBufferSize(length);
}
public void setTimeout(long milliseconds)
{
jettySession.setIdleTimeout(milliseconds);
} }
} }

View File

@ -125,25 +125,34 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
{ {
assertIsPublicNonStatic(method); assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE); assertIsReturn(method,Void.TYPE);
// ParameterizedMethod msgMethod = establishCallable(metadata,pojo,method,paramsOnMessage,OnMessage.class); OnMessageCallable onmessage = new OnMessageCallable(pojo,method);
// switch (msgMethod.getMessageType()) visitMethod(onmessage,pojo,method,paramsOnMessage,OnMessage.class);
// {
// case TEXT: Param param = onmessage.getMessageObjectParam();
// metadata.onText = msgMethod; switch (param.role)
// break; {
// case BINARY: case MESSAGE_BINARY:
// metadata.onBinary = msgMethod; metadata.onBinary = new OnMessageBinaryCallable(onmessage);
// break; break;
// case PONG: case MESSAGE_BINARY_STREAM:
// metadata.onPong = msgMethod; metadata.onBinaryStream = new OnMessageBinaryStreamCallable(onmessage);
// break; break;
// default: case MESSAGE_TEXT:
// StringBuilder err = new StringBuilder(); metadata.onText = new OnMessageTextCallable(onmessage);
// err.append("Invalid @OnMessage method signature,"); break;
// err.append(" Missing type TEXT, BINARY, or PONG parameter: "); case MESSAGE_TEXT_STREAM:
// err.append(msgMethod.getFullyQualifiedMethodName()); metadata.onTextStream = new OnMessageTextStreamCallable(onmessage);
// throw new InvalidSignatureException(err.toString()); break;
// } case MESSAGE_PONG:
metadata.onPong = new OnMessagePongCallable(onmessage);
break;
default:
StringBuilder err = new StringBuilder();
err.append("An unrecognized message type <");
err.append(param.type);
err.append(">: does not meet specified type categories of [TEXT, BINARY, DECODER, or PONG]");
throw new InvalidSignatureException(err.toString());
}
} }
} }
@ -181,6 +190,7 @@ public class AnnotatedEndpointScanner extends AbstractMethodAnnotationScanner<Js
if (paramId.process(param,callable)) if (paramId.process(param,callable))
{ {
// Successfully identified // Successfully identified
LOG.debug("Identified: {}",param);
return true; return true;
} }
} }

View File

@ -34,7 +34,7 @@ public abstract class JsrCallable extends CallableMethod
protected final Object[] args; protected final Object[] args;
protected int idxSession = -1; protected int idxSession = -1;
// Optional decoder (used for OnMessage) // Optional decoder (used for OnMessage)
private Decoder decoder; protected Decoder decoder;
public JsrCallable(Class<?> pojo, Method method) public JsrCallable(Class<?> pojo, Method method)
{ {
@ -51,12 +51,16 @@ public abstract class JsrCallable extends CallableMethod
args = new Object[len]; args = new Object[len];
} }
protected void copyTo(JsrCallable copy) /**
* Copy Constructor
*/
public JsrCallable(JsrCallable copy)
{ {
copy.decoder = this.decoder; this(copy.getPojo(),copy.getMethod());
copy.idxSession = this.idxSession; this.decoder = copy.decoder;
System.arraycopy(this.params,0,copy.params,0,params.length); this.idxSession = copy.idxSession;
System.arraycopy(this.args,0,copy.args,0,args.length); System.arraycopy(copy.params,0,this.params,0,params.length);
System.arraycopy(copy.args,0,this.args,0,args.length);
} }
/** /**
@ -105,7 +109,7 @@ public abstract class JsrCallable extends CallableMethod
return params; return params;
} }
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
// Default the session. // Default the session.
// Session is an optional parameter (always) // Session is an optional parameter (always)
@ -117,7 +121,8 @@ public abstract class JsrCallable extends CallableMethod
// Default the path parameters // Default the path parameters
// PathParam's are optional parameters (always) // PathParam's are optional parameters (always)
if (pathParams != null) Map<String, String> pathParams = session.getPathParameters();
if ((pathParams != null) && (pathParams.size() > 0))
{ {
for (Param param : params) for (Param param : params)
{ {

View File

@ -0,0 +1,235 @@
//
// ========================================================================
// 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.annotations;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.util.Map;
import javax.websocket.DecodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.common.CloseInfo;
/**
* The live event methods found for a specific Annotated Endpoint
*/
public class JsrEvents
{
private final JsrMetadata<?> metadata;
/**
* Callable for &#064;{@link OnOpen} annotation.
*/
private final OnOpenCallable onOpen;
/**
* Callable for &#064;{@link OnClose} annotation
*/
private final OnCloseCallable onClose;
/**
* Callable for &#064;{@link OnError} annotation
*/
private final OnErrorCallable onError;
/**
* Callable for &#064;{@link OnMessage} annotation dealing with Text Message Format
*/
private final OnMessageTextCallable onText;
/**
* Callable for &#064;{@link OnMessage} annotation dealing with Text Streaming Message Format
*/
private final OnMessageTextStreamCallable onTextStream;
/**
* Callable for &#064;{@link OnMessage} annotation dealing with Binary Message Format
*/
private final OnMessageBinaryCallable onBinary;
/**
* Callable for &#064;{@link OnMessage} annotation dealing with Binary Streaming Message Format
*/
private final OnMessageBinaryStreamCallable onBinaryStream;
/**
* Callable for &#064;{@link OnMessage} annotation dealing with Pong Message Format
*/
private OnMessagePongCallable onPong;
public JsrEvents(JsrMetadata<?> metadata)
{
this.metadata = metadata;
this.onOpen = (metadata.onOpen == null)?null:new OnOpenCallable(metadata.onOpen);
this.onClose = (metadata.onClose == null)?null:new OnCloseCallable(metadata.onClose);
this.onError = (metadata.onError == null)?null:new OnErrorCallable(metadata.onError);
this.onBinary = (metadata.onBinary == null)?null:new OnMessageBinaryCallable(metadata.onBinary);
this.onBinaryStream = (metadata.onBinaryStream == null)?null:new OnMessageBinaryStreamCallable(metadata.onBinaryStream);
this.onText = (metadata.onText == null)?null:new OnMessageTextCallable(metadata.onText);
this.onTextStream = (metadata.onTextStream == null)?null:new OnMessageTextStreamCallable(metadata.onTextStream);
this.onPong = (metadata.onPong == null)?null:new OnMessagePongCallable(metadata.onPong);
}
public void callBinary(Object websocket, ByteBuffer buf, boolean fin) throws DecodeException
{
if (onBinary == null)
{
return;
}
onBinary.call(websocket,buf,fin);
}
public void callBinaryStream(Object websocket, InputStream stream) throws DecodeException, IOException
{
if (onBinaryStream == null)
{
return;
}
onBinaryStream.call(websocket,stream);
}
public void callClose(Object websocket, CloseInfo close)
{
if (onClose == null)
{
return;
}
onClose.call(websocket,close);
}
public void callError(Object websocket, Throwable cause)
{
if (onError == null)
{
return;
}
onError.call(websocket,cause);
}
public void callOpen(Object websocket, EndpointConfig config)
{
if (onOpen == null)
{
return;
}
onOpen.call(websocket,config);
}
public void callText(Object websocket, String text, boolean fin) throws DecodeException
{
if (onText == null)
{
return;
}
onText.call(websocket,text,fin);
}
public void callTextStream(Object websocket, Reader reader) throws DecodeException, IOException
{
if (onTextStream == null)
{
return;
}
onTextStream.call(websocket,reader);
}
public boolean hasBinary()
{
return (onBinary != null);
}
public boolean hasBinaryStream()
{
return (onBinaryStream != null);
}
public boolean hasText()
{
return (onText != null);
}
public boolean hasTextStream()
{
return (onTextStream != null);
}
public void init(Session session)
{
Map<String, String> pathParams = session.getPathParameters();
if (onOpen != null)
{
onOpen.init(session);
}
if (onClose != null)
{
onClose.init(session);
}
if (onError != null)
{
onError.init(session);
}
if (onText != null)
{
onText.init(session);
}
if (onTextStream != null)
{
onTextStream.init(session);
}
if (onBinary != null)
{
onBinary.init(session);
}
if (onBinaryStream != null)
{
onBinaryStream.init(session);
}
if (onPong != null)
{
onPong.init(session);
}
}
public boolean isBinaryPartialSupported()
{
if (onBinary == null)
{
return false;
}
return onBinary.isPartialMessageSupported();
}
public boolean isTextPartialSupported()
{
if (onText == null)
{
return false;
}
return onText.isPartialMessageSupported();
}
}

View File

@ -29,6 +29,12 @@ import javax.websocket.OnOpen;
import org.eclipse.jetty.websocket.jsr356.decoders.Decoders; import org.eclipse.jetty.websocket.jsr356.decoders.Decoders;
import org.eclipse.jetty.websocket.jsr356.encoders.Encoders; import org.eclipse.jetty.websocket.jsr356.encoders.Encoders;
/**
* Static reference to a specific annotated classes metadata.
*
* @param <T>
* the annotation this metadata is based off of
*/
public abstract class JsrMetadata<T extends Annotation> public abstract class JsrMetadata<T extends Annotation>
{ {
/** /**

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes; import javax.websocket.CloseReason.CloseCodes;
@ -41,6 +40,12 @@ public class OnCloseCallable extends JsrCallable
super(pojo,method); super(pojo,method);
} }
public OnCloseCallable(OnCloseCallable copy)
{
super(copy);
this.idxCloseReason = copy.idxCloseReason;
}
public void call(Object endpoint, CloseInfo close) public void call(Object endpoint, CloseInfo close)
{ {
this.call(endpoint,close.getStatusCode(),close.getReason()); this.call(endpoint,close.getStatusCode(),close.getReason());
@ -58,18 +63,10 @@ public class OnCloseCallable extends JsrCallable
super.call(endpoint,super.args); super.call(endpoint,super.args);
} }
public OnCloseCallable copy()
{
OnCloseCallable copy = new OnCloseCallable(pojo,method);
super.copyTo(copy);
copy.idxCloseReason = this.idxCloseReason;
return copy;
}
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxCloseReason = findIndexForRole(Role.CLOSE_REASON); idxCloseReason = findIndexForRole(Role.CLOSE_REASON);
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.OnError; import javax.websocket.OnError;
import javax.websocket.Session; import javax.websocket.Session;
@ -36,6 +35,12 @@ public class OnErrorCallable extends JsrCallable
super(pojo,method); super(pojo,method);
} }
public OnErrorCallable(OnErrorCallable copy)
{
super(copy);
this.idxThrowable = copy.idxThrowable;
}
public void call(Object endpoint, Throwable cause) public void call(Object endpoint, Throwable cause)
{ {
// Throwable is a mandatory parameter // Throwable is a mandatory parameter
@ -43,18 +48,10 @@ public class OnErrorCallable extends JsrCallable
super.call(endpoint,super.args); super.call(endpoint,super.args);
} }
public OnErrorCallable copy()
{
OnErrorCallable copy = new OnErrorCallable(pojo,method);
super.copyTo(copy);
copy.idxThrowable = this.idxThrowable;
return copy;
}
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxThrowable = findIndexForRole(Param.Role.ERROR_CAUSE); idxThrowable = findIndexForRole(Param.Role.ERROR_CAUSE);
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.Decoder; import javax.websocket.Decoder;
@ -45,6 +44,14 @@ public class OnMessageBinaryCallable extends OnMessageCallable
super(pojo,method); super(pojo,method);
} }
/**
* Copy Constructor
*/
public OnMessageBinaryCallable(OnMessageCallable copy)
{
super(copy);
}
public void call(Object endpoint, ByteBuffer buf, boolean partialFlag) throws DecodeException public void call(Object endpoint, ByteBuffer buf, boolean partialFlag) throws DecodeException
{ {
super.args[idxMessageObject] = binaryDecoder.decode(buf); super.args[idxMessageObject] = binaryDecoder.decode(buf);
@ -56,12 +63,12 @@ public class OnMessageBinaryCallable extends OnMessageCallable
} }
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY); idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY);
assertRoleRequired(idxMessageObject,"Binary Message Object"); assertRoleRequired(idxMessageObject,"Binary Message Object");
assertDecoderRequired(); assertDecoderRequired();
binaryDecoder = (Decoder.Binary<?>)getDecoder(); binaryDecoder = (Decoder.Binary<?>)getDecoder();
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.Decoder; import javax.websocket.Decoder;
@ -44,6 +43,14 @@ public class OnMessageBinaryStreamCallable extends OnMessageCallable
super(pojo,method); super(pojo,method);
} }
/**
* Copy Constructor
*/
public OnMessageBinaryStreamCallable(OnMessageCallable copy)
{
super(copy);
}
public void call(Object endpoint, InputStream stream) throws DecodeException, IOException public void call(Object endpoint, InputStream stream) throws DecodeException, IOException
{ {
super.args[idxMessageObject] = binaryDecoder.decode(stream); super.args[idxMessageObject] = binaryDecoder.decode(stream);
@ -51,12 +58,12 @@ public class OnMessageBinaryStreamCallable extends OnMessageCallable
} }
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY_STREAM); idxMessageObject = findIndexForRole(Role.MESSAGE_BINARY_STREAM);
assertRoleRequired(idxMessageObject,"Binary InputStream Message Object"); assertRoleRequired(idxMessageObject,"Binary InputStream Message Object");
assertDecoderRequired(); assertDecoderRequired();
binaryDecoder = (Decoder.BinaryStream<?>)getDecoder(); binaryDecoder = (Decoder.BinaryStream<?>)getDecoder();
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -25,7 +25,7 @@ import javax.websocket.Decoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException; import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils; import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
public abstract class OnMessageCallable extends JsrCallable public class OnMessageCallable extends JsrCallable
{ {
protected int idxPartialMessageFlag = -1; protected int idxPartialMessageFlag = -1;
protected int idxMessageObject = -1; protected int idxMessageObject = -1;
@ -36,6 +36,14 @@ public abstract class OnMessageCallable extends JsrCallable
super(pojo,method); super(pojo,method);
} }
public OnMessageCallable(OnMessageCallable copy)
{
super(copy);
this.idxPartialMessageFlag = copy.idxPartialMessageFlag;
this.idxMessageObject = copy.idxMessageObject;
this.messageRole = copy.messageRole;
}
protected void assertDecoderRequired() protected void assertDecoderRequired()
{ {
if (getDecoder() == null) if (getDecoder() == null)
@ -64,6 +72,39 @@ public abstract class OnMessageCallable extends JsrCallable
} }
} }
private int findMessageObjectIndex()
{
int index = -1;
for (Param.Role role : Param.Role.getMessageRoles())
{
index = findIndexForRole(role);
if (index >= 0)
{
return index;
}
}
return -1;
}
public Param getMessageObjectParam()
{
if (idxMessageObject < 0)
{
idxMessageObject = findMessageObjectIndex();
if (idxMessageObject < 0)
{
StringBuilder err = new StringBuilder();
err.append("A message type must be specified [TEXT, BINARY, DECODER, or PONG]");
throw new InvalidSignatureException(err.toString());
}
}
return super.params[idxMessageObject];
}
public boolean isPartialMessageSupported() public boolean isPartialMessageSupported()
{ {
return (idxPartialMessageFlag >= 0); return (idxPartialMessageFlag >= 0);

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.OnMessage; import javax.websocket.OnMessage;
@ -40,6 +39,14 @@ public class OnMessagePongCallable extends OnMessageCallable
super(pojo,method); super(pojo,method);
} }
/**
* Copy Constructor
*/
public OnMessagePongCallable(OnMessageCallable copy)
{
super(copy);
}
public void call(Object endpoint, ByteBuffer buf) throws DecodeException public void call(Object endpoint, ByteBuffer buf) throws DecodeException
{ {
super.args[idxMessageObject] = new JsrPongMessage(buf); super.args[idxMessageObject] = new JsrPongMessage(buf);
@ -47,10 +54,10 @@ public class OnMessagePongCallable extends OnMessageCallable
} }
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxMessageObject = findIndexForRole(Role.MESSAGE_PONG); idxMessageObject = findIndexForRole(Role.MESSAGE_PONG);
assertRoleRequired(idxMessageObject,"Pong Message Object"); assertRoleRequired(idxMessageObject,"Pong Message Object");
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.Decoder; import javax.websocket.Decoder;
@ -45,6 +44,14 @@ public class OnMessageTextCallable extends OnMessageCallable
super(pojo,method); super(pojo,method);
} }
/**
* Copy Constructor
*/
public OnMessageTextCallable(OnMessageCallable copy)
{
super(copy);
}
public void call(Object endpoint, String str, boolean partialFlag) throws DecodeException public void call(Object endpoint, String str, boolean partialFlag) throws DecodeException
{ {
super.args[idxMessageObject] = textDecoder.decode(str); super.args[idxMessageObject] = textDecoder.decode(str);
@ -56,12 +63,12 @@ public class OnMessageTextCallable extends OnMessageCallable
} }
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT); idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT);
assertRoleRequired(idxMessageObject,"Text Message Object"); assertRoleRequired(idxMessageObject,"Text Message Object");
assertDecoderRequired(); assertDecoderRequired();
textDecoder = (Decoder.Text<?>)getDecoder(); textDecoder = (Decoder.Text<?>)getDecoder();
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.Decoder; import javax.websocket.Decoder;
@ -44,6 +43,14 @@ public class OnMessageTextStreamCallable extends OnMessageCallable
super(pojo,method); super(pojo,method);
} }
/**
* Copy Constructor
*/
public OnMessageTextStreamCallable(OnMessageCallable copy)
{
super(copy);
}
public void call(Object endpoint, Reader reader) throws DecodeException, IOException public void call(Object endpoint, Reader reader) throws DecodeException, IOException
{ {
super.args[idxMessageObject] = textDecoder.decode(reader); super.args[idxMessageObject] = textDecoder.decode(reader);
@ -51,12 +58,12 @@ public class OnMessageTextStreamCallable extends OnMessageCallable
} }
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT_STREAM); idxMessageObject = findIndexForRole(Role.MESSAGE_TEXT_STREAM);
assertRoleRequired(idxMessageObject,"Text Reader Message Object"); assertRoleRequired(idxMessageObject,"Text Reader Message Object");
assertDecoderRequired(); assertDecoderRequired();
textDecoder = (Decoder.TextStream<?>)getDecoder(); textDecoder = (Decoder.TextStream<?>)getDecoder();
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen; import javax.websocket.OnOpen;
@ -39,6 +38,12 @@ public class OnOpenCallable extends JsrCallable
super(pojo,method); super(pojo,method);
} }
public OnOpenCallable(OnOpenCallable copy)
{
super(copy);
this.idxEndpointConfig = copy.idxEndpointConfig;
}
public void call(Object endpoint, EndpointConfig config) public void call(Object endpoint, EndpointConfig config)
{ {
// EndpointConfig is an optional parameter // EndpointConfig is an optional parameter
@ -49,18 +54,10 @@ public class OnOpenCallable extends JsrCallable
super.call(endpoint,super.args); super.call(endpoint,super.args);
} }
public OnOpenCallable copy()
{
OnOpenCallable copy = new OnOpenCallable(pojo,method);
super.copyTo(copy);
copy.idxEndpointConfig = this.idxEndpointConfig;
return copy;
}
@Override @Override
public void init(Session session, Map<String, String> pathParams) public void init(Session session)
{ {
idxEndpointConfig = findIndexForRole(Role.ENDPOINT_CONFIG); idxEndpointConfig = findIndexForRole(Role.ENDPOINT_CONFIG);
super.init(session,pathParams); super.init(session);
} }
} }

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.websocket.jsr356.annotations; package org.eclipse.jetty.websocket.jsr356.annotations;
import org.eclipse.jetty.websocket.jsr356.utils.MethodUtils;
public class Param public class Param
{ {
/** /**
@ -35,7 +37,20 @@ public class Param
MESSAGE_BINARY_STREAM, MESSAGE_BINARY_STREAM,
MESSAGE_PONG, MESSAGE_PONG,
MESSAGE_PARTIAL_FLAG, MESSAGE_PARTIAL_FLAG,
PATH_PARAM PATH_PARAM;
private static Role[] messageRoles;
static
{
messageRoles = new Role[]
{ MESSAGE_TEXT, MESSAGE_TEXT_STREAM, MESSAGE_BINARY, MESSAGE_BINARY_STREAM, MESSAGE_PONG, };
}
public static Role[] getMessageRoles()
{
return messageRoles;
}
} }
public int index; public int index;
@ -72,6 +87,22 @@ public class Param
this.pathParamName = name; this.pathParamName = name;
} }
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("Param[");
str.append("index=").append(index);
str.append(",type=").append(MethodUtils.toString(type));
str.append(",role=").append(role);
if (pathParamName != null)
{
str.append(",pathParamName=").append(pathParamName);
}
str.append(']');
return str.toString();
}
public void unbind() public void unbind()
{ {
this.role = null; this.role = null;

View File

@ -0,0 +1,29 @@
//
// ========================================================================
// 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 javax.websocket.Session;
/**
* Used to tag and expose JSR based EventDriver's that expose the JSR {@link Session}
*/
public interface IJsrSession
{
public Session getJsrSession();
}

View File

@ -25,11 +25,12 @@ import java.nio.ByteBuffer;
import javax.websocket.ClientEndpointConfig; import javax.websocket.ClientEndpointConfig;
import javax.websocket.DecodeException; import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.Decoder.Text;
import javax.websocket.MessageHandler; import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.CloseInfo;
@ -43,21 +44,29 @@ import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
import org.eclipse.jetty.websocket.common.message.SimpleTextMessage; import org.eclipse.jetty.websocket.common.message.SimpleTextMessage;
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer; import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
import org.eclipse.jetty.websocket.jsr356.JsrSession; import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implements EventDriver public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implements EventDriver, IJsrSession
{ {
private static final Logger LOG = Log.getLogger(JsrClientAnnotatedEventDriver.class);
private final JettyWebSocketContainer container; private final JettyWebSocketContainer container;
private final JsrClientMetadata events; private final JsrEvents events;
private boolean hasCloseBeenCalled = false; private boolean hasCloseBeenCalled = false;
private JsrSession jsrsession; private JsrSession jsrsession;
private ClientEndpointConfig endpointconfig; private ClientEndpointConfig endpointconfig;
private MessageAppender activeMessage; private MessageAppender activeMessage;
public JsrClientAnnotatedEventDriver(JettyWebSocketContainer container, WebSocketPolicy policy, Object websocket, JsrClientMetadata metadata) public JsrClientAnnotatedEventDriver(JettyWebSocketContainer container, WebSocketPolicy policy, Object websocket, JsrEvents events)
{ {
super(policy,websocket); super(policy,websocket);
this.container = container; this.container = container;
this.events = metadata; this.events = events;
}
@Override
public Session getJsrSession()
{
return this.jsrsession;
} }
/** /**
@ -66,17 +75,24 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException
{ {
if (LOG.isDebugEnabled())
{
LOG.debug("onBinaryFrame({}, {})",BufferUtil.toDetailString(buffer),fin);
LOG.debug("events.onBinary={}",events.hasBinary());
LOG.debug("events.onBinaryStream={}",events.hasBinaryStream());
}
boolean handled = false; boolean handled = false;
if (events.onBinary != null) if (events.hasBinary())
{ {
handled = true; handled = true;
if (events.onBinary.isPartialMessageSupported()) if (events.isBinaryPartialSupported())
{ {
LOG.debug("Partial Binary Message: fin={}",fin);
// Partial Message Support (does not use messageAppender) // Partial Message Support (does not use messageAppender)
try try
{ {
events.onBinary.call(websocket,buffer,fin); events.callBinary(websocket,buffer,fin);
} }
catch (DecodeException e) catch (DecodeException e)
{ {
@ -89,28 +105,34 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
// Whole Message Support // Whole Message Support
if (activeMessage == null) if (activeMessage == null)
{ {
LOG.debug("Whole Binary Message");
activeMessage = new SimpleBinaryMessage(this); activeMessage = new SimpleBinaryMessage(this);
} }
} }
} }
if (events.onBinaryStream != null) if (events.hasBinaryStream())
{ {
handled = true; handled = true;
// Streaming Message Support // Streaming Message Support
if (activeMessage == null) if (activeMessage == null)
{ {
LOG.debug("Binary Message InputStream");
activeMessage = new MessageInputStream(this); activeMessage = new MessageInputStream(this);
} }
} }
LOG.debug("handled = {}",handled);
// Process any active MessageAppender // Process any active MessageAppender
if (handled && (activeMessage != null)) if (handled && (activeMessage != null))
{ {
LOG.debug("Appending Binary Message");
activeMessage.appendMessage(buffer); activeMessage.appendMessage(buffer);
if (fin) if (fin)
{ {
LOG.debug("Binary Message Complete");
activeMessage.messageComplete(); activeMessage.messageComplete();
activeMessage = null; activeMessage = null;
} }
@ -123,15 +145,15 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onBinaryMessage(byte[] data) public void onBinaryMessage(byte[] data)
{ {
if (events.onBinary == null) if (LOG.isDebugEnabled())
{ {
// not interested in text events LOG.debug("onBinary({})",data);
return;
} }
try try
{ {
events.onBinary.call(websocket,ByteBuffer.wrap(data),false); // FIN is always true here
events.callBinary(websocket,ByteBuffer.wrap(data),true);
} }
catch (DecodeException e) catch (DecodeException e)
{ {
@ -148,28 +170,19 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
return; return;
} }
hasCloseBeenCalled = true; hasCloseBeenCalled = true;
if (events.onClose != null) events.callClose(websocket,close);
{
events.onClose.call(websocket,close);
}
} }
@Override @Override
public void onConnect() public void onConnect()
{ {
if (events.onOpen != null) events.callOpen(websocket,endpointconfig);
{
events.onOpen.call(websocket,endpointconfig);
}
} }
@Override @Override
public void onError(Throwable cause) public void onError(Throwable cause)
{ {
if (events.onError != null) events.callError(websocket,cause);
{
events.onError.call(websocket,cause);
}
} }
private void onFatalError(Throwable t) private void onFatalError(Throwable t)
@ -187,15 +200,9 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onInputStream(InputStream stream) public void onInputStream(InputStream stream)
{ {
if (events.onBinaryStream == null)
{
// not interested in text events
return;
}
try try
{ {
events.onBinaryStream.call(websocket,stream); events.callBinaryStream(websocket,stream);
} }
catch (DecodeException | IOException e) catch (DecodeException | IOException e)
{ {
@ -206,15 +213,9 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onReader(Reader reader) public void onReader(Reader reader)
{ {
if (events.onTextStream == null)
{
// not interested in text events
return;
}
try try
{ {
events.onTextStream.call(websocket,reader); events.callTextStream(websocket,reader);
} }
catch (DecodeException | IOException e) catch (DecodeException | IOException e)
{ {
@ -228,18 +229,26 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException
{ {
if (LOG.isDebugEnabled())
{
LOG.debug("onTextFrame({}, {})",BufferUtil.toDetailString(buffer),fin);
LOG.debug("events.hasText={}",events.hasText());
LOG.debug("events.hasTextStream={}",events.hasTextStream());
}
boolean handled = false; boolean handled = false;
if (events.onText != null) if (events.hasText())
{ {
handled = true; handled = true;
if (events.onText.isPartialMessageSupported()) if (events.isTextPartialSupported())
{ {
LOG.debug("Partial Text Message: fin={}",fin);
// Partial Message Support (does not use messageAppender) // Partial Message Support (does not use messageAppender)
try try
{ {
String text = BufferUtil.toUTF8String(buffer); String text = BufferUtil.toUTF8String(buffer);
events.onText.call(websocket,text,fin); events.callText(websocket,text,fin);
} }
catch (DecodeException e) catch (DecodeException e)
{ {
@ -252,28 +261,34 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
// Whole Message Support // Whole Message Support
if (activeMessage == null) if (activeMessage == null)
{ {
LOG.debug("Whole Text Message");
activeMessage = new SimpleTextMessage(this); activeMessage = new SimpleTextMessage(this);
} }
} }
} }
if (events.onTextStream != null) if (events.hasTextStream())
{ {
handled = true; handled = true;
// Streaming Message Support // Streaming Message Support
if (activeMessage == null) if (activeMessage == null)
{ {
LOG.debug("Text Message Writer");
activeMessage = new MessageReader(this); activeMessage = new MessageReader(this);
} }
} }
LOG.debug("handled = {}",handled);
// Process any active MessageAppender // Process any active MessageAppender
if (handled && (activeMessage != null)) if (handled && (activeMessage != null))
{ {
LOG.debug("Appending Text Message");
activeMessage.appendMessage(buffer); activeMessage.appendMessage(buffer);
if (fin) if (fin)
{ {
LOG.debug("Text Message Complete");
activeMessage.messageComplete(); activeMessage.messageComplete();
activeMessage = null; activeMessage = null;
} }
@ -286,26 +301,17 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
@Override @Override
public void onTextMessage(String message) public void onTextMessage(String message)
{ {
if (events.onText == null) LOG.debug("onText({})",message);
{
// not interested in text events
return;
}
Decoder.Text<?> decoder = (Text<?>)events.onText.getDecoder();
try try
{ {
decoder.init(endpointconfig); // FIN is always true here
events.onText.call(websocket,jsrsession,decoder.decode(message)); events.callText(websocket,message,true);
} }
catch (DecodeException e) catch (DecodeException e)
{ {
onFatalError(e); onFatalError(e);
} }
finally
{
decoder.destroy();
}
} }
@Override @Override
@ -314,5 +320,12 @@ public class JsrClientAnnotatedEventDriver extends AbstractEventDriver implement
super.openSession(session); super.openSession(session);
String id = container.getNextId(); String id = container.getNextId();
this.jsrsession = new JsrSession(container,session,id); this.jsrsession = new JsrSession(container,session,id);
this.events.init(jsrsession);
}
@Override
public String toString()
{
return String.format("%s[websocket=%s]",this.getClass().getSimpleName(),websocket);
} }
} }

View File

@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverImpl; import org.eclipse.jetty.websocket.common.events.EventDriverImpl;
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer; import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner; import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
import org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents;
public class JsrClientEndpointImpl implements EventDriverImpl public class JsrClientEndpointImpl implements EventDriverImpl
{ {
@ -52,22 +53,21 @@ public class JsrClientEndpointImpl implements EventDriverImpl
} }
Class<?> endpointClass = endpoint.getClass(); Class<?> endpointClass = endpoint.getClass();
JsrClientMetadata metadata = cache.get(endpointClass); // Get the base metadata for this class
if (metadata == null) JsrClientMetadata basemetadata = cache.get(endpointClass);
if (basemetadata == null)
{ {
metadata = new JsrClientMetadata(endpointClass); basemetadata = new JsrClientMetadata(endpointClass);
AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(metadata); AnnotatedEndpointScanner scanner = new AnnotatedEndpointScanner(basemetadata);
scanner.scan(); scanner.scan();
cache.put(endpointClass,metadata); cache.put(endpointClass,basemetadata);
} }
// The potential decoders // At this point we have a base metadata, now we need to copy it for
// this specific instance of the WebSocket Endpoint (as we will be
if (config != null) // modifying the metadata)
{ JsrEvents events = new JsrEvents(basemetadata);
return new JsrClientAnnotatedEventDriver(container,policy,endpoint,events);
}
return new JsrClientAnnotatedEventDriver(container,policy,endpoint,metadata);
} }
@Override @Override
@ -79,7 +79,16 @@ public class JsrClientEndpointImpl implements EventDriverImpl
@Override @Override
public boolean supports(Object websocket) public boolean supports(Object websocket)
{ {
ClientEndpoint anno = websocket.getClass().getAnnotation(ClientEndpoint.class); Object endpoint = websocket;
if (endpoint instanceof ConfiguredEndpoint)
{
// unwrap
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
endpoint = ce.getEndpoint();
}
ClientEndpoint anno = endpoint.getClass().getAnnotation(ClientEndpoint.class);
return (anno != null); return (anno != null);
} }
} }

View File

@ -23,6 +23,8 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import javax.websocket.Session;
import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
@ -30,8 +32,15 @@ import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.WebSocketSession; import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.common.events.EventDriver;
public class JsrEndpointEventDriver implements EventDriver public class JsrEndpointEventDriver implements EventDriver, IJsrSession
{ {
@Override
public Session getJsrSession()
{
// TODO Auto-generated method stub
return null;
}
@Override @Override
public WebSocketPolicy getPolicy() public WebSocketPolicy getPolicy()
{ {

View File

@ -27,9 +27,11 @@ import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
public class JsrEndpointImpl implements EventDriverImpl public class JsrEndpointImpl implements EventDriverImpl
{ {
public JsrEndpointImpl(JettyWebSocketContainer jettyWebSocketContainer) private final JettyWebSocketContainer container;
public JsrEndpointImpl(JettyWebSocketContainer container)
{ {
// TODO Auto-generated constructor stub this.container = container;
} }
@Override @Override
@ -42,8 +44,7 @@ public class JsrEndpointImpl implements EventDriverImpl
@Override @Override
public String describeRule() public String describeRule()
{ {
// TODO Auto-generated method stub return "class extends " + Endpoint.class.getName();
return null;
} }
@Override @Override

View File

@ -0,0 +1,50 @@
//
// ========================================================================
// 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 org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.jsr356.JettyWebSocketContainer;
public class JsrEventDriverFactory extends EventDriverFactory
{
public JsrEventDriverFactory(WebSocketPolicy policy, JettyWebSocketContainer container)
{
super(policy);
clearImplementations();
addImplementation(new JsrEndpointImpl(container));
addImplementation(new JsrClientEndpointImpl(container));
}
/**
* Unwrap ConfiguredEndpoint for end-user.
*/
@Override
protected String getClassName(Object websocket)
{
if (websocket instanceof ConfiguredEndpoint)
{
ConfiguredEndpoint ce = (ConfiguredEndpoint)websocket;
return ce.getEndpoint().getClass().getName();
}
return websocket.getClass().getName();
}
}

View File

@ -105,4 +105,11 @@ public final class MethodUtils
// TODO: show exceptions? // TODO: show exceptions?
return str.toString(); return str.toString();
} }
public static String toString(Type type)
{
StringBuilder str = new StringBuilder();
appendTypeName(str,type,true);
return str.toString();
}
} }

View File

@ -63,7 +63,7 @@ public class WebSocketClient extends ContainerLifeCycle
private final WebSocketPolicy policy; private final WebSocketPolicy policy;
private final SslContextFactory sslContextFactory; private final SslContextFactory sslContextFactory;
private final WebSocketExtensionFactory extensionRegistry; private final WebSocketExtensionFactory extensionRegistry;
private final EventDriverFactory eventDriverFactory; private EventDriverFactory eventDriverFactory;
private ByteBufferPool bufferPool; private ByteBufferPool bufferPool;
private Executor executor; private Executor executor;
private Scheduler scheduler; private Scheduler scheduler;
@ -139,7 +139,17 @@ public class WebSocketClient extends ContainerLifeCycle
ConnectionManager manager = getConnectionManager(); ConnectionManager manager = getConnectionManager();
// Setup Driver for user provided websocket // Setup Driver for user provided websocket
EventDriver driver = eventDriverFactory.wrap(websocket); EventDriver driver = null;
if (websocket instanceof EventDriver)
{
// Use the EventDriver as-is
driver = (EventDriver)websocket;
}
else
{
// Wrap websocket with appropriate EventDriver
driver = eventDriverFactory.wrap(websocket);
}
if (driver == null) if (driver == null)
{ {
@ -405,6 +415,11 @@ public class WebSocketClient extends ContainerLifeCycle
this.cookieStore = cookieStore; this.cookieStore = cookieStore;
} }
public void setEventDriverFactory(EventDriverFactory factory)
{
this.eventDriverFactory = factory;
}
public void setExecutor(Executor executor) public void setExecutor(Executor executor)
{ {
this.executor = executor; this.executor = executor;

View File

@ -57,6 +57,16 @@ public class EventDriverFactory
implementations.add(impl); implementations.add(impl);
} }
public void clearImplementations()
{
this.implementations.clear();
}
protected String getClassName(Object websocket)
{
return websocket.getClass().getName();
}
public List<EventDriverImpl> getImplementations() public List<EventDriverImpl> getImplementations()
{ {
return implementations; return implementations;
@ -67,8 +77,6 @@ public class EventDriverFactory
return this.implementations.remove(impl); return this.implementations.remove(impl);
} }
@Override @Override
public String toString() public String toString()
{ {
@ -113,7 +121,7 @@ public class EventDriverFactory
// Create a clear error message for the developer // Create a clear error message for the developer
StringBuilder err = new StringBuilder(); StringBuilder err = new StringBuilder();
err.append(websocket.getClass().getName()); err.append(getClassName(websocket));
err.append(" is not a valid WebSocket object."); err.append(" is not a valid WebSocket object.");
err.append(" Object must obey one of the following rules: "); err.append(" Object must obey one of the following rules: ");
@ -123,9 +131,9 @@ public class EventDriverFactory
EventDriverImpl impl = implementations.get(i); EventDriverImpl impl = implementations.get(i);
if (i > 0) if (i > 0)
{ {
err.append("or "); err.append(" or ");
} }
err.append('(').append(i + 1).append(") "); err.append("\n(").append(i + 1).append(") ");
err.append(impl.describeRule()); err.append(impl.describeRule());
} }

View File

@ -21,29 +21,37 @@ package org.eclipse.jetty.websocket.common.message;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames; import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.LogicalConnection;
public class MessageOutputStream extends OutputStream public class MessageOutputStream extends OutputStream
{ {
private final LogicalConnection connection; private final WebSocketSession session;
private final OutgoingFrames outgoing; private final int bufferSize;
public MessageOutputStream(LogicalConnection connection, OutgoingFrames outgoing) public MessageOutputStream(WebSocketSession session)
{ {
this.connection = connection; this.session = session;
this.outgoing = outgoing; this.bufferSize = session.getPolicy().getMaxBinaryMessageBufferSize();
} }
public boolean isClosed() @Override
public void close() throws IOException
{ {
// TODO Auto-generated method stub // TODO finish sending whatever in the buffer with FIN=true
return false; // TODO or just send an empty buffer with FIN=true
super.close();
}
@Override
public void flush() throws IOException
{
// TODO flush whatever is in the buffer with FIN=false
super.flush();
} }
@Override @Override
public void write(int b) throws IOException public void write(int b) throws IOException
{ {
// TODO Auto-generated method stub // TODO buffer up to limit, flush once buffer reached.
} }
} }

View File

@ -21,45 +21,35 @@ package org.eclipse.jetty.websocket.common.message;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames; import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.LogicalConnection;
public class MessageWriter extends Writer public class MessageWriter extends Writer
{ {
private final LogicalConnection connection; private final WebSocketSession session;
private final OutgoingFrames outgoing; private final int bufferSize;
public MessageWriter(LogicalConnection connection, OutgoingFrames outgoing) public MessageWriter(WebSocketSession session)
{ {
this.connection = connection; this.session = session;
this.outgoing = outgoing; this.bufferSize = session.getPolicy().getMaxTextMessageBufferSize();
} }
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {
// TODO Auto-generated method stub // TODO finish sending whatever in the buffer with FIN=true
// TODO or just send an empty buffer with FIN=true
} }
@Override @Override
public void flush() throws IOException public void flush() throws IOException
{ {
// TODO Auto-generated method stub // TODO flush whatever is in the buffer with FIN=false
}
public boolean isClosed()
{
// TODO Auto-generated method stub
return false;
} }
@Override @Override
public void write(char[] cbuf, int off, int len) throws IOException public void write(char[] cbuf, int off, int len) throws IOException
{ {
// TODO Auto-generated method stub // TODO buffer up to limit, flush once buffer reached.
} }
} }