Skip to content

Commit

Permalink
Rework Roster's SubscribeListener
Browse files Browse the repository at this point in the history
allow multiple of them to be installed, instead of at most one. Fixes
deadlock in LowLevelRosterIntegration test because
IoTProvisioningManager's SubscribeListener would not come up with a
decission.
  • Loading branch information
Flowdalic committed Jul 31, 2016
1 parent 5b13761 commit f1e24e2
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 21 deletions.
Expand Up @@ -151,7 +151,7 @@ public IQ handleIQRequest(IQ iqRequest) {
});

roster = Roster.getInstanceFor(connection);
roster.setSubscribeListener(new SubscribeListener() {
roster.addSubscribeListener(new SubscribeListener() {
@Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
// First check if the subscription request comes from a known registry and accept the request if so.
Expand Down
47 changes: 34 additions & 13 deletions smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java
Expand Up @@ -199,7 +199,9 @@ private enum RosterState {

private SubscriptionMode subscriptionMode = getDefaultSubscriptionMode();

private SubscribeListener subscribeListener;
private final Set<SubscribeListener> subscribeListeners = new CopyOnWriteArraySet<>();

private SubscriptionMode previousSubscriptionMode;

/**
* Returns the default subscription processing mode to use when a new Roster is created. The
Expand Down Expand Up @@ -249,11 +251,12 @@ public void processPacket(Stanza stanza) throws NotConnectedException,
SubscribeAnswer subscribeAnswer = null;
switch (subscriptionMode) {
case manual:
final SubscribeListener subscribeListener = Roster.this.subscribeListener;
if (subscribeListener == null) {
return;
for (SubscribeListener subscribeListener : subscribeListeners) {
subscribeAnswer = subscribeListener.processSubscribe(from, presence);
if (subscribeAnswer != null) {
break;
}
}
subscribeAnswer = subscribeListener.processSubscribe(from, presence);
if (subscribeAnswer == null) {
return;
}
Expand Down Expand Up @@ -666,19 +669,37 @@ public void sendSubscriptionRequest(BareJid jid) throws NotLoggedInException, No
}

/**
* Set the subscribe listener, which is invoked on incoming subscription requests and if
* {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. If
* <code>subscribeListener</code> is not <code>null</code>, then this also sets subscription
* Add a subscribe listener, which is invoked on incoming subscription requests and if
* {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. This also sets subscription
* mode to {@link SubscriptionMode#manual}.
*
* @param subscribeListener the subscribe listener to set.
* @param subscribeListener the subscribe listener to add.
* @return <code>true</code> if the listener was not already added.
* @since 4.2
*/
public boolean addSubscribeListener(SubscribeListener subscribeListener) {
Objects.requireNonNull(subscribeListener, "SubscribeListener argument must not be null");
if (subscriptionMode != SubscriptionMode.manual) {
previousSubscriptionMode = subscriptionMode;
}
return subscribeListeners.add(subscribeListener);
}

/**
* Remove a subscribe listener. Also restores the previous subscription mode
* state, if the last listener got removed.
*
* @param subscribeListener
* the subscribe listener to remove.
* @return <code>true</code> if the listener registered and got removed.
* @since 4.2
*/
public void setSubscribeListener(SubscribeListener subscribeListener) {
if (subscribeListener != null) {
setSubscriptionMode(SubscriptionMode.manual);
public boolean removeSubscribeListener(SubscribeListener subscribeListener) {
boolean removed = subscribeListeners.remove(subscribeListener);
if (removed && subscribeListeners.isEmpty()) {
setSubscriptionMode(previousSubscriptionMode);
}
this.subscribeListener = subscribeListener;
return removed;
}

/**
Expand Down
Expand Up @@ -50,15 +50,16 @@ public RosterIntegrationTest(SmackIntegrationTestEnvironment environment) {
public void subscribeRequestListenerTest() throws TimeoutException, Exception {
ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);

rosterTwo.setSubscribeListener(new SubscribeListener() {
final SubscribeListener subscribeListener = new SubscribeListener() {
@Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conOne.getUser().asBareJid())) {
return SubscribeAnswer.Approve;
}
return SubscribeAnswer.Deny;
}
});
};
rosterTwo.addSubscribeListener(subscribeListener);

final String conTwosRosterName = "ConTwo " + testRunId;
final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint();
Expand Down Expand Up @@ -88,9 +89,15 @@ private void checkIfAddedAndSubscribed(Collection<Jid> addresses) {
}
}
});
rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null);

assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout()));
try {
rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null);

assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout()));
}
finally {
rosterTwo.removeSubscribeListener(subscribeListener);
}
}

public static void ensureBothAccountsAreNotInEachOthersRoster(XMPPConnection conOne, XMPPConnection conTwo) throws NotLoggedInException,
Expand Down Expand Up @@ -124,15 +131,16 @@ private static void ensureSubscribedTo(final XMPPConnection conOne, final XMPPCo
return;
}

rosterOne.setSubscribeListener(new SubscribeListener() {
final SubscribeListener subscribeListener = new SubscribeListener() {
@Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conTwo.getUser().asBareJid())) {
return SubscribeAnswer.Approve;
}
return SubscribeAnswer.Deny;
}
});
};
rosterOne.addSubscribeListener(subscribeListener);

final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint();
rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() {
Expand All @@ -149,7 +157,7 @@ public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
try {
syncPoint.waitForResult(timeout);
} finally {
rosterOne.setSubscribeListener(null);
rosterOne.removeSubscribeListener(subscribeListener);
}
}
}

0 comments on commit f1e24e2

Please sign in to comment.