ste.xtest.mail.BugFreeFileTransport.java Source code

Java tutorial

Introduction

Here is the source code for ste.xtest.mail.BugFreeFileTransport.java

Source

/*
 * xTest
 * Copyright (C) 2015 Stefano Fornari
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY Stefano Fornari, Stefano Fornari
 * DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 */
package ste.xtest.mail;

import java.io.File;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.AuthenticationFailedException;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.io.FileUtils;
import static org.assertj.core.api.BDDAssertions.then;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.ClearSystemProperties;
import org.junit.rules.TemporaryFolder;
import static ste.xtest.Constants.BLANKS;
import static ste.xtest.mail.FileTransport.MAIL_FILE_PATH;
import static ste.xtest.mail.FileTransport.MAIL_FILE_REQUIRE_SSL;

/**
 * java mail transport that saves the message to a given file instead of
 * sending it via SMTP.
 *
 * Example:
 * 
 * <code>
 * Properties props = new Properties();
 * props.put("mail.transport.protocol", "file");
 * props.put("mail.from", "test@example.com");
 * props.put("mail.senderName", "Test Sender"); 
 * props.put("mail.debug", true);
 * props.put("mail.file.path", "messages");
 * 
 * Session session = Session.getInstance(props);
 * MimeMessage message = new MimeMessage(session);
 * message.addRecipient(Message.RecipientType.TO, new InternetAddress("user@domain.com"));
 * message.setSubject("Subject");
 * message.setText("Body");
 * 
 * session.getTransport().sendMessage(message, message.getAllRecipients());
 * 
 * FileTransport checks also allowed credentials with the properties 
 * mail.file.allowed.[user]=[password] (and mail.smtp.auth is true)
 * 
 * If TSL shall be forced, set the config or system property mail.file.force_tsl
 * to true.
 * 
 * The properties mail.file.path, mail.file.allowed.[user] can also be set a 
 * system properties.
 * 
 * </code>
 * 
 */
public class BugFreeFileTransport {

    private final Properties config = new Properties();

    @Rule
    public final TemporaryFolder TMP = new TemporaryFolder();

    @Rule
    public final ClearSystemProperties CLEAR_FILE_PATH = new ClearSystemProperties(MAIL_FILE_PATH,
            MAIL_FILE_REQUIRE_SSL, "mail.file.allowed.user1", "mail.file.allowed.user2");

    @Before
    public void setUp() {
        config.put("mail.transport.protocol", "file");
        config.put(MAIL_FILE_PATH, TMP.getRoot().getAbsolutePath() + "/message");
    }

    @Test
    public void send_simple_message() throws Exception {
        sendSimpleMessage();

        then(FileUtils.readFileToString(new File(TMP.getRoot(), "message"))).contains("From: from@domain.com\r")
                .contains("To: to@domain.com\r").contains("Subject: the subject\r").endsWith("hello world");
    }

    @Test
    public void multiple_messages_are_appended() throws Exception {
        sendSimpleMessage();
        sendSimpleMessage("newfrom@a.com", "newto@b.com", "another one", "hello again");

        then(FileUtils.readFileToString(new File(TMP.getRoot(), "message"))).contains("From: from@domain.com\r")
                .contains("To: to@domain.com\r").contains("Subject: the subject\r").contains("hello world")
                .contains("From: newfrom@a.com\r").contains("To: newto@b.com\r").contains("Subject: another one\r")
                .contains("hello again");
    }

    @Test
    public void missing_path_throws_an_error() throws Exception {
        config.remove(FileTransport.MAIL_FILE_PATH);
        try {
            sendSimpleMessage();
            fail("missing check for invalid path");
        } catch (MessagingException x) {
            then(x).hasMessageContaining("missing message path").hasMessageContaining(MAIL_FILE_PATH);
        }
    }

    @Test
    public void invalid_path_throws_an_error() throws Exception {
        //
        // it's a directory, it is not writeble as file...
        //
        config.put(MAIL_FILE_PATH, TMP.getRoot().getAbsolutePath());
        try {
            sendSimpleMessage();
            fail("a directory shall not be writable");
        } catch (MessagingException x) {
            then(x).hasMessageContaining(TMP.getRoot().getAbsolutePath()).hasMessageContaining("failed to write");

        }

        //
        // not enough permission
        //
        config.setProperty(FileTransport.MAIL_FILE_PATH, "/none");
        try {
            sendSimpleMessage();
            fail("missing check for invalid path");
        } catch (MessagingException x) {
            then(x).hasMessageContaining("/none").hasMessageContaining("permission denied");
        }

        //
        // invalid directory
        //
        config.setProperty(FileTransport.MAIL_FILE_PATH, TMP.getRoot().getAbsolutePath() + "/one/two");
        try {
            sendSimpleMessage();
            fail("missing check for invalid path");
        } catch (MessagingException x) {
            then(x).hasMessageContaining(config.getProperty(MAIL_FILE_PATH))
                    .hasMessageContaining("no such file or directory");
        }
    }

    @Test
    public void send_multipart_message() throws Exception {
        Session session = Session.getInstance(config);

        Message message = new MimeMessage(Session.getInstance(config));
        message.setFrom(new InternetAddress("from@domain.com"));
        message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@domain.com"));
        message.setSubject("the subject");
        MimeMultipart multipart = new MimeMultipart("related");

        BodyPart messageBodyPart = new MimeBodyPart();
        String htmlText = "<H1>hello world</H1><img src=\"cid:image\">";
        messageBodyPart.setContent(htmlText, "text/html");
        multipart.addBodyPart(messageBodyPart);
        messageBodyPart = new MimeBodyPart();
        DataSource fds = new FileDataSource("src/test/resources/images/6096.png");

        messageBodyPart.setDataHandler(new DataHandler(fds));
        messageBodyPart.setHeader("Content-ID", "<image>");

        multipart.addBodyPart(messageBodyPart);
        message.setContent(multipart);

        session.getTransport().sendMessage(message, message.getAllRecipients());

        then(FileUtils.readFileToString(new File(TMP.getRoot(), "message"))).contains("From: from@domain.com\r")
                .contains("To: to@domain.com\r").contains("Subject: the subject\r").contains("hello world")
                .contains("Content-ID: <image>");
    }

    @Test
    public void set_path_as_system_property() throws Exception {
        System.setProperty(FileTransport.MAIL_FILE_PATH, TMP.getRoot().getAbsolutePath() + "/message");
        config.remove(FileTransport.MAIL_FILE_PATH);

        sendSimpleMessage();

        then(new File(TMP.getRoot(), "message")).exists();
    }

    @Test
    public void simulate_connection_error_if_tsl_is_required() throws Exception {
        System.setProperty(FileTransport.MAIL_FILE_REQUIRE_SSL, "true");
        config.setProperty(FileTransport.MAIL_FILE_REQUIRE_SSL, "true");
        try {
            sendSimpleMessage();
            fail("not tsl error raised");
        } catch (MessagingException x) {
            then(x).hasMessageContaining("invalid connection request");
        }
    }

    @Test
    public void configuration_from_local_or_system() throws Exception {
        config.remove(MAIL_FILE_PATH);
        config.remove(MAIL_FILE_REQUIRE_SSL);

        //
        // system property $MAIL_FILE_PATH set in setUp()
        //
        System.setProperty(MAIL_FILE_PATH, "path_from_system");

        Session s = Session.getInstance(config);
        FileTransport t = (FileTransport) s.getTransport();
        then(t.getProperty(MAIL_FILE_PATH)).isEqualTo("path_from_system");

        //
        // local property overrides system property
        //
        config.setProperty(MAIL_FILE_PATH, "path_from_local");
        s = Session.getInstance(config);
        t = (FileTransport) s.getTransport();
        then(t.getProperty(MAIL_FILE_PATH)).isEqualTo("path_from_local");

        //
        // system property $MAIL_FILE_REQUIRE_TLS set in setUp()
        //
        System.setProperty(MAIL_FILE_REQUIRE_SSL, "tls_from_system");

        s = Session.getInstance(config);
        t = (FileTransport) s.getTransport();
        then(t.getProperty(MAIL_FILE_REQUIRE_SSL)).isEqualTo("tls_from_system");

        //
        // local property overrides system property
        //
        config.setProperty(MAIL_FILE_REQUIRE_SSL, "tls_from_local");
        s = Session.getInstance(config);
        t = (FileTransport) s.getTransport();
        then(t.getProperty(MAIL_FILE_REQUIRE_SSL)).isEqualTo("tls_from_local");
    }

    @Test
    public void get_property_with_empty_parameter() throws Exception {
        FileTransport t = (FileTransport) Session.getInstance(config).getTransport();

        for (String BLANK : BLANKS) {
            try {
                t.getProperty(BLANK);
                fail("missing illegal parameter check for [" + BLANK + "]");
            } catch (IllegalArgumentException x) {
                then(x).hasMessage("name can not be empty");
            }
        }
    }

    // ----------------------------------------------------------------- private

    private void sendSimpleMessage() throws Exception {
        sendSimpleMessage("from@domain.com", "to@domain.com", "the subject", "hello world");
    }

    private void sendSimpleMessage(String from, String to, String subject, String body) throws Exception {
        Session session = Session.getInstance(config);

        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        message.setSubject(subject);
        message.setText(body);

        session.getTransport().sendMessage(message, message.getAllRecipients());
    }

}