• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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@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 {
Connection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(Connection connection) { new LastActivityManager(connection); } })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      */
LastActivityManager(Connection connection)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      */
resetIdleTime()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      */
getIdleTime()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      */
getLastActivity(Connection con, String jid)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      */
isLastActivitySupported(Connection connection, String jid)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