[mam] Support multiple versions of MAM

Support multiple version of MAM and do discovery of the version of MAM supported by
the archive. Currently supported MAM versions are v1 and v2.

Fixes SMACK-911

See https://discourse.igniterealtime.org/t/mam-support-of-multiple-mam-versions/90136
This commit is contained in:
Frank Matheron 2021-08-26 16:43:34 +02:00
parent 3f2418dec7
commit 5b857a1b82
21 changed files with 460 additions and 88 deletions

View File

@ -47,15 +47,17 @@ import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.commands.AdHocCommandManager;
import org.jivesoftware.smackx.commands.RemoteCommand;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
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.element.MamVersion;
import org.jivesoftware.smackx.mam.filter.MamResultFilter;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
@ -225,6 +227,8 @@ public final class MamManager extends Manager {
private final AdHocCommandManager adHocCommandManager;
private MamVersion mamVersion = null;
private MamManager(XMPPConnection connection, Jid archiveAddress) {
super(connection);
this.archiveAddress = archiveAddress;
@ -250,6 +254,52 @@ public final class MamManager extends Manager {
return archiveAddress;
}
/**
* Returns the MAM namespace used by this {@link MamManager}. If the archive does not support any MAM namespace
* supported by Smack, null is returned.
*
* @return the MAM namespace used by this manager, null if MAM is not supported
* @throws NoResponseException if there was no response from the remote entity.
* @throws XMPPErrorException if there was an XMPP error returned.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
public String getMamNamespace() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
MamVersion mamVersion = getSupportedMamVersionOrNull();
return mamVersion == null ? null : mamVersion.getNamespace();
}
private MamVersion getSupportedMamVersionOrNull() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
if (mamVersion != null) {
return mamVersion;
}
DiscoverInfo info = serviceDiscoveryManager.discoverInfo(getArchiveAddress());
// Enum values are always returned the order they are declared (see https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3).
// We pick the first version supported by the server.
for (MamVersion v : MamVersion.values()) {
if (info.containsFeature(v.getNamespace())) {
mamVersion = v;
break;
}
}
return mamVersion;
}
private MamVersion getSupportedMamVersionOrThrow() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
MamVersion mamVersion = getSupportedMamVersionOrNull();
if (mamVersion == null) {
throw new UnsupportedOperationException("Message Archive Management is not supported by " + getArchiveAddress());
}
return mamVersion;
}
private MamElementFactory getElementFactory() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
return getSupportedMamVersionOrThrow().newElementFactory();
}
public static final class MamQueryArgs {
private final String node;
@ -275,11 +325,11 @@ public final class MamManager extends Manager {
private DataForm dataForm;
DataForm getDataForm() {
DataForm getDataForm(MamVersion version) {
if (dataForm != null) {
return dataForm;
}
DataForm.Builder dataFormBuilder = getNewMamForm();
DataForm.Builder dataFormBuilder = getNewMamForm(version);
dataFormBuilder.addFields(formFields.values());
dataForm = dataFormBuilder.build();
return dataForm;
@ -472,9 +522,9 @@ public final class MamManager extends Manager {
NotConnectedException, NotLoggedInException, InterruptedException {
String queryId = StringUtils.secureUniqueRandomString();
String node = mamQueryArgs.node;
DataForm dataForm = mamQueryArgs.getDataForm();
DataForm dataForm = mamQueryArgs.getDataForm(mamVersion);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm);
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setTo(archiveAddress);
@ -530,7 +580,7 @@ public final class MamManager extends Manager {
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException, NotLoggedInException {
String queryId = StringUtils.secureUniqueRandomString();
MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null);
MamQueryIQ mamQueryIq = getElementFactory().newQueryIQ(queryId, node, null);
mamQueryIq.setTo(archiveAddress);
MamQueryIQ mamResponseQueryIq = connection().sendIqRequestAndWaitForResponse(mamQueryIq);
@ -592,7 +642,7 @@ public final class MamManager extends Manager {
private List<Message> page(RSMSet requestRsmSet) throws NoResponseException, XMPPErrorException,
NotConnectedException, NotLoggedInException, InterruptedException {
String queryId = StringUtils.secureUniqueRandomString();
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, form);
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, form);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setTo(archiveAddress);
mamQueryIQ.addExtension(requestRsmSet);
@ -696,9 +746,7 @@ public final class MamManager extends Manager {
* @see <a href="https://xmpp.org/extensions/xep-0313.html#support">XEP-0313 § 7. Determining support</a>
*/
public boolean isSupported() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
// Note that this may return 'null' but SDM's supportsFeature() does the right thing then.
Jid archiveAddress = getArchiveAddress();
return serviceDiscoveryManager.supportsFeature(archiveAddress, MamElements.NAMESPACE);
return getSupportedMamVersionOrNull() != null;
}
public boolean isAdvancedConfigurationSupported() throws InterruptedException, XMPPException, SmackException {
@ -720,8 +768,8 @@ public final class MamManager extends Manager {
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
}
private static DataForm.Builder getNewMamForm() {
FormField field = FormField.buildHiddenFormType(MamElements.NAMESPACE);
private static DataForm.Builder getNewMamForm(MamVersion version) {
FormField field = FormField.buildHiddenFormType(version.getNamespace());
DataForm.Builder form = DataForm.builder();
form.addField(field);
return form;
@ -765,7 +813,7 @@ public final class MamManager extends Manager {
*/
public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
MamPrefsIQ mamPrefIQ = getElementFactory().newPrefsIQ();
return queryMamPrefs(mamPrefIQ);
}
@ -830,6 +878,7 @@ public final class MamManager extends Manager {
public static final class MamPrefs {
private final List<Jid> alwaysJids;
private final List<Jid> neverJids;
private final MamVersion mamVersion;
private DefaultBehavior defaultBehavior;
private MamPrefs(MamPrefsResult mamPrefsResult) {
@ -837,6 +886,7 @@ public final class MamManager extends Manager {
this.alwaysJids = new ArrayList<>(mamPrefsIq.getAlwaysJids());
this.neverJids = new ArrayList<>(mamPrefsIq.getNeverJids());
this.defaultBehavior = mamPrefsIq.getDefault();
this.mamVersion = MamVersion.fromNamespace(mamPrefsIq.getNamespace());
}
public void setDefaultBehavior(DefaultBehavior defaultBehavior) {
@ -856,7 +906,7 @@ public final class MamManager extends Manager {
}
private MamPrefsIQ constructMamPrefsIq() {
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
return mamVersion.newElementFactory().newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
}
}

View File

@ -0,0 +1,94 @@
/**
*
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
*
* 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.Message;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jxmpp.jid.Jid;
/**
* Factory that creates MAM objects.
*
* @since 4.5.0
*/
public interface MamElementFactory {
/**
* Creates a new {@link MamElementFactory} for the parser based on the namespace of the parser.
* @param parser the XML parser to retrieve the MAM namespace from
* @return the factory suitable for the MAM namespace
*/
static MamElementFactory forParser(XmlPullParser parser) {
String namespace = parser.getNamespace();
return MamVersion.fromNamespace(namespace).newElementFactory();
}
/**
* Create a MAM result extension class.
*
* @param queryId id of the query
* @param id the message's archive UID
* @param forwarded the original message as it was received
* @return the result extension
*/
MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded);
/**
* Create a MAM fin IQ class.
*
* @param queryId id of the query
* @param rsmSet the RSM set included in the {@code <fin/>}
* @param complete true if the results returned by the server are complete (no further paging in needed)
* @param stable false if the results returned by the sever are unstable (e.g. they might later change in sequence or content)
* @return the fin IQ
*/
MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable);
/**
* Create a new MAM preferences IQ.
*
* @param alwaysJids JIDs for which all messages are archived by default
* @param neverJids JIDs for which messages are never archived
* @param defaultBehavior default archive behavior
* @return the prefs IQ
*/
MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior);
/**
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
*
* @return the prefs IQ
*/
MamPrefsIQ newPrefsIQ();
/**
* Create a new MAM Query IQ.
*
* @param queryId id of the query
* @param node pubsub node id when querying a pubsub node, null when not querying a pubsub node
* @param dataForm the dataform containing the query parameters
* @return the query IQ
*/
MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm);
}

View File

@ -18,15 +18,13 @@ package org.jivesoftware.smackx.mam.element;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageView;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jxmpp.jid.Jid;
@ -41,8 +39,6 @@ import org.jxmpp.jid.Jid;
*/
public class MamElements {
public static final String NAMESPACE = "urn:xmpp:mam:2";
/**
* MAM result extension class.
*
@ -50,18 +46,13 @@ public class MamElements {
* Archive Management</a>
*
*/
public static class MamResultExtension implements ExtensionElement {
public abstract static class MamResultExtension implements ExtensionElement {
/**
* result element.
*/
public static final String ELEMENT = "result";
/**
* The qualified name of the MAM result extension element.
*/
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
/**
* id of the result.
*/
@ -77,20 +68,27 @@ public class MamElements {
*/
private String queryId;
protected final MamVersion version;
/**
* MAM result extension constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param id TODO javadoc me please
* @param forwarded TODO javadoc me please
*/
public MamResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
public MamResultExtension(MamVersion version, String queryId, String id, Forwarded<Message> 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");
}
if (version == null) {
throw new IllegalArgumentException("version must not be null");
}
this.version = version;
this.id = id;
this.forwarded = forwarded;
this.queryId = queryId;
@ -130,7 +128,7 @@ public class MamElements {
@Override
public final String getNamespace() {
return NAMESPACE;
return version.getNamespace();
}
@Override
@ -148,7 +146,13 @@ public class MamElements {
}
public static MamResultExtension from(MessageView message) {
return message.getExtension(MamResultExtension.class);
for (XmlElement extension : message.getExtensions()) {
if (extension instanceof MamResultExtension) {
return (MamResultExtension) extension;
}
}
return null;
}
}

View File

@ -35,11 +35,6 @@ public class MamFinIQ extends IQ {
*/
public static final String ELEMENT = "fin";
/**
* the IQ NAMESPACE.
*/
public static final String NAMESPACE = MamElements.NAMESPACE;
/**
* RSM set.
*/
@ -63,13 +58,14 @@ public class MamFinIQ extends IQ {
/**
* MamFinIQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param rsmSet TODO javadoc me please
* @param complete TODO javadoc me please
* @param stable TODO javadoc me please
*/
public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
super(ELEMENT, NAMESPACE);
public MamFinIQ(MamVersion version, String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
super(ELEMENT, version.getNamespace());
if (rsmSet == null) {
throw new IllegalArgumentException("rsmSet must not be null");
}

View File

@ -46,11 +46,6 @@ public class MamPrefsIQ extends IQ {
*/
public static final String ELEMENT = "prefs";
/**
* the IQ NAMESPACE.
*/
public static final String NAMESPACE = MamElements.NAMESPACE;
/**
* list of always.
*/
@ -68,9 +63,11 @@ public class MamPrefsIQ extends IQ {
/**
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
*
* @param version TODO javadoc me please *
*/
public MamPrefsIQ() {
super(ELEMENT, NAMESPACE);
public MamPrefsIQ(MamVersion version) {
super(ELEMENT, version.getNamespace());
alwaysJids = null;
neverJids = null;
defaultBehavior = null;
@ -79,12 +76,13 @@ public class MamPrefsIQ extends IQ {
/**
* MAM preferences IQ constructor.
*
* @param version TODO javadoc me please
* @param alwaysJids TODO javadoc me please
* @param neverJids TODO javadoc me please
* @param defaultBehavior TODO javadoc me please
*/
public MamPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
super(ELEMENT, NAMESPACE);
public MamPrefsIQ(MamVersion version, List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
super(ELEMENT, version.getNamespace());
setType(Type.set);
this.alwaysJids = alwaysJids;
this.neverJids = neverJids;

View File

@ -35,11 +35,6 @@ public class MamQueryIQ extends IQ {
*/
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;
@ -47,41 +42,45 @@ public class MamQueryIQ extends IQ {
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
*/
public MamQueryIQ(String queryId) {
this(queryId, null, null);
public MamQueryIQ(MamVersion version, String queryId) {
this(version, queryId, null, null);
setType(IQ.Type.get);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param form TODO javadoc me please
*/
public MamQueryIQ(DataForm form) {
this(null, null, form);
public MamQueryIQ(MamVersion version, DataForm form) {
this(version, null, null, form);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param form TODO javadoc me please
*/
public MamQueryIQ(String queryId, DataForm form) {
this(queryId, null, form);
public MamQueryIQ(MamVersion version, String queryId, DataForm form) {
this(version, queryId, null, form);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param node TODO javadoc me please
* @param dataForm TODO javadoc me please
*/
public MamQueryIQ(String queryId, String node, DataForm dataForm) {
super(ELEMENT, NAMESPACE);
public MamQueryIQ(MamVersion version, String queryId, String node, DataForm dataForm) {
super(ELEMENT, version.getNamespace());
this.queryId = queryId;
this.node = node;
this.dataForm = dataForm;
@ -91,9 +90,9 @@ public class MamQueryIQ extends IQ {
if (formType == null) {
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
}
if (!formType.equals(MamElements.NAMESPACE)) {
if (!formType.equals(version.getNamespace())) {
throw new IllegalArgumentException(
"Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'");
"Value of the hidden form type field must be '" + version.getNamespace() + "'");
}
addExtension(dataForm);
}

View File

@ -0,0 +1,66 @@
/**
*
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
*
* 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 javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jxmpp.jid.Jid;
class MamV1ElementFactory implements MamElementFactory {
@Override
public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
return new MamV1ResultExtension(queryId, id, forwarded);
}
@Override
public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
return new MamFinIQ(MamVersion.MAM1, queryId, rsmSet, complete, stable);
}
@Override
public MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) {
return new MamPrefsIQ(MamVersion.MAM1, alwaysJids, neverJids, defaultBehavior);
}
@Override
public MamPrefsIQ newPrefsIQ() {
return new MamPrefsIQ(MamVersion.MAM1);
}
@Override
public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) {
return new MamQueryIQ(MamVersion.MAM1, queryId, node, dataForm);
}
public static class MamV1ResultExtension extends MamElements.MamResultExtension {
/**
* The qualified name of the MAM result extension element.
*/
public static final QName QNAME = new QName(MamVersion.MAM1.getNamespace(), ELEMENT);
MamV1ResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
super(MamVersion.MAM1, queryId, id, forwarded);
}
}
}

View File

@ -0,0 +1,66 @@
/**
*
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
*
* 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 javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jxmpp.jid.Jid;
class MamV2ElementFactory implements MamElementFactory {
@Override
public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
return new MamV2ResultExtension(queryId, id, forwarded);
}
@Override
public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
return new MamFinIQ(MamVersion.MAM2, queryId, rsmSet, complete, stable);
}
@Override
public MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) {
return new MamPrefsIQ(MamVersion.MAM2, alwaysJids, neverJids, defaultBehavior);
}
@Override
public MamPrefsIQ newPrefsIQ() {
return new MamPrefsIQ(MamVersion.MAM2);
}
@Override
public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) {
return new MamQueryIQ(MamVersion.MAM2, queryId, node, dataForm);
}
public static class MamV2ResultExtension extends MamElements.MamResultExtension {
/**
* The qualified name of the MAM result extension element.
*/
public static final QName QNAME = new QName(MamVersion.MAM2.getNamespace(), ELEMENT);
MamV2ResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
super(MamVersion.MAM2, queryId, id, forwarded);
}
}
}

View File

@ -0,0 +1,69 @@
/**
*
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
*
* 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;
/**
* MAM versions supported by Smack.
*
* @since 4.5.0
*/
public enum MamVersion {
// Note that the order in which the enum values are defined, is also the order in which we attempt to find a
// supported version. The versions should therefore be listed in order of newest to oldest, so that Smack prefers
// using a newer version over an older version.
MAM2("urn:xmpp:mam:2") {
@Override
public MamElementFactory newElementFactory() {
return new MamV2ElementFactory();
}
},
MAM1("urn:xmpp:mam:1") {
@Override
public MamElementFactory newElementFactory() {
return new MamV1ElementFactory();
}
};
private final String namespace;
MamVersion(String namespace) {
this.namespace = namespace;
}
/**
* Each MAM version is identified by its namespace. Returns the namespace for this MAM version.
* @return the namespace of the MAM version
*/
public String getNamespace() {
return namespace;
}
/**
* Creates a new factory that creates IQ's and extension objects for this MAM version.
* @return the factory
*/
public abstract MamElementFactory newElementFactory();
public static MamVersion fromNamespace(String namespace) {
for (MamVersion v : MamVersion.values()) {
if (v.namespace.equals(namespace)) {
return v;
}
}
throw new IllegalArgumentException("Unsupported namespace: " + namespace);
}
}

View File

@ -25,6 +25,7 @@ import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamFinIQ;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.rsm.provider.RSMSetProvider;
@ -41,6 +42,7 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
@Override
public MamFinIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
String queryId = parser.getAttributeValue("", "queryid");
boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false);
boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true);
@ -65,7 +67,7 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
}
}
return new MamFinIQ(queryId, rsmSet, complete, stable);
return elementFactory.newFinIQ(queryId, rsmSet, complete, stable);
}
}

View File

@ -25,6 +25,7 @@ import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
@ -45,6 +46,7 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
@Override
public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
String defaultBehaviorString = parser.getAttributeValue("", "default");
DefaultBehavior defaultBehavior = null;
if (defaultBehaviorString != null) {
@ -79,7 +81,7 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
}
}
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
return elementFactory.newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
}
private static List<Jid> iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException {

View File

@ -24,6 +24,7 @@ import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jivesoftware.smackx.xdata.provider.DataFormProvider;
@ -41,6 +42,7 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
@Override
public MamQueryIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
DataForm dataForm = null;
String queryId = parser.getAttributeValue("", "queryid");
String node = parser.getAttributeValue("", "node");
@ -68,7 +70,7 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
}
}
return new MamQueryIQ(queryId, node, dataForm);
return elementFactory.newQueryIQ(queryId, node, dataForm);
}
}

View File

@ -28,6 +28,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
/**
@ -43,6 +44,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
@Override
public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException, ParseException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
Forwarded<Message> forwarded = null;
String queryId = parser.getAttributeValue("", "queryid");
String id = parser.getAttributeValue("", "id");
@ -69,7 +71,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
}
}
return new MamResultExtension(queryId, id, forwarded);
return elementFactory.newResultExtension(queryId, id, forwarded);
}
}

View File

@ -35,6 +35,26 @@
<namespace>urn:xmpp:mam:2</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
</extensionProvider>
<iqProvider>
<elementName>prefs</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider</className>
</iqProvider>
<iqProvider>
<elementName>query</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamQueryIQProvider</className>
</iqProvider>
<iqProvider>
<elementName>fin</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamFinIQProvider</className>
</iqProvider>
<extensionProvider>
<elementName>result</elementName>
<namespace>urn:xmpp:mam:1</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
</extensionProvider>
<!-- XEP-0323: Internet of Things - Data -->
<iqProvider>

View File

@ -23,7 +23,7 @@ import java.util.Date;
import java.util.List;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.jupiter.api.Test;
@ -35,7 +35,7 @@ public class FiltersTest extends MamTest {
private static String getMamXMemberWith(List<String> fieldsNames, List<? extends CharSequence> fieldsValues) {
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamElements.NAMESPACE + "</value>" + "</field>";
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>";
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
xml += "<field var='" + fieldsNames.get(i) + "'>" + "<value>" + fieldsValues.get(i) + "</value>"
@ -51,7 +51,7 @@ public class FiltersTest extends MamTest {
Date date = new Date();
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsSince(date).build();
DataForm dataForm = mamQueryArgs.getDataForm();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
List<String> fields = new ArrayList<>();
fields.add("start");
@ -66,7 +66,7 @@ public class FiltersTest extends MamTest {
Date date = new Date();
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsBefore(date).build();
DataForm dataForm = mamQueryArgs.getDataForm();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
List<String> fields = new ArrayList<>();
fields.add("end");
@ -81,7 +81,7 @@ public class FiltersTest extends MamTest {
Jid jid = JidTestUtil.BARE_JID_1;
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsToJid(jid).build();
DataForm dataForm = mamQueryArgs.getDataForm();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
List<String> fields = new ArrayList<>();
fields.add("with");

View File

@ -23,6 +23,7 @@ import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.jupiter.api.BeforeAll;
@ -47,9 +48,9 @@ public class MamTest extends SmackTestSuite {
protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm");
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm", MamVersion.class);
methodGetNewMamForm.setAccessible(true);
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager);
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager, MamVersion.MAM2);
return dataFormBuilder.build();
}

View File

@ -22,6 +22,7 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.xdata.packet.DataForm;
@ -40,7 +41,7 @@ public class PagingTest extends MamTest {
int max = 10;
RSMSet rsmSet = new RSMSet(max);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
mamQueryIQ.setStanzaId("sarasa");
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.addExtension(rsmSet);

View File

@ -23,9 +23,9 @@ import java.util.List;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.Jid;
@ -33,16 +33,16 @@ import org.jxmpp.jid.impl.JidCreate;
public class PreferencesTest {
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamElements.NAMESPACE
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
+ "'/>" + "</iq>";
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamElements.NAMESPACE
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
+ "' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>other@montague.lit</jid>"
+ "</always>" + "<never>" + "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
@Test
public void checkRetrievePrefsStanza() throws Exception {
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ();
mamPrefIQ.setStanzaId("sarasa");
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), retrievePrefsStanzaExample);
}
@ -56,7 +56,7 @@ public class PreferencesTest {
List<Jid> neverJids = new ArrayList<>();
neverJids.add(JidCreate.from("montague@montague.lit"));
MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
mamPrefIQ.setStanzaId("sarasa");
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), updatePrefsStanzaExample);
}

View File

@ -29,9 +29,9 @@ import org.jivesoftware.smack.packet.StreamOpen;
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.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.jupiter.api.Test;
@ -41,7 +41,7 @@ public class QueryArchiveTest extends MamTest {
private static final String mamSimpleQueryIQ = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
private static final String mamQueryResultExample = "<message to='hag66@shakespeare.lit/pda' from='coven@chat.shakespeare.lit' id='iasd207'>"
+ "<result xmlns='urn:xmpp:mam:2' queryid='g27' id='34482-21985-73620'>"
@ -54,7 +54,7 @@ public class QueryArchiveTest extends MamTest {
@Test
public void checkMamQueryIQ() throws Exception {
DataForm dataForm = getNewMamForm();
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setStanzaId("sarasa");
assertEquals(mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), mamSimpleQueryIQ);
@ -80,7 +80,7 @@ public class QueryArchiveTest extends MamTest {
Forwarded<Message> forwarded = new Forwarded<>(forwardedMessage, delay);
message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded));
message.addExtension(MamVersion.MAM2.newElementFactory().newResultExtension("g27", "34482-21985-73620", forwarded));
assertEquals(mamQueryResultExample, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());

View File

@ -22,8 +22,8 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.jupiter.api.Test;
@ -32,13 +32,13 @@ public class ResultsLimitTest extends MamTest {
private static final String resultsLimitStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
@Test
public void checkResultsLimit() throws Exception {
DataForm dataForm = getNewMamForm();
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setStanzaId("sarasa");

View File

@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.packet.DataForm;
@ -32,18 +32,18 @@ import org.jxmpp.jid.JidTestUtil;
public class RetrieveFormFieldsTest extends MamTest {
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamElements.NAMESPACE
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamVersion.MAM2.getNamespace()
+ "' queryid='testid'></query>" + "</iq>";
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
+ "<value>" + MamElements.NAMESPACE + "</value>" + "</field>"
+ "<value>" + MamVersion.MAM2.getNamespace() + "</value>" + "</field>"
+ "<field var='urn:example:xmpp:free-text-search'>" + "<value>Hi</value>" + "</field>"
+ "<field var='urn:example:xmpp:stanza-content'>" + "<value>one@exampleone.org</value>" + "</field>"
+ "</x>";
@Test
public void checkRetrieveFormFieldsStanza() throws Exception {
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId);
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId);
mamQueryIQ.setStanzaId("sarasa");
assertEquals(retrieveFormFieldStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
@ -63,7 +63,7 @@ public class RetrieveFormFieldsTest extends MamTest {
.withAdditionalFormField(field1)
.withAdditionalFormField(field2)
.build();
DataForm dataForm = mamQueryArgs.getDataForm();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
String dataFormResult = dataForm.toXML().toString();