diff --git a/core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java b/core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java index f18d78f0d..40cdd2ce4 100644 --- a/core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java +++ b/core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java @@ -17,7 +17,9 @@ package org.jivesoftware.smack.filter; +import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Presence; /** * Filters for packets of a particular type. The type is given as a Class object, so @@ -32,6 +34,9 @@ import org.jivesoftware.smack.packet.Packet; */ public class PacketTypeFilter implements PacketFilter { + public static final PacketTypeFilter PRESENCE = new PacketTypeFilter(Presence.class); + public static final PacketTypeFilter MESSAGE = new PacketTypeFilter(Message.class); + Class packetType; /** diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java index 534e34a1f..b1fa8331a 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/LastActivityManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2006 Jive Software. + * Copyright 2003-2006 Jive Software, 2014 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,25 @@ package org.jivesoftware.smackx.iqlast; -import org.jivesoftware.smack.SmackException; +import java.util.Map; +import java.util.WeakHashMap; + import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.PacketListener; -import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.IQTypeFilter; +import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.iqlast.packet.LastActivity; /** @@ -79,25 +81,46 @@ import org.jivesoftware.smackx.iqlast.packet.LastActivity; * * * @author Gabriel Guardincerri + * @author Florian Schmaus * @see XEP-0012: Last * Activity */ -public class LastActivityManager { +public class LastActivityManager extends Manager { + private static final Map instances = new WeakHashMap(); + private static final PacketFilter IQ_GET_LAST_FILTER = new AndFilter(new IQTypeFilter( + IQ.Type.GET), new PacketTypeFilter(LastActivity.class)); - private long lastMessageSent; + private static boolean enabledPerDefault = true; - private XMPPConnection connection; + /** + * Enable or disable Last Activity for new XMPPConnections. + * + * @param enabledPerDefault + */ + public static void setEnabledPerDefault(boolean enabledPerDefault) { + LastActivityManager.enabledPerDefault = enabledPerDefault; + } // Enable the LastActivity support on every established connection static { XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(XMPPConnection connection) { - new LastActivityManager(connection); + LastActivityManager.getInstanceFor(connection); } }); } + public static synchronized LastActivityManager getInstanceFor(XMPPConnection connection) { + LastActivityManager lastActivityManager = instances.get(connection); + if (lastActivityManager == null) + lastActivityManager = new LastActivityManager(connection); + return lastActivityManager; + } + + private long lastMessageSent; + private boolean enabled = false; + /** * Creates a last activity manager to response last activity requests. * @@ -105,7 +128,7 @@ public class LastActivityManager { * The XMPPConnection that the last activity requests will use. */ private LastActivityManager(XMPPConnection connection) { - this.connection = connection; + super(connection); // Listen to all the sent messages to reset the idle time on each one connection.addPacketSendingListener(new PacketListener() { @@ -123,9 +146,9 @@ public class LastActivityManager { break; } } - }, new PacketTypeFilter(Presence.class)); + }, PacketTypeFilter.PRESENCE); - connection.addPacketListener(new PacketListener() { + connection.addPacketSendingListener(new PacketListener() { @Override public void processPacket(Packet packet) { Message message = (Message) packet; @@ -133,12 +156,13 @@ public class LastActivityManager { if (message.getType() == Message.Type.error) return; resetIdleTime(); } - }, new PacketTypeFilter(Message.class)); + }, PacketTypeFilter.MESSAGE); // Register a listener for a last activity query connection.addPacketListener(new PacketListener() { public void processPacket(Packet packet) throws NotConnectedException { + if (!enabled) return; LastActivity message = new LastActivity(); message.setType(IQ.Type.RESULT); message.setTo(packet.getFrom()); @@ -146,12 +170,26 @@ public class LastActivityManager { message.setPacketID(packet.getPacketID()); message.setLastActivity(getIdleTime()); - LastActivityManager.this.connection.sendPacket(message); + connection().sendPacket(message); } - }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class))); - ServiceDiscoveryManager.getInstanceFor(connection).addFeature(LastActivity.NAMESPACE); + }, IQ_GET_LAST_FILTER); + + if (enabledPerDefault) { + enable(); + } resetIdleTime(); + instances.put(connection, this); + } + + public synchronized void enable() { + ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(LastActivity.NAMESPACE); + enabled = true; + } + + public synchronized void disable() { + ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(LastActivity.NAMESPACE); + enabled = false; } /** @@ -188,8 +226,6 @@ public class LastActivityManager { * 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. @@ -198,31 +234,22 @@ public class LastActivityManager { * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException */ - public static LastActivity getLastActivity(XMPPConnection con, String jid) - throws NoResponseException, XMPPErrorException, NotConnectedException { - LastActivity activity = new LastActivity(); - activity.setTo(jid); - - LastActivity response = (LastActivity) con.createPacketCollectorAndSend(activity).nextResultOrThrow(); - return response; + public LastActivity getLastActivity(String jid) throws NoResponseException, XMPPErrorException, + NotConnectedException { + LastActivity activity = new LastActivity(jid); + return (LastActivity) connection().createPacketCollectorAndSend(activity).nextResultOrThrow(); } /** * Returns true if Last Activity (XEP-0012) is supported by a given JID * - * @param connection the connection to be used * @param jid a JID to be tested for Last Activity support * @return true if Last Activity is supported, otherwise false - * @throws SmackException if there was no response from the server. + * @throws NotConnectedException + * @throws XMPPErrorException + * @throws NoResponseException */ - public static boolean isLastActivitySupported(XMPPConnection connection, String jid) throws SmackException { - try { - DiscoverInfo result = - ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid); - return result.containsFeature(LastActivity.NAMESPACE); - } - catch (XMPPException e) { - return false; - } + public boolean isLastActivitySupported(String jid) throws NoResponseException, XMPPErrorException, NotConnectedException { + return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, LastActivity.NAMESPACE); } } diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java index c6478898f..933254b71 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software. + * Copyright 2003-2007 Jive Software, 2014 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.io.IOException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.util.XmlStringBuilder; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -32,6 +33,7 @@ import org.xmlpull.v1.XmlPullParserException; * to get the last activity of a user. * * @author Derek DeMoro + * @author Florian Schmaus */ public class LastActivity extends IQ { @@ -44,14 +46,23 @@ public class LastActivity extends IQ { setType(IQ.Type.GET); } - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append(""); - return buf.toString(); + // We don't support adding the optional message attribute, because it is usually only added + // by XMPP servers and not by client entities. + xml.closeEmptyElement(); + return xml; } @@ -103,22 +114,17 @@ public class LastActivity extends IQ { LastActivity lastActivity = new LastActivity(); String seconds = parser.getAttributeValue("", "seconds"); - String message = null; - try { - message = parser.nextText(); - } catch (IOException e1) { - // Ignore - } if (seconds != null) { try { lastActivity.setLastActivity(Long.parseLong(seconds)); } catch (NumberFormatException e) { - // Ignore + throw new SmackException("Could not parse last activity number", e); } } - - if (message != null) { - lastActivity.setMessage(message); + try { + lastActivity.setMessage(parser.nextText()); + } catch (IOException e) { + throw new SmackException(e); } return lastActivity; } diff --git a/extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java b/extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java new file mode 100644 index 000000000..16b730499 --- /dev/null +++ b/extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java @@ -0,0 +1,56 @@ +/** + * + * Copyright 2014 Florian Schmaus + * + * 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.iqlast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.jivesoftware.smack.DummyConnection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.InitExtensions; +import org.jivesoftware.smackx.iqlast.packet.LastActivity; +import org.junit.Test; + +import com.jamesmurty.utils.XMLBuilder; + +public class LastActivityTest extends InitExtensions { + + @Test + public void checkProvider() throws Exception { + XMLBuilder xml = XMLBuilder.create("iq"); + xml.a("from", "romeo@montague.net/orchard") + .a("id", "last2") + .a("to", "juliet@capulet.com/balcony") + .a("type", "get") + .e("query") + .namespace(LastActivity.NAMESPACE); + + DummyConnection c = new DummyConnection(); + IQ lastRequest = PacketParserUtils.parseIQ(TestUtils.getIQParser(xml.asString()), c); + assertTrue(lastRequest instanceof LastActivity); + + c.processPacket(lastRequest);; + Packet reply = c.getSentPacket(); + assertTrue(reply instanceof LastActivity); + LastActivity l = (LastActivity) reply; + assertEquals("last2", l.getPacketID()); + assertEquals(IQ.Type.RESULT, l.getType()); + } +}