From 62de78ef7c9be86cdc1321d5f2e32e82515ee786 Mon Sep 17 00:00:00 2001 From: Georg Lukas Date: Wed, 6 Nov 2013 17:54:30 +0100 Subject: [PATCH] SMACK-53: create VersionProvider and VersionManager The VersionProvider is used to parse Version IQs. Integration: providerManager.addIQProvider("query", Version.NAMESPACE, new VersionProvider()); The VersionManager is used to reply to Version IQs. Integration: VersionManger.getInstanceFor(connection).setVersion( new Version("App Name", "1.23", "Operating System")); --- .../smackx/iqversion/VersionManager.java | 95 +++++++++++++++++++ .../smackx/iqversion/packet/Version.java | 31 +++++- .../iqversion/provider/VersionProvider.java | 49 ++++++++++ .../extensions.providers | 2 +- .../smackx/iqversion/VersionTest.java | 72 ++++++++++++++ 5 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java create mode 100644 extensions/src/main/java/org/jivesoftware/smackx/iqversion/provider/VersionProvider.java create mode 100644 extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java new file mode 100644 index 000000000..547f3f0a7 --- /dev/null +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/VersionManager.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2014 Georg Lukas. + * + * 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.iqversion; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.IQTypeFilter; +import org.jivesoftware.smack.filter.PacketTypeFilter; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.iqversion.packet.Version; + +/** + * A Version Manager that automatically responds to version IQs with a predetermined reply. + * + *

+ * The VersionManager takes care of handling incoming version request IQs, according to + * XEP-0092 (Software Version). You can configure the version reply for a given connection + * by running the following code: + *

+ * + *
+ * Version MY_VERSION = new Version("My Little XMPP Application", "v1.23", "OS/2 32-bit");
+ * VersionManager.getInstanceFor(mConnection).setVersion(MY_VERSION);
+ * 
+ * + * @author Georg Lukas + */ +public class VersionManager { + private static final Map instances = + Collections.synchronizedMap(new WeakHashMap()); + + private Version own_version; + private WeakReference weakRefConnection; + + private VersionManager(final Connection connection) { + this.weakRefConnection = new WeakReference(connection); + instances.put(connection, this); + + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + sdm.addFeature(Version.NAMESPACE); + + connection.addPacketListener(new PacketListener() { + /** + * Sends a Version reply on request + */ + public void processPacket(Packet packet) { + if (own_version == null) + return; + + Version reply = new Version(own_version); + reply.setPacketID(packet.getPacketID()); + reply.setTo(packet.getFrom()); + weakRefConnection.get().sendPacket(reply); + } + } + , new AndFilter(new PacketTypeFilter(Version.class), new IQTypeFilter(Type.GET))); + } + + public static synchronized VersionManager getInstanceFor(Connection connection) { + VersionManager versionManager = instances.get(connection); + + if (versionManager == null) { + versionManager = new VersionManager(connection); + } + + return versionManager; + } + + public void setVersion(Version v) { + own_version = v; + } +} diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java index b687218d2..869bb17e6 100644 --- a/extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java @@ -17,7 +17,9 @@ package org.jivesoftware.smackx.iqversion.packet; + import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.StringUtils; /** * A Version IQ packet, which is used by XMPP clients to discover version information @@ -46,11 +48,30 @@ import org.jivesoftware.smack.packet.IQ; * @author Gaston Dombiak */ public class Version extends IQ { + public static final String NAMESPACE = "jabber:iq:version"; private String name; private String version; private String os; + /** + * Creates a new Version object with given details. + * + * @param name The natural-language name of the software. This element is REQUIRED. + * @param version The specific version of the software. This element is REQUIRED. + * @param os The operating system of the queried entity. This element is OPTIONAL. + */ + public Version(String name, String version, String os) { + this.setType(IQ.Type.RESULT); + this.name = name; + this.version = version; + this.os = os; + } + + public Version(Version original) { + this(original.name, original.version, original.os); + } + /** * Returns the natural-language name of the software. This property will always be * present in a result. @@ -113,15 +134,17 @@ public class Version extends IQ { public String getChildElementXML() { StringBuilder buf = new StringBuilder(); - buf.append(""); + buf.append(""); if (name != null) { - buf.append("").append(name).append(""); + buf.append("").append(StringUtils.escapeForXML(name)).append(""); } if (version != null) { - buf.append("").append(version).append(""); + buf.append("").append(StringUtils.escapeForXML(version)).append(""); } if (os != null) { - buf.append("").append(os).append(""); + buf.append("").append(StringUtils.escapeForXML(os)).append(""); } buf.append(""); return buf.toString(); diff --git a/extensions/src/main/java/org/jivesoftware/smackx/iqversion/provider/VersionProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/provider/VersionProvider.java new file mode 100644 index 000000000..8db31bb8c --- /dev/null +++ b/extensions/src/main/java/org/jivesoftware/smackx/iqversion/provider/VersionProvider.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2014 Georg Lukas. + * + * 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.iqversion.provider; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.iqversion.packet.Version; +import org.xmlpull.v1.XmlPullParser; + +public class VersionProvider implements IQProvider { + public IQ parseIQ(XmlPullParser parser) throws Exception { + String name = null, version = null, os = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + String tagName = parser.getName(); + if (eventType == XmlPullParser.START_TAG) { + if (tagName.equals("name")) { + name = parser.nextText(); + } + else if (tagName.equals("version")) { + version = parser.nextText(); + } + else if (tagName.equals("os")) { + os = parser.nextText(); + } + } else if (eventType == XmlPullParser.END_TAG && tagName.equals("query")) { + done = true; + } + } + return new Version(name, version, os); + } +} diff --git a/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers index 2845b0824..72fc8d29b 100644 --- a/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers +++ b/extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers @@ -127,7 +127,7 @@ query jabber:iq:version - org.jivesoftware.smackx.iqversion.packet.Version + org.jivesoftware.smackx.iqversion.provider.VersionProvider diff --git a/extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java b/extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java new file mode 100644 index 000000000..eec178651 --- /dev/null +++ b/extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2014 Georg Lukas. + * + * 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.iqversion; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.jivesoftware.smack.DummyConnection; +import org.jivesoftware.smack.ThreadedDummyConnection; +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.iqversion.packet.Version; +import org.junit.Before; +import org.junit.Test; + +public class VersionTest { + private DummyConnection dummyCon; + private ThreadedDummyConnection threadedCon; + + @Before + public void setup() { + dummyCon = new DummyConnection(); + threadedCon = new ThreadedDummyConnection(); + } + + @Test + public void checkProvider() throws Exception { + // @formatter:off + String control = "" + + "" + + ""; + // @formatter:on + DummyConnection con = new DummyConnection(); + + // Enable version replys for this connection + VersionManager.getInstanceFor(con).setVersion(new Version("Test", "0.23", "DummyOS")); + IQ versionRequest = PacketParserUtils.parseIQ(TestUtils.getIQParser(control), con); + + assertTrue(versionRequest instanceof Version); + + con.processPacket(versionRequest); + + Packet replyPacket = con.getSentPacket(); + assertTrue(replyPacket instanceof Version); + + Version reply = (Version) replyPacket; + //getFrom check is pending for SMACK-547 + //assertEquals("juliet@capulet.lit/balcony", reply.getFrom()); + assertEquals("capulet.lit", reply.getTo()); + assertEquals("s2c1", reply.getPacketID()); + assertEquals(IQ.Type.RESULT, reply.getType()); + assertEquals("Test", reply.getName()); + assertEquals("0.23", reply.getVersion()); + assertEquals("DummyOS", reply.getOs()); + } +}