Skip to content

Commit d750ce3

Browse files
committed
Add sub/unsub APIs + multiple subscribers to multiple channels tests
1 parent 3d6426a commit d750ce3

File tree

12 files changed

+541
-6
lines changed

12 files changed

+541
-6
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.servicestack.client.sse;
2+
3+
import com.google.gson.reflect.TypeToken;
4+
5+
import net.servicestack.client.IGet;
6+
import net.servicestack.client.IReturn;
7+
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
11+
/**
12+
* Created by mythz on 2/12/2017.
13+
*/
14+
15+
public class GetEventSubscribers implements IReturn<ArrayList<HashMap<String,String>>>, IGet
16+
{
17+
public ArrayList<String> Channels = null;
18+
19+
public ArrayList<String> getChannels() { return Channels; }
20+
public GetEventSubscribers setChannels(ArrayList<String> value) { this.Channels = value; return this; }
21+
private static Object responseType = new TypeToken<ArrayList<HashMap<String,String>>>(){}.getType();
22+
public Object getResponseType() { return responseType; }
23+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package net.servicestack.client.sse;
2+
3+
import java.util.Map;
4+
5+
/**
6+
* Created by mythz on 2/12/2017.
7+
*/
8+
9+
public class ServerEventUser {
10+
11+
private String userId;
12+
private String displayName;
13+
private String profileUrl;
14+
private String[] channels;
15+
private Map<String, String> meta;
16+
17+
public String getUserId() {
18+
return userId;
19+
}
20+
21+
public ServerEventUser setUserId(String userId) {
22+
this.userId = userId;
23+
return this;
24+
}
25+
26+
public String getDisplayName() {
27+
return displayName;
28+
}
29+
30+
public ServerEventUser setDisplayName(String displayName) {
31+
this.displayName = displayName;
32+
return this;
33+
}
34+
35+
public String getProfileUrl() {
36+
return profileUrl;
37+
}
38+
39+
public ServerEventUser setProfileUrl(String profileUrl) {
40+
this.profileUrl = profileUrl;
41+
return this;
42+
}
43+
44+
public String[] getChannels() {
45+
return channels;
46+
}
47+
48+
public ServerEventUser setChannels(String[] channels) {
49+
this.channels = channels;
50+
return this;
51+
}
52+
53+
public Map<String, String> getMeta() {
54+
return meta;
55+
}
56+
57+
public ServerEventUser setMeta(Map<String, String> meta) {
58+
this.meta = meta;
59+
return this;
60+
}
61+
}

src/AndroidClient/android/src/main/java/net/servicestack/client/sse/ServerEventsClient.java

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import net.servicestack.client.JsonUtils;
99
import net.servicestack.client.Log;
1010
import net.servicestack.client.Utils;
11+
import net.servicestack.func.Func;
12+
import net.servicestack.func.Function;
1113

1214
import java.io.BufferedInputStream;
1315
import java.io.FileNotFoundException;
@@ -19,8 +21,10 @@
1921
import java.net.HttpURLConnection;
2022
import java.net.URL;
2123
import java.text.SimpleDateFormat;
24+
import java.util.ArrayList;
2225
import java.util.Date;
2326
import java.util.HashMap;
27+
import java.util.List;
2428
import java.util.Locale;
2529
import java.util.Map;
2630
import java.util.Random;
@@ -62,7 +66,7 @@ public class ServerEventsClient implements AutoCloseable {
6266
static int DefaultIdleTimeoutMs = 30 * 1000;
6367
public static String UnknownChannel = "*";
6468

65-
public ServerEventsClient(String baseUri, String[] channels) {
69+
public ServerEventsClient(String baseUri, String... channels) {
6670
setBaseUri(baseUri);
6771
setChannels(channels);
6872
this.serviceClient = new JsonServiceClient(baseUri);
@@ -121,8 +125,9 @@ public IResolver getResolver() {
121125
return resolver;
122126
}
123127

124-
public void setResolver(IResolver resolver) {
128+
public ServerEventsClient setResolver(IResolver resolver) {
125129
this.resolver = resolver;
130+
return this;
126131
}
127132

128133
public ServerEventsClient setOnConnect(ServerEventConnectCallback onConnect) {
@@ -172,6 +177,10 @@ public Map<String, ServerEventCallback> getNamedReceivers() {
172177
return namedReceivers;
173178
}
174179

180+
public ServerEventsClient registerReceiver(Class<?> receiverClass) {
181+
return registerNamedReceiver("cmd", receiverClass);
182+
}
183+
175184
public ServerEventsClient registerNamedReceiver(String name, Class<?> namedReceiverClass) {
176185

177186
if (!IReceiver.class.isAssignableFrom(namedReceiverClass))
@@ -204,7 +213,7 @@ public void execute(ServerEventsClient client, ServerEventMessage msg) {
204213
Class requestType = args[0].getType();
205214

206215
if (target.equals(requestType.getSimpleName())) {
207-
Object request = msg.getJson() != null
216+
Object request = !Utils.isNullOrEmpty(msg.getJson())
208217
? JsonUtils.fromJson(msg.getJson(), requestType)
209218
: requestType.newInstance();
210219
mi.invoke(receiver, request);
@@ -216,7 +225,7 @@ public void execute(ServerEventsClient client, ServerEventMessage msg) {
216225
actionName = actionName.substring(3); //= "set".length()
217226

218227
if (target.equalsIgnoreCase(actionName)) {
219-
Object request = msg.getJson() != null
228+
Object request = !Utils.isNullOrEmpty(msg.getJson())
220229
? JsonUtils.fromJson(msg.getJson(), requestType)
221230
: requestType.newInstance();
222231
mi.invoke(receiver, request);
@@ -663,5 +672,82 @@ private void processEventMessage(ServerEventMessage e) {
663672
client.onMessageReceived(e);
664673
}
665674
}
675+
676+
public List<ServerEventUser> getChannelSubscribers(){
677+
ArrayList<HashMap<String,String>> response = this.serviceClient.get(
678+
new GetEventSubscribers().setChannels(Func.toList(this.getChannels())));
679+
680+
return Func.map(response, new Function<HashMap<String, String>, ServerEventUser>() {
681+
@Override
682+
public ServerEventUser apply(HashMap<String, String> map) {
683+
String channels = map.get("channels");
684+
ServerEventUser to = new ServerEventUser()
685+
.setUserId(map.get("userId"))
686+
.setDisplayName(map.get("displayName"))
687+
.setProfileUrl(map.get("profileUrl"))
688+
.setChannels(Utils.isNullOrEmpty(channels) ? channels.split(",") : null);
689+
690+
final ArrayList<String> reservedNames = Func.toList("userId", "displayName", "profileUrl", "channels");
691+
for (Map.Entry<String,String> entry : map.entrySet()){
692+
if (reservedNames.contains(entry.getKey()))
693+
continue;
694+
695+
if (to.getMeta() == null)
696+
to.setMeta(new HashMap<>());
697+
698+
to.getMeta().put(entry.getKey(), entry.getValue());
699+
}
700+
701+
return to;
702+
}
703+
});
704+
}
705+
706+
public void updateSubscriber(UpdateEventSubscriber request){
707+
if (request.getId() == null)
708+
request.setId(connectionInfo.getId());
709+
710+
serviceClient.post(request);
711+
712+
update(
713+
Func.toArray(request.getSubscribeChannels(), String.class),
714+
Func.toArray(request.getUnsubscribeChannels(), String.class));
715+
}
716+
717+
public void subscribeToChannels(String... channels)
718+
{
719+
serviceClient.post(new UpdateEventSubscriber()
720+
.setId(connectionInfo.getId())
721+
.setSubscribeChannels(Func.toList(channels)));
722+
723+
update(channels, null);
724+
}
725+
726+
public void unSubscribeFromChannels(String... channels)
727+
{
728+
serviceClient.post(new UpdateEventSubscriber()
729+
.setId(connectionInfo.getId())
730+
.setUnsubscribeChannels(Func.toList(channels)));
731+
732+
update(null, channels);
733+
}
734+
735+
public void update(String[] subscribe, String[] unsubscribe)
736+
{
737+
List<String> snapshot = Func.toList(getChannels());
738+
if (subscribe != null)
739+
{
740+
for (String channel : subscribe){
741+
if (!snapshot.contains(channel))
742+
snapshot.add(channel);
743+
}
744+
}
745+
if (unsubscribe != null)
746+
{
747+
snapshot.removeAll(Func.toList(unsubscribe));
748+
}
749+
setChannels(Func.toArray(snapshot, String.class));
750+
}
751+
666752
}
667753

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package net.servicestack.client.sse;
2+
3+
import net.servicestack.client.IResolver;
4+
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.concurrent.ConcurrentMap;
7+
import java.util.function.Function;
8+
9+
/**
10+
* Created by mythz on 2/12/2017.
11+
*/
12+
13+
public class SingletonInstanceResolver implements IResolver {
14+
15+
ConcurrentMap<Class, Object> cache = new ConcurrentHashMap<>();
16+
17+
@Override
18+
public Object TryResolve(Class cls) {
19+
return cache.computeIfAbsent(cls, new Function<Class, Object>() {
20+
@Override
21+
public Object apply(Class aClass) {
22+
try {
23+
return aClass.newInstance();
24+
} catch (Exception e) {
25+
throw new RuntimeException(e);
26+
}
27+
}
28+
});
29+
}
30+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package net.servicestack.client.sse;
2+
3+
import net.servicestack.client.DataMember;
4+
import net.servicestack.client.IPost;
5+
import net.servicestack.client.IReturn;
6+
import net.servicestack.client.Route;
7+
8+
import java.util.ArrayList;
9+
10+
/**
11+
* Created by mythz on 2/12/2017.
12+
*/
13+
14+
@Route(Path="/event-subscribers/{Id}", Verbs="POST")
15+
public class UpdateEventSubscriber implements IReturn<UpdateEventSubscriberResponse>, IPost
16+
{
17+
@DataMember(Order=1)
18+
public String Id = null;
19+
20+
@DataMember(Order=2)
21+
public ArrayList<String> SubscribeChannels = null;
22+
23+
@DataMember(Order=3)
24+
public ArrayList<String> UnsubscribeChannels = null;
25+
26+
public String getId() { return Id; }
27+
public UpdateEventSubscriber setId(String value) { this.Id = value; return this; }
28+
public ArrayList<String> getSubscribeChannels() { return SubscribeChannels; }
29+
public UpdateEventSubscriber setSubscribeChannels(ArrayList<String> value) { this.SubscribeChannels = value; return this; }
30+
public ArrayList<String> getUnsubscribeChannels() { return UnsubscribeChannels; }
31+
public UpdateEventSubscriber setUnsubscribeChannels(ArrayList<String> value) { this.UnsubscribeChannels = value; return this; }
32+
private static Object responseType = UpdateEventSubscriberResponse.class;
33+
public Object getResponseType() { return responseType; }
34+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package net.servicestack.client.sse;
2+
3+
import net.servicestack.client.DataContract;
4+
import net.servicestack.client.DataMember;
5+
import net.servicestack.client.ResponseStatus;
6+
7+
/**
8+
* Created by mythz on 2/12/2017.
9+
*/
10+
11+
@DataContract
12+
public class UpdateEventSubscriberResponse
13+
{
14+
@DataMember(Order=1)
15+
public ResponseStatus ResponseStatus = null;
16+
public ResponseStatus responseStatus = null;
17+
18+
public ResponseStatus getResponseStatus() { return ResponseStatus != null ? ResponseStatus : responseStatus; }
19+
public UpdateEventSubscriberResponse setResponseStatus(ResponseStatus value) { this.ResponseStatus = this.responseStatus = value; return this; }
20+
}
21+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.servicestack.client.sse;
2+
3+
import com.google.gson.reflect.TypeToken;
4+
5+
import net.servicestack.client.IGet;
6+
import net.servicestack.client.IReturn;
7+
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
11+
/**
12+
* Created by mythz on 2/12/2017.
13+
*/
14+
15+
public class GetEventSubscribers implements IReturn<ArrayList<HashMap<String,String>>>, IGet
16+
{
17+
public ArrayList<String> Channels = null;
18+
19+
public ArrayList<String> getChannels() { return Channels; }
20+
public GetEventSubscribers setChannels(ArrayList<String> value) { this.Channels = value; return this; }
21+
private static Object responseType = new TypeToken<ArrayList<HashMap<String,String>>>(){}.getType();
22+
public Object getResponseType() { return responseType; }
23+
}

0 commit comments

Comments
 (0)