Adding the HubProtocol Layer to the Java Client (#2524)
This commit is contained in:
parent
d318d733c4
commit
b0ce55e6c8
|
|
@ -1,7 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class DefaultJsonProtocolHandShakeMessage {
|
||||
String protocol = "json";
|
||||
int version = 1;
|
||||
private String protocol = "json";
|
||||
private int version = 1;
|
||||
private static final String RECORD_SEPARATOR = "\u001e";
|
||||
|
||||
public String createHandshakeMessage() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this) + RECORD_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class HubConnection {
|
||||
private String _url;
|
||||
private ITransport _transport;
|
||||
private Transport _transport;
|
||||
private OnReceiveCallBack callback;
|
||||
private HashMap<String, Action> handlers = new HashMap<>();
|
||||
private JsonParser jsonParser = new JsonParser();
|
||||
private static final String RECORD_SEPARATOR = "\u001e";
|
||||
private HubProtocol protocol;
|
||||
|
||||
public Boolean connected = false;
|
||||
|
||||
public HubConnection(String url) {
|
||||
_url = url;
|
||||
protocol = new JsonHubProtocol();
|
||||
callback = (payload) -> {
|
||||
String[] messages = payload.split(RECORD_SEPARATOR);
|
||||
|
||||
for (String splitMessage : messages) {
|
||||
InvocationMessage[] messages = protocol.parseMessages(payload);
|
||||
|
||||
// Empty handshake response "{}". We can ignore it
|
||||
if (splitMessage.length() == 2) {
|
||||
continue;
|
||||
// message will be null if we receive any message other than an invocation.
|
||||
// Adding this to avoid getting error messages on pings for now.
|
||||
for (InvocationMessage message : messages) {
|
||||
if (message != null && handlers.containsKey(message.target)) {
|
||||
handlers.get(message.target).invoke(message.arguments[0]);
|
||||
}
|
||||
processMessage(splitMessage);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -39,44 +36,6 @@ public class HubConnection {
|
|||
}
|
||||
}
|
||||
|
||||
private void processMessage(String message) {
|
||||
JsonObject jsonMessage = jsonParser.parse(message).getAsJsonObject();
|
||||
String messageType = jsonMessage.get("type").toString();
|
||||
switch(messageType) {
|
||||
case "1":
|
||||
//Invocation Message
|
||||
String target = jsonMessage.get("target").getAsString();
|
||||
if (handlers.containsKey(target)) {
|
||||
handlers.get(target).invoke(jsonMessage.get("arguments"));
|
||||
}
|
||||
break;
|
||||
case "2":
|
||||
//Stream item
|
||||
//Don't care yet
|
||||
break;
|
||||
case "3":
|
||||
//Completion
|
||||
//Don't care yet
|
||||
break;
|
||||
case "4":
|
||||
//Stream invocation
|
||||
//Don't care yet;
|
||||
break;
|
||||
case "5":
|
||||
//Cancel invocation
|
||||
//Don't care yet
|
||||
break;
|
||||
case "6":
|
||||
//Ping
|
||||
//Don't care yet
|
||||
break;
|
||||
case "7":
|
||||
// Close message
|
||||
//Don't care yet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws InterruptedException {
|
||||
_transport.setOnReceive(this.callback);
|
||||
_transport.start();
|
||||
|
|
@ -88,12 +47,13 @@ public class HubConnection {
|
|||
connected = false;
|
||||
}
|
||||
|
||||
public void send(String method, Object arg1) {
|
||||
InvocationMessage message = new InvocationMessage(method, new Object[]{ arg1 });
|
||||
public void send(String method, Object... args) {
|
||||
InvocationMessage invocationMessage = new InvocationMessage(method, args);
|
||||
String message = protocol.writeMessage(invocationMessage);
|
||||
_transport.send(message);
|
||||
}
|
||||
|
||||
public void On(String target, Action callback) {
|
||||
handlers.put(target, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
public interface HubProtocol {
|
||||
String getName();
|
||||
int getVersion();
|
||||
TransferFormat getTransferFormat();
|
||||
InvocationMessage[] parseMessages(String message);
|
||||
String writeMessage(InvocationMessage message);
|
||||
}
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
public class InvocationMessage {
|
||||
private int type = 1;
|
||||
int type = 1;
|
||||
String invocationId;
|
||||
String target;
|
||||
Object[] arguments;
|
||||
|
|
@ -28,7 +28,7 @@ public class InvocationMessage {
|
|||
this.target = target;
|
||||
}
|
||||
|
||||
public Object getArguments() {
|
||||
public Object[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonHubProtocol implements HubProtocol {
|
||||
private final JsonParser jsonParser = new JsonParser();
|
||||
private final Gson gson = new Gson();
|
||||
private static final String RECORD_SEPARATOR = "\u001e";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransferFormat getTransferFormat() {
|
||||
return TransferFormat.Text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationMessage[] parseMessages(String payload) {
|
||||
String[] messages = payload.split(RECORD_SEPARATOR);
|
||||
List<InvocationMessage> invocationMessages = new ArrayList<>();
|
||||
for (String splitMessage : messages) {
|
||||
// Empty handshake response "{}". We can ignore it
|
||||
if (splitMessage.equals("{}")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject jsonMessage = jsonParser.parse(splitMessage).getAsJsonObject();
|
||||
String messageType = jsonMessage.get("type").toString();
|
||||
switch (messageType) {
|
||||
case "1":
|
||||
//Invocation Message
|
||||
String target = jsonMessage.get("target").getAsString();
|
||||
JsonElement args = jsonMessage.get("arguments");
|
||||
invocationMessages.add(new InvocationMessage(target, new Object[] {args}));
|
||||
break;
|
||||
case "2":
|
||||
//Stream item
|
||||
//Don't care yet
|
||||
break;
|
||||
case "3":
|
||||
//Completion
|
||||
//Don't care yet
|
||||
break;
|
||||
case "4":
|
||||
//Stream invocation
|
||||
//Don't care yet;
|
||||
break;
|
||||
case "5":
|
||||
//Cancel invocation
|
||||
//Don't care yet
|
||||
break;
|
||||
case "6":
|
||||
//Ping
|
||||
//Don't care yet
|
||||
break;
|
||||
case "7":
|
||||
// Close message
|
||||
//Don't care yet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return invocationMessages.toArray(new InvocationMessage[invocationMessages.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeMessage(InvocationMessage invocationMessage) {
|
||||
return gson.toJson(invocationMessage) + RECORD_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
public enum TransferFormat {
|
||||
Text,
|
||||
Binary
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
public interface ITransport {
|
||||
public interface Transport {
|
||||
void start() throws InterruptedException;
|
||||
void send(InvocationMessage invocationMessage);
|
||||
void send(String message);
|
||||
void setOnReceive(OnReceiveCallBack callback);
|
||||
void onReceive(String message);
|
||||
void stop();
|
||||
|
|
@ -8,8 +8,7 @@ import org.java_websocket.handshake.ServerHandshake;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class WebSocketTransport implements ITransport {
|
||||
private static final String RECORD_SEPARATOR = "\u001e";
|
||||
public class WebSocketTransport implements Transport {
|
||||
private WebSocketClient _webSocket;
|
||||
private OnReceiveCallBack onReceiveCallBack;
|
||||
private URI _url;
|
||||
|
|
@ -23,13 +22,11 @@ public class WebSocketTransport implements ITransport {
|
|||
@Override
|
||||
public void start() throws InterruptedException {
|
||||
_webSocket.connectBlocking();
|
||||
_webSocket.send(createHandshakeMessage() + RECORD_SEPARATOR);
|
||||
_webSocket.send((new DefaultJsonProtocolHandShakeMessage()).createHandshakeMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(InvocationMessage invocationMessage) {
|
||||
Gson gson = new Gson();
|
||||
String message = gson.toJson(invocationMessage) + RECORD_SEPARATOR;
|
||||
public void send(String message) {
|
||||
_webSocket.send(message);
|
||||
}
|
||||
|
||||
|
|
@ -43,11 +40,6 @@ public class WebSocketTransport implements ITransport {
|
|||
this.onReceiveCallBack.invoke(message);
|
||||
}
|
||||
|
||||
private String createHandshakeMessage() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(new DefaultJsonProtocolHandShakeMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
_webSocket.closeConnection(0, "HubConnection Stopped");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class JsonHubProtocolTest {
|
||||
private JsonHubProtocol jsonHubProtocol = new JsonHubProtocol();
|
||||
private static final String RECORD_SEPARATOR = "\u001e";
|
||||
|
||||
@Test
|
||||
public void checkProtocolName() {
|
||||
assertEquals("json", jsonHubProtocol.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkVersionNumber() {
|
||||
assertEquals(1, jsonHubProtocol.getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkTransferFormat() {
|
||||
assertEquals(TransferFormat.Text, jsonHubProtocol.getTransferFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void VerifyWriteMessage() {
|
||||
InvocationMessage invocationMessage = new InvocationMessage("test", new Object[] {"42"});
|
||||
String result = jsonHubProtocol.writeMessage(invocationMessage);
|
||||
String expectedResult = "{\"type\":1,\"target\":\"test\",\"arguments\":[\"42\"]}\u001E";
|
||||
assertEquals(expectedResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ParseSingleMessage() {
|
||||
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
|
||||
InvocationMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
|
||||
|
||||
//We know it's only one message
|
||||
assertEquals(1, messages.length);
|
||||
InvocationMessage message = messages[0];
|
||||
assertEquals("test", message.target);
|
||||
assertEquals(null, message.invocationId);
|
||||
assertEquals(1, message.type);
|
||||
JsonArray messageResult = (JsonArray) message.arguments[0];
|
||||
assertEquals(42, messageResult.getAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ParseHandshakeResponsePlusMessage() {
|
||||
String twoMessages = "{}\u001E{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
|
||||
InvocationMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
|
||||
|
||||
//We ignore the Handshake response for now
|
||||
InvocationMessage message = messages[0];
|
||||
assertEquals("test", message.target);
|
||||
assertEquals(null, message.invocationId);
|
||||
assertEquals(1, message.type);
|
||||
JsonArray messageResult = (JsonArray) message.arguments[0];
|
||||
assertEquals(42, messageResult.getAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ParseTwoMessages() {
|
||||
String twoMessages = "{\"type\":1,\"target\":\"one\",\"arguments\":[42]}\u001E{\"type\":1,\"target\":\"two\",\"arguments\":[43]}\u001E";
|
||||
InvocationMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
|
||||
assertEquals(2, messages.length);
|
||||
|
||||
// Check the first message
|
||||
InvocationMessage message = messages[0];
|
||||
assertEquals("one", message.target);
|
||||
assertEquals(null, message.invocationId);
|
||||
assertEquals(1, message.type);
|
||||
JsonArray messageResult = (JsonArray) message.arguments[0];
|
||||
assertEquals(42, messageResult.getAsInt());
|
||||
|
||||
// Check the second message
|
||||
InvocationMessage secondMessage = messages[1];
|
||||
assertEquals("two", secondMessage.target);
|
||||
assertEquals(null, secondMessage.invocationId);
|
||||
assertEquals(1, secondMessage.type);
|
||||
JsonArray secondMessageResult = (JsonArray) secondMessage.arguments[0];
|
||||
assertEquals(43, secondMessageResult.getAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ParseSingleMessageMutipleArgs() {
|
||||
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42, 24]}\u001E";
|
||||
InvocationMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
|
||||
|
||||
//We know it's only one message
|
||||
InvocationMessage message = messages[0];
|
||||
assertEquals("test", message.target);
|
||||
assertEquals(null, message.invocationId);
|
||||
assertEquals(1, message.type);
|
||||
JsonArray messageResult = ((JsonArray) message.arguments[0]);
|
||||
assertEquals(42, messageResult.get(0).getAsInt());
|
||||
assertEquals(24, messageResult.get(1).getAsInt());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue