Skip to content

Commit 80b1c8b

Browse files
authored
Merge pull request #309 from pusher/add_global_event_listner
Add global event listner
2 parents 7afdfde + 2414cc9 commit 80b1c8b

File tree

4 files changed

+131
-3
lines changed

4 files changed

+131
-3
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ public interface Channel {
4444
*/
4545
void bind(String eventName, SubscriptionEventListener listener);
4646

47+
/**
48+
* Binds a {@link SubscriptionEventListener} to all events. The
49+
* {@link SubscriptionEventListener} will be notified whenever an
50+
* event is received on this channel.
51+
*
52+
* @param listener
53+
* A listener to receive notifications when the event is
54+
* received.
55+
* @throws IllegalArgumentException
56+
* If the {@link SubscriptionEventListener} is null.
57+
* @throws IllegalStateException
58+
* If the channel has been unsubscribed by calling
59+
* {@link com.pusher.client.Pusher#unsubscribe(String)}. This
60+
* puts the {@linkplain Channel} in a terminal state from which
61+
* it can no longer be used. To resubscribe, call
62+
* {@link com.pusher.client.Pusher#subscribe(String)} or
63+
* {@link com.pusher.client.Pusher#subscribe(String, ChannelEventListener, String...)}
64+
* again to receive a fresh {@linkplain Channel} instance.
65+
*/
66+
void bindGlobal(SubscriptionEventListener listener);
67+
4768
/**
4869
* <p>
4970
* Unbinds a previously bound {@link SubscriptionEventListener} from an
@@ -80,6 +101,36 @@ public interface Channel {
80101
*/
81102
void unbind(String eventName, SubscriptionEventListener listener);
82103

104+
/**
105+
* <p>
106+
* Unbinds a previously bound {@link SubscriptionEventListener} from global
107+
* events. The {@link SubscriptionEventListener} will no longer be notified
108+
* whenever the any event is received on this channel.
109+
* </p>
110+
*
111+
* <p>
112+
* Calling this method does not unsubscribe from the channel even if there
113+
* are no more {@link SubscriptionEventListener}s bound to it. If you want
114+
* to unsubscribe from the channel completely, call
115+
* {@link com.pusher.client.Pusher#unsubscribe(String)}. It is not necessary
116+
* to unbind your {@link SubscriptionEventListener}s first.
117+
* </p>
118+
*
119+
* @param listener
120+
* The listener to unbind from the event.
121+
* @throws IllegalArgumentException
122+
* If the {@link SubscriptionEventListener} is null.
123+
* @throws IllegalStateException
124+
* If the channel has been unsubscribed by calling
125+
* {@link com.pusher.client.Pusher#unsubscribe(String)}. This
126+
* puts the {@linkplain Channel} in a terminal state from which
127+
* it can no longer be used. To resubscribe, call
128+
* {@link com.pusher.client.Pusher#subscribe(String)} or
129+
* {@link com.pusher.client.Pusher#subscribe(String, ChannelEventListener, String...)}
130+
* again to receive a fresh {@linkplain Channel} instance.
131+
*/
132+
void unbindGlobal(SubscriptionEventListener listener);
133+
83134
/**
84135
*
85136
* @return Whether or not the channel is subscribed.

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

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class ChannelImpl implements InternalChannel {
1717
private static final String INTERNAL_EVENT_PREFIX = "pusher_internal:";
1818
protected static final String SUBSCRIPTION_SUCCESS_EVENT = "pusher_internal:subscription_succeeded";
1919
protected final String name;
20+
private Set<SubscriptionEventListener> globalListeners = new HashSet<SubscriptionEventListener>();
2021
private final Map<String, Set<SubscriptionEventListener>> eventNameToListenerMap = new HashMap<String, Set<SubscriptionEventListener>>();
2122
protected volatile ChannelState state = ChannelState.INITIAL;
2223
private ChannelEventListener eventListener;
@@ -66,6 +67,15 @@ public void bind(final String eventName, final SubscriptionEventListener listene
6667
}
6768
}
6869

70+
@Override
71+
public void bindGlobal(SubscriptionEventListener listener) {
72+
validateArguments("", listener);
73+
74+
synchronized(lock) {
75+
globalListeners.add(listener);
76+
}
77+
}
78+
6979
@Override
7080
public void unbind(final String eventName, final SubscriptionEventListener listener) {
7181

@@ -82,6 +92,17 @@ public void unbind(final String eventName, final SubscriptionEventListener liste
8292
}
8393
}
8494

95+
@Override
96+
public void unbindGlobal(SubscriptionEventListener listener) {
97+
validateArguments("", listener);
98+
99+
synchronized(lock) {
100+
if (globalListeners != null) {
101+
globalListeners.remove(listener);
102+
}
103+
}
104+
}
105+
85106
@Override
86107
public boolean isSubscribed() {
87108
return state == ChannelState.SUBSCRIBED;
@@ -212,15 +233,23 @@ private void validateArguments(final String eventName, final SubscriptionEventLi
212233

213234
protected Set<SubscriptionEventListener> getInterestedListeners(String event) {
214235
synchronized (lock) {
215-
236+
Set<SubscriptionEventListener> listeners = new HashSet<SubscriptionEventListener>();
237+
216238
final Set<SubscriptionEventListener> sharedListeners =
217239
eventNameToListenerMap.get(event);
218240

219-
if (sharedListeners == null) {
241+
if (sharedListeners != null ) {
242+
listeners.addAll(sharedListeners);
243+
}
244+
if (!globalListeners.isEmpty()) {
245+
listeners.addAll(globalListeners);
246+
}
247+
248+
if (listeners.isEmpty()){
220249
return null;
221250
}
222251

223-
return new HashSet<>(sharedListeners);
252+
return listeners;
224253
}
225254
}
226255
}

src/test/java/com/pusher/client/channel/impl/ChannelImplTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ public void testDataIsExtractedFromMessageAndPassedToSingleListener() {
120120
assertEquals("{\"fish\":\"chips\"}", argCaptor.getValue().getData());
121121
}
122122
@Test
123+
public void testDataIsExtractedFromMessageAndPassedToSingleListenerGlobalEvent() {
124+
channel.bindGlobal(mockListener);
125+
channel.onMessage(EVENT_NAME, "{\"event\":\"event1\",\"data\":\"{\\\"fish\\\":\\\"chips\\\"}\"}");
126+
127+
verify(mockListener, times(1)).onEvent(argCaptor.capture());
128+
assertEquals("event1", argCaptor.getValue().getEventName());
129+
assertEquals("{\"fish\":\"chips\"}", argCaptor.getValue().getData());
130+
}
131+
@Test
123132
public void testDataIsExtractedFromMessageAndPassedToMultipleListeners() {
124133
final ChannelEventListener mockListener2 = getEventListener();
125134

@@ -154,6 +163,15 @@ public void testEventIsNotPassedOnIfListenerHasUnboundFromEvent() {
154163

155164
verify(mockListener, never()).onEvent(any(PusherEvent.class));
156165
}
166+
@Test
167+
public void testEventIsNotPassedOnIfListenerHasUnboundFromGlobalEvent() {
168+
169+
channel.bindGlobal(mockListener);
170+
channel.unbindGlobal(mockListener);
171+
channel.onMessage(EVENT_NAME, "{\"event\":\"event1\",\"data\":\"{\\\"fish\\\":\\\"chips\\\"}\"}");
172+
173+
verify(mockListener, never()).onEvent(any(PusherEvent.class));
174+
}
157175

158176
@Test(expected = IllegalArgumentException.class)
159177
public void testBindWithNullEventNameThrowsException() {

src/test/java/com/pusher/client/channel/impl/PrivateEncryptedChannelImplTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,36 @@ public void testDataIsExtractedFromMessageAndPassedToSingleListener() {
190190
assertEquals("{\"message\":\"hello world\"}", argCaptor.getValue().getData());
191191
}
192192

193+
@Test
194+
public void testDataIsExtractedFromMessageAndPassedToSingleListenerGlobalEvent() {
195+
PrivateEncryptedChannelImpl channel = new PrivateEncryptedChannelImpl(
196+
mockInternalConnection,
197+
getChannelName(),
198+
mockAuthorizer,
199+
factory,
200+
mockSecretBoxOpenerFactory);
201+
202+
when(mockAuthorizer.authorize(Matchers.anyString(), Matchers.anyString()))
203+
.thenReturn(AUTH_RESPONSE);
204+
when(mockSecretBoxOpenerFactory.create(any()))
205+
.thenReturn(new SecretBoxOpener(Base64.decode(SHARED_SECRET)));
206+
207+
channel.toSubscribeMessage();
208+
209+
PrivateEncryptedChannelEventListener mockListener = mock(PrivateEncryptedChannelEventListener.class);
210+
211+
channel.bindGlobal(mockListener);
212+
213+
channel.onMessage("my-event", "{\"event\":\"event1\",\"data\":\"{" +
214+
"\\\"nonce\\\": \\\"4sVYwy4j/8dCcjyxtPCWyk19GaaViaW9\\\"," +
215+
"\\\"ciphertext\\\": \\\"/GMESnFGlbNn01BuBjp31XYa3i9vZsGKR8fgR9EDhXKx3lzGiUD501A=\\\"" +
216+
"}\"}");
217+
218+
verify(mockListener, times(1)).onEvent(argCaptor.capture());
219+
assertEquals("event1", argCaptor.getValue().getEventName());
220+
assertEquals("{\"message\":\"hello world\"}", argCaptor.getValue().getData());
221+
}
222+
193223
@Test
194224
public void testDataIsExtractedFromMessageAndPassedToMultipleListeners() {
195225
PrivateEncryptedChannelImpl channel = new PrivateEncryptedChannelImpl(

0 commit comments

Comments
 (0)