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/03 22:21:50 UTC
[mina-vysper] branch master updated: XEP-0313 Message Archive
Management: Archive the message even if the receiver is offline
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 63cb357 XEP-0313 Message Archive Management: Archive the message even if the receiver is offline
63cb357 is described below
commit 63cb3574ef5bcb5a8db01b792129c3eb118a7871
Author: Réda Housni Alaoui <re...@gmail.com>
AuthorDate: Tue Sep 3 23:30:41 2019 +0200
XEP-0313 Message Archive Management: Archive the message even if the receiver is offline
---
.../xep0313_mam/user/UserMessageStanzaBroker.java | 54 ++++++++++++--------
.../extension/xep0313_mam/IntegrationTest.java | 10 +++-
.../xep0313_mam/ServerRuntimeContextMock.java | 9 +++-
.../ToggleableOfflineStorageProvider.java | 59 ++++++++++++++++++++++
.../extension/xep0313_mam/UserArchiveTest.java | 44 ++++++++++++++--
.../user/UserArchiveQueryHandlerTest.java | 2 +-
.../user/UserMessageStanzaBrokerTest.java | 20 ++------
7 files changed, 151 insertions(+), 47 deletions(-)
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 1539447..8d236b5 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
@@ -24,6 +24,7 @@ import static java.util.Objects.requireNonNull;
import java.util.Optional;
import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.addressing.EntityUtils;
import org.apache.vysper.xmpp.delivery.failure.DeliveryException;
import org.apache.vysper.xmpp.delivery.failure.DeliveryFailureStrategy;
import org.apache.vysper.xmpp.modules.core.base.handler.XMPPCoreStanzaHandler;
@@ -75,6 +76,9 @@ class UserMessageStanzaBroker extends DelegatingStanzaBroker {
}
private Stanza archive(Stanza stanza) {
+ if (!isOutbound) {
+ return stanza;
+ }
if (!MessageStanza.isOfType(stanza)) {
return stanza;
}
@@ -88,33 +92,39 @@ class UserMessageStanzaBroker extends DelegatingStanzaBroker {
return messageStanza;
}
- Entity archiveJID;
- if (isOutbound) {
- // We will store the message in the sender archive
- archiveJID = XMPPCoreStanzaHandler.extractSenderJID(messageStanza, sessionContext);
- } else {
- // We will store the message in the receiver archive
- archiveJID = requireNonNull(messageStanza.getTo(), "No 'to' found in " + messageStanza);
- }
+ addToSenderArchive(messageStanza, sessionContext);
+ return addToReceiverArchive(messageStanza).map(MessageStanzaWithId::new).map(MessageStanzaWithId::toStanza)
+ .orElse(stanza);
+ }
+ private void addToSenderArchive(MessageStanza messageStanza, SessionContext sessionContext) {
// 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.
- archiveJID = archiveJID.getBareJID();
-
- MessageArchives archives = requireNonNull(serverRuntimeContext.getStorageProvider(MessageArchives.class),
- "Could not find an instance of " + MessageArchives.class);
-
- Optional<MessageArchive> userArchive = archives.retrieveUserMessageArchive(archiveJID);
- if (!userArchive.isPresent()) {
- LOG.debug("No archive returned for user with bare JID '{}'", archiveJID);
- return messageStanza;
+ Entity senderArchiveId = XMPPCoreStanzaHandler.extractSenderJID(messageStanza, sessionContext).getBareJID();
+ Optional<MessageArchive> senderArchive = messageArchives().retrieveUserMessageArchive(senderArchiveId);
+ if (!senderArchive.isPresent()) {
+ LOG.debug("No archive returned for sender with bare JID '{}'", senderArchiveId);
+ return;
}
+ senderArchive.get().archive(new SimpleMessage(messageStanza));
+ }
- ArchivedMessage archivedMessage = userArchive.get().archive(new SimpleMessage(messageStanza));
- if (isOutbound) {
- return messageStanza;
- } else {
- return new MessageStanzaWithId(archivedMessage).toStanza();
+ private Optional<ArchivedMessage> addToReceiverArchive(MessageStanza messageStanza) {
+ Entity to = requireNonNull(messageStanza.getTo(), "No 'to' found in " + messageStanza);
+ if (!EntityUtils.isAddressingServer(to, serverRuntimeContext.getServerEntity())) {
+ LOG.debug("Receiver {} is not managed by this server", to);
+ return Optional.empty();
}
+ // 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 receiverArchiveId = requireNonNull(messageStanza.getTo(), "No 'to' found in " + messageStanza)
+ .getBareJID();
+ return messageArchives().retrieveUserMessageArchive(receiverArchiveId)
+ .map(messageArchive -> messageArchive.archive(new SimpleMessage(messageStanza)));
+ }
+
+ private MessageArchives messageArchives() {
+ return requireNonNull(serverRuntimeContext.getStorageProvider(MessageArchives.class),
+ "Could not find an instance of " + MessageArchives.class);
}
}
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/IntegrationTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/IntegrationTest.java
index 648e9e2..32b21a1 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/IntegrationTest.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/IntegrationTest.java
@@ -30,7 +30,6 @@ import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
import org.apache.vysper.xmpp.addressing.EntityImpl;
import org.apache.vysper.xmpp.authentication.AccountManagement;
import org.apache.vysper.xmpp.cryptography.NonCheckingX509TrustManagerFactory;
-import org.apache.vysper.xmpp.modules.extension.xep0160_offline_storage.MemoryOfflineStorageProvider;
import org.apache.vysper.xmpp.modules.extension.xep0313_mam.in_memory.InMemoryMessageArchives;
import org.apache.vysper.xmpp.server.XMPPServer;
import org.jivesoftware.smack.AbstractXMPPConnection;
@@ -77,9 +76,12 @@ public abstract class IntegrationTest {
private XMPPServer server;
+ private ToggleableOfflineStorageProvider offlineStorageProvider;
+
@Before
public void setUp() throws Exception {
SmackConfiguration.setDefaultReplyTimeout(5000);
+ offlineStorageProvider = new ToggleableOfflineStorageProvider();
int port = findFreePort();
@@ -97,6 +99,10 @@ public abstract class IntegrationTest {
return carolClient;
}
+ protected ToggleableOfflineStorageProvider offlineStorageProvider() {
+ return offlineStorageProvider;
+ }
+
protected Stanza sendSync(XMPPConnection client, Stanza request)
throws SmackException.NotConnectedException, InterruptedException {
StanzaCollector collector = client.createStanzaCollector(new StanzaIdFilter(request.getStanzaId()));
@@ -113,7 +119,7 @@ public abstract class IntegrationTest {
accountManagement.addUser(EntityImpl.parseUnchecked(ALICE_USERNAME), PASSWORD);
accountManagement.addUser(EntityImpl.parseUnchecked(CAROL_USERNAME), PASSWORD);
- providerRegistry.add(new MemoryOfflineStorageProvider());
+ providerRegistry.add(offlineStorageProvider);
server = new XMPPServer(SERVER_DOMAIN);
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ServerRuntimeContextMock.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ServerRuntimeContextMock.java
index 3130dab..c42043e 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ServerRuntimeContextMock.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ServerRuntimeContextMock.java
@@ -19,6 +19,7 @@
*/
package org.apache.vysper.xmpp.modules.extension.xep0313_mam;
+import static java.util.Objects.requireNonNull;
import static org.mockito.Mockito.mock;
import java.util.Collections;
@@ -47,9 +48,15 @@ import org.apache.vysper.xmpp.state.resourcebinding.ResourceRegistry;
* @author Réda Housni Alaoui
*/
public class ServerRuntimeContextMock implements ServerRuntimeContext {
+
+ private final Entity serverEntity;
private MessageArchivesMock userMessageArchives;
+ public ServerRuntimeContextMock(Entity serverEntity) {
+ this.serverEntity = requireNonNull(serverEntity);
+ }
+
public MessageArchivesMock givenUserMessageArchives() {
userMessageArchives = new MessageArchivesMock();
return userMessageArchives;
@@ -67,7 +74,7 @@ public class ServerRuntimeContextMock implements ServerRuntimeContext {
@Override
public Entity getServerEntity() {
- throw new UnsupportedOperationException();
+ return serverEntity;
}
@Override
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ToggleableOfflineStorageProvider.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ToggleableOfflineStorageProvider.java
new file mode 100644
index 0000000..0fea747
--- /dev/null
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/ToggleableOfflineStorageProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+import java.util.Collection;
+
+import org.apache.vysper.xmpp.addressing.Entity;
+import org.apache.vysper.xmpp.modules.extension.xep0160_offline_storage.MemoryOfflineStorageProvider;
+import org.apache.vysper.xmpp.modules.extension.xep0160_offline_storage.OfflineStorageProvider;
+import org.apache.vysper.xmpp.stanza.Stanza;
+
+/**
+ * @author Réda Housni Alaoui
+ */
+public class ToggleableOfflineStorageProvider implements OfflineStorageProvider {
+
+ private final MemoryOfflineStorageProvider memoryOfflineStorageProvider;
+
+ private boolean disabled;
+
+ public void disable() {
+ disabled = true;
+ }
+
+ public ToggleableOfflineStorageProvider() {
+ this.memoryOfflineStorageProvider = new MemoryOfflineStorageProvider();
+ }
+
+ @Override
+ public Collection<Stanza> getStanzasFor(Entity jid) {
+ return memoryOfflineStorageProvider.getStanzasFor(jid);
+ }
+
+ @Override
+ public void receive(Stanza stanza) {
+ if (disabled) {
+ return;
+ }
+ memoryOfflineStorageProvider.receive(stanza);
+ }
+
+}
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/UserArchiveTest.java
index 3908aad..0e5bf4d 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/UserArchiveTest.java
@@ -22,6 +22,7 @@ package org.apache.vysper.xmpp.modules.extension.xep0313_mam;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -31,6 +32,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
+import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat2.Chat;
@@ -164,7 +166,8 @@ public class UserArchiveTest extends IntegrationTest {
}
@Test
- public void sendMessageToOfflineReceiver() throws SmackException, InterruptedException, XMPPException, IOException {
+ public void givenOfflineStorageSendMessageToOfflineReceiver()
+ throws SmackException, InterruptedException, XMPPException, IOException {
carol().instantShutdown();
Chat chatFromAliceToCarol = ChatManager.getInstanceFor(alice()).chatWith(carol().getUser().asEntityBareJid());
@@ -182,10 +185,8 @@ public class UserArchiveTest extends IntegrationTest {
assertNotNull(carolReceivedMessage.get());
assertEquals("Hello carol", carolReceivedMessage.get().getBody());
- MamManager.MamQueryArgs archiveFullQuery = MamManager.MamQueryArgs.builder().build();
- MamManager.MamQuery carolArchive = MamManager.getInstanceFor(carol()).queryArchive(archiveFullQuery);
- assertEquals(1, carolArchive.getMessageCount());
- String storedStanzaId = extractStanzaId(carolArchive.getMessages().get(0));
+ Message archivedMessage = fetchUniqueArchivedMessage(carol());
+ String storedStanzaId = extractStanzaId(archivedMessage);
assertNotNull(storedStanzaId);
String receivedStanzaId = extractStanzaId(carolReceivedMessage.get());
@@ -193,6 +194,39 @@ public class UserArchiveTest extends IntegrationTest {
assertEquals(storedStanzaId, receivedStanzaId);
}
+ @Test
+ public void givenDisabledOfflineStorageSendMessageToOfflineReceiver()
+ throws SmackException, InterruptedException, XMPPException, IOException {
+ offlineStorageProvider().disable();
+ carol().instantShutdown();
+
+ Chat chatFromAliceToCarol = ChatManager.getInstanceFor(alice()).chatWith(carol().getUser().asEntityBareJid());
+ chatFromAliceToCarol.send("Hello carol");
+
+ AtomicReference<Message> carolReceivedMessage = new AtomicReference<>();
+ ChatManager.getInstanceFor(carol())
+ .addIncomingListener((from, message, chat) -> carolReceivedMessage.set(message));
+
+ carol().connect();
+ carol().login();
+
+ Thread.sleep(200);
+
+ assertNull(carolReceivedMessage.get());
+
+ Message message = fetchUniqueArchivedMessage(carol());
+ assertEquals("Hello carol", message.getBody());
+ }
+
+ private Message fetchUniqueArchivedMessage(AbstractXMPPConnection connection)
+ throws XMPPException.XMPPErrorException, InterruptedException, SmackException.NotConnectedException,
+ SmackException.NotLoggedInException, SmackException.NoResponseException {
+ MamManager.MamQueryArgs archiveFullQuery = MamManager.MamQueryArgs.builder().build();
+ MamManager.MamQuery archive = MamManager.getInstanceFor(connection).queryArchive(archiveFullQuery);
+ assertEquals(1, archive.getMessageCount());
+ return archive.getMessages().get(0);
+ }
+
private String extractStanzaId(Stanza stanza) {
assertNotNull(stanza);
ExtensionElement extensionElement = stanza.getExtension(NamespaceURIs.XEP0359_STANZA_IDS);
diff --git a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandlerTest.java b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandlerTest.java
index 0150861..bd97a08 100644
--- a/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandlerTest.java
+++ b/server/extensions/xep0313-mam/src/test/java/org/apache/vysper/xmpp/modules/extension/xep0313_mam/user/UserArchiveQueryHandlerTest.java
@@ -80,7 +80,7 @@ public class UserArchiveQueryHandlerTest {
XMLParserUtil.parseRequiredDocument("<iq type='set'><query xmlns='urn:xmpp:mam:2'/></iq>"),
true, Collections.emptyList()).build()));
- serverRuntimeContext = new ServerRuntimeContextMock();
+ serverRuntimeContext = new ServerRuntimeContextMock(EntityImpl.parseUnchecked("capulet.lit"));
archives = serverRuntimeContext.givenUserMessageArchives();
sessionContext = new SessionContextMock();
sessionContext.givenInitiatingEntity(INITIATING_ENTITY);
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 e197a07..1c0d868 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
@@ -20,7 +20,6 @@
package org.apache.vysper.xmpp.modules.extension.xep0313_mam.user;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import java.util.stream.Stream;
@@ -44,11 +43,11 @@ public class UserMessageStanzaBrokerTest {
private static final Entity JULIET_IN_CHAMBER = EntityImpl.parseUnchecked("juliet@capulet.lit/chamber");
- private static final Entity ROMEO_IN_ORCHARD = EntityImpl.parseUnchecked("romeo@montague.lit/orchard");
+ private static final Entity ROMEO_IN_ORCHARD = EntityImpl.parseUnchecked("romeo@capulet.lit/orchard");
- private static final Entity MACBETH_IN_KITCHEN = EntityImpl.parseUnchecked("macbeth@shakespeare.lit/kitchen");
+ private static final Entity MACBETH_IN_KITCHEN = EntityImpl.parseUnchecked("macbeth@capulet.lit/kitchen");
- private static final Entity ALICE_IN_RABBIT_HOLE = EntityImpl.parseUnchecked("alice@carol.lit/rabbit-hole");
+ private static final Entity ALICE_IN_RABBIT_HOLE = EntityImpl.parseUnchecked("alice@capulet.lit/rabbit-hole");
private static final Entity INITIATING_ENTITY = JULIET_IN_CHAMBER;
@@ -66,7 +65,7 @@ public class UserMessageStanzaBrokerTest {
@Before
public void before() {
- serverRuntimeContext = new ServerRuntimeContextMock();
+ serverRuntimeContext = new ServerRuntimeContextMock(EntityImpl.parseUnchecked("capulet.lit"));
MessageArchivesMock archives = serverRuntimeContext.givenUserMessageArchives();
@@ -137,17 +136,6 @@ public class UserMessageStanzaBrokerTest {
}
@Test
- public void inboundMessage() {
- UserMessageStanzaBroker tested = buildTested(false);
-
- MessageStanza messageStanza = buildMessageStanza(MessageStanzaType.NORMAL, null, MACBETH_IN_KITCHEN);
-
- tested.writeToSession(messageStanza);
-
- macbethArchive.assertUniqueArchivedMessageStanza(messageStanza);
- }
-
- @Test
public void unexistingArchive() {
UserMessageStanzaBroker tested = buildTested(true);