GithubHelp home page GithubHelp logo

webbit-gwt's Introduction

(Name in flux, probably will be renamed to gwt-websockets)

GWT library to make RPC calls over websockets. As in regular GWT-RPC, the client can call the server at any time, but with this library, the server can also call back the the client.

WebSockets are established from the client to the server, and while open are maintained to allow them to continue to communicate over the same channel. Either side can send a string to the other at any time. This project uses this to enable one-way RPC methods to easily be sent.

The default setup for this is to only use a pair of interfaces, one for the client, and one for the server. Since the client API needs to know about the server and vice versa, generics are used so that either side of the API can see to the other.

Methods can optionally take a Callback<T,F> object as the final parameter, but instead of the server implementing the same method synchronously, both client and server code are written to assume async behavior. Note however that callbacks are often not required - messages can be passed without expecting a reply back. Note also that callbacks are one-time use, and cannot be invoked multiple times - use a different method on the opposite interface to achieve that effect.

JSR-356 is used presently as the only server-side implementation (the spec for javax.websocket, implemented by Glassfish, Jetty, and Tomcat). The library uses version 1.0 of this API, as Jetty (and perhaps others) do not yet support 1.1.

A new project has also been started adding rpc-like communication between web/shared/service workers.

Example

Client-Server Contract

These interfaces refer to each other in their generics. Here is a simple client interface for a chat app:

/**
 * Simple example of methods implemented by a GWT client that can be called from the server
 *
 */
public interface ChatClient extends Client<ChatClient, ChatServer> {
	/**
	 * Tells the client that a user posted a message to the chat room
	 * @param username the user who sent the message
	 * @param message the message the user sent
	 */
	void say(String username, String message);

	/**
	 * Indicates that a new user has entered the chat room
	 * @param username the user who logged in
	 */
	void join(String username);

	/**
	 * Indicates that a user has left the chat room
	 * @param username the user who left
	 */
	void part(String username);

	/**
	 * Test method to have the server send the client a message and get a response right away.
	 * This demonstrates that the server can call the client with a callback to get its response
	 * other than via a Server method.
	 *
	 * @param callback response that the client should call upon receipt of this method
	 */
	void ping(Callback<Void, Void> callback);
}

The client code must implement this interface, and will be called when the server invokes any of those methods. Once implemented, the client may do anything with these details - fire events to the rest of the app, call other methods, directly interact with the UI, etc.

/**
 * Simple example of methods a server can have that can be invoked by a client.
 *
 */
@RemoteServiceRelativePath("/chat")
public interface ChatServer extends Server<ChatServer, ChatClient> {
	/**
	 * Brings the user into the chat room, with the given username
	 * @param username the name to use
	 * @param callback indicates the login was successful, or passes back an error message
	 */
	void login(String username, Callback<Void, String> callback);

	/**
	 * Sends the given message to the chatroom
	 * @param message the message to say to the room
	 */
	void say(String message);
}

In this matching server interface, we can see the messages that any client can send to the server after connecting. The server in turn must implement this, and may call back to the client using any of the methods defined in the first interface.

Client Wiring

The client first builds an implementation of the client interface - this will be its way of recieving all 'callbacks' from the server. Then, we declare an interface to connect to the server:

interface ChatServerBuilder extends ServerBuilder<ChatServer> {}

This interface can then be implemented with GWT.create, given a url and client instance, and the connection established:

		// Create an instance of the build
		final ChatServerBuilder builder = GWT.create(ChatServerBuilder.class);
		// Set the url to connect to - may or may not be the same as the original server
		builder.setUrl("ws://" + Window.Location.getHost() + "/chat");

		// Get an instance of the client impl
		ChatClient impl = ...;

		// Start a connection to the server, and attach the client impl
		final ChatServer server = builder.start();
		server.setClient(impl);

Once the onOpen() method gets called on the client object, the connection is established, and the client may invoke any server method until onClose() is called. To restart the connection (or start another sim simultaneous connect), call start() again and then talk to the newly returned server object.

The AbstractClientImpl class can serve as a handy base class, providing default implementations of the onOpen() and onClose() methods that fire events.

Server Wiring

With either API there is an AbstractServerImpl class. This provides the working details of the Server interface as well as the specifics how to interact with JSR-356.

From within either client or server implementation, you can always get a reference to the other side - the server can call getClient(), and the client already has an instance (see below). Our ChatServerImpl probably needs to track all connected clients, so we'll start off with a field to hold them:

public class ChatServerImpl extends AbstractServerImpl<ChatServer, ChatClient> implements ChatServer {
	private final Map<ChatClient, String> loggedIn =
	            Collections.synchronizedMap(new HashMap<ChatClient, String>());

Next, we'll want to implement each method - for example, when any user says something, we'll send it to all other connected users:

	@Override
	public void say(String message) {
		ChatClient c = getClient();
		String userName = loggedIn.get(c);

		for (ChatClient connected : loggedIn.keySet()) {
			connected.say(userName, message);
		}
	}

Note that this is not the only way to keep several clients in communication with each other, just one possible implementation, made deliberately simple for the sake of an example. For a larger solution, some kind of message queue could be used to send out messages to the proper recipients.

JSR-356

For the javax.websocket api, we have two basic options, as documented at http://docs.oracle.com/javaee/7/tutorial/doc/websocket.htm. The simplest way to do this is usually the annotated approach, by which the endpoint class must be decorated with @ServerEndpoint() to indicate the url it should be used to respond to. The other annotations are already present in the RpcEndpoint class (the superclass of AbstractServerImpl).

@ServerEndpoint("/chat")
public class ChatServerImpl extends AbstractServerImpl<ChatServer, ChatClient> implements ChatServer {
	//...

Check out the javaee-websocket-gwt-rpc-sample project for a working, runnable example of the above code.

webbit-gwt's People

Contributors

ericbets avatar jamesxnelson avatar niloc132 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

webbit-gwt's Issues

Add support for callbacks

In traditional GWT RPC, all return values come back via a callback, as there is no other way for the server to asynchronously send the client a message. In Webbit-GWT, the server can call any client method at any time, so while there can be matching pairs of methods (i.e. after the server finished getData(a,b,c) it calls newData(d) on the client), it can be more convenient to have a callback wired up to the method.

The implementation of this will likely look a little different than traditional RPC - rather than a pair of interfaces (one synchronous and one not), there could still only be one interface in each direction, but with two possibilities.

  • The first looks like RequestFactory a bit - the method no longer returns void but some kinda of Future/Promise object - when you call this code you get a sort of proxy that will be notified when the remote end returns, and when you return, you'll hand back a standard impl wrapping the object in question. Advantages: simple implementation, fun with promises; disadvantages: strict pattern to follow.
  • The second feels more like traditional RPC, at least in calling code - the final parameter is an optional annotated (to instruct that this isn't actually a parameter to send over the wire) callback. The invoking code passes in a traditional callback-like method, which will then be proxied in the implementation, to be called whenever the result is available. Advantages: easy and obvious to use, possibility for async invocation instead of immediate return, possibility for re-use of callbacks; disadvantages: reuse feature could mean memory leaks without explicit cleanup/cancel mechanism.

Must use SE 8 only? java.lang.UnsupportedClassVersionError

Hello Colin,

Thank you for your effort to create this library. I am trying to integrate it into an existing GWT application following the small guide on the main page and your sample project. I have however faced this problem when after making the basic set-up and launching the app (I am pasting the stack below). I am trying to run on Oracle 7 JDK and am using GWT 2.7.0.

               Loading inherited module 'com.colinalworth.gwt.websockets.RpcOverWebSockets'
                  [ERROR] Unexpected error while processing XML
java.lang.UnsupportedClassVersionError: com/colinalworth/gwt/websockets/rebind/ServerBuilderGenerator : Unsupported major.minor version 52.0
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
	at com.google.gwt.dev.cfg.ModuleDefSchema$ClassAttrCvt.convertToArg(ModuleDefSchema.java:902)
	at com.google.gwt.dev.util.xml.HandlerArgs.convertToArg(HandlerArgs.java:64)
	at com.google.gwt.dev.util.xml.HandlerMethod.invokeBegin(HandlerMethod.java:221)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.startElement(ReflectiveParser.java:296)
	at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.parse(ReflectiveParser.java:349)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.access$200(ReflectiveParser.java:70)
	at com.google.gwt.dev.util.xml.ReflectiveParser.parse(ReflectiveParser.java:431)
	at com.google.gwt.dev.cfg.ModuleDefLoader.nestedLoad(ModuleDefLoader.java:377)
	at com.google.gwt.dev.cfg.ModuleDefSchema$BodySchema.__inherits_begin(ModuleDefSchema.java:504)
	at sun.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.google.gwt.dev.util.xml.HandlerMethod.invokeBegin(HandlerMethod.java:230)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.startElement(ReflectiveParser.java:296)
	at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
	at org.apache.xerces.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.parse(ReflectiveParser.java:349)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.access$200(ReflectiveParser.java:70)
	at com.google.gwt.dev.util.xml.ReflectiveParser.parse(ReflectiveParser.java:431)
	at com.google.gwt.dev.cfg.ModuleDefLoader.nestedLoad(ModuleDefLoader.java:377)
	at com.google.gwt.dev.cfg.ModuleDefSchema$BodySchema.__inherits_begin(ModuleDefSchema.java:504)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.google.gwt.dev.util.xml.HandlerMethod.invokeBegin(HandlerMethod.java:230)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.startElement(ReflectiveParser.java:296)
	at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
	at org.apache.xerces.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.parse(ReflectiveParser.java:349)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.access$200(ReflectiveParser.java:70)
	at com.google.gwt.dev.util.xml.ReflectiveParser.parse(ReflectiveParser.java:431)
	at com.google.gwt.dev.cfg.ModuleDefLoader.nestedLoad(ModuleDefLoader.java:377)
	at com.google.gwt.dev.cfg.ModuleDefSchema$BodySchema.__inherits_begin(ModuleDefSchema.java:504)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.google.gwt.dev.util.xml.HandlerMethod.invokeBegin(HandlerMethod.java:230)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.startElement(ReflectiveParser.java:296)
	at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
	at org.apache.xerces.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
	at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.parse(ReflectiveParser.java:349)
	at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.access$200(ReflectiveParser.java:70)
	at com.google.gwt.dev.util.xml.ReflectiveParser.parse(ReflectiveParser.java:431)
	at com.google.gwt.dev.cfg.ModuleDefLoader.nestedLoad(ModuleDefLoader.java:377)
	at com.google.gwt.dev.cfg.ModuleDefLoader.load(ModuleDefLoader.java:290)
	at com.google.gwt.dev.cfg.ModuleDefLoader.doLoadModule(ModuleDefLoader.java:229)
	at com.google.gwt.dev.cfg.ModuleDefLoader.loadFromResources(ModuleDefLoader.java:167)
	at com.google.gwt.dev.cfg.ModuleDefLoader.loadFromClassPath(ModuleDefLoader.java:138)
	at com.google.gwt.dev.DevModeBase.loadModule(DevModeBase.java:1005)
	at com.google.gwt.dev.DevMode.loadModule(DevMode.java:695)
	at com.google.gwt.dev.DevMode.doStartup(DevMode.java:566)
	at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1044)
	at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:836)
	at com.google.gwt.dev.DevMode.main(DevMode.java:413)
[ERROR] shell failed in doStartup method

Broken custom serializers will throw exceptions, but won't fail the callback.

Including stacktrace for posterity.

`<redacted>`: removing com.colinalworth.gwt.websockets.server.RpcEndpoint$IH@b72270 after Throwable: com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: java.lang.IllegalStateException: `<user code message redacted>`
        at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:323)
        at com.colinalworth.gwt.websockets.server.RpcEndpoint.handleMessage(RpcEndpoint.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
        at org.eclipse.jetty.websocket.jsr356.annotations.OnMessageTextCallable.call(OnMessageTextCallable.java:63)
        at org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents.callText(JsrEvents.java:197)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextMessage(JsrAnnotatedEventDriver.java:386)
        at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:66)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextFrame(JsrAnnotatedEventDriver.java:368)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:162)
        at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:474)
        at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:182)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:105)
        at org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension.forwardIncoming(CompressExtension.java:142)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:85)
        at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:220)
        at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)
        at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:245)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:560)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:392)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:289)
        at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:149)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
        at java.lang.Thread.run(Thread.java:748)
caused by:
com.google.gwt.user.client.rpc.SerializationException: java.lang.IllegalStateException: `<user code message redacted>`
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:716)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:612)
        at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader.readObject(AbstractSerializationStreamReader.java:119)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserialize(ServerInvocation_CustomFieldSerializer.java:40)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserializeInstance(ServerInvocation_CustomFieldSerializer.java:30)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserializeInstance(ServerInvocation_CustomFieldSerializer.java:27)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeImpl(ServerSerializationStreamReader.java:889)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:687)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.readObject(ServerSerializationStreamReader.java:592)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:149)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:434)
        at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:312)
        at com.colinalworth.gwt.websockets.server.RpcEndpoint.handleMessage(RpcEndpoint.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
        at org.eclipse.jetty.websocket.jsr356.annotations.OnMessageTextCallable.call(OnMessageTextCallable.java:63)
        at org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents.callText(JsrEvents.java:197)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextMessage(JsrAnnotatedEventDriver.java:386)
        at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:66)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextFrame(JsrAnnotatedEventDriver.java:368)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:162)
        at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:474)
        at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:182)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:105)
        at org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension.forwardIncoming(CompressExtension.java:142)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:85)
        at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:220)
        at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)
        at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:245)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:560)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:392)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:289)
        at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:149)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
        at java.lang.Thread.run(Thread.java:748)
caused by:
java.lang.IllegalStateException: `<user code message redacted>`
        at `<redacted>`ScriptHandle_CustomFieldSerializer.lambda$getFactory$1(ScriptHandle_CustomFieldSerializer.java:47)
        at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
        at `<redacted>`.ScriptHandle_CustomFieldSerializer.getFactory(ScriptHandle_CustomFieldSerializer.java:46)
        at `<redacted>`.ScriptHandle_CustomFieldSerializer.instantiate(ScriptHandle_CustomFieldSerializer.java:40)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.instantiateWithCustomFieldInstantiator(ServerSerializationStreamReader.java:1127)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.instantiate(ServerSerializationStreamReader.java:1080)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:682)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:612)
        at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader.readObject(AbstractSerializationStreamReader.java:119)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserialize(ServerInvocation_CustomFieldSerializer.java:40)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserializeInstance(ServerInvocation_CustomFieldSerializer.java:30)
        at com.colinalworth.gwt.websockets.shared.impl.ServerInvocation_CustomFieldSerializer.deserializeInstance(ServerInvocation_CustomFieldSerializer.java:27)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeImpl(ServerSerializationStreamReader.java:889)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:687)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.readObject(ServerSerializationStreamReader.java:592)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:149)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:434)
        at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:312)
        at com.colinalworth.gwt.websockets.server.RpcEndpoint.handleMessage(RpcEndpoint.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
        at org.eclipse.jetty.websocket.jsr356.annotations.OnMessageTextCallable.call(OnMessageTextCallable.java:63)
        at org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents.callText(JsrEvents.java:197)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextMessage(JsrAnnotatedEventDriver.java:386)
        at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:66)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextFrame(JsrAnnotatedEventDriver.java:368)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:162)
        at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:474)
        at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:182)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:105)
        at org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension.forwardIncoming(CompressExtension.java:142)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:85)
        at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:220)
        at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)
        at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:245)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:560)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:392)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:289)
        at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:149)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
        at java.lang.Thread.run(Thread.java:748)

Better error handling

As the client/server contract only includes void methods, there is no way to report to the far side when an exception occurs. Both client and server must have a way to detect and handle errors, and optionally pass them over the wire to the far end.

Better support for overloaded methods

Presently methods that are called on either client or server are differentiated only by name, not by arguments. The client at least has support for checking argument count, but if two overloads have the same arg count, they will appear the same.

Use server serialization policy

In traditional GWT RPC a serialization policy file is used to ensure that only expected types are created, sent over the wire, and only allowed methods are invoked. This project does not yet support this, but circumvents it instead.

Varargs do not work

Server throws an IllegalStateException.

java.lang.IllegalArgumentException: argument type mismatch
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.colinalworth.gwt.websockets.server.RpcEndpoint.handleMessage(RpcEndpoint.java:161)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
        at org.eclipse.jetty.websocket.jsr356.annotations.OnMessageTextCallable.call(OnMessageTextCallable.java:63)
        at org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents.callText(JsrEvents.java:197)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextMessage(JsrAnnotatedEventDriver.java:386)
        at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:66)
        at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextFrame(JsrAnnotatedEventDriver.java:368)
        at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:162)
        at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:474)
        at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:182)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:105)
        at org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension.forwardIncoming(CompressExtension.java:142)
        at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:85)
        at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:220)
        at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)
        at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:245)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:560)
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:392)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
        at java.lang.Thread.run(Thread.java:745)

Consider 'session' objects

This could take several forms, but the goal is to allow 'sub' client/server interfaces for ongoing, scoped communication. One example would be passing some callback-like type to a method which can then be invoked several times. Another would be to allow client/server methods to return non-void interface types, to be asynchronously created on the other end of the wire, and interacted with in the same way as a client/server type.

Combining both ideas give us the ability to easily spin up matched pairs of interfaces. Using only one of the two requires that the client/server have matched pairs of methods that have to be invoked to correctly start this situation.

In any case, these session types must have some kind of close method to shut down the corresponding instance on the far side of the wire. Failure to do so would result in a memory leak (which is why Callbacks are single-use-only).

ServerBuilderImpl does not work with https

Window.Location.getProtocol() (which returns js $wnd.location.protocol) returns 'https:', including the colon (:) but ServerBuilderImpl does not respect it within the constructor:

public ServerBuilderImpl() {
		urlBuilder.setProtocol("https".equals(Window.Location.getProtocol()) ? "wss": "ws").setHash(null);
	}

should be:

urlBuilder.setProtocol("https:".equals(Window.Location.getProtocol()) ? "wss": "ws").setHash(null);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.