You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ra...@apache.org on 2019/09/07 20:30:55 UTC

[mina-vysper] branch master updated: Message archive management: implement preferences get and set

This is an automated email from the ASF dual-hosted git repository.

ralaoui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-vysper.git


The following commit(s) were added to refs/heads/master by this push:
     new fc9035e  Message archive management: implement preferences get and set
fc9035e is described below

commit fc9035eba106fca4447391bc23751fd86c20b14f
Author: Réda Housni Alaoui <re...@gmail.com>
AuthorDate: Sat Sep 7 22:30:47 2019 +0200

    Message archive management: implement preferences get and set
---
 .../modules/extension/xep0313_mam/MAMModule.java   |  12 +-
 .../in_memory/InMemoryMessageArchives.java         |  10 +-
 .../in_memory/InMemoryUserMessageArchive.java      |  70 ++++++++++++
 ...MAMQueryHandler.java => MAMIQQueryHandler.java} |   8 +-
 .../query/MatchingArchivedMessageResult.java       |   2 -
 ...hives.java => DefaultUserArchiveBehaviour.java} |  21 ++--
 .../extension/xep0313_mam/spi/MessageArchives.java |   2 +-
 ...essageArchives.java => UserMessageArchive.java} |  11 +-
 .../UserMessageArchivePreferences.java}            |  25 +++--
 .../xep0313_mam/user/MAMIQPreferenceHandler.java   | 121 +++++++++++++++++++++
 .../user/SimpleUserMessageArchivePreferences.java  |  68 ++++++++++++
 .../xep0313_mam/user/UserArchiveQueryHandler.java  |   4 +-
 .../user/UserMessageArchivePreferencesElement.java |  89 +++++++++++++++
 .../xep0313_mam/user/UserMessageStanzaBroker.java  |   8 +-
 .../xep0313_mam/query/MAMIQHandlerTest.java        |   4 +-
 .../xep0313_mam/spi/MessageArchivesMock.java       |  12 +-
 ...rchiveMock.java => UserMessageArchiveMock.java} |  12 +-
 .../xep0313_mam/{ => user}/UserArchiveTest.java    |  48 ++++++--
 .../UserMessageArchivePreferencesElementTest.java  |  80 ++++++++++++++
 .../user/UserMessageStanzaBrokerTest.java          |   8 +-
 20 files changed, 543 insertions(+), 72 deletions(-)

diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java
index 0c75eb3..ae1f46f 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/MAMModule.java
@@ -28,8 +28,8 @@ import java.util.List;
 import org.apache.commons.lang.StringUtils;
 import org.apache.vysper.xmpp.modules.DefaultDiscoAwareModule;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.interceptor.MAMStanzaHandlerInterceptor;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.preferences.MAMPreferenceHandler;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMQueryHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.user.MAMIQPreferenceHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MAMIQQueryHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchives;
 import org.apache.vysper.xmpp.modules.servicediscovery.management.Feature;
 import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoElement;
@@ -92,10 +92,10 @@ public class MAMModule extends DefaultDiscoAwareModule implements ServerInfoRequ
 
     @Override
     protected void addHandlerDictionaries(List<HandlerDictionary> dictionary) {
-        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V1, new MAMQueryHandler(NAMESPACE_V1)));
-        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V2, new MAMQueryHandler(NAMESPACE_V2)));
-        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V1, new MAMPreferenceHandler(NAMESPACE_V1)));
-        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V2, new MAMPreferenceHandler(NAMESPACE_V2)));
+        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V1, new MAMIQQueryHandler(NAMESPACE_V1)));
+        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V2, new MAMIQQueryHandler(NAMESPACE_V2)));
+        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V1, new MAMIQPreferenceHandler(NAMESPACE_V1)));
+        dictionary.add(new NamespaceHandlerDictionary(NAMESPACE_V2, new MAMIQPreferenceHandler(NAMESPACE_V2)));
     }
 
     @Override
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryMessageArchives.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryMessageArchives.java
index 52b35da..a5d4073 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryMessageArchives.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryMessageArchives.java
@@ -24,20 +24,20 @@ import java.util.Map;
 import java.util.Optional;
 
 import org.apache.vysper.xmpp.addressing.Entity;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchive;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchives;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchive;
 
 /**
  * @author Réda Housni Alaoui
  */
 public class InMemoryMessageArchives implements MessageArchives {
 
-    private final Map<Entity, MessageArchive> userMessageArchiveById = new HashMap<>();
+    private final Map<Entity, UserMessageArchive> userMessageArchiveById = new HashMap<>();
 
     @Override
-    public Optional<MessageArchive> retrieveUserMessageArchive(Entity userBareJid) {
-        MessageArchive messageArchive = userMessageArchiveById.computeIfAbsent(userBareJid,
-                id -> new InMemoryMessageArchive());
+    public Optional<UserMessageArchive> retrieveUserMessageArchive(Entity userBareJid) {
+        UserMessageArchive messageArchive = userMessageArchiveById.computeIfAbsent(userBareJid,
+                id -> new InMemoryUserMessageArchive());
         return Optional.of(messageArchive);
     }
 }
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryUserMessageArchive.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryUserMessageArchive.java
new file mode 100644
index 0000000..4042530
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/in_memory/InMemoryUserMessageArchive.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.in_memory;
+
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessage;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessages;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.Message;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchive;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageFilter;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessagePageRequest;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchive;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchivePreferences;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.user.SimpleUserMessageArchivePreferences;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class InMemoryUserMessageArchive implements UserMessageArchive {
+
+    private final MessageArchive delegate;
+
+    private UserMessageArchivePreferences preferences;
+
+    public InMemoryUserMessageArchive() {
+        this.delegate = new InMemoryMessageArchive();
+        this.preferences = new SimpleUserMessageArchivePreferences();
+    }
+
+    @Override
+    public ArchivedMessage archive(Message message) {
+        return delegate.archive(message);
+    }
+
+    @Override
+    public ArchivedMessages fetchSortedByOldestFirst(MessageFilter messageFilter, MessagePageRequest pageRequest) {
+        return delegate.fetchSortedByOldestFirst(messageFilter, pageRequest);
+    }
+
+    @Override
+    public ArchivedMessages fetchLastPageSortedByOldestFirst(MessageFilter messageFilter, long pageSize) {
+        return delegate.fetchLastPageSortedByOldestFirst(messageFilter, pageSize);
+    }
+
+    @Override
+    public UserMessageArchivePreferences preferences() {
+        return preferences;
+    }
+
+    @Override
+    public void changePreferences(UserMessageArchivePreferences preferences) {
+        this.preferences = preferences;
+    }
+}
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQQueryHandler.java
similarity index 92%
rename from server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.java
rename to server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQQueryHandler.java
index f26d51e..5421230 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMQueryHandler.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQQueryHandler.java
@@ -42,19 +42,19 @@ import org.apache.vysper.xmpp.stanza.StanzaErrorType;
 /**
  * @author Réda Housni Alaoui
  */
-public class MAMQueryHandler extends DefaultIQHandler {
+public class MAMIQQueryHandler extends DefaultIQHandler {
 
     private final String namespace;
 
     private final List<QueryHandler> queryHandlers;
 
-    public MAMQueryHandler(String namespace) {
+    public MAMIQQueryHandler(String namespace) {
         this(namespace, new PubsubNodeArchiveQueryHandler(), new MUCArchiveQueryHandler(),
                 new UserArchiveQueryHandler());
     }
 
-    public MAMQueryHandler(String namespace, QueryHandler pubsubNodeArchiveQueryHandler,
-            QueryHandler mucArchiveQueryHandler, QueryHandler userArchiveQueryHandler) {
+    public MAMIQQueryHandler(String namespace, QueryHandler pubsubNodeArchiveQueryHandler,
+                             QueryHandler mucArchiveQueryHandler, QueryHandler userArchiveQueryHandler) {
         this.namespace = requireNonNull(namespace);
         List<QueryHandler> modifiableQueryHandlers = new ArrayList<>();
         modifiableQueryHandlers.add(pubsubNodeArchiveQueryHandler);
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MatchingArchivedMessageResult.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MatchingArchivedMessageResult.java
index fc69510..be8b9e7 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MatchingArchivedMessageResult.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MatchingArchivedMessageResult.java
@@ -41,8 +41,6 @@ import org.apache.vysper.xmpp.stanza.StanzaBuilder;
  */
 class MatchingArchivedMessageResult {
 
-    private static final String STANZA_ID = "stanza-id";
-
     private final Entity initiatingEntity;
 
     private final Entity archiveId;
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DefaultUserArchiveBehaviour.java
similarity index 75%
copy from server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
copy to server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DefaultUserArchiveBehaviour.java
index e4df43b..e1e2906 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/DefaultUserArchiveBehaviour.java
@@ -19,16 +19,21 @@
  */
 package org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi;
 
-import java.util.Optional;
-
-import org.apache.vysper.storage.StorageProvider;
-import org.apache.vysper.xmpp.addressing.Entity;
-
 /**
  * @author Réda Housni Alaoui
  */
-public interface MessageArchives extends StorageProvider {
-
-    Optional<MessageArchive> retrieveUserMessageArchive(Entity userBareJid);
+public enum DefaultUserArchiveBehaviour {
+    /**
+     * All messages are archived by default
+     */
+    ALWAYS,
+    /**
+     * Messages are never archived by default
+     */
+    NEVER,
+    /**
+     * Messages are archived only if the contact's bare JID is in the user's roster
+     */
+    ROSTER
 
 }
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
index e4df43b..85cda3e 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
@@ -29,6 +29,6 @@ import org.apache.vysper.xmpp.addressing.Entity;
  */
 public interface MessageArchives extends StorageProvider {
 
-    Optional<MessageArchive> retrieveUserMessageArchive(Entity userBareJid);
+    Optional<UserMessageArchive> retrieveUserMessageArchive(Entity userBareJid);
 
 }
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchive.java
similarity index 78%
copy from server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
copy to server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchive.java
index e4df43b..9f1637c 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchives.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchive.java
@@ -19,16 +19,13 @@
  */
 package org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi;
 
-import java.util.Optional;
-
-import org.apache.vysper.storage.StorageProvider;
-import org.apache.vysper.xmpp.addressing.Entity;
-
 /**
  * @author Réda Housni Alaoui
  */
-public interface MessageArchives extends StorageProvider {
+public interface UserMessageArchive extends MessageArchive {
+
+    UserMessageArchivePreferences preferences();
 
-    Optional<MessageArchive> retrieveUserMessageArchive(Entity userBareJid);
+    void changePreferences(UserMessageArchivePreferences preferences);
 
 }
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/preferences/MAMPreferenceHandler.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchivePreferences.java
similarity index 60%
rename from server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/preferences/MAMPreferenceHandler.java
rename to server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchivePreferences.java
index d649d45..472a391 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/preferences/MAMPreferenceHandler.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchivePreferences.java
@@ -17,24 +17,25 @@
  *  under the License.
  *  
  */
-package org.apache.vysper.xmpp.modules.extension.xep0313_mam.preferences;
+package org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi;
 
-import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler;
-import org.apache.vysper.xmpp.stanza.Stanza;
+import java.util.Set;
+
+import org.apache.vysper.xmpp.addressing.Entity;
 
 /**
  * @author Réda Housni Alaoui
  */
-public class MAMPreferenceHandler extends DefaultIQHandler {
+public interface UserMessageArchivePreferences {
+
+    /**
+     * If a JID is in neither the 'always archive' nor the 'never archive' list then
+     * whether it is archived depends on this setting, the default.
+     */
+    DefaultUserArchiveBehaviour getDefaultBehaviour();
 
-    private final String namespace;
+    Set<Entity> getAlwaysArchivedToOrFromJids();
 
-    public MAMPreferenceHandler(String namespace) {
-        this.namespace = namespace;
-    }
+    Set<Entity> getNeverArchivedToOrFromJids();
 
-    @Override
-    protected boolean verifyInnerElement(Stanza stanza) {
-        return verifyInnerElementWorker(stanza, "prefs") && verifyInnerNamespace(stanza, namespace);
-    }
 }
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/MAMIQPreferenceHandler.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/MAMIQPreferenceHandler.java
new file mode 100644
index 0000000..677b185
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/MAMIQPreferenceHandler.java
@@ -0,0 +1,121 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.vysper.xml.fragment.XMLElement;
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchives;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchive;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchivePreferences;
+import org.apache.vysper.xmpp.protocol.StanzaBroker;
+import org.apache.vysper.xmpp.server.ServerRuntimeContext;
+import org.apache.vysper.xmpp.server.SessionContext;
+import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
+import org.apache.vysper.xmpp.stanza.IQStanza;
+import org.apache.vysper.xmpp.stanza.IQStanzaType;
+import org.apache.vysper.xmpp.stanza.Stanza;
+import org.apache.vysper.xmpp.stanza.StanzaBuilder;
+import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
+import org.apache.vysper.xmpp.stanza.StanzaErrorType;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class MAMIQPreferenceHandler extends DefaultIQHandler {
+
+    private final String namespace;
+
+    public MAMIQPreferenceHandler(String namespace) {
+        this.namespace = namespace;
+    }
+
+    @Override
+    protected boolean verifyInnerElement(Stanza stanza) {
+        return verifyInnerElementWorker(stanza, UserMessageArchivePreferencesElement.NAME)
+                && verifyInnerNamespace(stanza, namespace);
+    }
+
+    @Override
+    protected List<Stanza> handleGet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext,
+            SessionContext sessionContext, StanzaBroker stanzaBroker) {
+        Entity archiveId = sessionContext.getInitiatingEntity().getBareJID();
+
+        MessageArchives archives = requireNonNull(serverRuntimeContext.getStorageProvider(MessageArchives.class),
+                "Could not find an instance of " + MessageArchives.class);
+
+        Optional<UserMessageArchive> optionalArchive = archives.retrieveUserMessageArchive(archiveId);
+        if (!optionalArchive.isPresent()) {
+            return Collections.singletonList(ServerErrorResponses.getStanzaError(StanzaErrorCondition.ITEM_NOT_FOUND,
+                    stanza, StanzaErrorType.CANCEL, "No user message archive found for entity " + archiveId, null,
+                    null));
+        }
+
+        UserMessageArchive archive = optionalArchive.get();
+        UserMessageArchivePreferences preferences = archive.preferences();
+
+        XMLElement prefsElement = UserMessageArchivePreferencesElement.toXml(namespace, preferences);
+
+        Stanza reply = StanzaBuilder.createDirectReply(stanza, false, IQStanzaType.RESULT)
+                .addPreparedElement(prefsElement).build();
+        return Collections.singletonList(reply);
+    }
+
+    @Override
+    protected List<Stanza> handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext,
+            SessionContext sessionContext, StanzaBroker stanzaBroker) {
+        Entity archiveId = sessionContext.getInitiatingEntity().getBareJID();
+
+        MessageArchives archives = requireNonNull(serverRuntimeContext.getStorageProvider(MessageArchives.class),
+                "Could not find an instance of " + MessageArchives.class);
+
+        Optional<UserMessageArchive> optionalArchive = archives.retrieveUserMessageArchive(archiveId);
+        if (!optionalArchive.isPresent()) {
+            return Collections.singletonList(ServerErrorResponses.getStanzaError(StanzaErrorCondition.ITEM_NOT_FOUND,
+                    stanza, StanzaErrorType.CANCEL, "No user message archive found for entity " + archiveId, null,
+                    null));
+        }
+
+        UserMessageArchive archive = optionalArchive.get();
+
+        UserMessageArchivePreferences preferences;
+        try {
+            XMLElement prefsElement = stanza.getSingleInnerElementsNamed(UserMessageArchivePreferencesElement.NAME);
+            preferences = UserMessageArchivePreferencesElement.fromXml(prefsElement);
+        } catch (XMLSemanticError xmlSemanticError) {
+            return Collections.singletonList(ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST,
+                    stanza, StanzaErrorType.MODIFY, null, null, null));
+        }
+
+        archive.changePreferences(preferences);
+
+        XMLElement effectivePrefsElement = UserMessageArchivePreferencesElement.toXml(namespace, archive.preferences());
+        Stanza reply = StanzaBuilder.createDirectReply(stanza, false, IQStanzaType.RESULT)
+                .addPreparedElement(effectivePrefsElement).build();
+        return Collections.singletonList(reply);
+    }
+}
\ No newline at end of file
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/SimpleUserMessageArchivePreferences.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/SimpleUserMessageArchivePreferences.java
new file mode 100644
index 0000000..ffdfd12
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/SimpleUserMessageArchivePreferences.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.DefaultUserArchiveBehaviour;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchivePreferences;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class SimpleUserMessageArchivePreferences implements UserMessageArchivePreferences {
+
+    private final DefaultUserArchiveBehaviour defaultBehaviour;
+
+    private final Set<Entity> alwaysArchivedToOrFromJids;
+
+    private final Set<Entity> neverArchivedToOrFromJids;
+
+    public SimpleUserMessageArchivePreferences() {
+        this(DefaultUserArchiveBehaviour.ALWAYS, Collections.emptySet(), Collections.emptySet());
+    }
+
+    public SimpleUserMessageArchivePreferences(DefaultUserArchiveBehaviour defaultBehaviour,
+            Set<Entity> alwaysArchivedToOrFromJids, Set<Entity> neverArchivedToOrFromJids) {
+        this.defaultBehaviour = requireNonNull(defaultBehaviour);
+        this.alwaysArchivedToOrFromJids = new HashSet<>(alwaysArchivedToOrFromJids);
+        this.neverArchivedToOrFromJids = new HashSet<>(neverArchivedToOrFromJids);
+    }
+
+    @Override
+    public DefaultUserArchiveBehaviour getDefaultBehaviour() {
+        return defaultBehaviour;
+    }
+
+    @Override
+    public Set<Entity> getAlwaysArchivedToOrFromJids() {
+        return alwaysArchivedToOrFromJids;
+    }
+
+    @Override
+    public Set<Entity> getNeverArchivedToOrFromJids() {
+        return neverArchivedToOrFromJids;
+    }
+}
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandler.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandler.java
index efa9b47..7e0be04 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandler.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandler.java
@@ -33,8 +33,8 @@ import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.MatchingArchiv
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.Query;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.query.QueryHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessages;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchive;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchives;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchive;
 import org.apache.vysper.xmpp.server.ServerRuntimeContext;
 import org.apache.vysper.xmpp.server.SessionContext;
 import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
@@ -79,7 +79,7 @@ public class UserArchiveQueryHandler implements QueryHandler {
         MessageArchives archives = requireNonNull(serverRuntimeContext.getStorageProvider(MessageArchives.class),
                 "Could not find an instance of " + MessageArchives.class);
 
-        Optional<MessageArchive> archive = archives.retrieveUserMessageArchive(archiveId);
+        Optional<UserMessageArchive> archive = archives.retrieveUserMessageArchive(archiveId);
         if (!archive.isPresent()) {
             return Collections.singletonList(ServerErrorResponses.getStanzaError(StanzaErrorCondition.ITEM_NOT_FOUND,
                     query.iqStanza(), StanzaErrorType.CANCEL, "No user message archive found for entity " + archiveId,
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElement.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElement.java
new file mode 100644
index 0000000..62de992
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElement.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.vysper.xml.fragment.Attribute;
+import org.apache.vysper.xml.fragment.XMLElement;
+import org.apache.vysper.xml.fragment.XMLFragment;
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xml.fragment.XMLText;
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.addressing.EntityImpl;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.DefaultUserArchiveBehaviour;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchivePreferences;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+class UserMessageArchivePreferencesElement {
+
+    static final String NAME = "prefs";
+
+    private UserMessageArchivePreferencesElement() {
+    }
+
+    static XMLElement toXml(String namespace, UserMessageArchivePreferences preferences) {
+        List<XMLFragment> alwaysJids = preferences.getAlwaysArchivedToOrFromJids().stream()
+                .map(entity -> new XMLElement(null, "jid", null, Collections.emptyList(),
+                        Collections.singletonList(new XMLText(entity.getFullQualifiedName()))))
+                .collect(Collectors.toList());
+        XMLElement alwaysElement = new XMLElement(null, "always", null, Collections.emptyList(), alwaysJids);
+
+        List<XMLFragment> neverJids = preferences.getNeverArchivedToOrFromJids().stream()
+                .map(entity -> new XMLElement(null, "jid", null, Collections.emptyList(),
+                        Collections.singletonList(new XMLText(entity.getFullQualifiedName()))))
+                .collect(Collectors.toList());
+        XMLElement neverElement = new XMLElement(null, "never", null, Collections.emptyList(), neverJids);
+
+        List<XMLFragment> prefsFragments = new ArrayList<>();
+        prefsFragments.add(alwaysElement);
+        prefsFragments.add(neverElement);
+
+        Attribute defaultBehaviour = new Attribute("default", preferences.getDefaultBehaviour().name().toLowerCase());
+        return new XMLElement(namespace, NAME, null, Collections.singletonList(defaultBehaviour), prefsFragments);
+    }
+
+    static UserMessageArchivePreferences fromXml(XMLElement prefsElement) throws XMLSemanticError {
+        DefaultUserArchiveBehaviour defaultbehaviour = DefaultUserArchiveBehaviour
+                .valueOf(prefsElement.getAttributeValue("default").toUpperCase());
+
+        Set<Entity> alwaysJids = parseJids(prefsElement, "always");
+        Set<Entity> neverJids = parseJids(prefsElement, "never");
+
+        return new SimpleUserMessageArchivePreferences(defaultbehaviour, alwaysJids, neverJids);
+    }
+
+    private static Set<Entity> parseJids(XMLElement prefsElement, String parentElementName) throws XMLSemanticError {
+        XMLElement parent = prefsElement.getSingleInnerElementsNamed(parentElementName);
+
+        if (parent == null) {
+            return Collections.emptySet();
+        }
+
+        return parent.getInnerElementsNamed("jid").stream().map(XMLElement::getInnerText).map(XMLText::getText)
+                .map(EntityImpl::parseUnchecked).collect(Collectors.toSet());
+    }
+}
diff --git a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBroker.java b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBroker.java
index 8d236b5..1acac01 100644
--- a/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBroker.java
+++ b/server/extensions/xep0313-mam/src/main/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBroker.java
@@ -31,8 +31,8 @@ import org.apache.vysper.xmpp.modules.core.base.handler.XMPPCoreStanzaHandler;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.MessageStanzaWithId;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.SimpleMessage;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.ArchivedMessage;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchive;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchives;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchive;
 import org.apache.vysper.xmpp.protocol.DelegatingStanzaBroker;
 import org.apache.vysper.xmpp.protocol.StanzaBroker;
 import org.apache.vysper.xmpp.server.ServerRuntimeContext;
@@ -56,7 +56,7 @@ class UserMessageStanzaBroker extends DelegatingStanzaBroker {
 
     private final boolean isOutbound;
 
-    public UserMessageStanzaBroker(StanzaBroker delegate, ServerRuntimeContext serverRuntimeContext,
+    UserMessageStanzaBroker(StanzaBroker delegate, ServerRuntimeContext serverRuntimeContext,
             SessionContext sessionContext, boolean isOutbound) {
         super(delegate);
         this.serverRuntimeContext = requireNonNull(serverRuntimeContext);
@@ -91,6 +91,8 @@ class UserMessageStanzaBroker extends DelegatingStanzaBroker {
             LOG.debug("Message {} is neither of type 'normal' or 'chat'. It will not be archived.", messageStanza);
             return messageStanza;
         }
+        
+        // TODO Check preferences and no-store element
 
         addToSenderArchive(messageStanza, sessionContext);
         return addToReceiverArchive(messageStanza).map(MessageStanzaWithId::new).map(MessageStanzaWithId::toStanza)
@@ -101,7 +103,7 @@ class UserMessageStanzaBroker extends DelegatingStanzaBroker {
         // Servers that expose archive messages of sent/received messages on behalf of
         // local users MUST expose these archives to the user on the user's bare JID.
         Entity senderArchiveId = XMPPCoreStanzaHandler.extractSenderJID(messageStanza, sessionContext).getBareJID();
-        Optional<MessageArchive> senderArchive = messageArchives().retrieveUserMessageArchive(senderArchiveId);
+        Optional<UserMessageArchive> senderArchive = messageArchives().retrieveUserMessageArchive(senderArchiveId);
         if (!senderArchive.isPresent()) {
             LOG.debug("No archive returned for sender with bare JID '{}'", senderArchiveId);
             return;
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandlerTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandlerTest.java
index 905bdef..b7850c2 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandlerTest.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/query/MAMIQHandlerTest.java
@@ -60,7 +60,7 @@ public class MAMIQHandlerTest {
 
     private SessionContext sessionContext;
 
-    private MAMQueryHandler tested;
+    private MAMIQQueryHandler tested;
 
     @Before
     public void before() throws IOException, SAXException {
@@ -68,7 +68,7 @@ public class MAMIQHandlerTest {
         mucArchiveQueryHandler = mock(MUCArchiveQueryHandler.class);
         userArchiveQueryHandler = mock(UserArchiveQueryHandler.class);
 
-        tested = new MAMQueryHandler("urn:xmpp:mam:2", pubsubNodeArchiveQueryHandler, mucArchiveQueryHandler,
+        tested = new MAMIQQueryHandler("urn:xmpp:mam:2", pubsubNodeArchiveQueryHandler, mucArchiveQueryHandler,
                 userArchiveQueryHandler);
 
         XMLElement queryIqElement = XMLParserUtil.parseRequiredDocument(
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchivesMock.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchivesMock.java
index b484fe9..e110b21 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchivesMock.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchivesMock.java
@@ -30,16 +30,16 @@ import org.apache.vysper.xmpp.addressing.Entity;
  */
 public class MessageArchivesMock implements MessageArchives {
 
-    private final Map<Entity, MessageArchive> archiveById = new HashMap<>();
+    private final Map<Entity, UserMessageArchive> userArchiveById = new HashMap<>();
 
-    public MessageArchiveMock givenArchive(Entity archiveId) {
-        MessageArchiveMock userMessageArchiveMock = new MessageArchiveMock();
-        archiveById.put(archiveId, userMessageArchiveMock);
+    public UserMessageArchiveMock givenArchive(Entity archiveId) {
+        UserMessageArchiveMock userMessageArchiveMock = new UserMessageArchiveMock();
+        userArchiveById.put(archiveId, userMessageArchiveMock);
         return userMessageArchiveMock;
     }
 
     @Override
-    public Optional<MessageArchive> retrieveUserMessageArchive(Entity userBareJid) {
-        return Optional.ofNullable(archiveById.get(userBareJid));
+    public Optional<UserMessageArchive> retrieveUserMessageArchive(Entity userBareJid) {
+        return Optional.ofNullable(userArchiveById.get(userBareJid));
     }
 }
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchiveMock.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchiveMock.java
similarity index 86%
rename from server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchiveMock.java
rename to server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchiveMock.java
index b927fbc..f9e4be4 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/MessageArchiveMock.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/spi/UserMessageArchiveMock.java
@@ -32,7 +32,7 @@ import org.apache.vysper.xmpp.stanza.MessageStanza;
 /**
  * @author Réda Housni Alaoui
  */
-public class MessageArchiveMock implements MessageArchive {
+public class UserMessageArchiveMock implements UserMessageArchive {
 
     private final Queue<Message> messages = new LinkedList<>();
 
@@ -64,4 +64,14 @@ public class MessageArchiveMock implements MessageArchive {
     public void assertEmpty() {
         assertTrue(messages.isEmpty());
     }
+
+    @Override
+    public UserMessageArchivePreferences preferences() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void changePreferences(UserMessageArchivePreferences preferences) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/UserArchiveTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveTest.java
similarity index 83%
rename from server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/UserArchiveTest.java
rename to server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveTest.java
index fd9b2b6..eb0572c 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/UserArchiveTest.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveTest.java
@@ -17,7 +17,7 @@
  *  under the License.
  *  
  */
-package org.apache.vysper.xmpp.modules.extension.xep0313_mam;
+package org.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.IntegrationTest;
 import org.apache.vysper.xmpp.protocol.NamespaceURIs;
 import org.jivesoftware.smack.AbstractXMPPConnection;
 import org.jivesoftware.smack.SmackException;
@@ -42,9 +43,13 @@ import org.jivesoftware.smack.packet.Message;
 import org.jivesoftware.smack.packet.Stanza;
 import org.jivesoftware.smackx.forward.packet.Forwarded;
 import org.jivesoftware.smackx.mam.MamManager;
+import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
 import org.jivesoftware.smackx.sid.element.StableAndUniqueIdElement;
 import org.jivesoftware.smackx.sid.element.StanzaIdElement;
 import org.junit.Test;
+import org.jxmpp.jid.Jid;
+import org.jxmpp.jid.impl.JidCreate;
+import org.jxmpp.stringprep.XmppStringprepException;
 
 /**
  * @author Réda Housni Alaoui
@@ -219,15 +224,40 @@ public class UserArchiveTest extends IntegrationTest {
     }
 
     @Test
-    public void checkGetPreferences() throws SmackException.NotLoggedInException, SmackException.NotConnectedException,
-            InterruptedException, SmackException.NoResponseException {
+    public void getPreferences() throws SmackException.NotLoggedInException, SmackException.NotConnectedException,
+            InterruptedException, SmackException.NoResponseException, XMPPException.XMPPErrorException {
         MamManager mamManager = MamManager.getInstanceFor(alice());
-        try {
-            mamManager.retrieveArchivingPreferences();
-            fail("Expected an feature not implemented error");
-        } catch (XMPPException.XMPPErrorException errorException) {
-            assertTrue(errorException.getMessage().contains("feature-not-implemented"));
-        }
+        MamManager.MamPrefsResult prefsResult = mamManager.retrieveArchivingPreferences();
+        MamManager.MamPrefs mamPrefs = prefsResult.asMamPrefs();
+
+        assertEquals(MamPrefsIQ.DefaultBehavior.always, mamPrefs.getDefaultBehavior());
+        assertTrue(mamPrefs.getAlwaysJids().isEmpty());
+        assertTrue(mamPrefs.getNeverJids().isEmpty());
+    }
+
+    @Test
+    public void setPreferences() throws XMPPException.XMPPErrorException, SmackException.NotLoggedInException,
+            SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
+            XmppStringprepException {
+        MamManager mamManager = MamManager.getInstanceFor(alice());
+        MamManager.MamPrefsResult prefsResult = mamManager.retrieveArchivingPreferences();
+        MamManager.MamPrefs prefs = prefsResult.asMamPrefs();
+
+        prefs.setDefaultBehavior(MamPrefsIQ.DefaultBehavior.never);
+        prefs.getAlwaysJids().add(JidCreate.bareFrom("always@foo.com"));
+        prefs.getNeverJids().add(JidCreate.bareFrom("never@foo.com"));
+
+        MamManager.MamPrefsResult updatedPrefsResult = mamManager.updateArchivingPreferences(prefs);
+        MamManager.MamPrefs updatedPrefs = updatedPrefsResult.asMamPrefs();
+        assertEquals(MamPrefsIQ.DefaultBehavior.never, updatedPrefs.getDefaultBehavior());
+
+        List<Jid> alwaysJids = updatedPrefs.getAlwaysJids();
+        assertEquals(1, alwaysJids.size());
+        assertEquals(JidCreate.bareFrom("always@foo.com"), alwaysJids.get(0));
+
+        List<Jid> neverJids = updatedPrefs.getNeverJids();
+        assertEquals(1, neverJids.size());
+        assertEquals(JidCreate.bareFrom("never@foo.com"), neverJids.get(0));
     }
 
     private Message fetchUniqueArchivedMessage(AbstractXMPPConnection connection)
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElementTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElementTest.java
new file mode 100644
index 0000000..4e924ca
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageArchivePreferencesElementTest.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.vysper.xml.fragment.XMLElement;
+import org.apache.vysper.xml.fragment.XMLSemanticError;
+import org.apache.vysper.xmpp.addressing.EntityImpl;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.DefaultUserArchiveBehaviour;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchivePreferences;
+import org.junit.Test;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class UserMessageArchivePreferencesElementTest {
+
+    @Test
+    public void toXml() {
+        UserMessageArchivePreferences preferences = new SimpleUserMessageArchivePreferences(
+                DefaultUserArchiveBehaviour.ROSTER, Collections.singleton(EntityImpl.parseUnchecked("always@foo.com")),
+                Collections.singleton(EntityImpl.parseUnchecked("never@foo.com")));
+
+        XMLElement prefs = UserMessageArchivePreferencesElement.toXml("foo", preferences);
+
+        assertEquals("prefs", prefs.getName());
+        assertEquals("foo", prefs.getNamespaceURI());
+
+        List<XMLElement> innerElements = prefs.getInnerElements();
+
+        XMLElement always = innerElements.get(0);
+        assertEquals("always", always.getName());
+        List<XMLElement> alwaysJids = always.getInnerElements();
+        assertEquals(1, alwaysJids.size());
+        assertEquals("always@foo.com", alwaysJids.get(0).getInnerText().getText());
+
+        XMLElement never = innerElements.get(1);
+        assertEquals("never", never.getName());
+        List<XMLElement> neverJids = never.getInnerElements();
+        assertEquals(1, neverJids.size());
+        assertEquals("never@foo.com", neverJids.get(0).getInnerText().getText());
+    }
+
+    @Test
+    public void fromXml() throws XMLSemanticError {
+        UserMessageArchivePreferences preferences = new SimpleUserMessageArchivePreferences(
+                DefaultUserArchiveBehaviour.ROSTER, Collections.singleton(EntityImpl.parseUnchecked("always@foo.com")),
+                Collections.singleton(EntityImpl.parseUnchecked("never@foo.com")));
+
+        XMLElement prefsElement = UserMessageArchivePreferencesElement.toXml("foo", preferences);
+
+        UserMessageArchivePreferences parsedPreferences = UserMessageArchivePreferencesElement.fromXml(prefsElement);
+        
+        assertEquals(preferences.getDefaultBehaviour(), parsedPreferences.getDefaultBehaviour());
+        assertEquals(preferences.getAlwaysArchivedToOrFromJids().size(), parsedPreferences.getAlwaysArchivedToOrFromJids().size());
+        assertEquals(preferences.getNeverArchivedToOrFromJids().size(), parsedPreferences.getNeverArchivedToOrFromJids().size());
+    }
+
+}
\ No newline at end of file
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBrokerTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBrokerTest.java
index 1c0d868..dcfb7f7 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBrokerTest.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserMessageStanzaBrokerTest.java
@@ -27,7 +27,7 @@ import org.apache.vysper.xmpp.addressing.Entity;
 import org.apache.vysper.xmpp.addressing.EntityImpl;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.ServerRuntimeContextMock;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.SessionContextMock;
-import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchiveMock;
+import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.UserMessageArchiveMock;
 import org.apache.vysper.xmpp.modules.extension.xep0313_mam.spi.MessageArchivesMock;
 import org.apache.vysper.xmpp.protocol.StanzaBroker;
 import org.apache.vysper.xmpp.stanza.MessageStanza;
@@ -51,11 +51,11 @@ public class UserMessageStanzaBrokerTest {
 
     private static final Entity INITIATING_ENTITY = JULIET_IN_CHAMBER;
 
-    private MessageArchiveMock julietArchive;
+    private UserMessageArchiveMock julietArchive;
 
-    private MessageArchiveMock romeoArchive;
+    private UserMessageArchiveMock romeoArchive;
 
-    private MessageArchiveMock macbethArchive;
+    private UserMessageArchiveMock macbethArchive;
 
     private ServerRuntimeContextMock serverRuntimeContext;