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

rouazana pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 9850741c13a24173783e04a20a8f658c61e9a7b6
Author: Tran Tien Duc <[email protected]>
AuthorDate: Fri Jul 19 11:32:53 2019 +0700

    JAMES-2835 Handling vault.loadMimeMessage() content not found exception
    
    The callers like ExportService, RestoreService can decide how to handle
    the case when content of the deleted message cannot be found.
---
 .../DeletedMessageContentNotFoundException.java    | 38 ++++++++++++++++++++++
 .../vault/blob/BlobStoreDeletedMessageVault.java   | 11 ++++++-
 .../james/blob/api/ObjectNotFoundException.java    | 31 ++++++++++++++++++
 .../apache/james/blob/api/BlobStoreContract.java   |  4 +--
 .../james/blob/cassandra/CassandraBlobsDAO.java    |  4 +--
 .../apache/james/blob/memory/MemoryBlobStore.java  |  4 +--
 .../blob/objectstorage/ObjectStorageBlobsDAO.java  |  3 +-
 .../james/webadmin/vault/routes/ExportService.java | 20 +++++++++++-
 .../webadmin/vault/routes/RestoreService.java      | 13 ++++++++
 9 files changed, 119 insertions(+), 9 deletions(-)

diff --git 
a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageContentNotFoundException.java
 
b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageContentNotFoundException.java
new file mode 100644
index 0000000..f8d5cd7
--- /dev/null
+++ 
b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageContentNotFoundException.java
@@ -0,0 +1,38 @@
+/****************************************************************
+ * 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.james.vault;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.MessageId;
+
+public class DeletedMessageContentNotFoundException extends RuntimeException {
+
+    public DeletedMessageContentNotFoundException(User user, MessageId 
messageId) {
+        this("cannot find mime message associated with id " + 
messageId.serialize() + " and user " + user.asString());
+    }
+
+    public DeletedMessageContentNotFoundException(String message) {
+        super(message);
+    }
+
+    public DeletedMessageContentNotFoundException(String message, Throwable 
cause) {
+        super(message,cause);
+    }
+}
diff --git 
a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
 
b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
index 0e92855..2efde1b 100644
--- 
a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
+++ 
b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
@@ -28,10 +28,12 @@ import javax.inject.Inject;
 
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BucketName;
+import org.apache.james.blob.api.ObjectNotFoundException;
 import org.apache.james.core.User;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.task.Task;
 import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageContentNotFoundException;
 import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.RetentionConfiguration;
 import org.apache.james.vault.metadata.DeletedMessageMetadataVault;
@@ -86,7 +88,14 @@ public class BlobStoreDeletedMessageVault implements 
DeletedMessageVault {
         Preconditions.checkNotNull(user);
         Preconditions.checkNotNull(messageId);
         return Mono.from(messageMetadataVault.retrieveStorageInformation(user, 
messageId))
-            .map(storageInformation -> 
blobStore.read(storageInformation.getBucketName(), 
storageInformation.getBlobId()));
+            .flatMap(storageInformation -> loadMimeMessage(storageInformation, 
user, messageId));
+    }
+
+    private Mono<InputStream> loadMimeMessage(StorageInformation 
storageInformation, User user, MessageId messageId) {
+        return Mono.fromSupplier(() -> 
blobStore.read(storageInformation.getBucketName(), 
storageInformation.getBlobId()))
+            .onErrorResume(
+                ObjectNotFoundException.class,
+                ex -> Mono.error(new 
DeletedMessageContentNotFoundException(user, messageId)));
     }
 
     @Override
diff --git 
a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectNotFoundException.java
 
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectNotFoundException.java
new file mode 100644
index 0000000..058287c
--- /dev/null
+++ 
b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/ObjectNotFoundException.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * 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.james.blob.api;
+
+public class ObjectNotFoundException extends ObjectStoreException {
+
+    public ObjectNotFoundException(String message) {
+        super(message);
+    }
+
+    public ObjectNotFoundException(String message, Throwable cause) {
+        super(message,cause);
+    }
+}
diff --git 
a/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobStoreContract.java
 
b/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobStoreContract.java
index 1b25ada..3c89b9c 100644
--- 
a/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobStoreContract.java
+++ 
b/server/blob/blob-api/src/test/java/org/apache/james/blob/api/BlobStoreContract.java
@@ -141,7 +141,7 @@ public interface BlobStoreContract {
         BucketName defaultBucketName = store.getDefaultBucketName();
 
         assertThatThrownBy(() -> store.readBytes(defaultBucketName, 
blobIdFactory().from("unknown")).block())
-            .isExactlyInstanceOf(ObjectStoreException.class);
+            .isExactlyInstanceOf(ObjectNotFoundException.class);
     }
 
     @Test
@@ -186,7 +186,7 @@ public interface BlobStoreContract {
         BucketName defaultBucketName = store.getDefaultBucketName();
 
         assertThatThrownBy(() -> store.read(defaultBucketName, 
blobIdFactory().from("unknown")))
-            .isInstanceOf(ObjectStoreException.class);
+            .isInstanceOf(ObjectNotFoundException.class);
     }
 
     @Test
diff --git 
a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/CassandraBlobsDAO.java
 
b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/CassandraBlobsDAO.java
index 29cef15..9f5660c 100644
--- 
a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/CassandraBlobsDAO.java
+++ 
b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/CassandraBlobsDAO.java
@@ -42,7 +42,7 @@ import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BucketName;
 import org.apache.james.blob.api.HashBlobId;
-import org.apache.james.blob.api.ObjectStoreException;
+import org.apache.james.blob.api.ObjectNotFoundException;
 import org.apache.james.blob.cassandra.BlobTable.BlobParts;
 import org.apache.james.blob.cassandra.utils.DataChunker;
 import org.apache.james.blob.cassandra.utils.PipedStreamSubscriber;
@@ -211,7 +211,7 @@ public class CassandraBlobsDAO implements BlobStore {
         Integer rowCount = selectRowCount(blobId)
             .publishOn(Schedulers.elastic())
             .switchIfEmpty(Mono.error(
-                new ObjectStoreException(String.format("Could not retrieve 
blob metadata for %s", blobId))))
+                new ObjectNotFoundException(String.format("Could not retrieve 
blob metadata for %s", blobId))))
             .block();
         return Flux.range(0, rowCount)
             .publishOn(Schedulers.elastic(), PREFETCH)
diff --git 
a/server/blob/blob-memory/src/main/java/org/apache/james/blob/memory/MemoryBlobStore.java
 
b/server/blob/blob-memory/src/main/java/org/apache/james/blob/memory/MemoryBlobStore.java
index f10e75e..6d107ac 100644
--- 
a/server/blob/blob-memory/src/main/java/org/apache/james/blob/memory/MemoryBlobStore.java
+++ 
b/server/blob/blob-memory/src/main/java/org/apache/james/blob/memory/MemoryBlobStore.java
@@ -30,7 +30,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BucketName;
-import org.apache.james.blob.api.ObjectStoreException;
+import org.apache.james.blob.api.ObjectNotFoundException;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
@@ -109,7 +109,7 @@ public class MemoryBlobStore implements BlobStore {
     private byte[] retrieveStoredValue(BucketName bucketName, BlobId blobId) {
         synchronized (blobs) {
             return Optional.ofNullable(blobs.get(bucketName, blobId))
-                .orElseThrow(() -> new ObjectStoreException("Unable to find 
blob with id " + blobId + " in bucket " + bucketName.asString()));
+                .orElseThrow(() -> new ObjectNotFoundException("Unable to find 
blob with id " + blobId + " in bucket " + bucketName.asString()));
         }
     }
 
diff --git 
a/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/ObjectStorageBlobsDAO.java
 
b/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/ObjectStorageBlobsDAO.java
index 81acaeb..6352d07 100644
--- 
a/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/ObjectStorageBlobsDAO.java
+++ 
b/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/ObjectStorageBlobsDAO.java
@@ -31,6 +31,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BucketName;
+import org.apache.james.blob.api.ObjectNotFoundException;
 import org.apache.james.blob.api.ObjectStoreException;
 import org.apache.james.blob.objectstorage.aws.AwsS3AuthConfiguration;
 import org.apache.james.blob.objectstorage.aws.AwsS3ObjectStorage;
@@ -167,7 +168,7 @@ public class ObjectStorageBlobsDAO implements BlobStore {
             if (blob != null) {
                 return payloadCodec.read(new Payload(blob.getPayload(), 
Optional.empty()));
             } else {
-                throw new ObjectStoreException("fail to load blob with id " + 
blobId);
+                throw new ObjectNotFoundException("fail to load blob with id " 
+ blobId);
             }
         } catch (IOException cause) {
             throw new ObjectStoreException(
diff --git 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
index 0ccef9f..56f254e 100644
--- 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
+++ 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
@@ -21,6 +21,7 @@ package org.apache.james.webadmin.vault.routes;
 
 import java.io.IOException;
 import java.util.Optional;
+import java.util.function.Predicate;
 
 import javax.inject.Inject;
 
@@ -32,9 +33,12 @@ import org.apache.james.blob.export.api.FileExtension;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.User;
 import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageContentNotFoundException;
 import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.DeletedMessageZipper;
 import org.apache.james.vault.search.Query;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.io.ByteSource;
@@ -44,6 +48,11 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 class ExportService {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ExportService.class);
+    private static final Predicate<Throwable> CONTENT_NOT_FOUND_PREDICATE =
+        throwable -> throwable instanceof 
DeletedMessageContentNotFoundException;
+
     private final BlobExportMechanism blobExport;
     private final BlobStore blobStore;
     private final DeletedMessageZipper zipper;
@@ -81,7 +90,16 @@ class ExportService {
     }
 
     private DeletedMessageZipper.DeletedMessageContentLoader 
contentLoader(User user) {
-        return message -> Mono.from(vault.loadMimeMessage(user, 
message.getMessageId())).blockOptional();
+        return message -> Mono.from(vault.loadMimeMessage(user, 
message.getMessageId()))
+            .onErrorResume(CONTENT_NOT_FOUND_PREDICATE, throwable -> {
+                LOGGER.info(
+                    "Error happened when loading mime message associated with 
id {} of user {} in the vault",
+                    message.getMessageId().serialize(),
+                    user.asString(),
+                    throwable);
+                return Mono.empty();
+            })
+            .blockOptional();
     }
 
     private String exportMessage(User user) {
diff --git 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
index 064a392..2cc36c9 100644
--- 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
+++ 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
@@ -23,6 +23,8 @@ import static 
org.apache.james.mailbox.MessageManager.AppendCommand;
 import static 
org.apache.james.webadmin.vault.routes.RestoreService.RestoreResult.RESTORE_FAILED;
 import static 
org.apache.james.webadmin.vault.routes.RestoreService.RestoreResult.RESTORE_SUCCEED;
 
+import java.util.function.Predicate;
+
 import javax.inject.Inject;
 
 import org.apache.james.core.User;
@@ -36,6 +38,7 @@ import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageContentNotFoundException;
 import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.search.Query;
 import org.slf4j.Logger;
@@ -54,6 +57,8 @@ class RestoreService {
     }
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(RestoreService.class);
+    private static final Predicate<Throwable> CONTENT_NOT_FOUND_PREDICATE =
+        throwable -> throwable instanceof 
DeletedMessageContentNotFoundException;
 
     private final DeletedMessageVault deletedMessageVault;
     private final MailboxManager mailboxManager;
@@ -86,6 +91,14 @@ class RestoreService {
 
     private Mono<AppendCommand> appendCommand(DeletedMessage deletedMessage) {
         return 
Mono.from(deletedMessageVault.loadMimeMessage(deletedMessage.getOwner(), 
deletedMessage.getMessageId()))
+            .onErrorResume(CONTENT_NOT_FOUND_PREDICATE, throwable -> {
+                LOGGER.info(
+                    "Error happened when loading mime message associated with 
id {} of user {} in the vault",
+                    deletedMessage.getMessageId().serialize(),
+                    deletedMessage.getOwner().asString(),
+                    throwable);
+                return Mono.empty();
+            })
             .map(messageContentStream -> AppendCommand.builder()
                 .build(messageContentStream));
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to