Home | History | Annotate | Download | only in smackx
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2003-2007 Jive Software.
      7  *
      8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *     http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 
     21 package org.jivesoftware.smackx;
     22 
     23 import org.jivesoftware.smack.PacketCollector;
     24 import org.jivesoftware.smack.SmackConfiguration;
     25 import org.jivesoftware.smack.Connection;
     26 import org.jivesoftware.smack.XMPPException;
     27 import org.jivesoftware.smack.filter.*;
     28 import org.jivesoftware.smack.packet.IQ;
     29 import org.jivesoftware.smack.packet.Message;
     30 import org.jivesoftware.smack.packet.Packet;
     31 import org.jivesoftware.smackx.packet.DiscoverInfo;
     32 import org.jivesoftware.smackx.packet.DiscoverItems;
     33 import org.jivesoftware.smackx.packet.OfflineMessageInfo;
     34 import org.jivesoftware.smackx.packet.OfflineMessageRequest;
     35 
     36 import java.util.ArrayList;
     37 import java.util.Iterator;
     38 import java.util.List;
     39 
     40 /**
     41  * The OfflineMessageManager helps manage offline messages even before the user has sent an
     42  * available presence. When a user asks for his offline messages before sending an available
     43  * presence then the server will not send a flood with all the offline messages when the user
     44  * becomes online. The server will not send a flood with all the offline messages to the session
     45  * that made the offline messages request or to any other session used by the user that becomes
     46  * online.<p>
     47  *
     48  * Once the session that made the offline messages request has been closed and the user becomes
     49  * offline in all the resources then the server will resume storing the messages offline and will
     50  * send all the offline messages to the user when he becomes online. Therefore, the server will
     51  * flood the user when he becomes online unless the user uses this class to manage his offline
     52  * messages.
     53  *
     54  * @author Gaston Dombiak
     55  */
     56 public class OfflineMessageManager {
     57 
     58     private final static String namespace = "http://jabber.org/protocol/offline";
     59 
     60     private Connection connection;
     61 
     62     private PacketFilter packetFilter;
     63 
     64     public OfflineMessageManager(Connection connection) {
     65         this.connection = connection;
     66         packetFilter =
     67                 new AndFilter(new PacketExtensionFilter("offline", namespace),
     68                         new PacketTypeFilter(Message.class));
     69     }
     70 
     71     /**
     72      * Returns true if the server supports Flexible Offline Message Retrieval. When the server
     73      * supports Flexible Offline Message Retrieval it is possible to get the header of the offline
     74      * messages, get specific messages, delete specific messages, etc.
     75      *
     76      * @return a boolean indicating if the server supports Flexible Offline Message Retrieval.
     77      * @throws XMPPException If the user is not allowed to make this request.
     78      */
     79     public boolean supportsFlexibleRetrieval() throws XMPPException {
     80         DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(connection.getServiceName());
     81         return info.containsFeature(namespace);
     82     }
     83 
     84     /**
     85      * Returns the number of offline messages for the user of the connection.
     86      *
     87      * @return the number of offline messages for the user of the connection.
     88      * @throws XMPPException If the user is not allowed to make this request or the server does
     89      *                       not support offline message retrieval.
     90      */
     91     public int getMessageCount() throws XMPPException {
     92         DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null,
     93                 namespace);
     94         Form extendedInfo = Form.getFormFrom(info);
     95         if (extendedInfo != null) {
     96             String value = extendedInfo.getField("number_of_messages").getValues().next();
     97             return Integer.parseInt(value);
     98         }
     99         return 0;
    100     }
    101 
    102     /**
    103      * Returns an iterator on <tt>OfflineMessageHeader</tt> that keep information about the
    104      * offline message. The OfflineMessageHeader includes a stamp that could be used to retrieve
    105      * the complete message or delete the specific message.
    106      *
    107      * @return an iterator on <tt>OfflineMessageHeader</tt> that keep information about the offline
    108      *         message.
    109      * @throws XMPPException If the user is not allowed to make this request or the server does
    110      *                       not support offline message retrieval.
    111      */
    112     public Iterator<OfflineMessageHeader> getHeaders() throws XMPPException {
    113         List<OfflineMessageHeader> answer = new ArrayList<OfflineMessageHeader>();
    114         DiscoverItems items = ServiceDiscoveryManager.getInstanceFor(connection).discoverItems(
    115                 null, namespace);
    116         for (Iterator<DiscoverItems.Item> it = items.getItems(); it.hasNext();) {
    117             DiscoverItems.Item item = it.next();
    118             answer.add(new OfflineMessageHeader(item));
    119         }
    120         return answer.iterator();
    121     }
    122 
    123     /**
    124      * Returns an Iterator with the offline <tt>Messages</tt> whose stamp matches the specified
    125      * request. The request will include the list of stamps that uniquely identifies
    126      * the offline messages to retrieve. The returned offline messages will not be deleted
    127      * from the server. Use {@link #deleteMessages(java.util.List)} to delete the messages.
    128      *
    129      * @param nodes the list of stamps that uniquely identifies offline message.
    130      * @return an Iterator with the offline <tt>Messages</tt> that were received as part of
    131      *         this request.
    132      * @throws XMPPException If the user is not allowed to make this request or the server does
    133      *                       not support offline message retrieval.
    134      */
    135     public Iterator<Message> getMessages(final List<String> nodes) throws XMPPException {
    136         List<Message> messages = new ArrayList<Message>();
    137         OfflineMessageRequest request = new OfflineMessageRequest();
    138         for (String node : nodes) {
    139             OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
    140             item.setAction("view");
    141             request.addItem(item);
    142         }
    143         // Filter packets looking for an answer from the server.
    144         PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
    145         PacketCollector response = connection.createPacketCollector(responseFilter);
    146         // Filter offline messages that were requested by this request
    147         PacketFilter messageFilter = new AndFilter(packetFilter, new PacketFilter() {
    148             public boolean accept(Packet packet) {
    149                 OfflineMessageInfo info = (OfflineMessageInfo) packet.getExtension("offline",
    150                         namespace);
    151                 return nodes.contains(info.getNode());
    152             }
    153         });
    154         PacketCollector messageCollector = connection.createPacketCollector(messageFilter);
    155         // Send the retrieval request to the server.
    156         connection.sendPacket(request);
    157         // Wait up to a certain number of seconds for a reply.
    158         IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
    159         // Stop queuing results
    160         response.cancel();
    161 
    162         if (answer == null) {
    163             throw new XMPPException("No response from server.");
    164         } else if (answer.getError() != null) {
    165             throw new XMPPException(answer.getError());
    166         }
    167 
    168         // Collect the received offline messages
    169         Message message = (Message) messageCollector.nextResult(
    170                 SmackConfiguration.getPacketReplyTimeout());
    171         while (message != null) {
    172             messages.add(message);
    173             message =
    174                     (Message) messageCollector.nextResult(
    175                             SmackConfiguration.getPacketReplyTimeout());
    176         }
    177         // Stop queuing offline messages
    178         messageCollector.cancel();
    179         return messages.iterator();
    180     }
    181 
    182     /**
    183      * Returns an Iterator with all the offline <tt>Messages</tt> of the user. The returned offline
    184      * messages will not be deleted from the server. Use {@link #deleteMessages(java.util.List)}
    185      * to delete the messages.
    186      *
    187      * @return an Iterator with all the offline <tt>Messages</tt> of the user.
    188      * @throws XMPPException If the user is not allowed to make this request or the server does
    189      *                       not support offline message retrieval.
    190      */
    191     public Iterator<Message> getMessages() throws XMPPException {
    192         List<Message> messages = new ArrayList<Message>();
    193         OfflineMessageRequest request = new OfflineMessageRequest();
    194         request.setFetch(true);
    195         // Filter packets looking for an answer from the server.
    196         PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
    197         PacketCollector response = connection.createPacketCollector(responseFilter);
    198         // Filter offline messages that were requested by this request
    199         PacketCollector messageCollector = connection.createPacketCollector(packetFilter);
    200         // Send the retrieval request to the server.
    201         connection.sendPacket(request);
    202         // Wait up to a certain number of seconds for a reply.
    203         IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
    204         // Stop queuing results
    205         response.cancel();
    206 
    207         if (answer == null) {
    208             throw new XMPPException("No response from server.");
    209         } else if (answer.getError() != null) {
    210             throw new XMPPException(answer.getError());
    211         }
    212 
    213         // Collect the received offline messages
    214         Message message = (Message) messageCollector.nextResult(
    215                 SmackConfiguration.getPacketReplyTimeout());
    216         while (message != null) {
    217             messages.add(message);
    218             message =
    219                     (Message) messageCollector.nextResult(
    220                             SmackConfiguration.getPacketReplyTimeout());
    221         }
    222         // Stop queuing offline messages
    223         messageCollector.cancel();
    224         return messages.iterator();
    225     }
    226 
    227     /**
    228      * Deletes the specified list of offline messages. The request will include the list of
    229      * stamps that uniquely identifies the offline messages to delete.
    230      *
    231      * @param nodes the list of stamps that uniquely identifies offline message.
    232      * @throws XMPPException If the user is not allowed to make this request or the server does
    233      *                       not support offline message retrieval.
    234      */
    235     public void deleteMessages(List<String> nodes) throws XMPPException {
    236         OfflineMessageRequest request = new OfflineMessageRequest();
    237         for (String node : nodes) {
    238             OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
    239             item.setAction("remove");
    240             request.addItem(item);
    241         }
    242         // Filter packets looking for an answer from the server.
    243         PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
    244         PacketCollector response = connection.createPacketCollector(responseFilter);
    245         // Send the deletion request to the server.
    246         connection.sendPacket(request);
    247         // Wait up to a certain number of seconds for a reply.
    248         IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
    249         // Stop queuing results
    250         response.cancel();
    251 
    252         if (answer == null) {
    253             throw new XMPPException("No response from server.");
    254         } else if (answer.getError() != null) {
    255             throw new XMPPException(answer.getError());
    256         }
    257     }
    258 
    259     /**
    260      * Deletes all offline messages of the user.
    261      *
    262      * @throws XMPPException If the user is not allowed to make this request or the server does
    263      *                       not support offline message retrieval.
    264      */
    265     public void deleteMessages() throws XMPPException {
    266         OfflineMessageRequest request = new OfflineMessageRequest();
    267         request.setPurge(true);
    268         // Filter packets looking for an answer from the server.
    269         PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
    270         PacketCollector response = connection.createPacketCollector(responseFilter);
    271         // Send the deletion request to the server.
    272         connection.sendPacket(request);
    273         // Wait up to a certain number of seconds for a reply.
    274         IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
    275         // Stop queuing results
    276         response.cancel();
    277 
    278         if (answer == null) {
    279             throw new XMPPException("No response from server.");
    280         } else if (answer.getError() != null) {
    281             throw new XMPPException(answer.getError());
    282         }
    283     }
    284 }
    285