RosterUtil.java

  1. /**
  2.  *
  3.  * Copyright 2016 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.roster;

  18. import java.util.Collection;
  19. import java.util.Date;
  20. import java.util.concurrent.TimeoutException;
  21. import java.util.concurrent.locks.Condition;
  22. import java.util.concurrent.locks.Lock;
  23. import java.util.concurrent.locks.ReentrantLock;

  24. import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
  25. import org.jivesoftware.smack.SmackException.NotConnectedException;
  26. import org.jivesoftware.smack.SmackException.NotLoggedInException;
  27. import org.jivesoftware.smack.XMPPConnection;
  28. import org.jivesoftware.smack.packet.Presence;

  29. import org.jxmpp.jid.BareJid;
  30. import org.jxmpp.jid.Jid;

  31. public class RosterUtil {

  32.     public static void waitUntilOtherEntityIsSubscribed(Roster roster, BareJid otherEntity, long timeoutMillis)
  33.                     throws InterruptedException, TimeoutException {
  34.         Date deadline = new Date(System.currentTimeMillis() + timeoutMillis);
  35.         waitUntilOtherEntityIsSubscribed(roster, otherEntity, deadline);
  36.     }

  37.     public static void waitUntilOtherEntityIsSubscribed(Roster roster, final BareJid otherEntity, Date deadline)
  38.                     throws InterruptedException, TimeoutException {
  39.         final Lock lock = new ReentrantLock();
  40.         final Condition maybeSubscribed = lock.newCondition();
  41.         RosterListener rosterListener = new AbstractRosterListener() {
  42.             private void signal() {
  43.                 lock.lock();
  44.                 try {
  45.                     // No need to use signalAll() here.
  46.                     maybeSubscribed.signal();
  47.                 }
  48.                 finally {
  49.                     lock.unlock();
  50.                 }
  51.             }

  52.             @Override
  53.             public void entriesAdded(Collection<Jid> addresses) {
  54.                 signal();
  55.             }

  56.             @Override
  57.             public void entriesUpdated(Collection<Jid> addresses) {
  58.                 signal();
  59.             }
  60.         };

  61.         roster.addRosterListener(rosterListener);

  62.         boolean stillWaiting = true;
  63.         // Using the example code pattern from Condition.awaitUntil(Date) javadoc.
  64.         lock.lock();
  65.         try {
  66.             while (!roster.isSubscribedToMyPresence(otherEntity)) {
  67.                 if (!stillWaiting) {
  68.                     throw new TimeoutException();
  69.                 }
  70.                 stillWaiting = maybeSubscribed.awaitUntil(deadline);
  71.             }
  72.         }
  73.         finally {
  74.             lock.unlock();
  75.             // Make sure the listener is removed, so we don't leak it.
  76.             roster.removeRosterListener(rosterListener);
  77.         }
  78.     }

  79.     /**
  80.      * Pre-approve the subscription if it is required and possible.
  81.      *
  82.      * @param roster The roster which should be used for the pre-approval.
  83.      * @param jid The XMPP address which should be pre-approved.
  84.      * @throws NotLoggedInException if the XMPP connection is not authenticated.
  85.      * @throws NotConnectedException if the XMPP connection is not connected.
  86.      * @throws InterruptedException if the calling thread was interrupted.
  87.      * @since 4.2.2
  88.      */
  89.     public static void preApproveSubscriptionIfRequiredAndPossible(Roster roster, BareJid jid)
  90.             throws NotLoggedInException, NotConnectedException, InterruptedException {
  91.         if (!roster.isSubscriptionPreApprovalSupported()) {
  92.             return;
  93.         }

  94.         RosterEntry entry = roster.getEntry(jid);
  95.         if (entry == null || (!entry.canSeeMyPresence() && !entry.isApproved())) {
  96.             try {
  97.                 roster.preApprove(jid);
  98.             } catch (FeatureNotSupportedException e) {
  99.                 // Should never happen since we checked for the feature above.
  100.                 throw new AssertionError(e);
  101.             }
  102.         }
  103.     }

  104.     public static void askForSubscriptionIfRequired(Roster roster, BareJid jid)
  105.             throws NotLoggedInException, NotConnectedException, InterruptedException {
  106.         RosterEntry entry = roster.getEntry(jid);
  107.         if (entry == null || !(entry.canSeeHisPresence() || entry.isSubscriptionPending())) {
  108.             roster.sendSubscriptionRequest(jid);
  109.         }
  110.     }

  111.     public static void ensureNotSubscribedToEachOther(XMPPConnection connectionOne, XMPPConnection connectionTwo)
  112.             throws NotConnectedException, InterruptedException {
  113.         final Roster rosterOne = Roster.getInstanceFor(connectionOne);
  114.         final BareJid jidOne = connectionOne.getUser().asBareJid();
  115.         final Roster rosterTwo = Roster.getInstanceFor(connectionTwo);
  116.         final BareJid jidTwo = connectionTwo.getUser().asBareJid();

  117.         ensureNotSubscribed(rosterOne, jidTwo);
  118.         ensureNotSubscribed(rosterTwo, jidOne);
  119.     }

  120.     public static void ensureNotSubscribed(Roster roster, BareJid jid)
  121.             throws NotConnectedException, InterruptedException {
  122.         RosterEntry entry = roster.getEntry(jid);
  123.         if (entry != null && entry.canSeeMyPresence()) {
  124.             entry.cancelSubscription();
  125.         }
  126.     }

  127.     public static void ensureSubscribed(XMPPConnection connectionOne, XMPPConnection connectionTwo, long timeout)
  128.                     throws NotLoggedInException, NotConnectedException, InterruptedException, TimeoutException {
  129.         ensureSubscribedTo(connectionOne, connectionTwo, timeout);
  130.         ensureSubscribedTo(connectionTwo, connectionOne, timeout);
  131.     }

  132.     public static void ensureSubscribedTo(XMPPConnection connectionOne, XMPPConnection connectionTwo, long timeout)
  133.                     throws NotLoggedInException, NotConnectedException, InterruptedException, TimeoutException {
  134.         Date deadline = new Date(System.currentTimeMillis() + timeout);
  135.         ensureSubscribedTo(connectionOne, connectionTwo, deadline);
  136.     }

  137.     public static void ensureSubscribedTo(final XMPPConnection connectionOne, final XMPPConnection connectionTwo,
  138.                     final Date deadline)
  139.                     throws NotLoggedInException, NotConnectedException, InterruptedException, TimeoutException {
  140.         final Roster rosterOne = Roster.getInstanceFor(connectionOne);
  141.         final BareJid jidTwo = connectionTwo.getUser().asBareJid();

  142.         if (rosterOne.iAmSubscribedTo(jidTwo))
  143.             return;

  144.         final BareJid jidOne = connectionOne.getUser().asBareJid();
  145.         final SubscribeListener subscribeListener = new SubscribeListener() {
  146.             @Override
  147.             public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
  148.                 if (from.equals(jidOne)) {
  149.                     return SubscribeAnswer.Approve;
  150.                 }
  151.                 return null;
  152.             }
  153.         };
  154.         final Roster rosterTwo = Roster.getInstanceFor(connectionTwo);

  155.         rosterTwo.addSubscribeListener(subscribeListener);
  156.         try {
  157.             rosterOne.sendSubscriptionRequest(jidTwo);
  158.             waitUntilOtherEntityIsSubscribed(rosterTwo, jidOne, deadline);
  159.         }
  160.         finally {
  161.             rosterTwo.removeSubscribeListener(subscribeListener);
  162.         }
  163.     }
  164. }