Skip to content

Commit 25c65a5

Browse files
authored
Add HubMessage abstraction and parse messages from the server into different message types (#2620)
1 parent 6c28d45 commit 25c65a5

File tree

8 files changed

+149
-62
lines changed

8 files changed

+149
-62
lines changed

clients/java/signalr/src/main/java/HubConnection.java

+25-12
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,32 @@ public HubConnection(String url, Transport transport){
2323
this.protocol = new JsonHubProtocol();
2424
this.callback = (payload) -> {
2525

26-
InvocationMessage[] messages = protocol.parseMessages(payload);
27-
28-
// message will be null if we receive any message other than an invocation.
29-
// Adding this to avoid getting error messages on pings for now.
30-
for (InvocationMessage message : messages) {
31-
if (message != null && handlers.containsKey(message.target)) {
32-
ArrayList<Object> args = gson.fromJson((JsonArray)message.arguments[0], (new ArrayList<Object>()).getClass());
33-
List<ActionBase> actions = handlers.get(message.target);
34-
if (actions != null) {
35-
for (ActionBase action: actions) {
36-
action.invoke(args.toArray());
26+
HubMessage[] messages = protocol.parseMessages(payload);
27+
28+
for (HubMessage message : messages) {
29+
switch (message.getMessageType()) {
30+
case INVOCATION:
31+
InvocationMessage invocationMessage = (InvocationMessage)message;
32+
if (message != null && handlers.containsKey(invocationMessage.target)) {
33+
ArrayList<Object> args = gson.fromJson((JsonArray)invocationMessage.arguments[0], (new ArrayList<Object>()).getClass());
34+
List<ActionBase> actions = handlers.get(invocationMessage.target);
35+
if (actions != null) {
36+
for (ActionBase action: actions) {
37+
action.invoke(args.toArray());
38+
}
39+
}
3740
}
38-
}
41+
break;
42+
case STREAM_INVOCATION:
43+
case STREAM_ITEM:
44+
throw new UnsupportedOperationException("Streaming is not yet supported");
45+
case CLOSE:
46+
case CANCEL_INVOCATION:
47+
case COMPLETION:
48+
case PING:
49+
// We don't need to do anything in the case of a ping message.
50+
// The other message types aren't supported
51+
break;
3952
}
4053
}
4154
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
public abstract class HubMessage {
5+
abstract HubMessageType getMessageType();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
public enum HubMessageType {
5+
INVOCATION(1),
6+
STREAM_ITEM(2),
7+
COMPLETION(3),
8+
STREAM_INVOCATION(4),
9+
CANCEL_INVOCATION(5),
10+
PING(6),
11+
CLOSE(7);
12+
13+
public int value;
14+
HubMessageType(int id) { this.value = id; }
15+
}

clients/java/signalr/src/main/java/HubProtocol.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public interface HubProtocol {
55
String getName();
66
int getVersion();
77
TransferFormat getTransferFormat();
8-
InvocationMessage[] parseMessages(String message);
8+
HubMessage[] parseMessages(String message);
99
String writeMessage(InvocationMessage message);
1010
}
1111

clients/java/signalr/src/main/java/InvocationMessage.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
public class InvocationMessage {
5-
int type = 1;
4+
public class InvocationMessage extends HubMessage {
5+
int type = HubMessageType.INVOCATION.value;
66
String invocationId;
77
String target;
88
Object[] arguments;
@@ -35,4 +35,9 @@ public Object[] getArguments() {
3535
public void setArguments(Object[] arguments) {
3636
this.arguments = arguments;
3737
}
38+
39+
@Override
40+
HubMessageType getMessageType() {
41+
return HubMessageType.INVOCATION;
42+
}
3843
}

clients/java/signalr/src/main/java/JsonHubProtocol.java

+16-22
Original file line numberDiff line numberDiff line change
@@ -30,51 +30,45 @@ public TransferFormat getTransferFormat() {
3030
}
3131

3232
@Override
33-
public InvocationMessage[] parseMessages(String payload) {
33+
public HubMessage[] parseMessages(String payload) {
3434
String[] messages = payload.split(RECORD_SEPARATOR);
35-
List<InvocationMessage> invocationMessages = new ArrayList<>();
35+
List<HubMessage> hubMessages = new ArrayList<>();
3636
for (String splitMessage : messages) {
3737
// Empty handshake response "{}". We can ignore it
3838
if (splitMessage.equals("{}")) {
3939
continue;
4040
}
4141

4242
JsonObject jsonMessage = jsonParser.parse(splitMessage).getAsJsonObject();
43-
String messageType = jsonMessage.get("type").toString();
43+
HubMessageType messageType = HubMessageType.values()[jsonMessage.get("type").getAsInt() -1];
4444
switch (messageType) {
45-
case "1":
45+
case INVOCATION:
4646
//Invocation Message
4747
String target = jsonMessage.get("target").getAsString();
4848
JsonElement args = jsonMessage.get("arguments");
49-
invocationMessages.add(new InvocationMessage(target, new Object[] {args}));
49+
hubMessages.add(new InvocationMessage(target, new Object[] {args}));
5050
break;
51-
case "2":
52-
//Stream item
51+
case STREAM_ITEM:
52+
throw new UnsupportedOperationException("Support for streaming is not yet available");
53+
case COMPLETION:
5354
//Don't care yet
5455
break;
55-
case "3":
56-
//Completion
57-
//Don't care yet
58-
break;
59-
case "4":
60-
//Stream invocation
56+
case STREAM_INVOCATION:
6157
//Don't care yet;
58+
throw new UnsupportedOperationException("Support for streaming is not yet available");
59+
case CANCEL_INVOCATION:
60+
// Not tracking invocations yet
6261
break;
63-
case "5":
64-
//Cancel invocation
65-
//Don't care yet
66-
break;
67-
case "6":
62+
case PING:
6863
//Ping
69-
//Don't care yet
64+
hubMessages.add(new PingMessage());
7065
break;
71-
case "7":
72-
// Close message
66+
case CLOSE:
7367
//Don't care yet;
7468
break;
7569
}
7670
}
77-
return invocationMessages.toArray(new InvocationMessage[invocationMessages.size()]);
71+
return hubMessages.toArray(new HubMessage[hubMessages.size()]);
7872
}
7973

8074
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
public class PingMessage extends HubMessage {
5+
6+
int type = HubMessageType.PING.value;
7+
8+
@Override
9+
HubMessageType getMessageType() {
10+
return HubMessageType.PING;
11+
}
12+
}

clients/java/signalr/src/test/java/JsonHubProtocolTest.java

+67-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
import com.google.gson.JsonArray;
5+
import org.junit.Rule;
56
import org.junit.Test;
7+
import org.junit.rules.ExpectedException;
68

79
import static org.junit.Assert.*;
810

@@ -33,68 +35,108 @@ public void VerifyWriteMessage() {
3335
assertEquals(expectedResult, result);
3436
}
3537

38+
@Test
39+
public void ParsePingMessage() {
40+
String stringifiedMessage = "{\"type\":6}\u001E";
41+
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
42+
43+
//We know it's only one message
44+
assertEquals(1, messages.length);
45+
assertEquals(HubMessageType.PING, messages[0].getMessageType());
46+
}
47+
3648
@Test
3749
public void ParseSingleMessage() {
3850
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
39-
InvocationMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
51+
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
4052

4153
//We know it's only one message
4254
assertEquals(1, messages.length);
43-
InvocationMessage message = messages[0];
44-
assertEquals("test", message.target);
45-
assertEquals(null, message.invocationId);
46-
assertEquals(1, message.type);
47-
JsonArray messageResult = (JsonArray) message.arguments[0];
55+
56+
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
57+
58+
//We can safely cast here because we know that it's an invocation message.
59+
InvocationMessage invocationMessage = (InvocationMessage) messages[0];
60+
61+
assertEquals("test", invocationMessage.target);
62+
assertEquals(null, invocationMessage.invocationId);
63+
64+
JsonArray messageResult = (JsonArray) invocationMessage.arguments[0];
4865
assertEquals(42, messageResult.getAsInt());
4966
}
5067

68+
@Rule
69+
public ExpectedException exceptionRule = ExpectedException.none();
70+
71+
@Test
72+
public void ParseSingleUnsupportedStreamItemMessage() {
73+
exceptionRule.expect(UnsupportedOperationException.class);
74+
exceptionRule.expectMessage("Support for streaming is not yet available");
75+
String stringifiedMessage = "{\"type\":2,\"Id\":1,\"Item\":42}\u001E";
76+
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
77+
}
78+
79+
@Test
80+
public void ParseSingleUnsupportedStreamInvocationMessage() {
81+
exceptionRule.expect(UnsupportedOperationException.class);
82+
exceptionRule.expectMessage("Support for streaming is not yet available");
83+
String stringifiedMessage = "{\"type\":4,\"Id\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
84+
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
85+
}
86+
5187
@Test
5288
public void ParseHandshakeResponsePlusMessage() {
5389
String twoMessages = "{}\u001E{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E";
54-
InvocationMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
90+
HubMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
91+
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
5592

56-
//We ignore the Handshake response for now
57-
InvocationMessage message = messages[0];
93+
//We ignore the Handshake response for now and we can cast because we know we have in invocation message.
94+
InvocationMessage message = (InvocationMessage) messages[0];
5895
assertEquals("test", message.target);
59-
assertEquals(null, message.invocationId);
60-
assertEquals(1, message.type);
6196
JsonArray messageResult = (JsonArray) message.arguments[0];
6297
assertEquals(42, messageResult.getAsInt());
6398
}
6499

65100
@Test
66101
public void ParseTwoMessages() {
67102
String twoMessages = "{\"type\":1,\"target\":\"one\",\"arguments\":[42]}\u001E{\"type\":1,\"target\":\"two\",\"arguments\":[43]}\u001E";
68-
InvocationMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
103+
HubMessage[] messages = jsonHubProtocol.parseMessages(twoMessages);
69104
assertEquals(2, messages.length);
70105

71106
// Check the first message
72-
InvocationMessage message = messages[0];
73-
assertEquals("one", message.target);
74-
assertEquals(null, message.invocationId);
75-
assertEquals(1, message.type);
76-
JsonArray messageResult = (JsonArray) message.arguments[0];
107+
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
108+
109+
//Now that we know we have an invocation message we can cast the hubMessage.
110+
InvocationMessage invocationMessage = (InvocationMessage) messages[0];
111+
112+
assertEquals("one", invocationMessage.target);
113+
assertEquals(null, invocationMessage.invocationId);
114+
JsonArray messageResult = (JsonArray) invocationMessage.arguments[0];
77115
assertEquals(42, messageResult.getAsInt());
78116

79117
// Check the second message
80-
InvocationMessage secondMessage = messages[1];
81-
assertEquals("two", secondMessage.target);
82-
assertEquals(null, secondMessage.invocationId);
83-
assertEquals(1, secondMessage.type);
84-
JsonArray secondMessageResult = (JsonArray) secondMessage.arguments[0];
118+
assertEquals(HubMessageType.INVOCATION, messages[1].getMessageType());
119+
120+
//Now that we know we have an invocation message we can cast the hubMessage.
121+
InvocationMessage invocationMessage2 = (InvocationMessage) messages[1];
122+
123+
assertEquals("two", invocationMessage2.target);
124+
assertEquals(null, invocationMessage2.invocationId);
125+
JsonArray secondMessageResult = (JsonArray) invocationMessage2.arguments[0];
85126
assertEquals(43, secondMessageResult.getAsInt());
86127
}
87128

88129
@Test
89130
public void ParseSingleMessageMutipleArgs() {
90131
String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42, 24]}\u001E";
91-
InvocationMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
132+
HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage);
92133

93134
//We know it's only one message
94-
InvocationMessage message = messages[0];
135+
assertEquals(HubMessageType.INVOCATION, messages[0].getMessageType());
136+
137+
InvocationMessage message = (InvocationMessage)messages[0];
95138
assertEquals("test", message.target);
96139
assertEquals(null, message.invocationId);
97-
assertEquals(1, message.type);
98140
JsonArray messageResult = ((JsonArray) message.arguments[0]);
99141
assertEquals(42, messageResult.get(0).getAsInt());
100142
assertEquals(24, messageResult.get(1).getAsInt());

0 commit comments

Comments
 (0)