Rework Roster's SubscribeListener

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.
This commit is contained in:
Florian Schmaus 2016-07-31 14:30:31 +02:00
parent 5b137616bb
commit f1e24e2273
3 changed files with 50 additions and 21 deletions

View File

@ -151,7 +151,7 @@ public final class IoTProvisioningManager extends Manager {
}); });
roster = Roster.getInstanceFor(connection); roster = Roster.getInstanceFor(connection);
roster.setSubscribeListener(new SubscribeListener() { roster.addSubscribeListener(new SubscribeListener() {
@Override @Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
// First check if the subscription request comes from a known registry and accept the request if so. // First check if the subscription request comes from a known registry and accept the request if so.

View File

@ -199,7 +199,9 @@ public final class Roster extends Manager {
private SubscriptionMode subscriptionMode = getDefaultSubscriptionMode(); 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 * Returns the default subscription processing mode to use when a new Roster is created. The
@ -249,11 +251,12 @@ public final class Roster extends Manager {
SubscribeAnswer subscribeAnswer = null; SubscribeAnswer subscribeAnswer = null;
switch (subscriptionMode) { switch (subscriptionMode) {
case manual: case manual:
final SubscribeListener subscribeListener = Roster.this.subscribeListener; for (SubscribeListener subscribeListener : subscribeListeners) {
if (subscribeListener == null) { subscribeAnswer = subscribeListener.processSubscribe(from, presence);
return; if (subscribeAnswer != null) {
break;
}
} }
subscribeAnswer = subscribeListener.processSubscribe(from, presence);
if (subscribeAnswer == null) { if (subscribeAnswer == null) {
return; return;
} }
@ -666,19 +669,37 @@ public final class Roster extends Manager {
} }
/** /**
* Set the subscribe listener, which is invoked on incoming subscription requests and if * Add a subscribe listener, which is invoked on incoming subscription requests and if
* {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. If * {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. This also sets subscription
* <code>subscribeListener</code> is not <code>null</code>, then this also sets subscription
* mode to {@link SubscriptionMode#manual}. * 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 * @since 4.2
*/ */
public void setSubscribeListener(SubscribeListener subscribeListener) { public boolean addSubscribeListener(SubscribeListener subscribeListener) {
if (subscribeListener != null) { Objects.requireNonNull(subscribeListener, "SubscribeListener argument must not be null");
setSubscriptionMode(SubscriptionMode.manual); if (subscriptionMode != SubscriptionMode.manual) {
previousSubscriptionMode = subscriptionMode;
} }
this.subscribeListener = subscribeListener; 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 boolean removeSubscribeListener(SubscribeListener subscribeListener) {
boolean removed = subscribeListeners.remove(subscribeListener);
if (removed && subscribeListeners.isEmpty()) {
setSubscriptionMode(previousSubscriptionMode);
}
return removed;
} }
/** /**

View File

@ -50,7 +50,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
public void subscribeRequestListenerTest() throws TimeoutException, Exception { public void subscribeRequestListenerTest() throws TimeoutException, Exception {
ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
rosterTwo.setSubscribeListener(new SubscribeListener() { final SubscribeListener subscribeListener = new SubscribeListener() {
@Override @Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conOne.getUser().asBareJid())) { if (from.equals(conOne.getUser().asBareJid())) {
@ -58,7 +58,8 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
} }
return SubscribeAnswer.Deny; return SubscribeAnswer.Deny;
} }
}); };
rosterTwo.addSubscribeListener(subscribeListener);
final String conTwosRosterName = "ConTwo " + testRunId; final String conTwosRosterName = "ConTwo " + testRunId;
final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint(); final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint();
@ -88,9 +89,15 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
} }
} }
}); });
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, public static void ensureBothAccountsAreNotInEachOthersRoster(XMPPConnection conOne, XMPPConnection conTwo) throws NotLoggedInException,
@ -124,7 +131,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
return; return;
} }
rosterOne.setSubscribeListener(new SubscribeListener() { final SubscribeListener subscribeListener = new SubscribeListener() {
@Override @Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conTwo.getUser().asBareJid())) { if (from.equals(conTwo.getUser().asBareJid())) {
@ -132,7 +139,8 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
} }
return SubscribeAnswer.Deny; return SubscribeAnswer.Deny;
} }
}); };
rosterOne.addSubscribeListener(subscribeListener);
final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint();
rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() { rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() {
@ -149,7 +157,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
try { try {
syncPoint.waitForResult(timeout); syncPoint.waitForResult(timeout);
} finally { } finally {
rosterOne.setSubscribeListener(null); rosterOne.removeSubscribeListener(subscribeListener);
} }
} }
} }