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

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

commit 12ef27cf15bbae671afbfac06941dd301770d305
Author: Benoit Tellier <[email protected]>
AuthorDate: Wed Mar 20 10:53:46 2019 +0700

    JAMES-2684 Add a LocalFile BlobExportMechanism implementation
    
     - Save the blob content in a file
     - Email the file URL as well as an explanation message
    
    We do not worry about access-right on the given file.
    
    As the API wraps a side effect, only unit test can be written.
---
 server/blob/blob-export-file/pom.xml               |  84 +++++++++++++
 .../export/file/LocalFileBlobExportMechanism.java  | 119 ++++++++++++++++++
 .../blob/export/file/FileSystemExtension.java      |  59 +++++++++
 .../file/LocalFileBlobExportMechanismTest.java     | 136 +++++++++++++++++++++
 server/blob/pom.xml                                |   1 +
 5 files changed, 399 insertions(+)

diff --git a/server/blob/blob-export-file/pom.xml 
b/server/blob/blob-export-file/pom.xml
new file mode 100644
index 0000000..ecb1d9c
--- /dev/null
+++ b/server/blob/blob-export-file/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>james-server-blob</artifactId>
+        <groupId>org.apache.james</groupId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>blob-export-file</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Apache James :: Server :: Blob :: Export :: File 
implementation</name>
+    <description>Provides a simple BlobExportMechanism exporting a blob in a 
file, then sharing the file URL by email</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-mailet-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-export-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-dnsservice-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-filesystem-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java
 
b/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java
new file mode 100644
index 0000000..f731c9a
--- /dev/null
+++ 
b/server/blob/blob-export-file/src/main/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanism.java
@@ -0,0 +1,119 @@
+/****************************************************************
+ * 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.export.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BlobStore;
+import org.apache.james.blob.export.api.BlobExportMechanism;
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.server.core.MailImpl;
+import org.apache.mailet.MailetContext;
+
+public class LocalFileBlobExportMechanism implements BlobExportMechanism {
+    private static final int STRING_LENGTH = 32;
+    private static final boolean WITH_LETTERS = true;
+    private static final boolean WITH_NUMBERS = true;
+    private static final String SUBJECT = "Some content had had just been 
exported";
+    static final String CORRESPONDING_FILE_HEADER = "corresponding-file";
+
+    public static class Configuration {
+        private final String exportDirectory;
+
+        public Configuration(String exportDirectory) {
+            this.exportDirectory = exportDirectory;
+        }
+    }
+
+    private final MailetContext mailetContext;
+    private final BlobStore blobStore;
+    private final FileSystem fileSystem;
+    private final DNSService dnsService;
+    private final Configuration configuration;
+
+    LocalFileBlobExportMechanism(MailetContext mailetContext, BlobStore 
blobStore, FileSystem fileSystem, DNSService dnsService, Configuration 
configuration) {
+        this.mailetContext = mailetContext;
+        this.blobStore = blobStore;
+        this.fileSystem = fileSystem;
+        this.dnsService = dnsService;
+        this.configuration = configuration;
+    }
+
+    @Override
+    public ShareeStage blobId(BlobId blobId) {
+        return mailAddress -> explanation -> () ->  {
+            String fileUrl = copyBlobToFile(blobId);
+            sendMail(mailAddress, fileUrl, explanation);
+        };
+    }
+
+    private String copyBlobToFile(BlobId blobId) {
+        try {
+            File exportingDirectory = 
fileSystem.getFile(configuration.exportDirectory);
+            FileUtils.forceMkdir(exportingDirectory);
+
+
+            String fileName = RandomStringUtils.random(STRING_LENGTH, 
WITH_LETTERS, !WITH_NUMBERS);
+            String fileURL = configuration.exportDirectory + "/" + fileName;
+            File file = fileSystem.getFile(fileURL);
+            FileUtils.copyToFile(blobStore.read(blobId), file);
+
+            return fileURL;
+        } catch (IOException e) {
+            throw new BlobExportException("Error while copying blob to file", 
e);
+        }
+    }
+
+    private void sendMail(MailAddress mailAddress, String fileUrl, String 
explanation) {
+        try {
+            MimeMessageBuilder mimeMessage = 
MimeMessageBuilder.mimeMessageBuilder()
+                .addHeader(CORRESPONDING_FILE_HEADER, fileUrl)
+                .addFrom(mailetContext.getPostmaster().asString())
+                .addToRecipient(mailAddress.asString())
+                .setSubject(SUBJECT)
+                .setText(computeMessage(explanation, fileUrl));
+
+            MailImpl mail = MailImpl.builder()
+                .name(MailImpl.getId())
+                .sender(mailetContext.getPostmaster())
+                .addRecipient(mailAddress)
+                .mimeMessage(mimeMessage)
+                .build();
+
+            mailetContext.sendMail(mail);
+        } catch (Exception e) {
+            throw new BlobExportException("Error while sending email", e);
+        }
+    }
+
+    private String computeMessage(String explanationText, String fileUrl) 
throws UnknownHostException {
+        String hostname = dnsService.getLocalHost().getHostName();
+        return explanationText + "\n\n" +
+            "The content of this blob can be read directly on James host 
filesystem (" + hostname + ") in this file: " + fileUrl;
+    }
+}
diff --git 
a/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/FileSystemExtension.java
 
b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/FileSystemExtension.java
new file mode 100644
index 0000000..ed1dbd8
--- /dev/null
+++ 
b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/FileSystemExtension.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.james.blob.export.file;
+
+import java.util.UUID;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.server.core.JamesServerResourceLoader;
+import org.apache.james.server.core.filesystem.FileSystemImpl;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public class FileSystemExtension implements ParameterResolver, 
BeforeAllCallback, AfterAllCallback {
+
+    private FileSystemImpl fileSystem;
+
+    @Override
+    public void beforeAll(ExtensionContext context) {
+        fileSystem = new FileSystemImpl(new 
JamesServerResourceLoader("../testsFileSystemExtension/" + UUID.randomUUID()));
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        FileUtils.forceDelete(fileSystem.getBasedir());
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == FileSystem.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext) throws ParameterResolutionException {
+        return fileSystem;
+    }
+
+}
diff --git 
a/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java
 
b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java
new file mode 100644
index 0000000..35983a0
--- /dev/null
+++ 
b/server/blob/blob-export-file/src/test/java/org/apache/james/blob/export/file/LocalFileBlobExportMechanismTest.java
@@ -0,0 +1,136 @@
+/****************************************************************
+ * 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.export.file;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+
+import javax.mail.Message;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BlobStore;
+import org.apache.james.blob.api.HashBlobId;
+import org.apache.james.blob.api.ObjectStoreException;
+import org.apache.james.blob.memory.MemoryBlobStore;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.util.MimeMessageUtil;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMailContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(FileSystemExtension.class)
+class LocalFileBlobExportMechanismTest {
+    private static final byte[] BLOB_CONTENT = 
"blob_content".getBytes(StandardCharsets.UTF_8);
+    private static final String JAMES_HOST = "james-host";
+
+    private BlobStore blobStore;
+    private FakeMailContext mailetContext;
+    private LocalFileBlobExportMechanism testee;
+
+    @BeforeEach
+    void setUp(FileSystem fileSystem) throws Exception {
+        mailetContext = 
FakeMailContext.builder().postmaster(MailAddressFixture.POSTMASTER_AT_JAMES).build();
+        blobStore = new MemoryBlobStore(new HashBlobId.Factory());
+
+        InetAddress localHost = mock(InetAddress.class);
+        when(localHost.getHostName()).thenReturn(JAMES_HOST);
+        DNSService dnsService = mock(DNSService.class);
+        when(dnsService.getLocalHost()).thenReturn(localHost);
+
+        LocalFileBlobExportMechanism.Configuration blobExportConfiguration = 
new LocalFileBlobExportMechanism.Configuration("file://var/blobExporting");
+
+        testee = new LocalFileBlobExportMechanism(mailetContext, blobStore, 
fileSystem, dnsService, blobExportConfiguration);
+    }
+
+    @Test
+    void exportingBlobShouldSendAMail() {
+        BlobId blobId = blobStore.save(BLOB_CONTENT).block();
+
+        String explanation = "The content of a deleted message vault had been 
shared with you.";
+        testee.blobId(blobId)
+            .with(MailAddressFixture.RECIPIENT1)
+            .explanation(explanation)
+            .export();
+
+        assertThat(mailetContext.getSentMails()).hasSize(1)
+            .element(0)
+            .satisfies(sentMail -> {
+                try {
+                    
assertThat(sentMail.getSender()).isEqualTo(MailAddressFixture.POSTMASTER_AT_JAMES);
+                    
assertThat(sentMail.getRecipients()).containsExactly(MailAddressFixture.RECIPIENT1);
+
+                    assertThat(sentMail.getMsg().getFrom()).contains(new 
InternetAddress(MailAddressFixture.POSTMASTER_AT_JAMES.asString()));
+                    
assertThat(sentMail.getMsg().getRecipients(Message.RecipientType.TO)).contains(new
 InternetAddress(MailAddressFixture.RECIPIENT1.asString()));
+                    assertThat(sentMail.getMsg().getSubject()).isEqualTo("Some 
content had had just been exported");
+
+                    String mailContent = 
MimeMessageUtil.asString(sentMail.getMsg());
+
+                    assertThat(mailContent).contains(explanation);
+                    assertThat(mailContent).contains("The content of this blob 
can be read directly on James host filesystem (james-host) in this file: ");
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            });
+    }
+
+    @Test
+    void exportingBlobShouldCreateAFileWithTheCorrespondingContent(FileSystem 
fileSystem) {
+        BlobId blobId = blobStore.save(BLOB_CONTENT).block();
+
+        testee.blobId(blobId)
+            .with(MailAddressFixture.RECIPIENT1)
+            .explanation("The content of a deleted message vault had been 
shared with you.")
+            .export();
+
+        assertThat(mailetContext.getSentMails())
+            .element(0)
+            .satisfies(sentMail -> {
+                try {
+                    String fileUrl = 
sentMail.getMsg().getHeader(LocalFileBlobExportMechanism.CORRESPONDING_FILE_HEADER)[0];
+
+                    
assertThat(fileSystem.getResource(fileUrl)).hasSameContentAs(new 
ByteArrayInputStream(BLOB_CONTENT));
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            });
+    }
+
+    @Test
+    void shareShouldFailWhenBlobDoesNotExist() {
+        BlobId blobId = new HashBlobId.Factory().forPayload("not 
existing".getBytes(StandardCharsets.UTF_8));
+
+        assertThatThrownBy(() ->
+            testee.blobId(blobId)
+                .with(MailAddressFixture.RECIPIENT1)
+                .explanation("The content of a deleted message vault had been 
shared with you.")
+                .export())
+            .isInstanceOf(ObjectStoreException.class);
+    }
+}
\ No newline at end of file
diff --git a/server/blob/pom.xml b/server/blob/pom.xml
index a49932a..6127838 100644
--- a/server/blob/pom.xml
+++ b/server/blob/pom.xml
@@ -36,6 +36,7 @@
         <module>blob-api</module>
         <module>blob-cassandra</module>
         <module>blob-export-api</module>
+        <module>blob-export-file</module>
         <module>blob-memory</module>
         <module>blob-objectstorage</module>
         <module>blob-union</module>


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

Reply via email to