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

Reply via email to