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("jabber.org"); 58 * con.login("john", "doe"); 59 * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org/Smack"); 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, "xray@jabber.org"); 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, "jabber.org"); 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