From 87b3c84f046d58729e218963ecc69db56f2de37e Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Mon, 4 Sep 2006 22:03:48 +0000 Subject: [PATCH] Initial version. SMACK-94 git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@5220 b35dd754-fafc-0310-a699-88a17e54d16e --- .../smackx/LastActivityManager.java | 172 ++++++++++++++++++ .../smackx/LastActivityManagerTest.java | 160 ++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 source/org/jivesoftware/smackx/LastActivityManager.java create mode 100644 test/org/jivesoftware/smackx/LastActivityManagerTest.java diff --git a/source/org/jivesoftware/smackx/LastActivityManager.java b/source/org/jivesoftware/smackx/LastActivityManager.java new file mode 100644 index 000000000..da9393012 --- /dev/null +++ b/source/org/jivesoftware/smackx/LastActivityManager.java @@ -0,0 +1,172 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: $ + * + * Copyright 2003-2006 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.IQTypeFilter; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.filter.PacketTypeFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.packet.LastActivity; + +/** + * A last activity manager for handling information about the last activity associated + * with a Jabber ID. A manager handles incoming LastActivity requests of existing + * XMPPConnections. It also allows to request last activity information of other users.

+ * + * LastActivity (JEP-012) based on the sending JID's type allows for retrieval of: + *

    + *
  1. How long a particular user has been idle + *
  2. How long a particular user has been logged-out and the message the specified when doing so. + *
  3. How long a host has been up. + *
+ *

+ * + * For example to get the idle time of a user logged in a resource, simple send the + * LastActivity packet to them, as in the following code:

+ * + *

+ * XMPPConnection con = new XMPPConnection("jabber.org");
+ * con.login("john", "doe");
+ * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org/Smack");
+ * 
+ * + * To get the lapsed time since the last user logout is the same as above but with + * out the resource: + *
+ * LastActivity activity = LastActivity.getLastActivity(con, "xray@jabber.org");
+ * 
+ * + * To get the uptime of a host, you simple send the LastActivity packet to it, as in the + * following code example:

+ * + *

+ * LastActivity activity = LastActivity.getLastActivity(con, "jabber.org");
+ * 
+ * + * @author Gabriel Guardincerri + */ + +public class LastActivityManager { + + private long lastMessageSent; + + private XMPPConnection connection; + + // Enable the LastActivity support on every established connection + static { + XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() { + public void connectionEstablished(XMPPConnection connection) { + new LastActivityManager(connection); + } + }); + } + + /** + * Creates a last activity manager to response last activity requests. + * + * @param connection The XMPPConnection that the last activity requests will use. + */ + private LastActivityManager(XMPPConnection connection) { + this.connection = connection; + + // Listen to all the sent messages to reset the idle time on each one + connection.addPacketWriterListener(new PacketListener() { + public void processPacket(Packet packet) { + resetIdleTime(); + } + }, null); + + // Register a listener for a last activity query + connection.addPacketListener(new PacketListener() { + + public void processPacket(Packet packet) { + LastActivity message = new LastActivity(); + message.setType(IQ.Type.RESULT); + message.setTo(packet.getFrom()); + message.setFrom(packet.getTo()); + message.setPacketID(packet.getPacketID()); + message.setLastActivity(getIdleTime()); + + LastActivityManager.this.connection.sendPacket(message); + } + + }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class))); + } + + /** + * Resets the idle time to 0, this should be invoked when a new message is + * sent. + */ + private void resetIdleTime() { + lastMessageSent = System.currentTimeMillis(); + } + + /** + * The idle time is the lapsed time between the last message sent and now. + * + * @return the lapsed time between the last message sent and now. + */ + private long getIdleTime() { + long now = System.currentTimeMillis(); + return ((now - lastMessageSent) / 1000); + } + + /** + * Returns the last activity of a particular jid. If the jid is a full JID + * (i.e., a JID of the form of 'user@host/resource') then the last activity + * is the idle time of that connected resource. On the other hand, when the + * jid is a bare JID (e.g. 'user@host') then the last activity is the lapsed + * time since the last logout or 0 if the user is currently logged in. Moreover, + * when the jid is a server or component (e.g., a JID of the form 'host') the + * last activity is the uptime. + * + * @param con the current XMPPConnection. + * @param jid the JID of the user. + * @return the LastActivity packet of the jid. + * @throws XMPPException thrown if a server error has occured. + */ + public static LastActivity getLastActivity(XMPPConnection con, String jid) + throws XMPPException { + LastActivity activity = new LastActivity(); + activity.setTo(jid); + + PacketCollector collector = + con.createPacketCollector(new PacketIDFilter(activity.getPacketID())); + con.sendPacket(activity); + + LastActivity response = + (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + if (response == null) { + throw new XMPPException("No response from server on status set."); + } + if (response.getError() != null) { + throw new XMPPException(response.getError()); + } + return response; + } + +} diff --git a/test/org/jivesoftware/smackx/LastActivityManagerTest.java b/test/org/jivesoftware/smackx/LastActivityManagerTest.java new file mode 100644 index 000000000..c5f75c514 --- /dev/null +++ b/test/org/jivesoftware/smackx/LastActivityManagerTest.java @@ -0,0 +1,160 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: $ + * + * Copyright 2003-2006 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smackx.packet.LastActivity; + +public class LastActivityManagerTest extends SmackTestCase { + + /** + * This is a test to check if a LastActivity request for idle time is + * answered and correct. + */ + public void testOnline() { + XMPPConnection conn0 = getConnection(0); + XMPPConnection conn1 = getConnection(1); + + // Send a message as the last activity action from connection 1 to + // connection 0 + conn1.sendPacket(new Message(getBareJID(0))); + + // Wait 1 seconds to have some idle time + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + fail("Thread sleep interrupted"); + } + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getFullJID(1)); + } catch (XMPPException e) { + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + // Asserts that the last activity packet was received + assertNotNull("No last activity packet", lastActivity); + // Asserts that there is at least a 1 second of idle time + assertTrue( + "The last activity idle time is less than expected: " + lastActivity.getIdleTime(), + lastActivity.getIdleTime() >= 1); + } + + /** + * This is a test to check if a denied LastActivity response is handled correctly. + */ + public void testOnlinePermisionDenied() { + XMPPConnection conn0 = getConnection(0); + XMPPConnection conn2 = getConnection(2); + + // Send a message as the last activity action from connection 2 to + // connection 0 + conn2.sendPacket(new Message(getBareJID(0))); + + // Wait 1 seconds to have some idle time + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + fail("Thread sleep interrupted"); + } + + try { + LastActivityManager.getLastActivity(conn0, getFullJID(2)); + fail("No error was received from the server. User was able to get info of other user not in his roster."); + } catch (XMPPException e) { + assertNotNull("No error was returned from the server", e.getXMPPError()); + assertEquals("Forbidden error was not returned from the server", 403, + e.getXMPPError().getCode()); + } + } + + /** + * This is a test to check if a LastActivity request for last logged out + * lapsed time is answered and correct + */ + public void testLastLoggedOut() { + XMPPConnection conn0 = getConnection(0); + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getBareJID(1)); + } catch (XMPPException e) { + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + assertNotNull("No last activity packet", lastActivity); + assertTrue("The last activity idle time should be 0 since the user is logged in: " + + lastActivity.getIdleTime(), lastActivity.getIdleTime() == 0); + } + + /** + * This is a test to check if a LastActivity request for server uptime + * is answered and correct + */ + public void testServerUptime() { + XMPPConnection conn0 = getConnection(0); + + LastActivity lastActivity = null; + try { + lastActivity = LastActivityManager.getLastActivity(conn0, getHost()); + } catch (XMPPException e) { + if (e.getXMPPError().getCode() == 403) { + //The test can not be done since the host do not allow this kind of request + return; + } + e.printStackTrace(); + fail("An error occurred requesting the Last Activity"); + } + + assertNotNull("No last activity packet", lastActivity); + assertTrue("The last activity idle time should be greater than 0 : " + + lastActivity.getIdleTime(), lastActivity.getIdleTime() > 0); + } + + public LastActivityManagerTest(String name) { + super(name); + } + + @Override + protected int getMaxConnections() { + return 3; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + getConnection(0).getRoster().createEntry(getBareJID(1), "User1", null); + Thread.sleep(300); + } catch (Exception e) { + fail(e.getMessage()); + } + } + +}