Skip to content

Commit aeb62de

Browse files
kn100WillSewell
authored andcommitted
Breaking change to pass a PusherEvent object to onEvent
Introduces a PusherEvent class which is now passed as a single argument to onEvent. onEvent used to take 3 arguments (channel name, event name, data), so this is a breaking change. The PusherEvent exposes a method getUserId which exposes user_ids which are added to client events on presence channels. This is a new feature, and the motivation for this breaking change.
1 parent 0696e0b commit aeb62de

File tree

11 files changed

+225
-56
lines changed

11 files changed

+225
-56
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
This Changelog is no longer being updated. For any further changes please see the Releases section on this Github repository - https://github.com/pusher/pusher-websocket-java/releases
44

5+
## Version 2.0.0
6+
* The onEvent handler now gets called with one parameter, called the PusherEvent. This PusherEvent has all the same information available as was available before with the 3 parameters, but now is accessible by calling getChannelName(), getData() or getEventName() on the PusherEvent object you receive. In addition, for presence channel client events, you can now retrieve an authenticated User ID by calling getUserId() on the PusherEvent object. To read more on Authenticated users, see the README or our docs [here](https://pusher.com/docs/channels/using_channels/events#user-id-in-client-events).
7+
8+
59
## Version 1.4.0
610
* Update the dependency to use pusher/java-websocket fork and remove dependency on clojars.org repository.
711

812
## Version 1.3.0
913
* Add retry logic when the connection is lost
10-
* Accept 201 status code from auth endpoints
14+
* Accept 201 status code from auth endpoints
1115

1216
## Version 1.2.2
1317

README.md

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ Channel channel = pusher.subscribe("my-channel");
107107
// Bind to listen for events called "my-event" sent to "my-channel"
108108
channel.bind("my-event", new SubscriptionEventListener() {
109109
@Override
110-
public void onEvent(String channel, String event, String data) {
111-
System.out.println("Received event with data: " + data);
110+
public void onEvent(PusherEvent event) {
111+
System.out.println("Received event with data: " + event.toString());
112112
}
113113
});
114114

115-
// Disconnect from the service (or become disconnected my network conditions)
115+
// Disconnect from the service
116116
pusher.disconnect();
117117

118118
// Reconnect, with all channel subscriptions and event bindings automatically recreated
@@ -340,6 +340,21 @@ UserInfo info = gson.fromJson(jsonInfo, UserInfo.class);
340340

341341
For more information on defining the user id and user info on the server see [Implementing the auth endpoint for a presence channel](https://pusher.com/docs/channels/server_api/authenticating-users#implementing-the-auth-endpoint-for-a-presence-channel) documentation.
342342

343+
#### Client event authenticity
344+
345+
Channels now provides a 'user-id' with client events sent from the server. With presence channels, your authentication endpoint provides your user with a user-id. Previously, it was up to you to include this user-id in every client-event triggered by your clients. Now, when a client of yours triggers a client event, Channels will append their user-id to their triggered message, so that the other clients in the channel receive it. This allows you to trust that a given user really did trigger a given payload.
346+
347+
If you’d like to make use of this feature, you’ll need to extract the user-id from the message delivered by Channels. To do this, call getUserId() on the event payload your event handler gets called with, like so:
348+
349+
```java
350+
channel.bind("client-my-event", new SubscriptionEventListener() {
351+
@Override
352+
public void onEvent(PusherEvent event) {
353+
System.out.println("Received event with userId: " + event.getUserId());
354+
}
355+
});
356+
```
357+
343358
## Binding and handling events
344359

345360
There are two types of events that occur on channel subscriptions.
@@ -359,7 +374,7 @@ Channel channel = pusher.subscribe("my-channel", new ChannelEventListener() {
359374
}
360375

361376
@Override
362-
public void onEvent(String channelName, String eventName, String data) {
377+
public void onEvent(PusherEvent event) {
363378
// Called for incoming events names "foo", "bar" or "baz"
364379
}
365380
}, "foo", "bar", "baz");
@@ -375,13 +390,13 @@ Events triggered by your application are received by the `onEvent` method on the
375390
Channel channel = pusher.subscribe("my-channel");
376391
channel.bind("my-event", new ChannelEventListener() {
377392
@Override
378-
public void onEvent(String channelName, String eventName, String data) {
393+
public void onEvent(PusherEvent event) {
379394
// Called for incoming events named "my-event"
380395
}
381396
});
382397
```
383398

384-
The event data will be passed as the third parameter to the `onEvent` method. From there you can handle the data as you like. Since we encourage data to be in JSON here's an example that uses [Gson object deserialization](https://sites.google.com/site/gson/gson-user-guide#TOC-Object-Examples):
399+
The event data is accessible by calling the `getData()` method on the event. From there you can handle the data as you like. Since we encourage data to be in JSON here's an example that uses [Gson object deserialization](https://sites.google.com/site/gson/gson-user-guide#TOC-Object-Examples):
385400

386401
```java
387402
public class Example implements ChannelEventListener {
@@ -392,9 +407,9 @@ public class Example implements ChannelEventListener {
392407
}
393408

394409
@Override
395-
public void onEvent(String channelName, String eventName, String data) {
410+
public void onEvent(PusherEvent event) {
396411
Gson gson = new Gson();
397-
EventExample exampleEvent = gson.fromJson(data, EventExample.class);
412+
EventExample exampleEvent = gson.fromJson(event.getData(), EventExample.class);
398413
}
399414
}
400415

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.pusher.client.channel;
2+
3+
import java.util.Map;
4+
5+
public class PusherEvent {
6+
private Map<String, Object> eventData;
7+
8+
public PusherEvent(Map<String, Object> eventData) {
9+
this.eventData = eventData;
10+
}
11+
12+
/**
13+
* getProperty returns the value associated with the key, or null.
14+
* It is recommended that you use the specialized getters in this class instead.
15+
*
16+
* @param key The key you wish to get.
17+
* @return
18+
* The object can be casted as follows:
19+
* - JSON strings - java.lang.String
20+
* - JSON number - java.lang.Double
21+
* - JSON boolean - java.lang.Boolean
22+
* - JSON array - java.util.ArrayList
23+
* - JSON object - java.util.Map
24+
* - JSON null - null
25+
*/
26+
public Object getProperty(String key) {
27+
return eventData.get(key);
28+
}
29+
30+
/**
31+
* Returns the userId associated with this event.
32+
*
33+
* @return
34+
* The userID string: https://pusher.com/docs/channels/using_channels/events#user-id-in-client-events,
35+
* or null if the event is not a client event on a presence channel.
36+
*/
37+
public String getUserId() { return (String)eventData.get("user_id"); }
38+
public String getChannelName() { return (String)eventData.get("channel"); }
39+
public String getEventName() { return (String)eventData.get("event"); }
40+
public String getData() { return (String)eventData.get("data"); }
41+
42+
public String toString() {
43+
return eventData.toString();
44+
}
45+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.pusher.client.channel;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.Map;
5+
6+
import com.google.gson.Gson;
7+
import com.google.gson.JsonDeserializationContext;
8+
import com.google.gson.JsonDeserializer;
9+
import com.google.gson.JsonElement;
10+
import com.google.gson.JsonParseException;
11+
12+
public class PusherEventDeserializer implements JsonDeserializer<PusherEvent> {
13+
private final Gson GSON = new Gson();
14+
15+
@Override
16+
@SuppressWarnings("unchecked")
17+
public PusherEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
18+
return new PusherEvent(GSON.fromJson(json, Map.class));
19+
}
20+
}

src/main/java/com/pusher/client/channel/SubscriptionEventListener.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,9 @@ public interface SubscriptionEventListener {
2828
* Callback that is fired whenever an event that this
2929
* {@linkplain SubscriptionEventListener} has been bound to is received.
3030
*
31-
* @param channelName
32-
* The name of the channel that the event has been received on.
33-
* This is useful if your {@linkplain ChannelEventListener} has
34-
* been bound to events on more than one channel.
35-
* @param eventName
36-
* The name of the event that has been received. This will always
37-
* be one of the events that your
38-
* {@linkplain ChannelEventListener} has been bound to.
39-
* @param data
40-
* The JSON data that was included with the event. This can be
41-
* parsed with Google's Gson library, which is a dependency of
42-
* this library, or your library of choice.
31+
* @param event
32+
* A PusherEvent object which exposes the whole event.
33+
* See {@linkplain PusherEvent} for more.
4334
*/
44-
void onEvent(String channelName, String eventName, String data);
35+
void onEvent(final PusherEvent event);
4536
}

src/main/java/com/pusher/client/channel/impl/ChannelImpl.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.pusher.client.channel.impl;
22

3-
import java.util.Collections;
43
import java.util.HashMap;
54
import java.util.HashSet;
65
import java.util.LinkedHashMap;
@@ -9,14 +8,12 @@
98

109
import com.google.gson.Gson;
1110

12-
import com.pusher.client.channel.ChannelEventListener;
13-
import com.pusher.client.channel.ChannelState;
14-
import com.pusher.client.channel.SubscriptionEventListener;
11+
import com.google.gson.GsonBuilder;
12+
import com.pusher.client.channel.*;
1513
import com.pusher.client.util.Factory;
1614

1715
public class ChannelImpl implements InternalChannel {
18-
19-
private static final Gson GSON = new Gson();
16+
private final Gson GSON;
2017
private static final String INTERNAL_EVENT_PREFIX = "pusher_internal:";
2118
protected static final String SUBSCRIPTION_SUCCESS_EVENT = "pusher_internal:subscription_succeeded";
2219
protected final String name;
@@ -27,7 +24,9 @@ public class ChannelImpl implements InternalChannel {
2724
private final Object lock = new Object();
2825

2926
public ChannelImpl(final String channelName, final Factory factory) {
30-
27+
GsonBuilder gsonBldr = new GsonBuilder();
28+
gsonBldr.registerTypeAdapter(PusherEvent.class, new PusherEventDeserializer());
29+
GSON = gsonBldr.create();
3130
if (channelName == null) {
3231
throw new IllegalArgumentException("Cannot subscribe to a channel with a null name");
3332
}
@@ -110,19 +109,19 @@ public void onMessage(final String event, final String message) {
110109

111110
if (listeners != null) {
112111
for (final SubscriptionEventListener listener : listeners) {
113-
final String data = extractDataFrom(message);
114-
112+
final PusherEvent e = GSON.fromJson(message, PusherEvent.class);
115113
factory.queueOnEventThread(new Runnable() {
116114
@Override
117115
public void run() {
118-
listener.onEvent(name, event, data);
116+
listener.onEvent(e);
119117
}
120118
});
121119
}
122120
}
123121
}
124122
}
125123

124+
126125
@Override
127126
public String toSubscribeMessage() {
128127

@@ -189,11 +188,6 @@ public String toString() {
189188
return String.format("[Public Channel: name=%s]", name);
190189
}
191190

192-
@SuppressWarnings("unchecked")
193-
private String extractDataFrom(final String message) {
194-
final Map<Object, Object> jsonObject = GSON.fromJson(message, Map.class);
195-
return (String)jsonObject.get("data");
196-
}
197191

198192
protected String[] getDisallowedNameExpressions() {
199193
return new String[] { "^private-.*", "^presence-.*" };

src/main/java/com/pusher/client/example/ExampleApp.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.pusher.client.Pusher;
88
import com.pusher.client.PusherOptions;
99
import com.pusher.client.channel.ChannelEventListener;
10+
import com.pusher.client.channel.PusherEvent;
1011
import com.pusher.client.connection.ConnectionEventListener;
1112
import com.pusher.client.connection.ConnectionStateChange;
1213

@@ -64,14 +65,12 @@ public void onError(final String message, final String code, final Exception e)
6465
/* ChannelEventListener implementation */
6566

6667
@Override
67-
public void onEvent(final String channelName, final String eventName, final String data) {
68-
69-
System.out.println(String.format("[%d] Received event [%s] on channel [%s] with data [%s]", timestamp(),
70-
eventName, channelName, data));
68+
public void onEvent(final PusherEvent event) {
69+
System.out.println(String.format("[%d] Received event [%s]", timestamp(), event.toString()));
7170

7271
final Gson gson = new Gson();
7372
@SuppressWarnings("unchecked")
74-
final Map<String, String> jsonObject = gson.fromJson(data, Map.class);
73+
final Map<String, String> jsonObject = gson.fromJson(event.getData(), Map.class);
7574
System.out.println(jsonObject);
7675
}
7776

src/main/java/com/pusher/client/example/PresenceChannelExampleApp.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.pusher.client.Pusher;
66
import com.pusher.client.PusherOptions;
7+
import com.pusher.client.channel.PusherEvent;
78
import com.pusher.client.channel.PresenceChannel;
89
import com.pusher.client.channel.PresenceChannelEventListener;
910
import com.pusher.client.channel.User;
@@ -93,10 +94,9 @@ public void userUnsubscribed(final String channelName, final User user) {
9394
}
9495

9596
@Override
96-
public void onEvent(final String channelName, final String eventName, final String data) {
97+
public void onEvent(final PusherEvent event) {
9798

98-
System.out.println(String.format("Received event [%s] on channel [%s] with data [%s]", eventName, channelName,
99-
data));
99+
System.out.println(String.format("Received event [%s]", event.toString()));
100100
}
101101

102102
@Override

src/main/java/com/pusher/client/example/PrivateChannelExampleApp.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.pusher.client.Pusher;
44
import com.pusher.client.PusherOptions;
5+
import com.pusher.client.channel.PusherEvent;
56
import com.pusher.client.channel.PrivateChannel;
67
import com.pusher.client.channel.PrivateChannelEventListener;
78
import com.pusher.client.connection.ConnectionEventListener;
@@ -66,10 +67,9 @@ public void onError(final String message, final String code, final Exception e)
6667
/* PrivateChannelEventListener implementation */
6768

6869
@Override
69-
public void onEvent(final String channelName, final String eventName, final String data) {
70+
public void onEvent(final PusherEvent event) {
7071

71-
System.out.println(String.format("Received event [%s] on channel [%s] with data [%s]", eventName, channelName,
72-
data));
72+
System.out.println(String.format("Received event [%s] on channel [%s] with data [%s] and event [%s]", event.getEventName(), event.getChannelName(), event.getData(), event.toString()));
7373
}
7474

7575
@Override

0 commit comments

Comments
 (0)