OfflineMessageManager.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software.
  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.smackx.offline;

  18. import org.jivesoftware.smack.PacketCollector;
  19. import org.jivesoftware.smack.SmackException.NoResponseException;
  20. import org.jivesoftware.smack.SmackException.NotConnectedException;
  21. import org.jivesoftware.smack.XMPPConnection;
  22. import org.jivesoftware.smack.XMPPException.XMPPErrorException;
  23. import org.jivesoftware.smack.filter.AndFilter;
  24. import org.jivesoftware.smack.filter.StanzaExtensionFilter;
  25. import org.jivesoftware.smack.filter.StanzaFilter;
  26. import org.jivesoftware.smack.filter.StanzaTypeFilter;
  27. import org.jivesoftware.smack.packet.Message;
  28. import org.jivesoftware.smack.packet.Stanza;
  29. import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
  30. import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
  31. import org.jivesoftware.smackx.disco.packet.DiscoverItems;
  32. import org.jivesoftware.smackx.offline.packet.OfflineMessageInfo;
  33. import org.jivesoftware.smackx.offline.packet.OfflineMessageRequest;
  34. import org.jivesoftware.smackx.xdata.Form;

  35. import java.util.ArrayList;
  36. import java.util.List;

  37. /**
  38.  * The OfflineMessageManager helps manage offline messages even before the user has sent an
  39.  * available presence. When a user asks for his offline messages before sending an available
  40.  * presence then the server will not send a flood with all the offline messages when the user
  41.  * becomes online. The server will not send a flood with all the offline messages to the session
  42.  * that made the offline messages request or to any other session used by the user that becomes
  43.  * online.<p>
  44.  *
  45.  * Once the session that made the offline messages request has been closed and the user becomes
  46.  * offline in all the resources then the server will resume storing the messages offline and will
  47.  * send all the offline messages to the user when he becomes online. Therefore, the server will
  48.  * flood the user when he becomes online unless the user uses this class to manage his offline
  49.  * messages.
  50.  *
  51.  * @author Gaston Dombiak
  52.  */
  53. public class OfflineMessageManager {

  54.     private final static String namespace = "http://jabber.org/protocol/offline";

  55.     private final XMPPConnection connection;

  56.     private static final StanzaFilter PACKET_FILTER = new AndFilter(new StanzaExtensionFilter(
  57.                     new OfflineMessageInfo()), StanzaTypeFilter.MESSAGE);

  58.     public OfflineMessageManager(XMPPConnection connection) {
  59.         this.connection = connection;
  60.     }

  61.     /**
  62.      * Returns true if the server supports Flexible Offline Message Retrieval. When the server
  63.      * supports Flexible Offline Message Retrieval it is possible to get the header of the offline
  64.      * messages, get specific messages, delete specific messages, etc.
  65.      *
  66.      * @return a boolean indicating if the server supports Flexible Offline Message Retrieval.
  67.      * @throws XMPPErrorException If the user is not allowed to make this request.
  68.      * @throws NoResponseException if there was no response from the server.
  69.      * @throws NotConnectedException
  70.      * @throws InterruptedException
  71.      */
  72.     public boolean supportsFlexibleRetrieval() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  73.         return ServiceDiscoveryManager.getInstanceFor(connection).serverSupportsFeature(namespace);
  74.     }

  75.     /**
  76.      * Returns the number of offline messages for the user of the connection.
  77.      *
  78.      * @return the number of offline messages for the user of the connection.
  79.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  80.      *                       not support offline message retrieval.
  81.      * @throws NoResponseException if there was no response from the server.
  82.      * @throws NotConnectedException
  83.      * @throws InterruptedException
  84.      */
  85.     public int getMessageCount() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  86.         DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null,
  87.                 namespace);
  88.         Form extendedInfo = Form.getFormFrom(info);
  89.         if (extendedInfo != null) {
  90.             String value = extendedInfo.getField("number_of_messages").getValues().get(0);
  91.             return Integer.parseInt(value);
  92.         }
  93.         return 0;
  94.     }

  95.     /**
  96.      * Returns a List of <tt>OfflineMessageHeader</tt> that keep information about the
  97.      * offline message. The OfflineMessageHeader includes a stamp that could be used to retrieve
  98.      * the complete message or delete the specific message.
  99.      *
  100.      * @return a List of <tt>OfflineMessageHeader</tt> that keep information about the offline
  101.      *         message.
  102.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  103.      *                       not support offline message retrieval.
  104.      * @throws NoResponseException if there was no response from the server.
  105.      * @throws NotConnectedException
  106.      * @throws InterruptedException
  107.      */
  108.     public List<OfflineMessageHeader> getHeaders() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  109.         List<OfflineMessageHeader> answer = new ArrayList<OfflineMessageHeader>();
  110.         DiscoverItems items = ServiceDiscoveryManager.getInstanceFor(connection).discoverItems(
  111.                 null, namespace);
  112.         for (DiscoverItems.Item item : items.getItems()) {
  113.             answer.add(new OfflineMessageHeader(item));
  114.         }
  115.         return answer;
  116.     }

  117.     /**
  118.      * Returns a List of the offline <tt>Messages</tt> whose stamp matches the specified
  119.      * request. The request will include the list of stamps that uniquely identifies
  120.      * the offline messages to retrieve. The returned offline messages will not be deleted
  121.      * from the server. Use {@link #deleteMessages(java.util.List)} to delete the messages.
  122.      *
  123.      * @param nodes the list of stamps that uniquely identifies offline message.
  124.      * @return a List with the offline <tt>Messages</tt> that were received as part of
  125.      *         this request.
  126.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  127.      *                       not support offline message retrieval.
  128.      * @throws NoResponseException if there was no response from the server.
  129.      * @throws NotConnectedException
  130.      * @throws InterruptedException
  131.      */
  132.     public List<Message> getMessages(final List<String> nodes) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  133.         List<Message> messages = new ArrayList<Message>();
  134.         OfflineMessageRequest request = new OfflineMessageRequest();
  135.         for (String node : nodes) {
  136.             OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
  137.             item.setAction("view");
  138.             request.addItem(item);
  139.         }
  140.         // Filter offline messages that were requested by this request
  141.         StanzaFilter messageFilter = new AndFilter(PACKET_FILTER, new StanzaFilter() {
  142.             public boolean accept(Stanza packet) {
  143.                 OfflineMessageInfo info = (OfflineMessageInfo) packet.getExtension("offline",
  144.                         namespace);
  145.                 return nodes.contains(info.getNode());
  146.             }
  147.         });
  148.         PacketCollector messageCollector = connection.createPacketCollector(messageFilter);
  149.         try {
  150.             connection.createPacketCollectorAndSend(request).nextResultOrThrow();
  151.             // Collect the received offline messages
  152.             Message message = messageCollector.nextResult();
  153.             while (message != null) {
  154.                 messages.add(message);
  155.                 message = messageCollector.nextResult();
  156.             }
  157.         }
  158.         finally {
  159.             // Stop queuing offline messages
  160.             messageCollector.cancel();
  161.         }
  162.         return messages;
  163.     }

  164.     /**
  165.      * Returns a List of Messages with all the offline <tt>Messages</tt> of the user. The returned offline
  166.      * messages will not be deleted from the server. Use {@link #deleteMessages(java.util.List)}
  167.      * to delete the messages.
  168.      *
  169.      * @return a List with all the offline <tt>Messages</tt> of the user.
  170.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  171.      *                       not support offline message retrieval.
  172.      * @throws NoResponseException if there was no response from the server.
  173.      * @throws NotConnectedException
  174.      * @throws InterruptedException
  175.      */
  176.     public List<Message> getMessages() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  177.         OfflineMessageRequest request = new OfflineMessageRequest();
  178.         request.setFetch(true);

  179.         PacketCollector resultCollector = connection.createPacketCollectorAndSend(request);
  180.         PacketCollector.Configuration messageCollectorConfiguration = PacketCollector.newConfiguration().setStanzaFilter(PACKET_FILTER).setCollectorToReset(resultCollector);
  181.         PacketCollector messageCollector = connection.createPacketCollector(messageCollectorConfiguration);

  182.         List<Message> messages = null;
  183.         try {
  184.             resultCollector.nextResultOrThrow();
  185.             // Be extra safe, cancel the message collector right here so that it does not collector
  186.             // other messages that eventually match (although I've no idea how this could happen in
  187.             // case of XEP-13).
  188.             messageCollector.cancel();
  189.             messages = new ArrayList<>(messageCollector.getCollectedCount());
  190.             Message message;
  191.             while ((message = messageCollector.pollResult()) != null) {
  192.                 messages.add(message);
  193.             }
  194.         }
  195.         finally {
  196.             // Ensure that the message collector is canceled even if nextResultOrThrow threw. It
  197.             // doesn't matter if we cancel the message collector twice
  198.             messageCollector.cancel();
  199.         }
  200.         return messages;
  201.     }

  202.     /**
  203.      * Deletes the specified list of offline messages. The request will include the list of
  204.      * stamps that uniquely identifies the offline messages to delete.
  205.      *
  206.      * @param nodes the list of stamps that uniquely identifies offline message.
  207.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  208.      *                       not support offline message retrieval.
  209.      * @throws NoResponseException if there was no response from the server.
  210.      * @throws NotConnectedException
  211.      * @throws InterruptedException
  212.      */
  213.     public void deleteMessages(List<String> nodes) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  214.         OfflineMessageRequest request = new OfflineMessageRequest();
  215.         for (String node : nodes) {
  216.             OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
  217.             item.setAction("remove");
  218.             request.addItem(item);
  219.         }
  220.         connection.createPacketCollectorAndSend(request).nextResultOrThrow();
  221.     }

  222.     /**
  223.      * Deletes all offline messages of the user.
  224.      *
  225.      * @throws XMPPErrorException If the user is not allowed to make this request or the server does
  226.      *                       not support offline message retrieval.
  227.      * @throws NoResponseException if there was no response from the server.
  228.      * @throws NotConnectedException
  229.      * @throws InterruptedException
  230.      */
  231.     public void deleteMessages() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
  232.         OfflineMessageRequest request = new OfflineMessageRequest();
  233.         request.setPurge(true);
  234.         connection.createPacketCollectorAndSend(request).nextResultOrThrow();
  235.     }
  236. }