MAILET-153 Store specific per recipient mail headers
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c56422b9 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c56422b9 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c56422b9 Branch: refs/heads/master Commit: c56422b9471733fafc5d391cfb9e0766fd7fe661 Parents: 97e40ba Author: Raphael Ouazana <raphael.ouaz...@linagora.com> Authored: Thu Apr 6 17:58:43 2017 +0200 Committer: benwa <btell...@linagora.com> Committed: Tue Apr 11 07:53:48 2017 +0700 ---------------------------------------------------------------------- mailet/api/pom.xml | 15 ++ .../src/main/java/org/apache/mailet/Mail.java | 19 ++ .../org/apache/mailet/PerRecipientHeaders.java | 153 +++++++++++++++ .../org/apache/mailet/base/test/FakeMail.java | 14 ++ .../java/org/apache/james/core/MailImpl.java | 17 ++ .../mailets/delivery/MailDispatcher.java | 59 ++++-- .../mailets/delivery/MailDispatcherTest.java | 190 +++++++++++++++++-- .../james/smtpserver/mock/mailet/MockMail.java | 13 ++ 8 files changed, 447 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/mailet/api/pom.xml ---------------------------------------------------------------------- diff --git a/mailet/api/pom.xml b/mailet/api/pom.xml index 69d5d85..7aa9ae0 100644 --- a/mailet/api/pom.xml +++ b/mailet/api/pom.xml @@ -37,6 +37,10 @@ <dependencies> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> </dependency> @@ -46,6 +50,17 @@ <scope>test</scope> </dependency> <dependency> + <groupId>nl.jqno.equalsverifier</groupId> + <artifactId>equalsverifier</artifactId> + <version>1.7.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/mailet/api/src/main/java/org/apache/mailet/Mail.java ---------------------------------------------------------------------- diff --git a/mailet/api/src/main/java/org/apache/mailet/Mail.java b/mailet/api/src/main/java/org/apache/mailet/Mail.java index 5712aea..6a25430 100644 --- a/mailet/api/src/main/java/org/apache/mailet/Mail.java +++ b/mailet/api/src/main/java/org/apache/mailet/Mail.java @@ -21,6 +21,9 @@ package org.apache.mailet; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; + +import org.apache.mailet.PerRecipientHeaders.Header; + import java.io.Serializable; import java.util.Collection; import java.util.Date; @@ -234,6 +237,22 @@ public interface Mail extends Serializable, Cloneable { Serializable setAttribute(String name, Serializable object); /** + * Store a header (and its specific values) for a recipient + * This header will be stored only for this recipient at delivery time + * + * Note that the headers must contain only US-ASCII characters, so a header that + * contains non US-ASCII characters must have been encoded by the + * caller as per the rules of RFC 2047. + */ + void addSpecificHeaderForRecipient(Header header, MailAddress recipient); + + /** + * Get the currently stored association between recipients and + * specific headers + */ + PerRecipientHeaders getPerRecipientSpecificHeaders(); + + /** * Returns the message size (including headers). * <p> * This is intended as a guide suitable for processing heuristics, and not http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/mailet/api/src/main/java/org/apache/mailet/PerRecipientHeaders.java ---------------------------------------------------------------------- diff --git a/mailet/api/src/main/java/org/apache/mailet/PerRecipientHeaders.java b/mailet/api/src/main/java/org/apache/mailet/PerRecipientHeaders.java new file mode 100644 index 0000000..791c2ed --- /dev/null +++ b/mailet/api/src/main/java/org/apache/mailet/PerRecipientHeaders.java @@ -0,0 +1,153 @@ +/**************************************************************** + * 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.mailet; +import java.util.Collection; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Multimap; + +public class PerRecipientHeaders { + public static final Function<Header, String> GET_HEADER_NAME = new Function<Header, String>() { + @Override + public String apply(Header input) { + return input.getName(); + } + }; + + private Multimap<MailAddress, Header> headersByRecipient; + + public PerRecipientHeaders() { + headersByRecipient = ArrayListMultimap.create(); + } + + public Collection<MailAddress> getRecipientsWithSpecificHeaders() { + return headersByRecipient.keySet(); + } + + public Collection<Header> getHeadersForRecipient(MailAddress recipient) { + return headersByRecipient.get(recipient); + } + + public Collection<String> getHeaderNamesForRecipient(MailAddress recipient) { + return FluentIterable.from(headersByRecipient.get(recipient)) + .transform(GET_HEADER_NAME) + .toSet(); + } + + public void addHeaderForRecipient(Header header, MailAddress recipient) { + headersByRecipient.put(recipient, header); + } + + public static class Header { + private final String name; + private final String value; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String name; + private String value; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder value(String value) { + this.value = value; + return this; + } + + public Header build() { + Preconditions.checkNotNull(name); + Preconditions.checkNotNull(value); + return new Header(name, value); + } + } + + @VisibleForTesting + Header(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof Header) { + Header that = (Header) o; + + return Objects.equal(this.name, that.name) + && Objects.equal(this.value, that.value); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hashCode(name, value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("value", value) + .toString(); + } + } + + @Override + public final boolean equals(Object o) { + if (o instanceof PerRecipientHeaders) { + PerRecipientHeaders that = (PerRecipientHeaders) o; + + return Objects.equal(this.headersByRecipient, that.headersByRecipient); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hashCode(headersByRecipient); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("headersByRecipient", headersByRecipient) + .toString(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java ---------------------------------------------------------------------- diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java index 4789224..7c462c4 100644 --- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java +++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java @@ -37,6 +37,8 @@ import javax.mail.internet.MimeMessage; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; +import org.apache.mailet.PerRecipientHeaders; +import org.apache.mailet.PerRecipientHeaders.Header; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; @@ -216,6 +218,7 @@ public class FakeMail implements Mail { private Map<String, Serializable> attributes; private long size; private String remoteAddr; + private PerRecipientHeaders perRecipientHeaders; public FakeMail(MimeMessage msg, List<MailAddress> recipients, String name, MailAddress sender, String state, String errorMessage, Date lastUpdated, Map<String, Serializable> attributes, long size, String remoteAddr) { @@ -229,6 +232,7 @@ public class FakeMail implements Mail { this.attributes = attributes; this.size = size; this.remoteAddr = remoteAddr; + this.perRecipientHeaders = new PerRecipientHeaders(); } @Override @@ -392,4 +396,14 @@ public class FakeMail implements Mail { .add("remoteAddr", remoteAddr) .toString(); } + + @Override + public PerRecipientHeaders getPerRecipientSpecificHeaders() { + return perRecipientHeaders; + } + + @Override + public void addSpecificHeaderForRecipient(Header header, MailAddress recipient) { + perRecipientHeaders.addHeaderForRecipient(header, recipient); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/server/container/core/src/main/java/org/apache/james/core/MailImpl.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/core/MailImpl.java index ebe451c..ca94a9d 100644 --- a/server/container/core/src/main/java/org/apache/james/core/MailImpl.java +++ b/server/container/core/src/main/java/org/apache/james/core/MailImpl.java @@ -23,6 +23,8 @@ import org.apache.james.lifecycle.api.Disposable; import org.apache.james.lifecycle.api.LifecycleUtil; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; +import org.apache.mailet.PerRecipientHeaders; +import org.apache.mailet.PerRecipientHeaders.Header; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; @@ -110,6 +112,11 @@ public class MailImpl implements Disposable, Mail { * Attributes added to this MailImpl instance */ private Map<String, Object> attributes; + /** + * Specific headers for some recipients + * These headers will be added at delivery time + */ + private PerRecipientHeaders perRecipientSpecificHeaders; /** * A constructor that creates a new, uninitialized MailImpl @@ -117,6 +124,7 @@ public class MailImpl implements Disposable, Mail { public MailImpl() { setState(Mail.DEFAULT); attributes = new HashMap<String, Object>(); + perRecipientSpecificHeaders = new PerRecipientHeaders(); } /** @@ -670,4 +678,13 @@ public class MailImpl implements Disposable, Mail { return "Mail" + System.currentTimeMillis() + "-" + UUID.randomUUID(); } + @Override + public PerRecipientHeaders getPerRecipientSpecificHeaders() { + return perRecipientSpecificHeaders; + } + + @Override + public void addSpecificHeaderForRecipient(Header header, MailAddress recipient) { + perRecipientSpecificHeaders.addHeaderForRecipient(header, recipient); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java index 0dc03c3..a6f48a5 100644 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java +++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java @@ -18,9 +18,9 @@ ****************************************************************/ package org.apache.james.transport.mailets.delivery; -import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Vector; import javax.mail.MessagingException; @@ -30,10 +30,14 @@ import org.apache.commons.logging.Log; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; import org.apache.mailet.MailetContext; +import org.apache.mailet.PerRecipientHeaders.Header; import org.apache.mailet.base.RFC2822Headers; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; public class MailDispatcher { @@ -116,33 +120,21 @@ public class MailDispatcher { // This only works because there is a placeholder inserted by MimeMessageWrapper message.setHeader(RFC2822Headers.RETURN_PATH, DeliveryUtils.prettyPrint(mail.getSender())); - List<String> deliveredToHeader = removeDeliveryHeaders(message); Collection<MailAddress> errors = deliver(mail, message); - putDeliveryHeadersBack(message, deliveredToHeader); return errors; } - private List<String> removeDeliveryHeaders(MimeMessage message) throws MessagingException { - List<String> deliveredToHeader = Arrays.asList(Optional.fromNullable(message.getHeader(DELIVERED_TO)).or(NO_HEADERS)); - message.removeHeader(DELIVERED_TO); - return deliveredToHeader; - } - - private void putDeliveryHeadersBack(MimeMessage message, List<String> deliveredToHeader) throws MessagingException { - for (String deliveredTo : deliveredToHeader) { - message.addHeader(DELIVERED_TO, deliveredTo); - } - } - private Collection<MailAddress> deliver(Mail mail, MimeMessage message) { Collection<MailAddress> errors = new Vector<MailAddress>(); for (MailAddress recipient : mail.getRecipients()) { try { - // Add qmail's de facto standard Delivered-To header - message.addHeader(DELIVERED_TO, recipient.toString()); + Map<String, List<String>> savedHeaders = saveHeaders(mail, recipient); + + addSpecificHeadersForRecipient(mail, message, recipient); mailStore.storeMail(recipient, mail); - message.removeHeader(DELIVERED_TO); + + restoreHeaders(mail.getMessage(), savedHeaders); } catch (Exception ex) { log.error("Error while storing mail.", ex); errors.add(recipient); @@ -150,4 +142,35 @@ public class MailDispatcher { } return errors; } + + private Map<String, List<String>> saveHeaders(Mail mail, MailAddress recipient) throws MessagingException { + ImmutableMap.Builder<String, List<String>> backup = ImmutableMap.builder(); + Collection<String> headersForRecipient = mail.getPerRecipientSpecificHeaders().getHeaderNamesForRecipient(recipient); + Iterable<String> headersToSave = Iterables.concat(headersForRecipient, ImmutableList.of(DELIVERED_TO)); + for (String headerName: headersToSave) { + List<String> values = ImmutableList.copyOf( + Optional.fromNullable(mail.getMessage().getHeader(headerName)) + .or(NO_HEADERS)); + backup.put(headerName, values); + } + return backup.build(); + } + + private void restoreHeaders(MimeMessage mimeMessage, Map<String, List<String>> savedHeaders) throws MessagingException { + for (Map.Entry<String, List<String>> header: savedHeaders.entrySet()) { + String name = header.getKey(); + mimeMessage.removeHeader(name); + for (String value: header.getValue()) { + mimeMessage.addHeader(name, value); + } + } + } + + private void addSpecificHeadersForRecipient(Mail mail, MimeMessage message, MailAddress recipient) throws MessagingException { + for (Header header: mail.getPerRecipientSpecificHeaders().getHeadersForRecipient(recipient)) { + message.addHeader(header.getName(), header.getValue()); + } + // Add qmail's de facto standard Delivered-To header + message.addHeader(DELIVERED_TO, recipient.toString()); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailDispatcherTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailDispatcherTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailDispatcherTest.java index 9fe8f69..73cca4e 100644 --- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailDispatcherTest.java +++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailDispatcherTest.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import javax.mail.MessagingException; @@ -36,6 +36,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; +import org.apache.mailet.PerRecipientHeaders.Header; import org.apache.mailet.base.MailAddressFixture; import org.apache.mailet.base.RFC2822Headers; import org.apache.mailet.base.test.FakeMail; @@ -46,8 +47,15 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import com.google.common.base.Charsets; +import com.google.common.collect.ArrayListMultimap; public class MailDispatcherTest { + private static final String TEST_HEADER_NAME = "X-HEADER"; + private static final String VALUE_FOR_USER_1 = "value for user 1"; + private static final String VALUE_FOR_USER_2 = "value for user 2"; + private static final Header TEST_HEADER_USER1 = Header.builder().name(TEST_HEADER_NAME).value(VALUE_FOR_USER_1).build(); + private static final Header TEST_HEADER_USER2 = Header.builder().name(TEST_HEADER_NAME).value(VALUE_FOR_USER_2).build(); + private FakeMailContext fakeMailContext; private MailStore mailStore; @@ -176,7 +184,7 @@ public class MailDispatcherTest { verify(mailStore).storeMail(any(MailAddress.class), mailCaptor.capture()); assertThat(mailCaptor.getValue().getMessage().getHeader(RFC2822Headers.RETURN_PATH)) - .containsExactly("<" + MailAddressFixture.OTHER_AT_JAMES +">"); + .containsOnly("<" + MailAddressFixture.OTHER_AT_JAMES +">"); } @Test @@ -202,16 +210,16 @@ public class MailDispatcherTest { .build(); testee.dispatch(mail); - assertThat(mimeMessage.getHeader(MailDispatcher.DELIVERED_TO)).containsExactly(delivered_to_1, delivered_to_2); + assertThat(mimeMessage.getHeader(MailDispatcher.DELIVERED_TO)).containsOnly(delivered_to_1, delivered_to_2); } @Test public void dispatchShouldCustomizeDeliveredToHeader() throws Exception { - AccumulatorDeliveredToHeaderMailStore accumulator = new AccumulatorDeliveredToHeaderMailStore(); + AccumulatorHeaderMailStore accumulatorDeliveredToHeaderMailStore = new AccumulatorHeaderMailStore(MailDispatcher.DELIVERED_TO); MailDispatcher testee = MailDispatcher.builder() .log(mock(Log.class)) .mailetContext(fakeMailContext) - .mailStore(accumulator) + .mailStore(accumulatorDeliveredToHeaderMailStore) .consume(false) .build(); @@ -223,25 +231,177 @@ public class MailDispatcherTest { .build(); testee.dispatch(mail); - assertThat(accumulator.getDeliveredToHeaderValues()) - .containsExactly(new String[]{MailAddressFixture.ANY_AT_JAMES.toString()}, - new String[]{MailAddressFixture.ANY_AT_JAMES2.toString()}); + assertThat(accumulatorDeliveredToHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES)) + .containsOnly(new String[]{MailAddressFixture.ANY_AT_JAMES.toString()}); + assertThat(accumulatorDeliveredToHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES2)) + .containsOnly(new String[]{MailAddressFixture.ANY_AT_JAMES2.toString()}); } - public static class AccumulatorDeliveredToHeaderMailStore implements MailStore { - public final List<String[]> deliveredToHeaderValues; + @Test + public void dispatchShouldNotAddSpecificHeaderIfRecipientDoesNotMatch() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.defaultMimeMessage()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER2, MailAddressFixture.ANY_AT_JAMES2); + testee.dispatch(mail); + + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES)) + .isEmpty(); + } + + @Test + public void dispatchShouldAddSpecificHeaderIfRecipientMatches() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.defaultMimeMessage()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER1, MailAddressFixture.ANY_AT_JAMES); + testee.dispatch(mail); + + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES)) + .containsOnly(new String[]{VALUE_FOR_USER_1}); + } + + @Test + public void dispatchShouldNotAddSpecificHeaderToOtherRecipients() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2) + .mimeMessage(MimeMessageBuilder.defaultMimeMessage()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER1, MailAddressFixture.ANY_AT_JAMES); + testee.dispatch(mail); + + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES)) + .containsOnly(new String[]{VALUE_FOR_USER_1}); + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES2)) + .isEmpty(); + } + + @Test + public void dispatchShouldAddSpecificHeaderToEachRecipients() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2) + .mimeMessage(MimeMessageBuilder.defaultMimeMessage()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER1, MailAddressFixture.ANY_AT_JAMES); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER2, MailAddressFixture.ANY_AT_JAMES2); + testee.dispatch(mail); + + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES)) + .containsOnly(new String[]{VALUE_FOR_USER_1}); + assertThat(accumulatorTestHeaderMailStore.getHeaderValues(MailAddressFixture.ANY_AT_JAMES2)) + .containsOnly(new String[]{VALUE_FOR_USER_2}); + } + + @Test + public void dispatchShouldNotAlterOriginalMessageWhenPerRecipientHeaderDoesNotExist() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2) + .mimeMessage(MimeMessageBuilder.defaultMimeMessage()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER1, MailAddressFixture.ANY_AT_JAMES); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER2, MailAddressFixture.ANY_AT_JAMES2); + testee.dispatch(mail); + + assertThat(mail.getMessage().getHeader(TEST_HEADER_NAME)).isNull(); + } + + @Test + public void dispatchShouldNotAlterOriginalMessageWhenPerRecipientHeaderExists() throws Exception { + AccumulatorHeaderMailStore accumulatorTestHeaderMailStore = new AccumulatorHeaderMailStore(TEST_HEADER_NAME); + MailDispatcher testee = MailDispatcher.builder() + .log(mock(Log.class)) + .mailetContext(fakeMailContext) + .mailStore(accumulatorTestHeaderMailStore) + .consume(false) + .build(); + + String headerValue = "arbitraryValue"; + FakeMail mail = FakeMail.builder() + .sender(MailAddressFixture.OTHER_AT_JAMES) + .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addHeader(TEST_HEADER_NAME, headerValue) + .build()) + .state("state") + .build(); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER1, MailAddressFixture.ANY_AT_JAMES); + mail.addSpecificHeaderForRecipient(TEST_HEADER_USER2, MailAddressFixture.ANY_AT_JAMES2); + testee.dispatch(mail); + + assertThat(mail.getMessage().getHeader(TEST_HEADER_NAME)).containsOnly(headerValue); + } + + public static class AccumulatorHeaderMailStore implements MailStore { + private final ArrayListMultimap<MailAddress, String[]> headerValues; + private final String headerName; - public AccumulatorDeliveredToHeaderMailStore() { - this.deliveredToHeaderValues = new ArrayList<String[]>(); + public AccumulatorHeaderMailStore(String headerName) { + this.headerName = headerName; + this.headerValues = ArrayListMultimap.create(); } @Override public void storeMail(MailAddress recipient, Mail mail) throws MessagingException { - deliveredToHeaderValues.add(mail.getMessage().getHeader(MailDispatcher.DELIVERED_TO)); + String[] header = mail.getMessage().getHeader(headerName); + if (header != null) { + headerValues.put(recipient, header); + } } - public List<String[]> getDeliveredToHeaderValues() { - return deliveredToHeaderValues; + public Collection<String[]> getHeaderValues(MailAddress recipient) { + return headerValues.get(recipient); } } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c56422b9/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/mock/mailet/MockMail.java ---------------------------------------------------------------------- diff --git a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/mock/mailet/MockMail.java b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/mock/mailet/MockMail.java index a7f6a16..07a8574 100644 --- a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/mock/mailet/MockMail.java +++ b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/mock/mailet/MockMail.java @@ -24,8 +24,11 @@ import java.util.*; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import org.apache.commons.lang.NotImplementedException; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; +import org.apache.mailet.PerRecipientHeaders; +import org.apache.mailet.PerRecipientHeaders.Header; public class MockMail implements Mail { @@ -160,4 +163,14 @@ public class MockMail implements Mail { public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; } + + @Override + public PerRecipientHeaders getPerRecipientSpecificHeaders() { + throw new NotImplementedException(); + } + + @Override + public void addSpecificHeaderForRecipient(Header header, MailAddress recipient) { + throw new NotImplementedException(); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org