JAMES-1692 Send the message throw the MailSpool
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/3b09ad75 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/3b09ad75 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/3b09ad75 Branch: refs/heads/master Commit: 3b09ad7534457312572a65d1bd6e61956d8b85ec Parents: 8174cb7 Author: Raphael Ouazana <[email protected]> Authored: Fri Feb 26 09:28:44 2016 +0100 Committer: Raphael Ouazana <[email protected]> Committed: Tue Mar 1 15:46:32 2016 +0100 ---------------------------------------------------------------------- .../org/apache/james/jmap/JMAPCommonModule.java | 2 + .../methods/SetMessagesCreationProcessor.java | 33 ++++-- .../org/apache/james/jmap/send/MailFactory.java | 78 +++++++++++++++ .../SetMessagesCreationProcessorTest.java | 48 +++++++-- .../apache/james/jmap/send/MailFactoryTest.java | 100 +++++++++++++++++++ 5 files changed, 248 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/3b09ad75/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java ---------------------------------------------------------------------- diff --git a/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java b/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java index 14c2d9f..d2ad4b0 100644 --- a/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java +++ b/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java @@ -29,6 +29,7 @@ import org.apache.james.jmap.crypto.JamesSignatureHandler; import org.apache.james.jmap.crypto.SignatureHandler; import org.apache.james.jmap.crypto.SignedContinuationTokenManager; import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository; +import org.apache.james.jmap.send.MailFactory; import org.apache.james.jmap.send.MailSpool; import org.apache.james.jmap.utils.DefaultZonedDateTimeProvider; import org.apache.james.jmap.utils.ZonedDateTimeProvider; @@ -54,6 +55,7 @@ public class JMAPCommonModule extends AbstractModule { bind(AccessTokenManager.class).to(AccessTokenManagerImpl.class); bind(MailSpool.class).in(Singleton.class); + bind(MailFactory.class).in(Singleton.class); } @Provides http://git-wip-us.apache.org/repos/asf/james-project/blob/3b09ad75/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java index 718734d..da871a2 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java @@ -19,8 +19,7 @@ package org.apache.james.jmap.methods; -import static org.apache.james.jmap.model.MessageProperties.MessageProperty; - +import java.io.IOException; import java.util.AbstractMap; import java.util.Date; import java.util.List; @@ -33,6 +32,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.mail.Flags; +import javax.mail.MessagingException; import javax.mail.internet.SharedInputStream; import javax.mail.util.SharedByteArrayInputStream; @@ -42,10 +42,14 @@ import org.apache.james.jmap.model.CreationMessageId; import org.apache.james.jmap.model.Message; import org.apache.james.jmap.model.MessageId; import org.apache.james.jmap.model.MessageProperties; +import org.apache.james.jmap.model.MessageProperties.MessageProperty; import org.apache.james.jmap.model.SetError; import org.apache.james.jmap.model.SetMessagesRequest; import org.apache.james.jmap.model.SetMessagesResponse; import org.apache.james.jmap.model.mailbox.Role; +import org.apache.james.jmap.send.MailFactory; +import org.apache.james.jmap.send.MailMetadata; +import org.apache.james.jmap.send.MailSpool; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; @@ -60,6 +64,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.mail.model.MailboxMessage; import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; +import org.apache.mailet.Mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,17 +82,23 @@ public class SetMessagesCreationProcessor<Id extends MailboxId> implements SetMe private final MailboxManager mailboxManager; private final MailboxSessionMapperFactory<Id> mailboxSessionMapperFactory; private final MIMEMessageConverter mimeMessageConverter; + private final MailSpool mailSpool; + private final MailFactory<Id> mailFactory; @Inject @VisibleForTesting SetMessagesCreationProcessor(MailboxMapperFactory<Id> mailboxMapperFactory, MailboxManager mailboxManager, MailboxSessionMapperFactory<Id> mailboxSessionMapperFactory, - MIMEMessageConverter mimeMessageConverter) { + MIMEMessageConverter mimeMessageConverter, + MailSpool mailSpool, + MailFactory<Id> mailFactory) { this.mailboxMapperFactory = mailboxMapperFactory; this.mailboxManager = mailboxManager; this.mailboxSessionMapperFactory = mailboxSessionMapperFactory; this.mimeMessageConverter = mimeMessageConverter; + this.mailSpool = mailSpool; + this.mailFactory = mailFactory; } @Override @@ -109,7 +120,7 @@ public class SetMessagesCreationProcessor<Id extends MailboxId> implements SetMe return request.getCreate().entrySet().stream() .filter(e -> validMessagesTester.test(e.getValue())) .map(e -> new MessageWithId.CreationMessageEntry(e.getKey(), e.getValue())) - .map(nuMsg -> createMessageInOutbox(nuMsg, mailboxSession, outbox, buildMessageIdFunc(mailboxSession, outbox))) + .map(nuMsg -> createMessageInOutboxAndSend(nuMsg, mailboxSession, outbox, buildMessageIdFunc(mailboxSession, outbox))) .map(msg -> SetMessagesResponse.builder().created(ImmutableMap.of(msg.getCreationId(), msg.getMessage())).build()) .reduce(responseBuilder, SetMessagesResponse.Builder::accumulator, SetMessagesResponse.Builder::combiner) .build(); @@ -140,15 +151,17 @@ public class SetMessagesCreationProcessor<Id extends MailboxId> implements SetMe } @VisibleForTesting - protected MessageWithId<Message> createMessageInOutbox(MessageWithId.CreationMessageEntry createdEntry, + protected MessageWithId<Message> createMessageInOutboxAndSend(MessageWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox<Id> outbox, Function<Long, MessageId> buildMessageIdFromUid) { try { MessageMapper<Id> messageMapper = mailboxSessionMapperFactory.createMessageMapper(session); MailboxMessage<Id> newMailboxMessage = buildMailboxMessage(createdEntry, outbox); messageMapper.add(outbox, newMailboxMessage); - return new MessageWithId<>(createdEntry.getCreationId(), Message.fromMailboxMessage(newMailboxMessage, buildMessageIdFromUid)); - } catch (MailboxException e) { + Message jmapMessage = Message.fromMailboxMessage(newMailboxMessage, buildMessageIdFromUid); + sendMessage(newMailboxMessage, jmapMessage, session); + return new MessageWithId<>(createdEntry.getCreationId(), jmapMessage); + } catch (MailboxException | MessagingException | IOException e) { throw Throwables.propagate(e); } catch (MailboxRoleNotFoundException e) { LOGGER.error("Could not find mailbox '%s' while trying to save message.", e.getRole().serialize()); @@ -216,4 +229,10 @@ public class SetMessagesCreationProcessor<Id extends MailboxId> implements SetMe } return result; } + + private void sendMessage(MailboxMessage<Id> mailboxMessage, Message jmapMessage, MailboxSession session) throws MessagingException, IOException { + Mail mail = mailFactory.build(mailboxMessage, jmapMessage); + MailMetadata metadata = new MailMetadata(jmapMessage.getId(), session.getUser().getUserName()); + mailSpool.send(mail, metadata); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/3b09ad75/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java new file mode 100644 index 0000000..4de9c1c --- /dev/null +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java @@ -0,0 +1,78 @@ +/**************************************************************** + * 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.jmap.send; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.mail.MessagingException; +import javax.mail.internet.AddressException; + +import org.apache.james.core.MailImpl; +import org.apache.james.jmap.model.Emailer; +import org.apache.james.jmap.model.Message; +import org.apache.james.mailbox.store.mail.model.MailboxId; +import org.apache.james.mailbox.store.mail.model.MailboxMessage; +import org.apache.mailet.Mail; +import org.apache.mailet.MailAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +public class MailFactory<Id extends MailboxId> { + private static final Logger LOGGER = LoggerFactory.getLogger(MailFactory.class); + + @VisibleForTesting MailFactory() { + } + + public Mail build(MailboxMessage<Id> mailboxMessage, Message jmapMessage) throws MessagingException, IOException { + MailAddress sender = jmapMessage.getFrom() + .map(this::emailerToMailAddress) + .orElseThrow(() -> new RuntimeException("Sender is mandatory")); + Set<MailAddress> to = emailersToMailAddressSet(jmapMessage.getTo()); + Set<MailAddress> cc = emailersToMailAddressSet(jmapMessage.getCc()); + Set<MailAddress> bcc = emailersToMailAddressSet(jmapMessage.getBcc()); + ImmutableSet<MailAddress> recipients = Sets.union( + Sets.union(to, cc), + bcc).immutableCopy(); + return new MailImpl(jmapMessage.getId().serialize(), sender, recipients, mailboxMessage.getBodyContent()); + } + + private MailAddress emailerToMailAddress(Emailer emailer) { + try { + return new MailAddress(emailer.getEmail()); + } catch (AddressException e) { + LOGGER.error("Invalid mail address", emailer.getEmail()); + throw Throwables.propagate(e); + } + } + + private Set<MailAddress> emailersToMailAddressSet(List<Emailer> emailers) { + return emailers.stream() + .map(this::emailerToMailAddress) + .collect(Collectors.toSet()); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/3b09ad75/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java index 5978f0e..4c73ca8 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java @@ -19,7 +19,6 @@ package org.apache.james.jmap.methods; -import static org.apache.james.jmap.model.CreationMessage.DraftEmailer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -33,11 +32,15 @@ import java.util.function.Function; import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException; import org.apache.james.jmap.model.CreationMessage; +import org.apache.james.jmap.model.CreationMessage.DraftEmailer; import org.apache.james.jmap.model.CreationMessageId; import org.apache.james.jmap.model.Message; import org.apache.james.jmap.model.MessageId; import org.apache.james.jmap.model.SetMessagesRequest; import org.apache.james.jmap.model.SetMessagesResponse; +import org.apache.james.jmap.send.MailFactory; +import org.apache.james.jmap.send.MailMetadata; +import org.apache.james.jmap.send.MailSpool; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; @@ -45,6 +48,7 @@ import org.apache.james.mailbox.store.TestId; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.Mailbox; import org.apache.james.mailbox.store.mail.model.MailboxMessage; +import org.apache.mailet.Mail; import org.junit.Test; import com.google.common.collect.ImmutableList; @@ -66,7 +70,7 @@ public class SetMessagesCreationProcessorTest { @Test public void processShouldReturnEmptyCreatedWhenRequestHasEmptyCreate() { - SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, null, null) { + SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, null, null, null, null) { @Override protected Optional<Mailbox<TestId>> getOutbox(MailboxSession session) throws MailboxException { @SuppressWarnings("unchecked") @@ -102,9 +106,9 @@ public class SetMessagesCreationProcessorTest { when(mockSessionMapperFactory.createMessageMapper(any(MailboxSession.class))) .thenReturn(stubMapper); - SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, mockSessionMapperFactory, null) { + SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, mockSessionMapperFactory, null, null, null) { @Override - protected MessageWithId<Message> createMessageInOutbox(MessageWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox<TestId> outbox, Function<Long, MessageId> buildMessageIdFromUid) { + protected MessageWithId<Message> createMessageInOutboxAndSend(MessageWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox<TestId> outbox, Function<Long, MessageId> buildMessageIdFromUid) { return new MessageWithId<>(createdEntry.getCreationId(), FAKE_MESSAGE); } @Override @@ -125,7 +129,7 @@ public class SetMessagesCreationProcessorTest { @Test(expected = MailboxRoleNotFoundException.class) public void processShouldThrowWhenOutboxNotFound() { // Given - SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, null, null) { + SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, null, null, null, null) { @Override protected Optional<Mailbox<TestId>> getOutbox(MailboxSession session) throws MailboxException { return Optional.empty(); @@ -144,9 +148,11 @@ public class SetMessagesCreationProcessorTest { MailboxSessionMapperFactory<TestId> stubSessionMapperFactory = mock(MailboxSessionMapperFactory.class); when(stubSessionMapperFactory.createMessageMapper(any(MailboxSession.class))) .thenReturn(mockMapper); + MailSpool mockedMailSpool = mock(MailSpool.class); + MailFactory<TestId> mockedMailFactory = mock(MailFactory.class); SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, - stubSessionMapperFactory, new MIMEMessageConverter()) { + stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory) { @Override protected Optional<Mailbox<TestId>> getOutbox(MailboxSession session) throws MailboxException { TestId stubMailboxId = mock(TestId.class); @@ -163,6 +169,36 @@ public class SetMessagesCreationProcessorTest { verify(mockMapper).add(eq(fakeOutbox), any(MailboxMessage.class)); } + @Test + @SuppressWarnings("unchecked") + public void processShouldSendMailWhenRequestHasNonEmptyCreate() throws Exception { + // Given + Mailbox<TestId> fakeOutbox = mock(Mailbox.class); + MessageMapper<TestId> mockMapper = mock(MessageMapper.class); + MailboxSessionMapperFactory<TestId> stubSessionMapperFactory = mock(MailboxSessionMapperFactory.class); + when(stubSessionMapperFactory.createMessageMapper(any(MailboxSession.class))) + .thenReturn(mockMapper); + MailSpool mockedMailSpool = mock(MailSpool.class); + MailFactory<TestId> mockedMailFactory = mock(MailFactory.class); + + SetMessagesCreationProcessor<TestId> sut = new SetMessagesCreationProcessor<TestId>(null, null, + stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory) { + @Override + protected Optional<Mailbox<TestId>> getOutbox(MailboxSession session) throws MailboxException { + TestId stubMailboxId = mock(TestId.class); + when(stubMailboxId.serialize()).thenReturn("user|outbox|12345"); + when(fakeOutbox.getMailboxId()).thenReturn(stubMailboxId); + when(fakeOutbox.getName()).thenReturn("outbox"); + return Optional.of(fakeOutbox); + } + }; + // When + sut.process(buildFakeCreationRequest(), buildStubbedSession()); + + // Then + verify(mockedMailSpool).send(any(Mail.class), any(MailMetadata.class)); + } + private SetMessagesRequest buildFakeCreationRequest() { return SetMessagesRequest.builder() .create(ImmutableMap.of(CreationMessageId.of("anything-really"), CreationMessage.builder() http://git-wip-us.apache.org/repos/asf/james-project/blob/3b09ad75/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java new file mode 100644 index 0000000..59f09e2 --- /dev/null +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java @@ -0,0 +1,100 @@ +/**************************************************************** + * 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.jmap.send; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collection; +import java.util.Date; + +import javax.mail.Flags; +import javax.mail.util.SharedByteArrayInputStream; + +import org.apache.james.jmap.model.Message; +import org.apache.james.jmap.model.MessageId; +import org.apache.james.mailbox.FlagsBuilder; +import org.apache.james.mailbox.store.TestId; +import org.apache.james.mailbox.store.mail.model.MailboxMessage; +import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; +import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; +import org.apache.mailet.Mail; +import org.apache.mailet.MailAddress; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableSet; + +public class MailFactoryTest { + + private MailFactory<TestId> testee; + private MailboxMessage<TestId> mailboxMessage; + private Message jmapMessage; + + @Before + public void init() { + testee = new MailFactory<TestId>(); + String headers = "From: [email protected]\n" + + "To: [email protected]\n" + + "Cc: [email protected], [email protected]\n" + + "Bcc: [email protected]\n" + + "Subject: news\n"; + String content = headers + + "Hello! How are you?"; + PropertyBuilder propertyBuilder = new PropertyBuilder(); + propertyBuilder.setMediaType("plain"); + propertyBuilder.setSubType("text"); + propertyBuilder.setTextualLineCount(18L); + mailboxMessage = new SimpleMailboxMessage<>( + new Date(), + content.length(), + headers.length(), + new SharedByteArrayInputStream(content.getBytes()), + new FlagsBuilder().add(Flags.Flag.SEEN).build(), + propertyBuilder, + TestId.of(2)); + jmapMessage = Message.fromMailboxMessage(mailboxMessage, x -> MessageId.of("test|test|" + x)); + } + + @Test(expected=NullPointerException.class) + public void buildMailShouldThrowWhenNullMailboxMessage() throws Exception { + testee.build(null, jmapMessage); + } + + @Test(expected=NullPointerException.class) + public void buildMailShouldThrowWhenNullJmapMessage() throws Exception { + testee.build(mailboxMessage, null); + } + + @Test + public void buildMailShouldWork() throws Exception { + String expectedName = jmapMessage.getId().serialize(); + MailAddress expectedSender = new MailAddress("[email protected]"); + Collection<MailAddress> expectedRecipients = ImmutableSet.of( + new MailAddress("[email protected]"), + new MailAddress("[email protected]"), + new MailAddress("[email protected]"), + new MailAddress("[email protected]")); + + Mail actual = testee.build(mailboxMessage, jmapMessage); + + assertThat(actual.getName()).isEqualTo(expectedName); + assertThat(actual.getSender()).isEqualTo(expectedSender); + assertThat(actual.getRecipients()).containsAll(expectedRecipients); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
