org.xwiki.mail.integration.JavaIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.mail.integration.JavaIntegrationTest.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.mail.integration;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.inject.Provider;
import javax.mail.BodyPart;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.xwiki.bridge.event.ApplicationReadyEvent;
import org.xwiki.component.phase.Disposable;
import org.xwiki.component.util.DefaultParameterizedType;
import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
import org.xwiki.context.ExecutionContextManager;
import org.xwiki.environment.internal.EnvironmentConfiguration;
import org.xwiki.environment.internal.StandardEnvironment;
import org.xwiki.mail.MailListener;
import org.xwiki.mail.MailSender;
import org.xwiki.mail.MailSenderConfiguration;
import org.xwiki.mail.MimeBodyPartFactory;
import org.xwiki.mail.internal.DefaultMailSender;
import org.xwiki.mail.internal.FileSystemMailContentStore;
import org.xwiki.mail.internal.MemoryMailListener;
import org.xwiki.mail.internal.factory.attachment.AttachmentMimeBodyPartFactory;
import org.xwiki.mail.internal.factory.html.HTMLMimeBodyPartFactory;
import org.xwiki.mail.internal.factory.text.TextMimeBodyPartFactory;
import org.xwiki.mail.internal.thread.MailSenderInitializerListener;
import org.xwiki.mail.internal.thread.PrepareMailQueueManager;
import org.xwiki.mail.internal.thread.PrepareMailRunnable;
import org.xwiki.mail.internal.thread.SendMailQueueManager;
import org.xwiki.mail.internal.thread.SendMailRunnable;
import org.xwiki.mail.internal.thread.context.Copier;
import org.xwiki.model.ModelContext;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.observation.EventListener;
import org.xwiki.test.annotation.BeforeComponent;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.mockito.MockitoComponentManagerRule;

import com.icegreen.greenmail.junit.GreenMailRule;
import com.icegreen.greenmail.util.ServerSetupTest;
import com.xpn.xwiki.XWikiContext;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

/**
 * Integration tests to prove that mail sending is working fully end to end with the Java API.
 *
 * @version $Id: 47f6f2287701de21e0db830b1a6a8b4368d55e8b $
 * @since 6.1M2
 */
@ComponentList({ MailSenderInitializerListener.class, TextMimeBodyPartFactory.class, HTMLMimeBodyPartFactory.class,
        AttachmentMimeBodyPartFactory.class, StandardEnvironment.class, DefaultMailSender.class,
        MemoryMailListener.class, SendMailRunnable.class, PrepareMailRunnable.class, PrepareMailQueueManager.class,
        SendMailQueueManager.class, FileSystemMailContentStore.class })
public class JavaIntegrationTest {
    @Rule
    public GreenMailRule mail = new GreenMailRule(ServerSetupTest.SMTP);

    @Rule
    public MockitoComponentManagerRule componentManager = new MockitoComponentManagerRule();

    private TestMailSenderConfiguration configuration;

    private MimeBodyPartFactory<String> defaultBodyPartFactory;

    private MimeBodyPartFactory<String> htmlBodyPartFactory;

    private MailSender sender;

    @BeforeComponent
    public void registerConfiguration() throws Exception {
        this.configuration = new TestMailSenderConfiguration(this.mail.getSmtp().getPort(), null, null,
                new Properties());
        this.componentManager.registerComponent(MailSenderConfiguration.class, this.configuration);

        // Set the current wiki in the Context
        ModelContext modelContext = this.componentManager.registerMockComponent(ModelContext.class);
        when(modelContext.getCurrentEntityReference()).thenReturn(new WikiReference("wiki"));

        XWikiContext xcontext = mock(XWikiContext.class);
        when(xcontext.getWikiId()).thenReturn("wiki");

        Provider<XWikiContext> xwikiContextProvider = this.componentManager
                .registerMockComponent(XWikiContext.TYPE_PROVIDER);
        when(xwikiContextProvider.get()).thenReturn(xcontext);

        this.componentManager.registerMockComponent(ExecutionContextManager.class);
        this.componentManager.registerMockComponent(Execution.class);

        this.componentManager
                .registerMockComponent(new DefaultParameterizedType(null, Copier.class, ExecutionContext.class));

        EnvironmentConfiguration environmentConfiguration = this.componentManager
                .registerMockComponent(EnvironmentConfiguration.class);
        when(environmentConfiguration.getPermanentDirectoryPath()).thenReturn(System.getProperty("java.io.tmpdir"));
    }

    @Before
    public void initialize() throws Exception {
        this.defaultBodyPartFactory = this.componentManager
                .getInstance(new DefaultParameterizedType(null, MimeBodyPartFactory.class, String.class));
        this.htmlBodyPartFactory = this.componentManager.getInstance(
                new DefaultParameterizedType(null, MimeBodyPartFactory.class, String.class), "text/html");
        this.sender = this.componentManager.getInstance(MailSender.class);

        // Set the EC
        Execution execution = this.componentManager.getInstance(Execution.class);
        ExecutionContext executionContext = new ExecutionContext();
        XWikiContext xContext = new XWikiContext();
        xContext.setWikiId("wiki");
        executionContext.setProperty(XWikiContext.EXECUTIONCONTEXT_KEY, xContext);
        when(execution.getContext()).thenReturn(executionContext);

        Copier<ExecutionContext> executionContextCloner = this.componentManager
                .getInstance(new DefaultParameterizedType(null, Copier.class, ExecutionContext.class));
        // Just return the same execution context
        when(executionContextCloner.copy(executionContext)).thenReturn(executionContext);

        // Simulate receiving the Application Ready Event to start the mail threads
        MailSenderInitializerListener listener = this.componentManager.getInstance(EventListener.class,
                MailSenderInitializerListener.LISTENER_NAME);
        listener.onEvent(new ApplicationReadyEvent(), null, null);
    }

    @After
    public void cleanUp() throws Exception {
        // Make sure we stop the Mail Sender thread after each test (since it's started automatically when looking
        // up the MailSender component.
        Disposable listener = this.componentManager.getInstance(EventListener.class,
                MailSenderInitializerListener.LISTENER_NAME);
        listener.dispose();
    }

    @Test
    public void sendTextMail() throws Exception {
        // Step 1: Create a JavaMail Session
        Session session = Session.getInstance(this.configuration.getAllProperties());

        // Step 2: Create the Message to send
        MimeMessage message = new MimeMessage(session);
        message.setSubject("subject");
        message.setRecipient(RecipientType.TO, new InternetAddress("john@doe.com"));

        // Step 3: Add the Message Body
        Multipart multipart = new MimeMultipart("mixed");
        // Add text in the body
        multipart.addBodyPart(this.defaultBodyPartFactory.create("some text here",
                Collections.<String, Object>singletonMap("mimetype", "text/plain")));
        message.setContent(multipart);

        // We also test using some default BCC addresses from configuration in this test
        this.configuration.setBCCAddresses(Arrays.asList("bcc1@doe.com", "bcc2@doe.com"));

        // Ensure we do not reuse the same message identifier for multiple similar messages in this test
        MimeMessage message2 = new MimeMessage(message);
        message2.saveChanges();
        MimeMessage message3 = new MimeMessage(message);
        message3.saveChanges();

        // Step 4: Send the mail and wait for it to be sent
        // Send 3 mails (3 times the same mail) to verify we can send several emails at once.
        MailListener memoryMailListener = this.componentManager.getInstance(MailListener.class, "memory");
        this.sender.sendAsynchronously(Arrays.asList(message, message2, message3), session, memoryMailListener);

        // Note: we don't test status reporting from the listener since this is already tested in the
        // ScriptingIntegrationTest test class.

        // Verify that the mails have been received (wait maximum 30 seconds).
        this.mail.waitForIncomingEmail(30000L, 3);
        MimeMessage[] messages = this.mail.getReceivedMessages();

        // Note: we're receiving 9 messages since we sent 3 with 3 recipients (2 BCC and 1 to)!
        assertEquals(9, messages.length);

        // Assert the email parts that are the same for all mails
        assertEquals("subject", messages[0].getHeader("Subject", null));
        assertEquals(1, ((MimeMultipart) messages[0].getContent()).getCount());
        BodyPart textBodyPart = ((MimeMultipart) messages[0].getContent()).getBodyPart(0);
        assertEquals("text/plain", textBodyPart.getHeader("Content-Type")[0]);
        assertEquals("some text here", textBodyPart.getContent());
        assertEquals("john@doe.com", messages[0].getHeader("To", null));

        // Note: We cannot assert that the BCC worked since by definition BCC information are not visible in received
        // messages ;) But we checked that we received 9 emails above so that's good enough.
    }

    @Test
    public void sendHTMLAndCalendarInvitationMail() throws Exception {
        // Step 1: Create a JavaMail Session
        Session session = Session.getInstance(this.configuration.getAllProperties());

        // Step 2: Create the Message to send
        MimeMessage message = new MimeMessage(session);
        message.setSubject("subject");
        message.setRecipient(RecipientType.TO, new InternetAddress("john@doe.com"));

        // Step 3: Add the Message Body
        Multipart multipart = new MimeMultipart("alternative");
        // Add an HTML body part
        multipart.addBodyPart(
                this.htmlBodyPartFactory.create("<font size=\"\\\"2\\\"\">simple meeting invitation</font>",
                        Collections.<String, Object>emptyMap()));
        // Add the Calendar invitation body part
        String calendarContent = "BEGIN:VCALENDAR\r\n" + "METHOD:REQUEST\r\n" + "PRODID: Meeting\r\n"
                + "VERSION:2.0\r\n" + "BEGIN:VEVENT\r\n" + "DTSTAMP:20140616T164100\r\n"
                + "DTSTART:20140616T164100\r\n" + "DTEND:20140616T194100\r\n" + "SUMMARY:test request\r\n"
                + "UID:324\r\n"
                + "ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:MAILTO:john@doe.com\r\n"
                + "ORGANIZER:MAILTO:john@doe.com\r\n" + "LOCATION:on the net\r\n"
                + "DESCRIPTION:learn some stuff\r\n" + "SEQUENCE:0\r\n" + "PRIORITY:5\r\n" + "CLASS:PUBLIC\r\n"
                + "STATUS:CONFIRMED\r\n" + "TRANSP:OPAQUE\r\n" + "BEGIN:VALARM\r\n" + "ACTION:DISPLAY\r\n"
                + "DESCRIPTION:REMINDER\r\n" + "TRIGGER;RELATED=START:-PT00H15M00S\r\n" + "END:VALARM\r\n"
                + "END:VEVENT\r\n" + "END:VCALENDAR";
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("mimetype", "text/calendar;method=CANCEL");
        parameters.put("headers", Collections.singletonMap("Content-Class", "urn:content-classes:calendarmessage"));
        multipart.addBodyPart(this.defaultBodyPartFactory.create(calendarContent, parameters));

        message.setContent(multipart);

        // Step 4: Send the mail and wait for it to be sent
        this.sender.sendAsynchronously(Arrays.asList(message), session, null);

        // Verify that the mail has been received (wait maximum 30 seconds).
        this.mail.waitForIncomingEmail(30000L, 1);
        MimeMessage[] messages = this.mail.getReceivedMessages();

        assertEquals("subject", messages[0].getHeader("Subject", null));
        assertEquals("john@doe.com", messages[0].getHeader("To", null));

        assertEquals(2, ((MimeMultipart) messages[0].getContent()).getCount());

        BodyPart htmlBodyPart = ((MimeMultipart) messages[0].getContent()).getBodyPart(0);
        assertEquals("text/html; charset=UTF-8", htmlBodyPart.getHeader("Content-Type")[0]);
        assertEquals("<font size=\"\\\"2\\\"\">simple meeting invitation</font>", htmlBodyPart.getContent());

        BodyPart calendarBodyPart = ((MimeMultipart) messages[0].getContent()).getBodyPart(1);
        assertEquals("text/calendar;method=CANCEL", calendarBodyPart.getHeader("Content-Type")[0]);
        InputStream is = (InputStream) calendarBodyPart.getContent();
        assertEquals(calendarContent, IOUtils.toString(is));
    }

    @Test
    public void sendMailWithCustomMessageId() throws Exception {
        Session session = Session.getInstance(this.configuration.getAllProperties());
        MimeMessage message = new MimeMessage(session) {
            @Override
            protected void updateMessageID() throws MessagingException {
                if (getMessageID() == null) {
                    super.updateMessageID();
                }
            }
        };
        message.setRecipient(RecipientType.TO, new InternetAddress("john@doe.com"));
        message.setText("Test message Id support");
        message.setHeader("Message-ID", "<custom@domain>");
        message.setSubject("subject");

        MailListener memoryMailListener = this.componentManager.getInstance(MailListener.class, "memory");
        this.sender.sendAsynchronously(Arrays.asList(message), session, memoryMailListener);

        // Verify that the mails have been received (wait maximum 30 seconds).
        this.mail.waitForIncomingEmail(30000L, 1);
        MimeMessage[] messages = this.mail.getReceivedMessages();

        assertEquals("<custom@domain>", messages[0].getMessageID());
    }
}