Home | History | Annotate | Download | only in smackx
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2003-2006 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.*;
     24 import org.jivesoftware.smack.filter.AndFilter;
     25 import org.jivesoftware.smack.filter.IQTypeFilter;
     26 import org.jivesoftware.smack.filter.PacketIDFilter;
     27 import org.jivesoftware.smack.filter.PacketTypeFilter;
     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.smack.packet.Presence;
     32 import org.jivesoftware.smackx.packet.DiscoverInfo;
     33 import org.jivesoftware.smackx.packet.LastActivity;
     34 
     35 /**
     36  * A last activity manager for handling information about the last activity
     37  * associated with a Jabber ID. A manager handles incoming LastActivity requests
     38  * of existing Connections. It also allows to request last activity information
     39  * of other users.
     40  * <p>
     41  *
     42  * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval
     43  * of:
     44  * <ol>
     45  * <li>How long a particular user has been idle
     46  * <li>How long a particular user has been logged-out and the message the
     47  * specified when doing so.
     48  * <li>How long a host has been up.
     49  * </ol>
     50  * <p/>
     51  *
     52  * For example to get the idle time of a user logged in a resource, simple send
     53  * the LastActivity packet to them, as in the following code:
     54  * <p>
     55  *
     56  * <pre>
     57  * Connection con = new XMPPConnection(&quot;jabber.org&quot;);
     58  * con.login(&quot;john&quot;, &quot;doe&quot;);
     59  * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray (at) jabber.org/Smack&quot;);
     60  * </pre>
     61  *
     62  * To get the lapsed time since the last user logout is the same as above but
     63  * with out the resource:
     64  *
     65  * <pre>
     66  * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray (at) jabber.org&quot;);
     67  * </pre>
     68  *
     69  * To get the uptime of a host, you simple send the LastActivity packet to it,
     70  * as in the following code example:
     71  * <p>
     72  *
     73  * <pre>
     74  * LastActivity activity = LastActivity.getLastActivity(con, &quot;jabber.org&quot;);
     75  * </pre>
     76  *
     77  * @author Gabriel Guardincerri
     78  * @see <a href="http://xmpp.org/extensions/xep-0012.html">XEP-0012: Last
     79  *      Activity</a>
     80  */
     81 
     82 public class LastActivityManager {
     83 
     84     private long lastMessageSent;
     85 
     86     private Connection connection;
     87 
     88     // Enable the LastActivity support on every established connection
     89     static {
     90         Connection.addConnectionCreationListener(new ConnectionCreationListener() {
     91             public void connectionCreated(Connection connection) {
     92                 new LastActivityManager(connection);
     93             }
     94         });
     95     }
     96 
     97     /**
     98      * Creates a last activity manager to response last activity requests.
     99      *
    100      * @param connection
    101      *            The Connection that the last activity requests will use.
    102      */
    103     private LastActivityManager(Connection connection) {
    104         this.connection = connection;
    105 
    106         // Listen to all the sent messages to reset the idle time on each one
    107         connection.addPacketSendingListener(new PacketListener() {
    108             public void processPacket(Packet packet) {
    109                 Presence presence = (Presence) packet;
    110                 Presence.Mode mode = presence.getMode();
    111                 if (mode == null) return;
    112                 switch (mode) {
    113                 case available:
    114                 case chat:
    115                     // We assume that only a switch to available and chat indicates user activity
    116                     // since other mode changes could be also a result of some sort of automatism
    117                     resetIdleTime();
    118                 }
    119             }
    120         }, new PacketTypeFilter(Presence.class));
    121 
    122         connection.addPacketListener(new PacketListener() {
    123             @Override
    124             public void processPacket(Packet packet) {
    125                 Message message = (Message) packet;
    126                 // if it's not an error message, reset the idle time
    127                 if (message.getType() == Message.Type.error) return;
    128                 resetIdleTime();
    129             }
    130         }, new PacketTypeFilter(Message.class));
    131 
    132         // Register a listener for a last activity query
    133         connection.addPacketListener(new PacketListener() {
    134 
    135             public void processPacket(Packet packet) {
    136                 LastActivity message = new LastActivity();
    137                 message.setType(IQ.Type.RESULT);
    138                 message.setTo(packet.getFrom());
    139                 message.setFrom(packet.getTo());
    140                 message.setPacketID(packet.getPacketID());
    141                 message.setLastActivity(getIdleTime());
    142 
    143                 LastActivityManager.this.connection.sendPacket(message);
    144             }
    145 
    146         }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class)));
    147         ServiceDiscoveryManager.getInstanceFor(connection).addFeature(LastActivity.NAMESPACE);
    148         resetIdleTime();
    149     }
    150 
    151     /**
    152      * Resets the idle time to 0, this should be invoked when a new message is
    153      * sent.
    154      */
    155     private void resetIdleTime() {
    156         long now = System.currentTimeMillis();
    157         synchronized (this) {
    158             lastMessageSent = now;
    159         }
    160     }
    161 
    162     /**
    163      * The idle time is the lapsed time between the last message sent and now.
    164      *
    165      * @return the lapsed time between the last message sent and now.
    166      */
    167     private long getIdleTime() {
    168         long lms;
    169         long now = System.currentTimeMillis();
    170         synchronized (this) {
    171             lms = lastMessageSent;
    172         }
    173         return ((now - lms) / 1000);
    174     }
    175 
    176     /**
    177      * Returns the last activity of a particular jid. If the jid is a full JID
    178      * (i.e., a JID of the form of 'user@host/resource') then the last activity
    179      * is the idle time of that connected resource. On the other hand, when the
    180      * jid is a bare JID (e.g. 'user@host') then the last activity is the lapsed
    181      * time since the last logout or 0 if the user is currently logged in.
    182      * Moreover, when the jid is a server or component (e.g., a JID of the form
    183      * 'host') the last activity is the uptime.
    184      *
    185      * @param con
    186      *            the current Connection.
    187      * @param jid
    188      *            the JID of the user.
    189      * @return the LastActivity packet of the jid.
    190      * @throws XMPPException
    191      *             thrown if a server error has occured.
    192      */
    193     public static LastActivity getLastActivity(Connection con, String jid) throws XMPPException {
    194         LastActivity activity = new LastActivity();
    195         activity.setTo(jid);
    196 
    197         PacketCollector collector = con.createPacketCollector(new PacketIDFilter(activity.getPacketID()));
    198         con.sendPacket(activity);
    199 
    200         LastActivity response = (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    201 
    202         // Cancel the collector.
    203         collector.cancel();
    204         if (response == null) {
    205             throw new XMPPException("No response from server on status set.");
    206         }
    207         if (response.getError() != null) {
    208             throw new XMPPException(response.getError());
    209         }
    210         return response;
    211     }
    212 
    213     /**
    214      * Returns true if Last Activity (XEP-0012) is supported by a given JID
    215      *
    216      * @param connection the connection to be used
    217      * @param jid a JID to be tested for Last Activity support
    218      * @return true if Last Activity is supported, otherwise false
    219      */
    220     public static boolean isLastActivitySupported(Connection connection, String jid) {
    221         try {
    222             DiscoverInfo result =
    223                 ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid);
    224             return result.containsFeature(LastActivity.NAMESPACE);
    225         }
    226         catch (XMPPException e) {
    227             return false;
    228         }
    229     }
    230 }
    231