From b8d9c38a39a6e9f1556658c14e227b078e8e9e2f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 14 Apr 2016 22:52:07 +0200 Subject: [PATCH 01/18] Smack 4.1.8-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 26b5c5a057..d19cd230ac 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { - shortVersion = '4.1.7' - isSnapshot = false + shortVersion = '4.1.8' + isSnapshot = true jxmppVersion = '0.4.2' smackMinAndroidSdk = 8 } From 1f87185ee843496831f09f51ffb8f5632a8217c6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 1 Jun 2016 13:10:11 +0200 Subject: [PATCH 02/18] Do not base64 twice in SASL X-OAUTH2 Fixes SMACK-722. --- .../jivesoftware/smack/sasl/core/SASLXOauth2Mechanism.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOauth2Mechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOauth2Mechanism.java index a271f01a74..e066fb87d8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOauth2Mechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOauth2Mechanism.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2015 Florian Schmaus + * Copyright 2014-2016 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.sasl.SASLMechanism; -import org.jivesoftware.smack.util.stringencoder.Base64; /** * The SASL X-OAUTH2 mechanism as described in Date: Sun, 19 Jun 2016 12:06:21 +0200 Subject: [PATCH 03/18] Do not re-use the Socket after connect() failed Fixes SMACK-724. --- .../java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 8a1d6e5a1b..d0ca18bf2a 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -579,6 +579,10 @@ private void connectUsingConfiguration() throws IOException, ConnectionException throw new UnknownHostException(host); } innerloop: while (inetAddresses.hasNext()) { + // Create a *new* Socket before every connection attempt, i.e. connect() call, since Sockets are not + // re-usable after a failed connection attempt. See also SMACK-724. + socket = socketFactory.createSocket(); + final InetAddress inetAddress = inetAddresses.next(); final String inetAddressAndPort = inetAddress + " at port " + port; LOGGER.finer("Trying to establish TCP connection to " + inetAddressAndPort); From e943bbbd91d7de1c98276c01c8cfffe218bd608c Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 1 Jul 2016 08:18:56 +0200 Subject: [PATCH 04/18] Handle AlreadyLoggedInException in ReconnectionManager Fixes SMACK-725. --- .../java/org/jivesoftware/smack/ReconnectionManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java index f143627369..b75159b518 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java @@ -250,6 +250,12 @@ public void run() { // Successfully reconnected. attempts = 0; } + catch (SmackException.AlreadyLoggedInException e) { + // This can happen if another thread concurrently triggers a reconnection + // and/or login. Obviously it should not be handled as a reconnection + // failure. See also SMACK-725. + LOGGER.log(Level.FINER, "Reconnection not required, was already logged in", e); + } catch (SmackException | IOException | XMPPException e) { // Fires the failed reconnection notification for (ConnectionListener listener : connection.connectionListeners) { From 45b04800a43583f0e7b3909cbdb23f203eb8d559 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 4 Jul 2016 08:20:57 +0200 Subject: [PATCH 05/18] Handle AlreadyConnectedException in ReconnectionManager Fixes SMACK-725. --- .../java/org/jivesoftware/smack/ReconnectionManager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java index b75159b518..f6711e74e5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java @@ -238,7 +238,11 @@ public void run() { // Makes a reconnection attempt try { if (isReconnectionPossible(connection)) { - connection.connect(); + try { + connection.connect(); + } catch (SmackException.AlreadyConnectedException e) { + LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e); + } } // TODO Starting with Smack 4.2, connect() will no // longer login automatically. So change this and the From 66f4b9cdffa503c37f2d5f55359b88744cc0137b Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 6 Jul 2016 08:38:26 +0200 Subject: [PATCH 06/18] Change type of purge/remove IQ (XEP-0013) to 'set' Fixes SMACK-726 --- .../org/jivesoftware/smackx/offline/OfflineMessageManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java index f47817b085..ef3c64a8ae 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java @@ -26,6 +26,7 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; @@ -222,6 +223,7 @@ public List getMessages() throws NoResponseException, XMPPErrorExceptio */ public void deleteMessages(List nodes) throws NoResponseException, XMPPErrorException, NotConnectedException { OfflineMessageRequest request = new OfflineMessageRequest(); + request.setType(IQ.Type.set); for (String node : nodes) { OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node); item.setAction("remove"); @@ -240,6 +242,7 @@ public void deleteMessages(List nodes) throws NoResponseException, XMPPE */ public void deleteMessages() throws NoResponseException, XMPPErrorException, NotConnectedException { OfflineMessageRequest request = new OfflineMessageRequest(); + request.setType(IQ.Type.set); request.setPurge(true); connection.createPacketCollectorAndSend(request).nextResultOrThrow(); } From 189cac072b8cedbcecca8bf2b3c5ec4368ea8cfa Mon Sep 17 00:00:00 2001 From: Fernando Ramirez Date: Thu, 21 Jul 2016 11:40:26 -0300 Subject: [PATCH 07/18] Implement Message Archive Management (MAM) XEP-0313 Fixes SMACK-625 --- documentation/extensions/index.md | 1 + .../jivesoftware/smackx/mam/MamManager.java | 561 ++++++++++++++++++ .../smackx/mam/element/MamElements.java | 215 +++++++ .../smackx/mam/element/MamFinIQ.java | 131 ++++ .../smackx/mam/element/MamPrefsIQ.java | 156 +++++ .../smackx/mam/element/MamQueryIQ.java | 127 ++++ .../smackx/mam/element/package-info.java | 25 + .../smackx/mam/filter/MamResultFilter.java | 53 ++ .../smackx/mam/filter/package-info.java | 25 + .../jivesoftware/smackx/mam/package-info.java | 25 + .../smackx/mam/provider/MamFinIQProvider.java | 58 ++ .../mam/provider/MamPrefsIQProvider.java | 92 +++ .../mam/provider/MamQueryIQProvider.java | 58 ++ .../mam/provider/MamResultProvider.java | 57 ++ .../smackx/mam/provider/package-info.java | 25 + .../experimental.providers | 22 + .../jivesoftware/smackx/mam/FiltersTest.java | 160 +++++ .../smackx/mam/MamFinProviderTest.java | 69 +++ .../smackx/mam/MamPrefIQProviderTest.java | 84 +++ .../smackx/mam/MamQueryIQProviderTest.java | 86 +++ .../smackx/mam/MamResultProviderTest.java | 90 +++ .../org/jivesoftware/smackx/mam/MamTest.java | 54 ++ .../jivesoftware/smackx/mam/PagingTest.java | 55 ++ .../smackx/mam/PreferencesTest.java | 68 +++ .../smackx/mam/QueryArchiveTest.java | 95 +++ .../smackx/mam/ResultsLimitTest.java | 50 ++ .../smackx/mam/RetrieveFormFieldsTest.java | 81 +++ .../smackx/mam/MamIntegrationTest.java | 93 +++ .../jivesoftware/smackx/mam/package-info.java | 21 + 29 files changed, 2637 insertions(+) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java create mode 100644 smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java create mode 100644 smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 28c556c143..274951a344 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -85,6 +85,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental | [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. | | Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers | | Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. | +| Message Archive Management | [XEP-0313](http://xmpp.org/extensions/xep-0313.html) | Query and control an archive of messages stored on a server. | Legacy Smack Extensions and currently supported XEPs of smack-legacy diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java new file mode 100644 index 0000000000..06e2d3dd42 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -0,0 +1,561 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPConnectionRegistry; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.filter.IQReplyFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.mam.filter.MamResultFilter; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jxmpp.jid.Jid; +import org.jxmpp.util.XmppDateTime; + +/** + * Message Archive Management Manager class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public final class MamManager extends Manager { + + static { + XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(XMPPConnection connection) { + getInstanceFor(connection); + } + }); + } + + private static final Map INSTANCES = new WeakHashMap<>(); + + /** + * Get the singleton instance of MamManager. + * + * @param connection + * @return the instance of MamManager + */ + public static synchronized MamManager getInstanceFor(XMPPConnection connection) { + MamManager mamManager = INSTANCES.get(connection); + + if (mamManager == null) { + mamManager = new MamManager(connection); + INSTANCES.put(connection, mamManager); + } + + return mamManager; + } + + private MamManager(XMPPConnection connection) { + super(connection); + } + + /** + * Query archive with a maximum amount of results. + * + * @param max + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Integer max) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(max, null, null, null, null); + } + + /** + * Query archive with a JID (only messages from/to the JID). + * + * @param withJid + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Jid withJid) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, null, withJid, null); + } + + /** + * Query archive filtering by start and/or end date. If start == null, the + * value of 'start' will be equal to the date/time of the earliest message + * stored in the archive. If end == null, the value of 'end' will be equal + * to the date/time of the most recent message stored in the archive. + * + * @param start + * @param end + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Date start, Date end) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, start, end, null, null); + } + + /** + * Query Archive adding filters with additional fields. + * + * @param additionalFields + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(List additionalFields) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, null, null, additionalFields); + } + + /** + * Query archive filtering by start date. The value of 'end' will be equal + * to the date/time of the most recent message stored in the archive. + * + * @param start + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchiveWithStartDate(Date start) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, start, null, null, null); + } + + /** + * Query archive filtering by end date. The value of 'start' will be equal + * to the date/time of the earliest message stored in the archive. + * + * @param end + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchiveWithEndDate(Date end) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + return queryArchive(null, null, end, null, null); + } + + /** + * Query archive applying filters: max count, start date, end date, from/to + * JID and with additional fields. + * + * @param max + * @param start + * @param end + * @param withJid + * @param additionalFields + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult queryArchive(Integer max, Date start, Date end, Jid withJid, List additionalFields) + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, + NotLoggedInException { + DataForm dataForm = null; + String queryId = UUID.randomUUID().toString(); + + if (start != null || end != null || withJid != null || additionalFields != null) { + dataForm = getNewMamForm(); + addStart(start, dataForm); + addEnd(end, dataForm); + addWithJid(withJid, dataForm); + addAdditionalFields(additionalFields, dataForm); + } + + MamQueryIQ mamQueryIQ = prepareMamQueryIQSet(dataForm, queryId); + addResultsLimit(max, mamQueryIQ); + return queryArchive(mamQueryIQ); + } + + private static void addAdditionalFields(List additionalFields, DataForm dataForm) { + if (additionalFields == null) { + return; + } + for (FormField formField : additionalFields) { + dataForm.addField(formField); + } + } + + private static void addResultsLimit(Integer max, MamQueryIQ mamQueryIQ) { + if (max == null) { + return; + } + RSMSet rsmSet = new RSMSet(max); + mamQueryIQ.addExtension(rsmSet); + + } + + private static void addWithJid(Jid withJid, DataForm dataForm) { + if (withJid == null) { + return; + } + FormField formField = new FormField("with"); + formField.addValue(withJid.toString()); + dataForm.addField(formField); + } + + private static void addEnd(Date end, DataForm dataForm) { + if (end == null) { + return; + } + FormField formField = new FormField("end"); + formField.addValue(XmppDateTime.formatXEP0082Date(end)); + dataForm.addField(formField); + } + + private static void addStart(Date start, DataForm dataForm) { + if (start == null) { + return; + } + FormField formField = new FormField("start"); + formField.addValue(XmppDateTime.formatXEP0082Date(start)); + dataForm.addField(formField); + } + + private void preparePageQuery(MamQueryIQ mamQueryIQ, RSMSet rsmSet) { + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.addExtension(rsmSet); + } + + /** + * Returns a page of the archive. + * + * @param dataForm + * @param rsmSet + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult page(DataForm dataForm, RSMSet rsmSet) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), dataForm); + preparePageQuery(mamQueryIQ, rsmSet); + return queryArchive(mamQueryIQ); + } + + /** + * Returns the next page of the archive. + * + * @param mamQueryResult + * is the previous query result + * @param count + * is the amount of messages that a page contains + * @return the MAM query result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamQueryResult pageNext(MamQueryResult mamQueryResult, int count) throws NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { + RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet(); + RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getLast(), RSMSet.PageDirection.after); + return page(mamQueryResult.form, requestRsmSet); + } + + /** + * Obtain page before the first message saved (specific chat). + * + * @param chatJid + * @param firstMessageId + * @param max + * @return the MAM query result + * @throws XMPPErrorException + * @throws NotLoggedInException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NoResponseException + */ + public MamQueryResult pageBefore(Jid chatJid, String firstMessageId, int max) throws XMPPErrorException, + NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { + RSMSet rsmSet = new RSMSet(null, firstMessageId, -1, -1, null, max, null, -1); + DataForm dataForm = getNewMamForm(); + addWithJid(chatJid, dataForm); + return page(dataForm, rsmSet); + } + + /** + * Obtain page after the last message saved (specific chat). + * + * @param chatJid + * @param lastMessageId + * @param max + * @return the MAM query result + * @throws XMPPErrorException + * @throws NotLoggedInException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NoResponseException + */ + public MamQueryResult pageAfter(Jid chatJid, String lastMessageId, int max) throws XMPPErrorException, + NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { + RSMSet rsmSet = new RSMSet(lastMessageId, null, -1, -1, null, max, null, -1); + DataForm dataForm = getNewMamForm(); + addWithJid(chatJid, dataForm); + return page(dataForm, rsmSet); + } + + /** + * Get the form fields supported by the server. + * + * @return the list of form fields. + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public List retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException, + InterruptedException, NotLoggedInException { + String queryId = UUID.randomUUID().toString(); + MamQueryIQ mamQueryIQ = prepareMamQueryIQGet(queryId); + return queryFormFields(mamQueryIQ); + } + + private MamQueryIQ prepareMamQueryIQSet(DataForm dataForm, String queryId) { + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + return mamQueryIQ; + } + + private MamQueryIQ prepareMamQueryIQGet(String queryId) { + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, null); + mamQueryIQ.setType(IQ.Type.get); + return mamQueryIQ; + } + + private MamQueryResult queryArchive(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); + MamFinIQ mamFinIQ = null; + + PacketCollector mamFinIQCollector = connection.createPacketCollector(new IQReplyFilter(mamQueryIq, connection)); + + PacketCollector.Configuration resultCollectorConfiguration = PacketCollector.newConfiguration() + .setStanzaFilter(new MamResultFilter(mamQueryIq)).setCollectorToReset(mamFinIQCollector); + PacketCollector resultCollector = connection.createPacketCollector(resultCollectorConfiguration); + + try { + connection.sendStanza(mamQueryIq); + mamFinIQ = mamFinIQCollector.nextResultOrThrow(); + } finally { + mamFinIQCollector.cancel(); + resultCollector.cancel(); + } + + List forwardedMessages = new ArrayList<>(resultCollector.getCollectedCount()); + + for (Message resultMessage = resultCollector + .pollResult(); resultMessage != null; resultMessage = resultCollector.pollResult()) { + MamElements.MamResultExtension mamResultExtension = MamElements.MamResultExtension.from(resultMessage); + forwardedMessages.add(mamResultExtension.getForwarded()); + } + + return new MamQueryResult(forwardedMessages, mamFinIQ, DataForm.from(mamQueryIq)); + } + + /** + * MAM query result class. + * + */ + public final static class MamQueryResult { + public final List forwardedMessages; + public final MamFinIQ mamFin; + private final DataForm form; + + private MamQueryResult(List forwardedMessages, MamFinIQ mamFin, DataForm form) { + this.forwardedMessages = forwardedMessages; + this.mamFin = mamFin; + this.form = form; + } + } + + private List queryFormFields(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + final XMPPConnection connection = connection(); + MamQueryIQ mamResponseQueryIQ = null; + PacketCollector mamResponseQueryIQCollector = connection + .createPacketCollector(new IQReplyFilter(mamQueryIq, connection)); + + try { + connection.sendStanza(mamQueryIq); + mamResponseQueryIQ = mamResponseQueryIQCollector.nextResultOrThrow(); + } finally { + mamResponseQueryIQCollector.cancel(); + } + + return mamResponseQueryIQ.getDataForm().getFields(); + } + + /** + * Returns true if Message Archive Management is supported by the server. + * + * @return true if Message Archive Management is supported by the server. + * @throws NotConnectedException + * @throws XMPPErrorException + * @throws NoResponseException + * @throws InterruptedException + */ + public boolean isSupportedByServer() + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(MamElements.NAMESPACE); + } + + private DataForm getNewMamForm() { + FormField field = new FormField(FormField.FORM_TYPE); + field.setType(FormField.Type.hidden); + field.addValue(MamElements.NAMESPACE); + DataForm form = new DataForm(DataForm.Type.submit); + form.addField(field); + return form; + } + + /** + * Get the preferences stored in the server. + * + * @return the MAM preferences result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + MamPrefsIQ mamPrefIQ = prepareRetrievePreferencesStanza(); + return queryMamPrefs(mamPrefIQ); + } + + private MamPrefsIQ prepareRetrievePreferencesStanza() { + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.get, null, null, null); + return mamPrefIQ; + } + + /** + * Update the preferences in the server. + * + * @param alwaysJids + * is the list of JIDs that should always have messages to/from + * archived in the user's store + * @param neverJids + * is the list of JIDs that should never have messages to/from + * archived in the user's store + * @param defaultField + * can be "roster", "always", "never" (look at the XEP-0313 + * documentation) + * @return the MAM preferences result + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + * @throws NotLoggedInException + */ + public MamPrefsResult updateArchivingPreferences(List alwaysJids, List neverJids, String defaultField) + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, + NotLoggedInException { + MamPrefsIQ mamPrefIQ = prepareUpdatePreferencesStanza(alwaysJids, neverJids, defaultField); + return queryMamPrefs(mamPrefIQ); + } + + private MamPrefsIQ prepareUpdatePreferencesStanza(List alwaysJids, List neverJids, String defaultField) { + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.set, alwaysJids, neverJids, defaultField); + return mamPrefIQ; + } + + /** + * MAM preferences result class. + * + */ + public final static class MamPrefsResult { + public final MamPrefsIQ mamPrefs; + public final DataForm form; + + private MamPrefsResult(MamPrefsIQ mamPrefs, DataForm form) { + this.mamPrefs = mamPrefs; + this.form = form; + } + } + + private MamPrefsResult queryMamPrefs(MamPrefsIQ mamPrefsIQ) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); + MamPrefsIQ mamPrefsResultIQ = null; + PacketCollector prefsResultIQCollector = connection + .createPacketCollector(new IQReplyFilter(mamPrefsIQ, connection)); + + try { + connection.sendStanza(mamPrefsIQ); + mamPrefsResultIQ = prefsResultIQCollector.nextResultOrThrow(); + } finally { + prefsResultIQCollector.cancel(); + } + + return new MamPrefsResult(mamPrefsResultIQ, DataForm.from(mamPrefsIQ)); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java new file mode 100644 index 0000000000..72e57e93d1 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java @@ -0,0 +1,215 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import java.util.List; + +import org.jivesoftware.smack.packet.Element; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jxmpp.jid.Jid; + +/** + * MAM elements. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamElements { + + public static final String NAMESPACE = "urn:xmpp:mam:1"; + + /** + * MAM result extension class. + * + * @see XEP-0313: Message + * Archive Management + * + */ + public static class MamResultExtension implements ExtensionElement { + + /** + * result element. + */ + public static final String ELEMENT = "result"; + + /** + * id of the result. + */ + private final String id; + + /** + * the forwarded element. + */ + private final Forwarded forwarded; + + /** + * the query id. + */ + private String queryId; + + /** + * MAM result extension constructor. + * + * @param queryId + * @param id + * @param forwarded + */ + public MamResultExtension(String queryId, String id, Forwarded forwarded) { + if (StringUtils.isEmpty(id)) { + throw new IllegalArgumentException("id must not be null or empty"); + } + if (forwarded == null) { + throw new IllegalArgumentException("forwarded must no be null"); + } + this.id = id; + this.forwarded = forwarded; + this.queryId = queryId; + } + + /** + * Get the id. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Get the forwarded element. + * + * @return the forwarded element + */ + public Forwarded getForwarded() { + return forwarded; + } + + /** + * Get query id. + * + * @return the query id + */ + public final String getQueryId() { + return queryId; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public final String getNamespace() { + return NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.halfOpenElement(this); + xml.xmlnsAttribute(NAMESPACE); + xml.optAttribute("queryid", getQueryId()); + xml.optAttribute("id", getId()); + xml.rightAngleBracket(); + + xml.element(getForwarded()); + + xml.closeElement(this); + return xml; + } + + public static MamResultExtension from(Message message) { + return (MamResultExtension) message.getExtension(ELEMENT, NAMESPACE); + } + + } + + /** + * Always JID list element class for the MamPrefsIQ. + * + */ + public static class AlwaysJidListElement implements Element { + + /** + * list of JIDs. + */ + private List alwaysJids; + + /** + * Always JID list element constructor. + * + * @param alwaysJids + */ + AlwaysJidListElement(List alwaysJids) { + this.alwaysJids = alwaysJids; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.openElement("always"); + + for (Jid jid : alwaysJids) { + xml.element("jid", jid); + } + + xml.closeElement("always"); + return xml; + } + } + + /** + * Never JID list element class for the MamPrefsIQ. + * + */ + public static class NeverJidListElement implements Element { + + /** + * list of JIDs + */ + private List neverJids; + + /** + * Never JID list element constructor. + * + * @param neverJids + */ + public NeverJidListElement(List neverJids) { + this.neverJids = neverJids; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.openElement("never"); + + for (Jid jid : neverJids) { + xml.element("jid", jid); + } + + xml.closeElement("never"); + return xml; + } + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java new file mode 100644 index 0000000000..4b8a61de87 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamFinIQ.java @@ -0,0 +1,131 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * 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.mam.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; + +/** + * MAM fin IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamFinIQ extends IQ { + + /** + * fin element. + */ + public static final String ELEMENT = "fin"; + + /** + * the IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + /** + * RSM set. + */ + private final RSMSet rsmSet; + + /** + * if is complete. + */ + private final boolean complete; + + /** + * if is stable. + */ + private final boolean stable; + + /** + * the query id. + */ + private final String queryId; + + /** + * MamFinIQ constructor. + * + * @param queryId + * @param rsmSet + * @param complete + * @param stable + */ + public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) { + super(ELEMENT, NAMESPACE); + if (rsmSet == null) { + throw new IllegalArgumentException("rsmSet must not be null"); + } + this.rsmSet = rsmSet; + this.complete = complete; + this.stable = stable; + this.queryId = queryId; + } + + /** + * Get RSM set. + * + * @return the RSM set + */ + public RSMSet getRSMSet() { + return rsmSet; + } + + /** + * Return if it is complete. + * + * @return true if it is complete + */ + public boolean isComplete() { + return complete; + } + + /** + * Return if it is stable. + * + * @return true if it is stable + */ + public boolean isStable() { + return stable; + } + + /** + * Get query id. + * + * @return the query id + */ + public final String getQueryId() { + return queryId; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.optAttribute("queryid", queryId); + xml.optBooleanAttribute("complete", complete); + xml.optBooleanAttribute("stable", stable); + if (rsmSet == null) { + xml.setEmptyElement(); + } else { + xml.rightAngleBracket(); + xml.element(rsmSet); + } + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java new file mode 100644 index 0000000000..1c5bedf263 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java @@ -0,0 +1,156 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mam.element.MamElements.AlwaysJidListElement; +import org.jivesoftware.smackx.mam.element.MamElements.NeverJidListElement; +import org.jxmpp.jid.Jid; + +/** + * MAM Preferences IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamPrefsIQ extends IQ { + + /** + * the preferences element. + */ + public static final String ELEMENT = "prefs"; + + /** + * the IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + /** + * true if it is a request for update preferences. + */ + private boolean isUpdate; + + /** + * true if it is a result preferences. + */ + private boolean isResult; + + /** + * list of always. + */ + private List alwaysJids; + + /** + * list of never. + */ + private List neverJids; + + /** + * default field. + */ + private String defaultField; + + /** + * MAM preferences IQ constructor. + * + * @param type + * @param alwaysJids + * @param neverJids + * @param defaultField + */ + public MamPrefsIQ(Type type, List alwaysJids, List neverJids, String defaultField) { + super(ELEMENT, NAMESPACE); + this.setType(type); + this.isUpdate = this.getType().equals(Type.set); + this.isResult = this.getType().equals(Type.result); + this.alwaysJids = alwaysJids; + this.neverJids = neverJids; + this.defaultField = defaultField; + } + + /** + * True if it is a request for update preferences. + * + * @return the update preferences boolean + */ + public boolean isUpdate() { + return isUpdate; + } + + /** + * True if it is a result. + * + * @return the result preferences boolean + */ + public boolean isResult() { + return isUpdate; + } + + /** + * Get the list of always store info JIDs. + * + * @return the always list + */ + public List getAlwaysJids() { + return alwaysJids; + } + + /** + * Get the list of never store info JIDs. + * + * @return the never list + */ + public List getNeverJids() { + return neverJids; + } + + /** + * Get the default field. + * + * @return the default field + */ + public String getDefault() { + return defaultField; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + + if (isUpdate || isResult) { + xml.attribute("default", defaultField); + } + + xml.rightAngleBracket(); + + if (alwaysJids != null) { + MamElements.AlwaysJidListElement alwaysElement = new AlwaysJidListElement(alwaysJids); + xml.element(alwaysElement); + } + + if (neverJids != null) { + MamElements.NeverJidListElement neverElement = new NeverJidListElement(neverJids); + xml.element(neverElement); + } + + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java new file mode 100644 index 0000000000..7a6f066783 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java @@ -0,0 +1,127 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * MAM Query IQ class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamQueryIQ extends IQ { + + /** + * the MAM query IQ element. + */ + public static final String ELEMENT = QUERY_ELEMENT; + + /** + * the MAM query IQ NAMESPACE. + */ + public static final String NAMESPACE = MamElements.NAMESPACE; + + private final String queryId; + private final String node; + private final DataForm dataForm; + + /** + * MAM query IQ constructor. + * + * @param queryId + */ + public MamQueryIQ(String queryId) { + this(queryId, null, null); + } + + /** + * MAM query IQ constructor. + * + * @param form + */ + public MamQueryIQ(DataForm form) { + this(null, null, form); + } + + /** + * MAM query IQ constructor. + * + * @param queryId + * @param form + */ + public MamQueryIQ(String queryId, DataForm form) { + this(queryId, null, form); + } + + /** + * MAM query IQ constructor. + * + * @param queryId + * @param node + * @param dataForm + */ + public MamQueryIQ(String queryId, String node, DataForm dataForm) { + super(ELEMENT, NAMESPACE); + this.queryId = queryId; + this.node = node; + this.dataForm = dataForm; + + if (dataForm != null) { + FormField field = dataForm.getHiddenFormTypeField(); + if (field == null) { + throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field"); + } + if (!field.getValues().get(0).equals(MamElements.NAMESPACE)) { + throw new IllegalArgumentException( + "Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'"); + } + addExtension(dataForm); + } + } + + /** + * Get query id. + * + * @return the query id + */ + public String getQueryId() { + return queryId; + } + + /** + * Get the data form. + * + * @return the data form + */ + public DataForm getDataForm() { + return dataForm; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.optAttribute("queryid", queryId); + xml.optAttribute("node", node); + xml.rightAngleBracket(); + return xml; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java new file mode 100644 index 0000000000..5c63d8c7cf --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Packet classes and interfaces for Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java new file mode 100644 index 0000000000..19e2a15619 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java @@ -0,0 +1,53 @@ +/** + * + * Copyright © 2016 Florian Schmaus and Fernando Ramirez + * + * 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.mam.filter; + +import org.jivesoftware.smack.filter.FlexibleStanzaTypeFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; + +/** + * MAM result filter class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez and Florian Schmaus + * + */ +public class MamResultFilter extends FlexibleStanzaTypeFilter { + + private String queryId; + + public MamResultFilter(MamQueryIQ mamQueryIQ) { + super(Message.class); + this.queryId = mamQueryIQ.getQueryId(); + } + + @Override + protected boolean acceptSpecific(Message message) { + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + if (mamResultExtension == null) { + return false; + } + + String resultQueryId = mamResultExtension.getQueryId(); + return ((queryId == null && resultQueryId == null) || (queryId != null && queryId.equals(resultQueryId))); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java new file mode 100644 index 0000000000..d568f7d04c --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Filters of Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.filter; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java new file mode 100644 index 0000000000..16b8b2b7b7 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * XEP-0313: Message Archive Management. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java new file mode 100644 index 0000000000..aa1f129048 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.rsm.provider.RSMSetProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Fin IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamFinIQProvider extends IQProvider { + + @Override + public MamFinIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + String queryId = parser.getAttributeValue("", "queryid"); + boolean complete = Boolean.parseBoolean(parser.getAttributeValue("", "complete")); + boolean stable = Boolean.parseBoolean(parser.getAttributeValue("", "stable")); + RSMSet rsmSet = null; + + outerloop: while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(RSMSet.ELEMENT)) { + rsmSet = new RSMSetProvider().parse(parser); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getDepth() == initialDepth) { + break outerloop; + } + } + } + + return new MamFinIQ(queryId, rsmSet, complete, stable); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java new file mode 100644 index 0000000000..5d2e7586a4 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Preferences IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamPrefsIQProvider extends IQProvider { + + @Override + public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + String iqType = parser.getAttributeValue("", "type"); + String defaultField = parser.getAttributeValue("", "default"); + + if (iqType == null) { + iqType = "result"; + } + + List alwaysJids = null; + List neverJids = null; + + outerloop: while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("always")) { + alwaysJids = iterateJids(parser); + } + if (parser.getName().equals("never")) { + neverJids = iterateJids(parser); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getDepth() == initialDepth) { + break outerloop; + } + } + } + + return new MamPrefsIQ(Type.fromString(iqType), alwaysJids, neverJids, defaultField); + } + + private List iterateJids(XmlPullParser parser) throws Exception { + List jids = new ArrayList<>(); + + int initialDepth = parser.getDepth(); + + outerloop: while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("jid")) { + parser.next(); + jids.add(JidCreate.from(parser.getText())); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getDepth() == initialDepth) { + break outerloop; + } + } + } + + return jids; + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java new file mode 100644 index 0000000000..2d763073ee --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jivesoftware.smackx.xdata.provider.DataFormProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Query IQ Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamQueryIQProvider extends IQProvider { + + @Override + public MamQueryIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + DataForm dataForm = null; + String queryId = parser.getAttributeValue("", "queryid"); + String node = parser.getAttributeValue("", "node"); + + outerloop: while (true) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(DataForm.ELEMENT)) { + dataForm = new DataFormProvider().parse(parser); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getDepth() == initialDepth) { + break outerloop; + } + } + } + + return new MamQueryIQ(queryId, node, dataForm); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java new file mode 100644 index 0000000000..5ecc10f5ac --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam.provider; + +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.xmlpull.v1.XmlPullParser; + +/** + * MAM Result Provider class. + * + * @see XEP-0313: Message + * Archive Management + * @author Fernando Ramirez + * + */ +public class MamResultProvider extends ExtensionElementProvider { + + @Override + public MamResultExtension parse(XmlPullParser parser, int initialDepth) throws Exception { + Forwarded forwarded = null; + String queryId = parser.getAttributeValue("", "queryid"); + String id = parser.getAttributeValue("", "id"); + + outerloop: while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(Forwarded.ELEMENT)) { + forwarded = new ForwardedProvider().parse(parser); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getDepth() == initialDepth) { + break outerloop; + } + } + } + + return new MamResultExtension(queryId, id, forwarded); + } + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java new file mode 100644 index 0000000000..a6c1a20b4b --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Provider classes of Message Archive Management (MAM) XEP-0313. + * + * @see XEP-0313: Message + * Archive Management + * + */ +package org.jivesoftware.smackx.mam.provider; diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers index e4906726ee..145309c84f 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers @@ -52,6 +52,28 @@ org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider + + + prefs + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider + + + query + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamQueryIQProvider + + + fin + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamFinIQProvider + + + result + urn:xmpp:mam:1 + org.jivesoftware.smackx.mam.provider.MamResultProvider + + register diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java new file mode 100644 index 0000000000..820ab1989f --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java @@ -0,0 +1,160 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.util.XmppDateTime; + +public class FiltersTest extends MamTest { + + private String getMamXMemberWith(List fieldsNames, List fieldsValues) { + String xml = "" + "" + "" + + MamElements.NAMESPACE + "" + ""; + + for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) { + xml += "" + "" + fieldsValues.get(i) + "" + + ""; + } + + xml += ""; + return xml; + } + + @Test + public void checkStartDateFilter() throws Exception { + Method methodAddStartDateFilter = MamManager.class.getDeclaredMethod("addStart", Date.class, DataForm.class); + methodAddStartDateFilter.setAccessible(true); + + Date date = new Date(); + DataForm dataForm = getNewMamForm(); + methodAddStartDateFilter.invoke(mamManager, date, dataForm); + + List fields = new ArrayList<>(); + fields.add("start"); + List values = new ArrayList<>(); + values.add(XmppDateTime.formatXEP0082Date(date)); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkEndDateFilter() throws Exception { + Method methodAddEndDateFilter = MamManager.class.getDeclaredMethod("addEnd", Date.class, DataForm.class); + methodAddEndDateFilter.setAccessible(true); + + Date date = new Date(); + DataForm dataForm = getNewMamForm(); + methodAddEndDateFilter.invoke(mamManager, date, dataForm); + + List fields = new ArrayList<>(); + fields.add("end"); + List values = new ArrayList<>(); + values.add(XmppDateTime.formatXEP0082Date(date)); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkWithJidFilter() throws Exception { + Method methodAddJidFilter = MamManager.class.getDeclaredMethod("addWithJid", Jid.class, DataForm.class); + methodAddJidFilter.setAccessible(true); + + String jid = "test@jid.com"; + DataForm dataForm = getNewMamForm(); + methodAddJidFilter.invoke(mamManager, JidCreate.from(jid), dataForm); + + List fields = new ArrayList<>(); + fields.add("with"); + List values = new ArrayList<>(); + values.add(jid); + + Assert.assertEquals(dataForm.toXML().toString(), getMamXMemberWith(fields, values)); + } + + @Test + public void checkMultipleFilters() throws Exception { + Method methodAddStartDateFilter = MamManager.class.getDeclaredMethod("addStart", Date.class, DataForm.class); + methodAddStartDateFilter.setAccessible(true); + Method methodAddEndDateFilter = MamManager.class.getDeclaredMethod("addEnd", Date.class, DataForm.class); + methodAddEndDateFilter.setAccessible(true); + Method methodAddJidFilter = MamManager.class.getDeclaredMethod("addWithJid", Jid.class, DataForm.class); + methodAddJidFilter.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + Date date = new Date(); + String dateString = XmppDateTime.formatXEP0082Date(date); + String jid = "test@jid.com"; + + methodAddStartDateFilter.invoke(mamManager, date, dataForm); + methodAddEndDateFilter.invoke(mamManager, date, dataForm); + methodAddJidFilter.invoke(mamManager, JidCreate.from(jid), dataForm); + String dataFormResult = dataForm.toXML().toString(); + + List fields = new ArrayList<>(); + List values = new ArrayList<>(); + + fields.add("start"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.add("end"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("start"); + values.add(dateString); + fields.add("with"); + values.add(jid); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("end"); + values.add(dateString); + fields.add("with"); + values.add(jid); + fields.add("start"); + values.add(dateString); + Assert.assertNotEquals(dataFormResult, getMamXMemberWith(fields, values)); + + fields.clear(); + values.clear(); + + fields.add("start"); + values.add(dateString); + fields.add("end"); + values.add(dateString); + fields.add("with"); + values.add(jid); + Assert.assertEquals(dataFormResult, getMamXMemberWith(fields, values)); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java new file mode 100644 index 0000000000..67654991e1 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java @@ -0,0 +1,69 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamFinIQ; +import org.jivesoftware.smackx.mam.provider.MamFinIQProvider; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class MamFinProviderTest extends MamTest { + + String exmapleMamFinXml = "" + + "" + "10" + "09af3-cc343-b409f" + + "" + ""; + + private String getIQLimitedResultsExample() { + return "" + "" + + "" + "23452-4534-1" + + "390-2342-22" + "16" + "" + "" + ""; + } + + @Test + public void checkMamFinProvider() throws Exception { + XmlPullParser parser = PacketParserUtils.getParserFor(exmapleMamFinXml); + MamFinIQ mamFinIQ = new MamFinIQProvider().parse(parser); + + Assert.assertFalse(mamFinIQ.isComplete()); + Assert.assertTrue(mamFinIQ.isStable()); + Assert.assertNull(mamFinIQ.getQueryId()); + + RSMSet rsmSet = mamFinIQ.getRSMSet(); + Assert.assertEquals(rsmSet.getAfter(), "09af3-cc343-b409f"); + Assert.assertEquals(rsmSet.getMax(), 10); + } + + @Test + public void checkQueryLimitedResults() throws Exception { + IQ iq = (IQ) PacketParserUtils.parseStanza(getIQLimitedResultsExample()); + + MamFinIQ mamFinIQ = (MamFinIQ) iq; + Assert.assertEquals(mamFinIQ.getType(), Type.result); + + Assert.assertTrue(mamFinIQ.isComplete()); + Assert.assertEquals(mamFinIQ.getRSMSet().getCount(), 16); + Assert.assertEquals(mamFinIQ.getRSMSet().getFirst(), "23452-4534-1"); + Assert.assertEquals(mamFinIQ.getRSMSet().getFirstIndex(), 0); + Assert.assertEquals(mamFinIQ.getRSMSet().getLast(), "390-2342-22"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java new file mode 100644 index 0000000000..6fb6ac2121 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.xmlpull.v1.XmlPullParser; + +public class MamPrefIQProviderTest extends MamTest { + + String exampleMamPrefsIQ1 = "" + "" + + "" + "romeo@montague.lit" + "" + "" + + "montague@montague.lit" + "" + "" + ""; + + String exampleMamPrefsIQ2 = "" + "" + + "" + "romeo@montague.lit" + "montague@montague.lit" + "" + + "" + "" + "" + ""; + + String exampleMamPrefsIQ3 = "" + "" + "" + + ""; + + String exampleMamPrefsResultIQ = "" + + "" + "" + "romeo@montague.lit" + + "" + "" + "sarasa@montague.lit" + "montague@montague.lit" + + "" + "" + ""; + + @Test + public void checkMamPrefsIQProvider() throws Exception { + XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1); + MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1); + + Assert.assertTrue(mamPrefIQ1.isUpdate()); + Assert.assertEquals(mamPrefIQ1.getAlwaysJids().get(0), "romeo@montague.lit"); + Assert.assertEquals(mamPrefIQ1.getNeverJids().get(0), "montague@montague.lit"); + + XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2); + MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2); + Assert.assertTrue(mamPrefIQ2.isUpdate()); + Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(0), "romeo@montague.lit"); + Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(1), "montague@montague.lit"); + Assert.assertTrue(mamPrefIQ2.getNeverJids().isEmpty()); + + XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3); + MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3); + Assert.assertFalse(mamPrefIQ3.isUpdate()); + } + + @Test + public void checkMamPrefResult() throws Exception { + IQ iq = (IQ) PacketParserUtils.parseStanza(exampleMamPrefsResultIQ); + + MamPrefsIQ mamPrefsIQ = (MamPrefsIQ) iq; + + List alwaysJids = mamPrefsIQ.getAlwaysJids(); + List neverJids = mamPrefsIQ.getNeverJids(); + + Assert.assertEquals(alwaysJids.size(), 1); + Assert.assertEquals(neverJids.size(), 2); + Assert.assertEquals(alwaysJids.get(0).toString(), "romeo@montague.lit"); + Assert.assertEquals(neverJids.get(1).toString(), "montague@montague.lit"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java new file mode 100644 index 0000000000..9141137c37 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java @@ -0,0 +1,86 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; + +public class MamQueryIQProviderTest { + + String exampleMamQueryIQ1 = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + + "" + + "Where arth thou, my Juliet?" + "" + + "" + + "{http://jabber.org/protocol/mood}mood/lonely" + "" + "" + "" + + ""; + + String exampleMamQueryIQ2 = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + "" + + "" + "" + + "" + + "" + "" + "" + ""; + + @Test + public void checkMamQueryIQProvider() throws Exception { + // example 1 + IQ iq1 = (IQ) PacketParserUtils.parseStanza(exampleMamQueryIQ1); + MamQueryIQ mamQueryIQ1 = (MamQueryIQ) iq1; + + Assert.assertEquals(mamQueryIQ1.getType(), Type.set); + Assert.assertEquals(mamQueryIQ1.getQueryId(), "test"); + + DataForm dataForm1 = (DataForm) mamQueryIQ1.getExtension(DataForm.NAMESPACE); + Assert.assertEquals(dataForm1.getType(), DataForm.Type.submit); + + List fields1 = dataForm1.getFields(); + Assert.assertEquals(fields1.get(0).getType(), FormField.Type.hidden); + Assert.assertEquals(fields1.get(1).getType(), FormField.Type.text_single); + Assert.assertEquals(fields1.get(1).getValues().get(0), "Where arth thou, my Juliet?"); + Assert.assertEquals(fields1.get(2).getValues().get(0), "{http://jabber.org/protocol/mood}mood/lonely"); + + // example2 + IQ iq2 = (IQ) PacketParserUtils.parseStanza(exampleMamQueryIQ2); + MamQueryIQ mamQueryIQ2 = (MamQueryIQ) iq2; + + Assert.assertEquals(mamQueryIQ2.getType(), Type.result); + Assert.assertNull(mamQueryIQ2.getQueryId()); + + DataForm dataForm2 = (DataForm) mamQueryIQ2.getExtension(DataForm.NAMESPACE); + Assert.assertEquals(dataForm2.getType(), DataForm.Type.form); + + List fields2 = dataForm2.getFields(); + Assert.assertEquals(fields2.get(0).getValues().get(0), "urn:xmpp:mam:1"); + Assert.assertTrue(fields2.get(0).getValues().size() == 1); + Assert.assertEquals(fields2.get(1).getType(), FormField.Type.jid_single); + Assert.assertEquals(fields2.get(2).getType(), FormField.Type.text_single); + Assert.assertEquals(fields2.get(2).getValues(), new ArrayList<>()); + Assert.assertEquals(fields2.get(4).getVariable(), "urn:example:xmpp:free-text-search"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java new file mode 100644 index 0000000000..c8c43eaea2 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java @@ -0,0 +1,90 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.provider.MamResultProvider; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class MamResultProviderTest { + + String exampleMamResultXml = "" + + "" + "" + + "" + + "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo." + + "" + "" + ""; + + String exampleResultMessage = "" + + "" + + "" + "" + + "" + + "Hail to thee" + "" + "" + "" + ""; + + @Test + public void checkMamResultProvider() throws Exception { + XmlPullParser parser = PacketParserUtils.getParserFor(exampleMamResultXml); + MamResultExtension mamResultExtension = new MamResultProvider().parse(parser); + + Assert.assertEquals(mamResultExtension.getQueryId(), "f27"); + Assert.assertEquals(mamResultExtension.getId(), "28482-98726-73623"); + + GregorianCalendar calendar = new GregorianCalendar(2010, 7 - 1, 10, 23, 8, 25); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + Forwarded forwarded = mamResultExtension.getForwarded(); + Assert.assertEquals(forwarded.getDelayInformation().getStamp(), date); + + Message message = (Message) forwarded.getForwardedStanza(); + Assert.assertEquals(message.getFrom(), "romeo@montague.lit/orchard"); + Assert.assertEquals(message.getTo(), "juliet@capulet.lit/balcony"); + Assert.assertEquals(message.getBody(), + "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo."); + } + + @Test + public void checkResultsParse() throws Exception { + Message message = (Message) PacketParserUtils.parseStanza(exampleResultMessage); + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + Assert.assertEquals(mamResultExtension.getQueryId(), "f27"); + Assert.assertEquals(mamResultExtension.getId(), "28482-98726-73623"); + + GregorianCalendar calendar = new GregorianCalendar(2010, 7 - 1, 10, 23, 8, 25); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + Forwarded forwarded = mamResultExtension.getForwarded(); + Assert.assertEquals(forwarded.getDelayInformation().getStamp(), date); + + Message forwardedMessage = (Message) forwarded.getForwardedStanza(); + Assert.assertEquals(forwardedMessage.getFrom(), "witch@shakespeare.lit"); + Assert.assertEquals(forwardedMessage.getTo(), "macbeth@shakespeare.lit"); + Assert.assertEquals(forwardedMessage.getBody(), "Hail to thee"); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java new file mode 100644 index 0000000000..6d7c7c46ee --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import static org.mockito.Mockito.mock; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.ExperimentalInitializerTest; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Before; + +public class MamTest extends ExperimentalInitializerTest { + + protected XMPPConnection connection; + protected String queryId; + protected MamManager mamManager; + + @Before + public void setup() { + // mock connection + connection = mock(XMPPConnection.class); + + // test query id + queryId = "testid"; + + // MamManager instance + mamManager = MamManager.getInstanceFor(connection); + } + + protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm"); + methodGetNewMamForm.setAccessible(true); + return (DataForm) methodGetNewMamForm.invoke(mamManager); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java new file mode 100644 index 0000000000..58d44aa07f --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; + +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.rsm.packet.RSMSet; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Test; + +import org.junit.Assert; + +public class PagingTest extends MamTest { + + String pagingStanza = "" + "" + + "" + "" + + "urn:xmpp:mam:1" + "" + "" + "" + + "10" + "" + "" + ""; + + @Test + public void checkPageQueryStanza() throws Exception { + Method methodPreparePageQuery = MamManager.class.getDeclaredMethod("preparePageQuery", MamQueryIQ.class, + RSMSet.class); + methodPreparePageQuery.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + int max = 10; + RSMSet rsmSet = new RSMSet(max); + + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setStanzaId("sarasa"); + + methodPreparePageQuery.invoke(mamManager, mamQueryIQ, rsmSet); + + Assert.assertEquals(mamQueryIQ.getDataForm(), dataForm); + Assert.assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0), "urn:xmpp:mam:1"); + Assert.assertEquals(mamQueryIQ.toXML().toString(), pagingStanza); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java new file mode 100644 index 0000000000..1c96812638 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java @@ -0,0 +1,68 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.junit.Assert; + +public class PreferencesTest extends MamTest { + + String retrievePrefsStanzaExample = "" + "" + ""; + + String updatePrefsStanzaExample = "" + "" + "" + "romeo@montague.lit" + "other@montague.lit" + + "" + "" + "montague@montague.lit" + "" + "" + ""; + + @Test + public void checkRetrievePrefsStanza() throws Exception { + Method prepareRetrievePreferencesStanza = MamManager.class + .getDeclaredMethod("prepareRetrievePreferencesStanza"); + prepareRetrievePreferencesStanza.setAccessible(true); + + MamPrefsIQ mamPrefIQ = (MamPrefsIQ) prepareRetrievePreferencesStanza.invoke(mamManager); + mamPrefIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamPrefIQ.toXML().toString(), retrievePrefsStanzaExample); + } + + @Test + public void checkUpdatePrefsStanza() throws Exception { + Method prepareUpdatePreferencesStanza = MamManager.class.getDeclaredMethod("prepareUpdatePreferencesStanza", + List.class, List.class, String.class); + prepareUpdatePreferencesStanza.setAccessible(true); + + List alwaysJids = new ArrayList<>(); + alwaysJids.add(JidCreate.from("romeo@montague.lit")); + alwaysJids.add(JidCreate.from("other@montague.lit")); + + List neverJids = new ArrayList<>(); + neverJids.add(JidCreate.from("montague@montague.lit")); + + MamPrefsIQ mamPrefIQ = (MamPrefsIQ) prepareUpdatePreferencesStanza.invoke(mamManager, alwaysJids, neverJids, "roster"); + mamPrefIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamPrefIQ.toXML().toString(), updatePrefsStanzaExample); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java new file mode 100644 index 0000000000..936aa33e14 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smackx.delay.packet.DelayInformation; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.impl.JidCreate; + +public class QueryArchiveTest extends MamTest { + + String mamSimpleQueryIQ = "" + "" + + "" + "" + "" + + MamElements.NAMESPACE + "" + "" + "" + "" + ""; + + String mamQueryResultExample = "" + + "" + + "" + + "" + "" + "Thrice the brinded cat hath mew." + "" + "" + + "" + ""; + + @Test + public void checkMamQueryIQ() throws Exception { + DataForm dataForm = getNewMamForm(); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.setStanzaId("sarasa"); + Assert.assertEquals(mamQueryIQ.toXML().toString(), mamSimpleQueryIQ); + } + + @Test + public void checkMamQueryResults() throws Exception { + Message message = new Message(); + message.setStanzaId("iasd207"); + message.setFrom(JidCreate.from("coven@chat.shakespeare.lit")); + message.setTo(JidCreate.from("hag66@shakespeare.lit/pda")); + + GregorianCalendar calendar = new GregorianCalendar(2002, 10 - 1, 13, 23, 58, 37); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = calendar.getTime(); + + DelayInformation delay = new DelayInformation(date); + Message forwardedMessage = new Message(); + forwardedMessage.setFrom(JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); + forwardedMessage.setStanzaId("162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); + forwardedMessage.setType(Type.chat); + forwardedMessage.setBody("Thrice the brinded cat hath mew."); + + Forwarded forwarded = new Forwarded(delay, forwardedMessage); + + message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded)); + + Assert.assertEquals(message.toXML().toString(), mamQueryResultExample); + + MamResultExtension mamResultExtension = MamResultExtension.from(message); + + Assert.assertEquals(mamResultExtension.getId(), "34482-21985-73620"); + Assert.assertEquals(mamResultExtension.getForwarded().getDelayInformation().getStamp(), date); + + Message resultMessage = (Message) mamResultExtension.getForwarded().getForwardedStanza(); + Assert.assertEquals(resultMessage.getFrom(), JidCreate.from("coven@chat.shakespeare.lit/firstwitch")); + Assert.assertEquals(resultMessage.getStanzaId(), "162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2"); + Assert.assertEquals(resultMessage.getType(), Type.chat); + Assert.assertEquals(resultMessage.getBody(), "Thrice the brinded cat hath mew."); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java new file mode 100644 index 0000000000..f932666d3c --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/ResultsLimitTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Assert; +import org.junit.Test; + +public class ResultsLimitTest extends MamTest { + + String resultsLimitStanza = "" + "" + + "" + "" + "" + + MamElements.NAMESPACE + "" + "" + "" + "" + + "10" + "" + "" + ""; + + @Test + public void checkResultsLimit() throws Exception { + Method methodAddResultsLimit = MamManager.class.getDeclaredMethod("addResultsLimit", Integer.class, + MamQueryIQ.class); + methodAddResultsLimit.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.setStanzaId("sarasa"); + + methodAddResultsLimit.invoke(mamManager, 10, mamQueryIQ); + Assert.assertEquals(mamQueryIQ.toXML().toString(), resultsLimitStanza); + } + +} diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java new file mode 100644 index 0000000000..df64ddc4af --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java @@ -0,0 +1,81 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smackx.mam.element.MamElements; +import org.jivesoftware.smackx.mam.element.MamQueryIQ; +import org.jivesoftware.smackx.xdata.FormField; +import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.junit.Test; + +import org.junit.Assert; + +public class RetrieveFormFieldsTest extends MamTest { + + String retrieveFormFieldStanza = "" + "" + ""; + + String additionalFieldsStanza = "" + "" + + "" + MamElements.NAMESPACE + "" + "" + + "" + "Hi" + "" + + "" + "Hi2" + "" + + ""; + + @Test + public void checkRetrieveFormFieldsStanza() throws Exception { + Method methodPrepareMamQueryIQGet = MamManager.class.getDeclaredMethod("prepareMamQueryIQGet", String.class); + methodPrepareMamQueryIQGet.setAccessible(true); + + MamQueryIQ mamQueryIQ = (MamQueryIQ) methodPrepareMamQueryIQGet.invoke(mamManager, queryId); + mamQueryIQ.setStanzaId("sarasa"); + + Assert.assertEquals(mamQueryIQ.toXML().toString(), retrieveFormFieldStanza); + } + + @Test + public void checkAddAdditionalFieldsStanza() throws Exception { + Method methodAddAdditionalFields = MamManager.class.getDeclaredMethod("addAdditionalFields", List.class, + DataForm.class); + methodAddAdditionalFields.setAccessible(true); + + DataForm dataForm = getNewMamForm(); + + List additionalFields = new ArrayList<>(); + + FormField field1 = new FormField("urn:example:xmpp:free-text-search"); + field1.setType(FormField.Type.text_single); + field1.addValue("Hi"); + + FormField field2 = new FormField("urn:example:xmpp:stanza-content"); + field2.setType(FormField.Type.jid_single); + field2.addValue("Hi2"); + + additionalFields.add(field1); + additionalFields.add(field2); + + methodAddAdditionalFields.invoke(mamManager, additionalFields, dataForm); + + String dataFormResult = dataForm.toXML().toString(); + + Assert.assertEquals(dataFormResult, additionalFieldsStanza); + } + +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java new file mode 100644 index 0000000000..d61fa3ac72 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java @@ -0,0 +1,93 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.mam; + +import java.util.List; +import java.util.UUID; + +import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.forward.packet.Forwarded; +import org.jivesoftware.smackx.mam.MamManager.MamQueryResult; +import org.junit.Assert; +import org.jxmpp.jid.Jid; + +public class MamIntegrationTest extends AbstractSmackIntegrationTest { + + private final MamManager mamManagerConTwo; + + public MamIntegrationTest(SmackIntegrationTestEnvironment environment) throws NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException { + super(environment); + + mamManagerConTwo = MamManager.getInstanceFor(conTwo); + + if (!mamManagerConTwo.isSupportedByServer()) { + throw new TestNotPossibleException("Message Archive Management (XEP-0313) is not supported by the server."); + } + + } + + private Message getConTwoLastMessageSentFrom(Jid userOne) throws NoResponseException, XMPPErrorException, + NotConnectedException, InterruptedException, NotLoggedInException { + int pageSize = 20; + MamQueryResult mamQueryResult = mamManagerConTwo.queryArchive(pageSize, null, null, userOne, null); + + while (!mamQueryResult.mamFin.isComplete()) { + mamQueryResult = mamManagerConTwo.pageNext(mamQueryResult, pageSize); + } + + List forwardedMessages = mamQueryResult.forwardedMessages; + Message messageFromMAM = (Message) forwardedMessages.get(forwardedMessages.size() - 1).getForwardedStanza(); + return messageFromMAM; + } + + private Message prepareMessage(Jid to, String messageId, String body) { + Message message = new Message(); + message.setTo(to); + message.setStanzaId(messageId); + message.setBody(body); + return message; + } + + @SmackIntegrationTest + public void mamTest() throws Exception { + Jid userOne = conOne.getUser().asEntityBareJid(); + Jid userTwo = conTwo.getUser().asEntityBareJid(); + + String messageId = UUID.randomUUID().toString(); + String messageBody = "test message"; + + Message message = prepareMessage(userTwo, messageId, messageBody); + conOne.sendStanza(message); + + Message mamMessage = getConTwoLastMessageSentFrom(userOne); + + Assert.assertEquals(messageId, mamMessage.getStanzaId()); + Assert.assertEquals(messageBody, mamMessage.getBody()); + Assert.assertEquals(conOne.getUser(), mamMessage.getFrom()); + Assert.assertEquals(userTwo, mamMessage.getTo()); + } + +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java new file mode 100644 index 0000000000..09b4b40744 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ + +/** + * Message Archive Management (XEP-0313) integration tests. + */ +package org.jivesoftware.smackx.mam; From aeb385a022528b5cda3c3fe4136f0d0cd4c21179 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 23 Jul 2016 15:45:45 +0200 Subject: [PATCH 08/18] Apply refinements and fixes to the MAM code SMACK-625. --- .../org/jivesoftware/smack/packet/Stanza.java | 13 +++ .../jivesoftware/smackx/mam/MamManager.java | 86 +++++-------------- .../smackx/mam/element/MamElements.java | 2 +- .../smackx/mam/element/MamPrefsIQ.java | 79 ++++++++--------- .../smackx/mam/element/MamQueryIQ.java | 1 + .../smackx/mam/filter/MamResultFilter.java | 2 +- .../smackx/mam/provider/MamFinIQProvider.java | 14 +-- .../mam/provider/MamPrefsIQProvider.java | 48 +++++++---- .../mam/provider/MamQueryIQProvider.java | 16 ++-- .../mam/provider/MamResultProvider.java | 16 ++-- .../jivesoftware/smackx/mam/FiltersTest.java | 2 +- .../smackx/mam/MamFinProviderTest.java | 21 +++-- .../smackx/mam/MamPrefIQProviderTest.java | 6 +- .../jivesoftware/smackx/mam/PagingTest.java | 13 +-- .../smackx/mam/PreferencesTest.java | 18 ++-- .../smackx/mam/RetrieveFormFieldsTest.java | 9 +- .../forward/provider/ForwardedProvider.java | 2 + .../smackx/rsm/provider/RSMSetProvider.java | 4 +- .../xdata/provider/DataFormProvider.java | 2 + .../smackx/mam/MamIntegrationTest.java | 54 +++++------- .../jivesoftware/smackx/mam/package-info.java | 22 +---- 21 files changed, 194 insertions(+), 236 deletions(-) mode change 100644 => 120000 smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java index c483b4bf9a..aed2905b18 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java @@ -148,6 +148,19 @@ public boolean hasStanzaIdSet() { return id != null; } + /** + * Set the stanza id if none is set. + * + * @return the stanza id. + * @since 4.2 + */ + public String setStanzaId() { + if (!hasStanzaIdSet()) { + setStanzaId(StanzaIdUtil.newStanzaId()); + } + return getStanzaId(); + } + /** * Returns who the stanza(/packet) is being sent "to", or null if * the value is not set. The XMPP protocol often makes the "to" diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java index 06e2d3dd42..2f70553ed8 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -34,13 +34,14 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.filter.IQReplyFilter; import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.forward.packet.Forwarded; import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamFinIQ; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; import org.jivesoftware.smackx.mam.element.MamQueryIQ; import org.jivesoftware.smackx.mam.filter.MamResultFilter; import org.jivesoftware.smackx.rsm.packet.RSMSet; @@ -50,11 +51,12 @@ import org.jxmpp.util.XmppDateTime; /** - * Message Archive Management Manager class. + * A Manager for Message Archive Management (XEP-0313). * * @see XEP-0313: Message * Archive Management - * @author Fernando Ramirez and Florian Schmaus + * @author Florian Schmaus + * @author Fernando Ramirez * */ public final class MamManager extends Manager { @@ -223,7 +225,9 @@ public MamQueryResult queryArchive(Integer max, Date start, Date end, Jid withJi addAdditionalFields(additionalFields, dataForm); } - MamQueryIQ mamQueryIQ = prepareMamQueryIQSet(dataForm, queryId); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); + mamQueryIQ.setType(IQ.Type.set); + addResultsLimit(max, mamQueryIQ); return queryArchive(mamQueryIQ); } @@ -273,11 +277,6 @@ private static void addStart(Date start, DataForm dataForm) { dataForm.addField(formField); } - private void preparePageQuery(MamQueryIQ mamQueryIQ, RSMSet rsmSet) { - mamQueryIQ.setType(IQ.Type.set); - mamQueryIQ.addExtension(rsmSet); - } - /** * Returns a page of the archive. * @@ -293,7 +292,8 @@ private void preparePageQuery(MamQueryIQ mamQueryIQ, RSMSet rsmSet) { public MamQueryResult page(DataForm dataForm, RSMSet rsmSet) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), dataForm); - preparePageQuery(mamQueryIQ, rsmSet); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.addExtension(rsmSet); return queryArchive(mamQueryIQ); } @@ -373,20 +373,11 @@ public MamQueryResult pageAfter(Jid chatJid, String lastMessageId, int max) thro public List retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { String queryId = UUID.randomUUID().toString(); - MamQueryIQ mamQueryIQ = prepareMamQueryIQGet(queryId); - return queryFormFields(mamQueryIQ); - } + MamQueryIQ mamQueryIq = new MamQueryIQ(queryId); - private MamQueryIQ prepareMamQueryIQSet(DataForm dataForm, String queryId) { - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); - mamQueryIQ.setType(IQ.Type.set); - return mamQueryIQ; - } + MamQueryIQ mamResponseQueryIq = connection().createPacketCollectorAndSend(mamQueryIq).nextResultOrThrow(); - private MamQueryIQ prepareMamQueryIQGet(String queryId) { - MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, null); - mamQueryIQ.setType(IQ.Type.get); - return mamQueryIQ; + return mamResponseQueryIq.getDataForm().getFields(); } private MamQueryResult queryArchive(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, @@ -435,23 +426,6 @@ private MamQueryResult(List forwardedMessages, MamFinIQ mamFin, DataF } } - private List queryFormFields(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, - NotConnectedException, InterruptedException, NotLoggedInException { - final XMPPConnection connection = connection(); - MamQueryIQ mamResponseQueryIQ = null; - PacketCollector mamResponseQueryIQCollector = connection - .createPacketCollector(new IQReplyFilter(mamQueryIq, connection)); - - try { - connection.sendStanza(mamQueryIq); - mamResponseQueryIQ = mamResponseQueryIQCollector.nextResultOrThrow(); - } finally { - mamResponseQueryIQCollector.cancel(); - } - - return mamResponseQueryIQ.getDataForm().getFields(); - } - /** * Returns true if Message Archive Management is supported by the server. * @@ -466,7 +440,7 @@ public boolean isSupportedByServer() return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(MamElements.NAMESPACE); } - private DataForm getNewMamForm() { + private static DataForm getNewMamForm() { FormField field = new FormField(FormField.FORM_TYPE); field.setType(FormField.Type.hidden); field.addValue(MamElements.NAMESPACE); @@ -487,15 +461,10 @@ private DataForm getNewMamForm() { */ public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { - MamPrefsIQ mamPrefIQ = prepareRetrievePreferencesStanza(); + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); return queryMamPrefs(mamPrefIQ); } - private MamPrefsIQ prepareRetrievePreferencesStanza() { - MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.get, null, null, null); - return mamPrefIQ; - } - /** * Update the preferences in the server. * @@ -505,9 +474,8 @@ private MamPrefsIQ prepareRetrievePreferencesStanza() { * @param neverJids * is the list of JIDs that should never have messages to/from * archived in the user's store - * @param defaultField - * can be "roster", "always", "never" (look at the XEP-0313 - * documentation) + * @param defaultBehavior + * can be "roster", "always", "never" (see XEP-0313) * @return the MAM preferences result * @throws NoResponseException * @throws XMPPErrorException @@ -515,18 +483,14 @@ private MamPrefsIQ prepareRetrievePreferencesStanza() { * @throws InterruptedException * @throws NotLoggedInException */ - public MamPrefsResult updateArchivingPreferences(List alwaysJids, List neverJids, String defaultField) + public MamPrefsResult updateArchivingPreferences(List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { - MamPrefsIQ mamPrefIQ = prepareUpdatePreferencesStanza(alwaysJids, neverJids, defaultField); + Objects.requireNonNull(defaultBehavior, "Default behavior must be set"); + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); return queryMamPrefs(mamPrefIQ); } - private MamPrefsIQ prepareUpdatePreferencesStanza(List alwaysJids, List neverJids, String defaultField) { - MamPrefsIQ mamPrefIQ = new MamPrefsIQ(Type.set, alwaysJids, neverJids, defaultField); - return mamPrefIQ; - } - /** * MAM preferences result class. * @@ -544,16 +508,8 @@ private MamPrefsResult(MamPrefsIQ mamPrefs, DataForm form) { private MamPrefsResult queryMamPrefs(MamPrefsIQ mamPrefsIQ) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); - MamPrefsIQ mamPrefsResultIQ = null; - PacketCollector prefsResultIQCollector = connection - .createPacketCollector(new IQReplyFilter(mamPrefsIQ, connection)); - try { - connection.sendStanza(mamPrefsIQ); - mamPrefsResultIQ = prefsResultIQCollector.nextResultOrThrow(); - } finally { - prefsResultIQCollector.cancel(); - } + MamPrefsIQ mamPrefsResultIQ = connection.createPacketCollectorAndSend(mamPrefsIQ).nextResultOrThrow(); return new MamPrefsResult(mamPrefsResultIQ, DataForm.from(mamPrefsIQ)); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java index 72e57e93d1..8c8eecfe15 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java @@ -153,7 +153,7 @@ public static class AlwaysJidListElement implements Element { /** * list of JIDs. */ - private List alwaysJids; + private final List alwaysJids; /** * Always JID list element constructor. diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java index 1c5bedf263..213737479c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java @@ -33,6 +33,13 @@ */ public class MamPrefsIQ extends IQ { + public enum DefaultBehavior { + always, + never, + roster, + ; + } + /** * the preferences element. */ @@ -43,65 +50,44 @@ public class MamPrefsIQ extends IQ { */ public static final String NAMESPACE = MamElements.NAMESPACE; - /** - * true if it is a request for update preferences. - */ - private boolean isUpdate; - - /** - * true if it is a result preferences. - */ - private boolean isResult; - /** * list of always. */ - private List alwaysJids; + private final List alwaysJids; /** * list of never. */ - private List neverJids; + private final List neverJids; /** * default field. */ - private String defaultField; + private final DefaultBehavior defaultBehavior; + + /** + * Construct a new MAM {@code } IQ retrieval request (IQ type 'get'). + */ + public MamPrefsIQ() { + super(ELEMENT, NAMESPACE); + alwaysJids = null; + neverJids = null; + defaultBehavior = null; + } /** * MAM preferences IQ constructor. * - * @param type * @param alwaysJids * @param neverJids - * @param defaultField + * @param defaultBehavior */ - public MamPrefsIQ(Type type, List alwaysJids, List neverJids, String defaultField) { + public MamPrefsIQ(List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) { super(ELEMENT, NAMESPACE); - this.setType(type); - this.isUpdate = this.getType().equals(Type.set); - this.isResult = this.getType().equals(Type.result); + setType(Type.set); this.alwaysJids = alwaysJids; this.neverJids = neverJids; - this.defaultField = defaultField; - } - - /** - * True if it is a request for update preferences. - * - * @return the update preferences boolean - */ - public boolean isUpdate() { - return isUpdate; - } - - /** - * True if it is a result. - * - * @return the result preferences boolean - */ - public boolean isResult() { - return isUpdate; + this.defaultBehavior = defaultBehavior; } /** @@ -123,19 +109,24 @@ public List getNeverJids() { } /** - * Get the default field. + * Get the default behavior. * - * @return the default field + * @return the default behavior. */ - public String getDefault() { - return defaultField; + public DefaultBehavior getDefault() { + return defaultBehavior; } @Override protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { - if (isUpdate || isResult) { - xml.attribute("default", defaultField); + if (getType().equals(IQ.Type.set) || getType().equals(IQ.Type.result)) { + xml.attribute("default", defaultBehavior); + } + + if (alwaysJids == null && neverJids == null) { + xml.setEmptyElement(); + return xml; } xml.rightAngleBracket(); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java index 7a6f066783..9a0c190457 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java @@ -51,6 +51,7 @@ public class MamQueryIQ extends IQ { */ public MamQueryIQ(String queryId) { this(queryId, null, null); + setType(IQ.Type.get); } /** diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java index 19e2a15619..8dcd318970 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/filter/MamResultFilter.java @@ -31,7 +31,7 @@ */ public class MamResultFilter extends FlexibleStanzaTypeFilter { - private String queryId; + private final String queryId; public MamResultFilter(MamQueryIQ mamQueryIQ) { super(Message.class); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java index aa1f129048..dbdd4942b5 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamFinIQProvider.java @@ -17,6 +17,7 @@ package org.jivesoftware.smackx.mam.provider; import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smackx.mam.element.MamFinIQ; import org.jivesoftware.smackx.rsm.packet.RSMSet; import org.jivesoftware.smackx.rsm.provider.RSMSetProvider; @@ -35,20 +36,23 @@ public class MamFinIQProvider extends IQProvider { @Override public MamFinIQ parse(XmlPullParser parser, int initialDepth) throws Exception { String queryId = parser.getAttributeValue("", "queryid"); - boolean complete = Boolean.parseBoolean(parser.getAttributeValue("", "complete")); - boolean stable = Boolean.parseBoolean(parser.getAttributeValue("", "stable")); + boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false); + boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true); RSMSet rsmSet = null; outerloop: while (true) { int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { + switch (eventType) { + case XmlPullParser.START_TAG: if (parser.getName().equals(RSMSet.ELEMENT)) { - rsmSet = new RSMSetProvider().parse(parser); + rsmSet = RSMSetProvider.INSTANCE.parse(parser); } - } else if (eventType == XmlPullParser.END_TAG) { + break; + case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } + break; } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java index 5d2e7586a4..e656297801 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamPrefsIQProvider.java @@ -16,15 +16,17 @@ */ package org.jivesoftware.smackx.mam.provider; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; import org.jxmpp.jid.Jid; import org.jxmpp.jid.impl.JidCreate; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; /** * MAM Preferences IQ Provider class. @@ -37,9 +39,13 @@ public class MamPrefsIQProvider extends IQProvider { @Override - public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException { String iqType = parser.getAttributeValue("", "type"); - String defaultField = parser.getAttributeValue("", "default"); + String defaultBehaviorString = parser.getAttributeValue("", "default"); + DefaultBehavior defaultBehavior = null; + if (defaultBehaviorString != null) { + defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString); + } if (iqType == null) { iqType = "result"; @@ -49,40 +55,52 @@ public MamPrefsIQ parse(XmlPullParser parser, int initialDepth) throws Exception List neverJids = null; outerloop: while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("always")) { + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case "always": alwaysJids = iterateJids(parser); - } - if (parser.getName().equals("never")) { + break; + case "never": neverJids = iterateJids(parser); + break; } - } else if (eventType == XmlPullParser.END_TAG) { + break; + case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } + break; } } - return new MamPrefsIQ(Type.fromString(iqType), alwaysJids, neverJids, defaultField); + return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); } - private List iterateJids(XmlPullParser parser) throws Exception { + private static List iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException { List jids = new ArrayList<>(); int initialDepth = parser.getDepth(); outerloop: while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("jid")) { + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case "jid": parser.next(); jids.add(JidCreate.from(parser.getText())); + break; } - } else if (eventType == XmlPullParser.END_TAG) { + break; + case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } + break; } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java index 2d763073ee..cdb859a04a 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamQueryIQProvider.java @@ -39,16 +39,22 @@ public MamQueryIQ parse(XmlPullParser parser, int initialDepth) throws Exception String node = parser.getAttributeValue("", "node"); outerloop: while (true) { - int eventType = parser.next(); + final int eventType = parser.next(); + final String name = parser.getName(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals(DataForm.ELEMENT)) { - dataForm = new DataFormProvider().parse(parser); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case DataForm.ELEMENT: + dataForm = DataFormProvider.INSTANCE.parse(parser); + break; } - } else if (eventType == XmlPullParser.END_TAG) { + break; + case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } + break; } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java index 5ecc10f5ac..c3b5be1987 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/provider/MamResultProvider.java @@ -39,15 +39,21 @@ public MamResultExtension parse(XmlPullParser parser, int initialDepth) throws E String id = parser.getAttributeValue("", "id"); outerloop: while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals(Forwarded.ELEMENT)) { - forwarded = new ForwardedProvider().parse(parser); + final int eventType = parser.next(); + final String name = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + switch (name) { + case Forwarded.ELEMENT: + forwarded = ForwardedProvider.INSTANCE.parse(parser); + break; } - } else if (eventType == XmlPullParser.END_TAG) { + break; + case XmlPullParser.END_TAG: if (parser.getDepth() == initialDepth) { break outerloop; } + break; } } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java index 820ab1989f..c9c4e37a5f 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java @@ -31,7 +31,7 @@ public class FiltersTest extends MamTest { - private String getMamXMemberWith(List fieldsNames, List fieldsValues) { + private static String getMamXMemberWith(List fieldsNames, List fieldsValues) { String xml = "" + "" + "" + MamElements.NAMESPACE + "" + ""; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java index 67654991e1..a44be134e1 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java @@ -28,16 +28,10 @@ public class MamFinProviderTest extends MamTest { - String exmapleMamFinXml = "" + static final String exmapleMamFinXml = "" + "" + "10" + "09af3-cc343-b409f" + "" + ""; - private String getIQLimitedResultsExample() { - return "" + "" - + "" + "23452-4534-1" - + "390-2342-22" + "16" + "" + "" + ""; - } - @Test public void checkMamFinProvider() throws Exception { XmlPullParser parser = PacketParserUtils.getParserFor(exmapleMamFinXml); @@ -54,7 +48,18 @@ public void checkMamFinProvider() throws Exception { @Test public void checkQueryLimitedResults() throws Exception { - IQ iq = (IQ) PacketParserUtils.parseStanza(getIQLimitedResultsExample()); + // @formatter:off + final String IQ_LIMITED_RESULTS_EXAMPLE = "" + + "" + + "" + + "23452-4534-1" + + "390-2342-22" + "16" + + "" + + "" + + ""; + // @formatter:on + + IQ iq = (IQ) PacketParserUtils.parseStanza(IQ_LIMITED_RESULTS_EXAMPLE); MamFinIQ mamFinIQ = (MamFinIQ) iq; Assert.assertEquals(mamFinIQ.getType(), Type.result); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java index 6fb6ac2121..30c270cd97 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java @@ -50,20 +50,20 @@ public void checkMamPrefsIQProvider() throws Exception { XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1); MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1); - Assert.assertTrue(mamPrefIQ1.isUpdate()); + Assert.assertEquals(IQ.Type.set, mamPrefIQ1.getType()); Assert.assertEquals(mamPrefIQ1.getAlwaysJids().get(0), "romeo@montague.lit"); Assert.assertEquals(mamPrefIQ1.getNeverJids().get(0), "montague@montague.lit"); XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2); MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2); - Assert.assertTrue(mamPrefIQ2.isUpdate()); + Assert.assertEquals(IQ.Type.set, mamPrefIQ2.getType()); Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(0), "romeo@montague.lit"); Assert.assertEquals(mamPrefIQ2.getAlwaysJids().get(1), "montague@montague.lit"); Assert.assertTrue(mamPrefIQ2.getNeverJids().isEmpty()); XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3); MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3); - Assert.assertFalse(mamPrefIQ3.isUpdate()); + Assert.assertEquals(IQ.Type.set, mamPrefIQ3.getType()); } @Test diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java index 58d44aa07f..ad32e32d51 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java @@ -16,8 +16,7 @@ */ package org.jivesoftware.smackx.mam; -import java.lang.reflect.Method; - +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smackx.mam.element.MamQueryIQ; import org.jivesoftware.smackx.rsm.packet.RSMSet; import org.jivesoftware.smackx.xdata.packet.DataForm; @@ -27,25 +26,21 @@ public class PagingTest extends MamTest { - String pagingStanza = "" + "" + private static final String pagingStanza = "" + "" + "" + "" + "urn:xmpp:mam:1" + "" + "" + "" + "10" + "" + "" + ""; @Test public void checkPageQueryStanza() throws Exception { - Method methodPreparePageQuery = MamManager.class.getDeclaredMethod("preparePageQuery", MamQueryIQ.class, - RSMSet.class); - methodPreparePageQuery.setAccessible(true); - DataForm dataForm = getNewMamForm(); int max = 10; RSMSet rsmSet = new RSMSet(max); MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm); mamQueryIQ.setStanzaId("sarasa"); - - methodPreparePageQuery.invoke(mamManager, mamQueryIQ, rsmSet); + mamQueryIQ.setType(IQ.Type.set); + mamQueryIQ.addExtension(rsmSet); Assert.assertEquals(mamQueryIQ.getDataForm(), dataForm); Assert.assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0), "urn:xmpp:mam:1"); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java index 1c96812638..3ce06cc816 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PreferencesTest.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.mam; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -25,12 +24,13 @@ import org.jxmpp.jid.impl.JidCreate; import org.jivesoftware.smackx.mam.element.MamElements; import org.jivesoftware.smackx.mam.element.MamPrefsIQ; +import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; import org.junit.Assert; -public class PreferencesTest extends MamTest { +public class PreferencesTest { String retrievePrefsStanzaExample = "" + "" + ""; + + "'/>" + ""; String updatePrefsStanzaExample = "" + "" + "" + "romeo@montague.lit" + "other@montague.lit" @@ -38,21 +38,13 @@ public class PreferencesTest extends MamTest { @Test public void checkRetrievePrefsStanza() throws Exception { - Method prepareRetrievePreferencesStanza = MamManager.class - .getDeclaredMethod("prepareRetrievePreferencesStanza"); - prepareRetrievePreferencesStanza.setAccessible(true); - - MamPrefsIQ mamPrefIQ = (MamPrefsIQ) prepareRetrievePreferencesStanza.invoke(mamManager); + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); mamPrefIQ.setStanzaId("sarasa"); Assert.assertEquals(mamPrefIQ.toXML().toString(), retrievePrefsStanzaExample); } @Test public void checkUpdatePrefsStanza() throws Exception { - Method prepareUpdatePreferencesStanza = MamManager.class.getDeclaredMethod("prepareUpdatePreferencesStanza", - List.class, List.class, String.class); - prepareUpdatePreferencesStanza.setAccessible(true); - List alwaysJids = new ArrayList<>(); alwaysJids.add(JidCreate.from("romeo@montague.lit")); alwaysJids.add(JidCreate.from("other@montague.lit")); @@ -60,7 +52,7 @@ public void checkUpdatePrefsStanza() throws Exception { List neverJids = new ArrayList<>(); neverJids.add(JidCreate.from("montague@montague.lit")); - MamPrefsIQ mamPrefIQ = (MamPrefsIQ) prepareUpdatePreferencesStanza.invoke(mamManager, alwaysJids, neverJids, "roster"); + MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster); mamPrefIQ.setStanzaId("sarasa"); Assert.assertEquals(mamPrefIQ.toXML().toString(), updatePrefsStanzaExample); } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java index df64ddc4af..46939c42d3 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java @@ -30,10 +30,10 @@ public class RetrieveFormFieldsTest extends MamTest { - String retrieveFormFieldStanza = "" + "" + "" + ""; - String additionalFieldsStanza = "" + "" + private static final String additionalFieldsStanza = "" + "" + "" + MamElements.NAMESPACE + "" + "" + "" + "Hi" + "" + "" + "Hi2" + "" @@ -41,10 +41,7 @@ public class RetrieveFormFieldsTest extends MamTest { @Test public void checkRetrieveFormFieldsStanza() throws Exception { - Method methodPrepareMamQueryIQGet = MamManager.class.getDeclaredMethod("prepareMamQueryIQGet", String.class); - methodPrepareMamQueryIQGet.setAccessible(true); - - MamQueryIQ mamQueryIQ = (MamQueryIQ) methodPrepareMamQueryIQGet.invoke(mamManager, queryId); + MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId); mamQueryIQ.setStanzaId("sarasa"); Assert.assertEquals(mamQueryIQ.toXML().toString(), retrieveFormFieldStanza); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java index f3564484e1..03a59a93fb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java @@ -36,6 +36,8 @@ */ public class ForwardedProvider extends ExtensionElementProvider { + public static final ForwardedProvider INSTANCE = new ForwardedProvider(); + private static final Logger LOGGER = Logger.getLogger(ForwardedProvider.class.getName()); @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java index 8cc90bb24b..c2bcbf7f19 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/provider/RSMSetProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-2016 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ public class RSMSetProvider extends ExtensionElementProvider { + public static final RSMSetProvider INSTANCE = new RSMSetProvider(); + @Override public RSMSet parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java index cd59cc2e21..aaf04a9b0f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java @@ -40,6 +40,8 @@ */ public class DataFormProvider extends ExtensionElementProvider { + public static final DataFormProvider INSTANCE = new DataFormProvider(); + @Override public DataForm parse(XmlPullParser parser, int initialDepth) throws Exception { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java index d61fa3ac72..c1ad1dce61 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java @@ -16,8 +16,9 @@ */ package org.jivesoftware.smackx.mam; +import static org.junit.Assert.assertEquals; + import java.util.List; -import java.util.UUID; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest; @@ -30,8 +31,7 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smackx.forward.packet.Forwarded; import org.jivesoftware.smackx.mam.MamManager.MamQueryResult; -import org.junit.Assert; -import org.jxmpp.jid.Jid; +import org.jxmpp.jid.EntityBareJid; public class MamIntegrationTest extends AbstractSmackIntegrationTest { @@ -49,8 +49,19 @@ public MamIntegrationTest(SmackIntegrationTestEnvironment environment) throws No } - private Message getConTwoLastMessageSentFrom(Jid userOne) throws NoResponseException, XMPPErrorException, - NotConnectedException, InterruptedException, NotLoggedInException { + @SmackIntegrationTest + public void mamTest() throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, + NotLoggedInException { + EntityBareJid userOne = conOne.getUser().asEntityBareJid(); + EntityBareJid userTwo = conTwo.getUser().asEntityBareJid(); + + Message message = new Message(userTwo); + String messageId = message.setStanzaId(); + String messageBody = "test message"; + message.setBody(messageBody); + + conOne.sendStanza(message); + int pageSize = 20; MamQueryResult mamQueryResult = mamManagerConTwo.queryArchive(pageSize, null, null, userOne, null); @@ -59,35 +70,12 @@ private Message getConTwoLastMessageSentFrom(Jid userOne) throws NoResponseExcep } List forwardedMessages = mamQueryResult.forwardedMessages; - Message messageFromMAM = (Message) forwardedMessages.get(forwardedMessages.size() - 1).getForwardedStanza(); - return messageFromMAM; - } - - private Message prepareMessage(Jid to, String messageId, String body) { - Message message = new Message(); - message.setTo(to); - message.setStanzaId(messageId); - message.setBody(body); - return message; - } - - @SmackIntegrationTest - public void mamTest() throws Exception { - Jid userOne = conOne.getUser().asEntityBareJid(); - Jid userTwo = conTwo.getUser().asEntityBareJid(); - - String messageId = UUID.randomUUID().toString(); - String messageBody = "test message"; - - Message message = prepareMessage(userTwo, messageId, messageBody); - conOne.sendStanza(message); - - Message mamMessage = getConTwoLastMessageSentFrom(userOne); + Message mamMessage = (Message) forwardedMessages.get(forwardedMessages.size() - 1).getForwardedStanza(); - Assert.assertEquals(messageId, mamMessage.getStanzaId()); - Assert.assertEquals(messageBody, mamMessage.getBody()); - Assert.assertEquals(conOne.getUser(), mamMessage.getFrom()); - Assert.assertEquals(userTwo, mamMessage.getTo()); + assertEquals(messageId, mamMessage.getStanzaId()); + assertEquals(messageBody, mamMessage.getBody()); + assertEquals(conOne.getUser(), mamMessage.getFrom()); + assertEquals(userTwo, mamMessage.getTo()); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java deleted file mode 100644 index 09b4b40744..0000000000 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * Copyright 2016 Fernando Ramirez - * - * 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. - */ - -/** - * Message Archive Management (XEP-0313) integration tests. - */ -package org.jivesoftware.smackx.mam; diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java new file mode 120000 index 0000000000..54c99cb446 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/package-info.java @@ -0,0 +1 @@ +../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/mam/package-info.java \ No newline at end of file From 4d7e4b2ab1243111ee286d9707fd7c8905bbc0c8 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 Jul 2016 21:21:21 +0200 Subject: [PATCH 09/18] Add Message(String, String) constructor to make simply Smack examples work, e.g. Message message = new Message("user@example.org", "Hi, how are you?"); --- .../java/org/jivesoftware/smack/packet/Message.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java index 17d1d3f23f..2a6321e1ef 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -27,6 +27,8 @@ import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; /** * Represents XMPP message packets. A message can be one of several types: @@ -101,6 +103,17 @@ public Message(Jid to, String body) { setBody(body); } + /** + * Creates a new message to the specified recipient and with the specified body. + * + * @param to the user to send the message to. + * @param body the body of the message. + * @throws XmppStringprepException if 'to' is not a valid XMPP address. + */ + public Message(String to, String body) throws XmppStringprepException { + this(JidCreate.from(to), body); + } + /** * Copy constructor. *

From 67cc513b2e45996ea4eef1564a36010ac1f91471 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 Jul 2016 23:40:07 +0200 Subject: [PATCH 10/18] Update releasedocs/README.html --- resources/releasedocs/README.html | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/resources/releasedocs/README.html b/resources/releasedocs/README.html index 962c43fbc8..195858a8f1 100644 --- a/resources/releasedocs/README.html +++ b/resources/releasedocs/README.html @@ -155,13 +155,28 @@

Smack Readme

Thank you for downloading Smack! This version of Smack is compatible -with JVMs @targetCompatibility@ or higher. If you dont' use a +with JVMs @targetCompatibility@ or higher. Using a build system which +is able to consume Maven artifacts, like gradle or Maven, is highly +recommended when using Smack. +

+ +

+ This is not the real README. Please visit +

+ https://www.igniterealtime.org/projects/smack/readme +
+ for the README of the current stable Smack version. +

+ +

+If you dont' use a dependency resolution system, like gradle or maven, then you will need to download at least the Xml Pull Parser 3rd Edition (XPP3) library or any other library that implements the XmlPullParser interface -(like kXML). +(like kXML) and the set of +jXMPP libraries.

@@ -174,21 +189,14 @@

Smack Readme

online forum.

-

About the Distribution

- -The smack-core.jar file in the main distribution folder. The optional -smack-extensions.jar contains the Smack extensions -while smack-debug.jar contains an enhanced debugger.

-

Changelog and Upgrading

View the changelog for a list of changes since the last release. -If you are upgrading from Smack 3 to Smack 4, then please consult the Smack 4 Readme and Upgrade Guide

License Agreements

    -
  • Use of the Smack source code is governed by the Apache License: +
  • Use of the Smack source code is governed by the Apache License Version 2.0:
      Copyright 2002-2008 Jive Software.
     
    
    From 125f858b453afb2907e8923012875da726ee074b Mon Sep 17 00:00:00 2001
    From: ramabit 
    Date: Fri, 29 Jul 2016 16:46:48 -0300
    Subject: [PATCH 11/18] add mam documentation
    
    ---
     documentation/extensions/mam.md | 192 ++++++++++++++++++++++++++++++++
     1 file changed, 192 insertions(+)
     create mode 100644 documentation/extensions/mam.md
    
    diff --git a/documentation/extensions/mam.md b/documentation/extensions/mam.md
    new file mode 100644
    index 0000000000..ed0de6b03c
    --- /dev/null
    +++ b/documentation/extensions/mam.md
    @@ -0,0 +1,192 @@
    +Message Archive Management
    +==========================
    +
    +Query and control an archive of messages stored on a server.
    +
    +  * Check MAM support
    +  * Query archive
    +  * Paging
    +  * Get form fields
    +  * Get preferences
    +  * Update preferences
    +
    +
    +**XEP related:** [XEP-0313](http://xmpp.org/extensions/xep-0313.html)
    +
    +
    +Get an instance of Message Archive Management Manager 
    +-----------------------------------------------------
    +
    +```
    +MamManager mamManager = MamManager.getInstanceFor(connection);
    +```
    +
    +
    +Check MAM support
    +-----------------
    +
    +```
    +boolean isSupported = mamManager.isSupportedByServer();
    +```
    +
    +
    +Query archive
    +-------------
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchive(max);
    +```
    +*max* is an `Integer`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchive(withJid);
    +```
    +*withJid* is a `Jid`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchive(start, end);
    +```
    +*start* is a `Date`
    +
    +*end* is a `Date`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchive(additionalFields);
    +```
    +*additionalFields* is a `List`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchiveWithStartDate(start);
    +```
    +*start* is a `Date`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchiveWithEndDate(end);
    +```
    +*end* is a `Date`
    +
    +or
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.queryArchive(max, start, end, withJid, additionalFields);
    +```
    +*max* is an `Integer`
    +
    +*start* is a `Date`
    +
    +*end* is a `Date`
    +
    +*withJid* is a `Jid`
    +
    +*additionalFields* is a `List`
    +
    +
    +**Get data from mamQueryResult object**
    +
    +```
    +// Get forwarded messages
    +List forwardedMessages = mamQueryResult.forwardedMessages;
    +
    +// Get fin IQ
    +MamFinIQ mamFinIQ = mamQueryResult.mamFinIQ;
    +```
    +
    +
    +Paging
    +------
    +
    +**Get a page**
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.page(dataForm, rsmSet);
    +```
    +*dataForm* is a `DataForm`
    +
    +*rsmSet* is a `RSMSet`
    +
    +
    +**Get the next page**
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.pageNext(previousMamQueryResult, count);
    +```
    +*previousMamQueryResult* is a `MamQueryResult`
    +
    +*count* is an `int`
    +
    +
    +**Get page before the first message saved (specific chat)**
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.pageBefore(chatJid, firstMessageId, max);
    +```
    +*chatJid* is a `Jid`
    +
    +*firstMessageId* is a `String`
    +
    +*max* is an `int`
    +
    +
    +**Get page after the last message saved (specific chat)**
    +
    +```
    +MamQueryResult mamQueryResult = mamManager.pageAfter(chatJid, lastMessageId, max);
    +```
    +*chatJid* is a `Jid`
    +
    +*lastMessageId* is a `String`
    +
    +*max* is an `int`
    +
    +
    +Get form fields
    +---------------
    +
    +```
    +List formFields = mamManager.retrieveFormFields();
    +```
    +
    +
    +Get preferences
    +---------------
    +
    +```
    +MamPrefsResult mamPrefsResult = mamManager.retrieveArchivingPreferences();
    +
    +// Get preferences IQ
    +MamPrefsIQ mamPrefs = mamPrefsResult.mamPrefs;
    +
    +// Obtain always and never list
    +List alwaysJids = mamPrefs.getAlwaysJids();
    +List neverJids = mamPrefs.getNeverJids();
    +
    +// Obtain default behaviour (can be 'always', 'never' or 'roster')
    +DefaultBehavior defaultBehavior = mamPrefs.getDefault();
    +
    +// Get the data form
    +DataForm dataForm = mamPrefs.form;
    +```
    +
    +
    +Update preferences
    +------------------
    +
    +```
    +MamPrefsResult mamPrefsResult = mamManager.updateArchivingPreferences(alwaysJids, neverJids, defaultBehavior);
    +```
    +*alwaysJids* is a `List`
    +
    +*neverJids* is a `List`
    +
    +*defaultBehavior* is a `DefaultBehavior`
    +
    
    From 656914dcb0a9747f852e748c5f31dc16371ff729 Mon Sep 17 00:00:00 2001
    From: ramabit 
    Date: Fri, 29 Jul 2016 17:55:10 -0300
    Subject: [PATCH 12/18] add mam.md in index.md
    
    ---
     documentation/extensions/index.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
    index 274951a344..6ab64d5de6 100644
    --- a/documentation/extensions/index.md
    +++ b/documentation/extensions/index.md
    @@ -85,7 +85,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
     | [Internet of Things - Discovery](iot.md)    | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
     | Google GCM JSON payload                     | n/a                                                  | Semantically the same as XEP-0335: JSON Containers |
     | Client State Indication                     | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |
    -| Message Archive Management                  | [XEP-0313](http://xmpp.org/extensions/xep-0313.html) | Query and control an archive of messages stored on a server. |
    +| [Message Archive Management](mam.md)                  | [XEP-0313](http://xmpp.org/extensions/xep-0313.html) | Query and control an archive of messages stored on a server. |
     
     
     Legacy Smack Extensions and currently supported XEPs of smack-legacy
    
    From a87007fb7795b52f067f907af628afed5e52fda5 Mon Sep 17 00:00:00 2001
    From: Florian Schmaus 
    Date: Sat, 30 Jul 2016 10:29:30 +0200
    Subject: [PATCH 13/18] Smack 4.1.8
    
    ---
     resources/releasedocs/changelog.html | 15 +++++++++++++++
     version.gradle                       |  2 +-
     2 files changed, 16 insertions(+), 1 deletion(-)
    
    diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html
    index b844e1e00b..01c8a003ba 100644
    --- a/resources/releasedocs/changelog.html
    +++ b/resources/releasedocs/changelog.html
    @@ -141,6 +141,21 @@ 

    Smack Changelog

    +

    4.1.8 -- 2016-07-30

    + +

    Bug +

    +
      +
    • [SMACK-722] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice +
    • +
    • [SMACK-724] - Do not re-use the Socket after connect() failed. +
    • +
    • [SMACK-725] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure +
    • +
    • [SMACK-726] - 'purge' and 'remove' IQ of XEP-0013 must be of type 'set' +
    • +
    +

    4.1.7 -- 2016-04-14

    Bug diff --git a/version.gradle b/version.gradle index d19cd230ac..41a5580b64 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { shortVersion = '4.1.8' - isSnapshot = true + isSnapshot = false jxmppVersion = '0.4.2' smackMinAndroidSdk = 8 } From 252f1b690cf66fa2012ab044890d515bc23976a4 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 30 Jul 2016 11:00:06 +0200 Subject: [PATCH 14/18] Smack 4.2.0-beta2 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index e74f1891ad..2337ae3609 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { shortVersion = '4.2.0-beta2' - isSnapshot = true + isSnapshot = false jxmppVersion = '0.5.0-alpha7' smackMinAndroidSdk = 8 } From 632acbe33f208de9740922daa78e8d571aa75ba9 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 30 Jul 2016 11:11:54 +0200 Subject: [PATCH 15/18] Smack 4.2.0-beta3-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 2337ae3609..16b0168971 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { - shortVersion = '4.2.0-beta2' - isSnapshot = false + shortVersion = '4.2.0-beta3' + isSnapshot = true jxmppVersion = '0.5.0-alpha7' smackMinAndroidSdk = 8 } From 8810f17460e7e5faea6431148c7297e9d1d2e9df Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 Jul 2016 12:09:06 +0200 Subject: [PATCH 16/18] Allow empty username/passwords in integration test --- .../org/igniterealtime/smack/inttest/Configuration.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index 9f36a4c99c..abc9a4fd22 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -266,7 +266,11 @@ public static Configuration newConfiguration() throws IOException { String accountTwoPassword = properties.getProperty("accountTwoPassword"); String accountThreeUsername = properties.getProperty("accountThreeUsername"); String accountThreePassword = properties.getProperty("accountThreePassword"); - builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword); + if (accountOneUsername != null || accountOnePassword != null || accountTwoUsername != null + || accountTwoPassword != null || accountThreeUsername != null || accountThreePassword != null) { + builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, + accountTwoPassword, accountThreeUsername, accountThreePassword); + } builder.setDebug(properties.getProperty("debug")); builder.setEnabledTests(properties.getProperty("enabledTests")); From 5b137616bbdd5e0ec21d09e3b271f50612f226bd Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 Jul 2016 14:30:23 +0200 Subject: [PATCH 17/18] Fix NPE in IoTDiscoveryManager --- .../smackx/iot/discovery/IoTDiscoveryManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java index 8851f84e1e..f1ce9c3625 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/IoTDiscoveryManager.java @@ -36,6 +36,7 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.iot.Thing; @@ -202,7 +203,7 @@ public IQ handleIQRequest(IQ iqRequest) { /** * Try to find an XMPP IoT registry. * - * @return the JID of a Thing Registry if one could be found. + * @return the JID of a Thing Registry if one could be found, null otherwise. * @throws InterruptedException * @throws NotConnectedException * @throws XMPPErrorException @@ -373,9 +374,11 @@ public void disownThing(Jid registry, Jid thing, NodeInfo nodeInfo) // Registry utility methods public boolean isRegistry(BareJid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + Objects.requireNonNull(jid, "JID argument must not be null"); // At some point 'usedRegistries' will also contain the registry returned by findRegistry(), but since this is // not the case from the beginning, we perform findRegistry().equals(jid) too. - if (findRegistry().equals(jid)) { + Jid registry = findRegistry(); + if (jid.equals(registry)) { return true; } if (usedRegistries.contains(jid)) { From f1e24e227367f3ec6723d2fd2c1a855c9b287017 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 Jul 2016 14:30:31 +0200 Subject: [PATCH 18/18] Rework Roster's SubscribeListener allow multiple of them to be installed, instead of at most one. Fixes deadlock in LowLevelRosterIntegration test because IoTProvisioningManager's SubscribeListener would not come up with a decission. --- .../provisioning/IoTProvisioningManager.java | 2 +- .../org/jivesoftware/smack/roster/Roster.java | 47 ++++++++++++++----- .../smack/roster/RosterIntegrationTest.java | 22 ++++++--- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java index 077a068827..d9264dfca1 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java @@ -151,7 +151,7 @@ public IQ handleIQRequest(IQ iqRequest) { }); roster = Roster.getInstanceFor(connection); - roster.setSubscribeListener(new SubscribeListener() { + roster.addSubscribeListener(new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { // First check if the subscription request comes from a known registry and accept the request if so. diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index 59513f454c..52a18dad29 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -199,7 +199,9 @@ private enum RosterState { private SubscriptionMode subscriptionMode = getDefaultSubscriptionMode(); - private SubscribeListener subscribeListener; + private final Set subscribeListeners = new CopyOnWriteArraySet<>(); + + private SubscriptionMode previousSubscriptionMode; /** * Returns the default subscription processing mode to use when a new Roster is created. The @@ -249,11 +251,12 @@ public void processPacket(Stanza stanza) throws NotConnectedException, SubscribeAnswer subscribeAnswer = null; switch (subscriptionMode) { case manual: - final SubscribeListener subscribeListener = Roster.this.subscribeListener; - if (subscribeListener == null) { - return; + for (SubscribeListener subscribeListener : subscribeListeners) { + subscribeAnswer = subscribeListener.processSubscribe(from, presence); + if (subscribeAnswer != null) { + break; + } } - subscribeAnswer = subscribeListener.processSubscribe(from, presence); if (subscribeAnswer == null) { return; } @@ -666,19 +669,37 @@ public void sendSubscriptionRequest(BareJid jid) throws NotLoggedInException, No } /** - * Set the subscribe listener, which is invoked on incoming subscription requests and if - * {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. If - * subscribeListener is not null, then this also sets subscription + * Add a subscribe listener, which is invoked on incoming subscription requests and if + * {@link SubscriptionMode} is set to {@link SubscriptionMode#manual}. This also sets subscription * mode to {@link SubscriptionMode#manual}. * - * @param subscribeListener the subscribe listener to set. + * @param subscribeListener the subscribe listener to add. + * @return true if the listener was not already added. + * @since 4.2 + */ + public boolean addSubscribeListener(SubscribeListener subscribeListener) { + Objects.requireNonNull(subscribeListener, "SubscribeListener argument must not be null"); + if (subscriptionMode != SubscriptionMode.manual) { + previousSubscriptionMode = subscriptionMode; + } + return subscribeListeners.add(subscribeListener); + } + + /** + * Remove a subscribe listener. Also restores the previous subscription mode + * state, if the last listener got removed. + * + * @param subscribeListener + * the subscribe listener to remove. + * @return true if the listener registered and got removed. * @since 4.2 */ - public void setSubscribeListener(SubscribeListener subscribeListener) { - if (subscribeListener != null) { - setSubscriptionMode(SubscriptionMode.manual); + public boolean removeSubscribeListener(SubscribeListener subscribeListener) { + boolean removed = subscribeListeners.remove(subscribeListener); + if (removed && subscribeListeners.isEmpty()) { + setSubscriptionMode(previousSubscriptionMode); } - this.subscribeListener = subscribeListener; + return removed; } /** diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java index d5a6394d30..8691bacaca 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java @@ -50,7 +50,7 @@ public RosterIntegrationTest(SmackIntegrationTestEnvironment environment) { public void subscribeRequestListenerTest() throws TimeoutException, Exception { ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); - rosterTwo.setSubscribeListener(new SubscribeListener() { + final SubscribeListener subscribeListener = new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { if (from.equals(conOne.getUser().asBareJid())) { @@ -58,7 +58,8 @@ public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { } return SubscribeAnswer.Deny; } - }); + }; + rosterTwo.addSubscribeListener(subscribeListener); final String conTwosRosterName = "ConTwo " + testRunId; final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint(); @@ -88,9 +89,15 @@ private void checkIfAddedAndSubscribed(Collection addresses) { } } }); - rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); - assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout())); + try { + rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); + + assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout())); + } + finally { + rosterTwo.removeSubscribeListener(subscribeListener); + } } public static void ensureBothAccountsAreNotInEachOthersRoster(XMPPConnection conOne, XMPPConnection conTwo) throws NotLoggedInException, @@ -124,7 +131,7 @@ private static void ensureSubscribedTo(final XMPPConnection conOne, final XMPPCo return; } - rosterOne.setSubscribeListener(new SubscribeListener() { + final SubscribeListener subscribeListener = new SubscribeListener() { @Override public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { if (from.equals(conTwo.getUser().asBareJid())) { @@ -132,7 +139,8 @@ public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { } return SubscribeAnswer.Deny; } - }); + }; + rosterOne.addSubscribeListener(subscribeListener); final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() { @@ -149,7 +157,7 @@ public void presenceSubscribed(BareJid address, Presence subscribedPresence) { try { syncPoint.waitForResult(timeout); } finally { - rosterOne.setSubscribeListener(null); + rosterOne.removeSubscribeListener(subscribeListener); } } }