Repository: camel
Updated Branches:
  refs/heads/master f8c140f01 -> 5eed4d966


CAMEL-7873 Add capability to sort received messages

Add copyright headers


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1149fae7
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1149fae7
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1149fae7

Branch: refs/heads/master
Commit: 1149fae798de25ec43b330d41b11db85273c3765
Parents: f8c140f
Author: Christian Amann <christian.am...@qaware.de>
Authored: Wed Sep 24 13:15:43 2014 +0200
Committer: Willem Jiang <willem.ji...@gmail.com>
Committed: Mon Oct 20 11:34:06 2014 +0800

----------------------------------------------------------------------
 components/camel-mail/pom.xml                   |   5 +
 .../camel/component/mail/MailConsumer.java      |  86 +++++++--
 .../camel/component/mail/MailEndpoint.java      |  36 ++--
 .../apache/camel/component/mail/MailSorter.java | 174 +++++++++++++++++++
 .../camel/component/mail/MailSortTermTest.java  | 121 +++++++++++++
 .../camel/component/mail/MailSorterTest.java    | 154 ++++++++++++++++
 6 files changed, 552 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-mail/pom.xml b/components/camel-mail/pom.xml
index 50cc9fa..4888f4d 100644
--- a/components/camel-mail/pom.xml
+++ b/components/camel-mail/pom.xml
@@ -88,6 +88,11 @@
             <artifactId>slf4j-log4j12</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
index 065a4ef..29835aa 100644
--- 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java
@@ -27,7 +27,11 @@ import javax.mail.Header;
 import javax.mail.Message;
 import javax.mail.MessagingException;
 import javax.mail.Store;
+import javax.mail.search.SearchTerm;
 
+import com.sun.mail.imap.IMAPFolder;
+import com.sun.mail.imap.IMAPStore;
+import com.sun.mail.imap.SortTerm;
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.impl.ScheduledBatchPollingConsumer;
@@ -51,6 +55,11 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
     private Folder folder;
     private Store store;
 
+    /**
+     * Is true if server is an IMAP server and supports IMAP SORT extension.
+     */
+    private boolean serverCanSort;
+
     public MailConsumer(MailEndpoint endpoint, Processor processor, 
JavaMailSender sender) {
         super(endpoint, processor);
         this.sender = sender;
@@ -103,18 +112,7 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
         try {
             int count = folder.getMessageCount();
             if (count > 0) {
-                Message[] messages;
-
-                if (getEndpoint().getSearchTerm() != null) {
-                    // use custom search term
-                    messages = folder.search(getEndpoint().getSearchTerm());
-                } else if (getEndpoint().getConfiguration().isUnseen()) {
-                    // only unseen messages
-                    messages = folder.search(new 
SearchTermBuilder().unseen().build());
-                } else {
-                    // get all messages
-                    messages = folder.getMessages();
-                }
+                Message[] messages = retrieveMessages();
 
                 polledMessages = 
processBatch(CastUtils.cast(createExchanges(messages)));
             } else if (count == -1) {
@@ -229,6 +227,51 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
         }
     }
 
+    /**
+     * @return Messages from input folder according to the search and sort 
criteria stored in the endpoint
+     * @throws MessagingException If message retrieval fails
+     */
+    private Message[] retrieveMessages() throws MessagingException {
+        Message[] messages;
+        final SortTerm[] sortTerm = getEndpoint().getSortTerm();
+        final SearchTerm searchTerm = computeSearchTerm();
+        if (sortTerm != null && serverCanSort) {
+            final IMAPFolder imapFolder = (IMAPFolder) folder;
+            if (searchTerm != null) {
+                // Sort and search using server capability
+                messages = imapFolder.getSortedMessages(sortTerm, searchTerm);
+            } else {
+                // Only sort using server capability
+                messages = imapFolder.getSortedMessages(sortTerm);
+            }
+        } else {
+            if (searchTerm != null) {
+                // Only search
+                messages = folder.search(searchTerm);
+            } else {
+                // No search
+                messages = folder.getMessages();
+            }
+            // Now we can sort (emulate email sort but restrict sort terms)
+            if (sortTerm != null) {
+                MailSorter.sortMessages(messages, sortTerm);
+            }
+        }
+        return messages;
+    }
+
+    /**
+     * @return Search term from endpoint (including "seen" check) or null if 
there is no search term
+     */
+    private SearchTerm computeSearchTerm() {
+        if (getEndpoint().getSearchTerm() != null) {
+            return getEndpoint().getSearchTerm();
+        } else if (getEndpoint().getConfiguration().isUnseen()) {
+            return new SearchTermBuilder().unseen().build();
+        }
+        return null;
+    }
+
     protected Queue<Exchange> createExchanges(Message[] messages) throws 
MessagingException {
         Queue<Exchange> answer = new LinkedList<Exchange>();
 
@@ -423,6 +466,8 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
             }
             store = sender.getSession().getStore(config.getProtocol());
             store.connect(config.getHost(), config.getPort(), 
config.getUsername(), config.getPassword());
+
+            serverCanSort = hasSortCapability(store);
         }
 
         if (folder == null) {
@@ -436,6 +481,23 @@ public class MailConsumer extends 
ScheduledBatchPollingConsumer {
         }
     }
 
+    /**
+     * Check whether the email store has the sort capability or not.
+     *
+     * @param store Email store
+     * @return true if the store is an IMAP store and it has the store 
capability
+     * @throws MessagingException In case capability check fails
+     */
+    private static boolean hasSortCapability(Store store) throws 
MessagingException {
+        if (store instanceof IMAPStore) {
+            IMAPStore imapStore = (IMAPStore) store;
+            if (imapStore.hasCapability("SORT*")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public MailEndpoint getEndpoint() {
         return (MailEndpoint) super.getEndpoint();

http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
index e1345ac..a875612 100644
--- 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java
@@ -16,14 +16,8 @@
  */
 package org.apache.camel.component.mail;
 
-import javax.mail.Message;
-import javax.mail.search.SearchTerm;
-
-import org.apache.camel.Consumer;
-import org.apache.camel.Exchange;
-import org.apache.camel.ExchangePattern;
-import org.apache.camel.Processor;
-import org.apache.camel.Producer;
+import com.sun.mail.imap.SortTerm;
+import org.apache.camel.*;
 import org.apache.camel.impl.DefaultExchange;
 import org.apache.camel.impl.DefaultHeaderFilterStrategy;
 import org.apache.camel.impl.ScheduledPollEndpoint;
@@ -31,10 +25,11 @@ import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 
+import javax.mail.Message;
+import javax.mail.search.SearchTerm;
+
 /**
  * Endpoint for Camel Mail.
- *
- * @version 
  */
 @UriEndpoint(scheme = "mail", consumerClass = MailConsumer.class)
 public class MailEndpoint extends ScheduledPollEndpoint {
@@ -46,6 +41,7 @@ public class MailEndpoint extends ScheduledPollEndpoint {
     @UriParam
     private int maxMessagesPerPoll;
     private SearchTerm searchTerm;
+    private SortTerm[] sortTerm;
 
     public MailEndpoint() {
     }
@@ -84,7 +80,7 @@ public class MailEndpoint extends ScheduledPollEndpoint {
     public Consumer createConsumer(Processor processor) throws Exception {
         if (configuration.getProtocol().startsWith("smtp")) {
             throw new IllegalArgumentException("Protocol " + 
configuration.getProtocol()
-                + " cannot be used for a MailConsumer. Please use another 
protocol such as pop3 or imap.");
+                    + " cannot be used for a MailConsumer. Please use another 
protocol such as pop3 or imap.");
         }
 
         // must use java mail sender impl as we need to get hold of a mail 
session
@@ -111,7 +107,7 @@ public class MailEndpoint extends ScheduledPollEndpoint {
     public boolean isSingleton() {
         return false;
     }
-    
+
     @Override
     public Exchange createExchange(ExchangePattern pattern) {
         return createExchange(pattern, null);
@@ -185,4 +181,20 @@ public class MailEndpoint extends ScheduledPollEndpoint {
         this.searchTerm = searchTerm;
     }
 
+    /**
+     * @return Sorting order for messages. Only natively supported for IMAP. 
Emulated to some degree when using POP3
+     * or when IMAP server does not have the SORT capability.
+     * @see com.sun.mail.imap.SortTerm
+     */
+    public SortTerm[] getSortTerm() {
+        return sortTerm == null ? null : sortTerm.clone();
+    }
+
+    /**
+     * @param sortTerm {@link #getSortTerm()}
+     */
+    public void setSortTerm(SortTerm[] sortTerm) {
+        this.sortTerm = (sortTerm == null ? null : sortTerm.clone());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java
----------------------------------------------------------------------
diff --git 
a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java
 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java
new file mode 100644
index 0000000..cdfb22c
--- /dev/null
+++ 
b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java
@@ -0,0 +1,174 @@
+/**
+ * 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.camel.component.mail;
+
+import com.sun.mail.imap.SortTerm;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+import java.util.*;
+
+/**
+ * Utility class for sorting of mail messages
+ */
+public final class MailSorter {
+    /**
+     * No instances
+     */
+    private MailSorter() {
+    }
+
+    /**
+     * Sort the messages. This emulates sorting the messages on the server if 
the server doesn't have the sorting
+     * capability. See RFC 5256
+     * Does not support complex sorting like in the RFC (with Base Subject or 
other similar stuff), just simple
+     * comparisons.
+     *
+     * @param messages Messages to sort. Are sorted in place
+     * @param sortTerm Sort term
+     */
+    public static void sortMessages(Message[] messages, final SortTerm[] 
sortTerm) {
+        final List<SortTermWithDescending> sortTermsWithDescending = 
getSortTermsWithDescending(sortTerm);
+        sortMessages(messages, sortTermsWithDescending);
+    }
+
+    /**
+     * Compute the potentially descendingd sort terms from the input list
+     *
+     * @param sortTerm Input list
+     * @return Sort terms list including if the respective sort should be 
sorted in descending order
+     */
+    private static List<SortTermWithDescending> 
getSortTermsWithDescending(SortTerm[] sortTerm) {
+        // List of reversable sort terms. If the boolean is true the 
respective sort term is descendingd
+        final List<SortTermWithDescending> sortTermsWithDescending = new 
ArrayList<>(sortTerm.length);
+        // Descending next item in input because the last item was a 
"descending"
+        boolean descendingNext = false;
+        for (SortTerm term : sortTerm) {
+            if (term.equals(SortTerm.REVERSE)) {
+                if (descendingNext) {
+                    throw new IllegalArgumentException("Double reverse in sort 
term is not allowed");
+                }
+                descendingNext = true;
+            } else {
+                sortTermsWithDescending.add(new SortTermWithDescending(term, 
descendingNext));
+                descendingNext = false;
+            }
+        }
+        return sortTermsWithDescending;
+    }
+
+    /**
+     * Sort messages using the list of properties
+     *
+     * @param messages             Messages to sort. Are sorted in place
+     * @param sortTermsWithDescending Sort terms list including if the 
respective sort should be sorted in descending order
+     */
+    private static void sortMessages(Message[] messages, final 
List<SortTermWithDescending> sortTermsWithDescending) {
+        Arrays.sort(messages, new Comparator<Message>() {
+            @Override
+            public int compare(Message m1, Message m2) {
+                try {
+                    for (SortTermWithDescending reversableTerm : 
sortTermsWithDescending) {
+                        int comparison = compareMessageProperty(m1, m2, 
reversableTerm.getTerm());
+                        // Descending
+                        if (reversableTerm.isDescending()) {
+                            comparison = -comparison;
+                        }
+                        // Abort on first non-equal
+                        if (comparison != 0) {
+                            return comparison;
+                        }
+                    }
+                    // Equal
+                    return 0;
+                } catch (MessagingException e) {
+                    throw new IllegalArgumentException(e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Compare the value of the property of the two messages.
+     *
+     * @param msg1     Message 1
+     * @param msg2     Message 2
+     * @param property Property to compare
+     * @return msg1.property.compareTo(msg2.property)
+     * @throws javax.mail.MessagingException If message data could not be read.
+     */
+    private static int compareMessageProperty(Message msg1, Message msg2, 
SortTerm property) throws MessagingException {
+        if (property.equals(SortTerm.TO)) {
+            InternetAddress addr1 = (InternetAddress) 
msg1.getRecipients(Message.RecipientType.TO)[0];
+            InternetAddress addr2 = (InternetAddress) 
msg2.getRecipients(Message.RecipientType.TO)[0];
+            return addr1.getAddress().compareTo(addr2.getAddress());
+        } else if (property.equals(SortTerm.CC)) {
+            InternetAddress addr1 = (InternetAddress) 
msg1.getRecipients(Message.RecipientType.CC)[0];
+            InternetAddress addr2 = (InternetAddress) 
msg2.getRecipients(Message.RecipientType.CC)[0];
+            return addr1.getAddress().compareTo(addr2.getAddress());
+        } else if (property.equals(SortTerm.FROM)) {
+            InternetAddress addr1 = (InternetAddress) msg1.getFrom()[0];
+            InternetAddress addr2 = (InternetAddress) msg2.getFrom()[0];
+            return addr1.getAddress().compareTo(addr2.getAddress());
+        } else if (property.equals(SortTerm.ARRIVAL)) {
+            Date arr1 = msg1.getReceivedDate();
+            Date arr2 = msg2.getReceivedDate();
+            return arr1.compareTo(arr2);
+        } else if (property.equals(SortTerm.DATE)) {
+            Date sent1 = msg1.getSentDate();
+            Date sent2 = msg2.getSentDate();
+            return sent1.compareTo(sent2);
+        } else if (property.equals(SortTerm.SIZE)) {
+            int size1 = msg1.getSize();
+            int size2 = msg2.getSize();
+            return Integer.compare(size1, size2);
+        } else if (property.equals(SortTerm.SUBJECT)) {
+            String sub1 = msg1.getSubject();
+            String sub2 = msg2.getSubject();
+            return sub1.compareTo(sub2);
+        }
+        throw new IllegalArgumentException(String.format("Unknown sort term: 
%s", property.toString()));
+    }
+
+    /**
+     * A sort term with a bit indicating if sorting should be descending for 
this term
+     */
+    private static final class SortTermWithDescending {
+        private SortTerm term;
+        private boolean descending;
+
+        private SortTermWithDescending(SortTerm term, boolean descending) {
+            this.term = term;
+            this.descending = descending;
+        }
+
+        /**
+         * @return Actual search term
+         */
+        public SortTerm getTerm() {
+            return term;
+        }
+
+        /**
+         * @return true if sorting should be descending, false if it should be 
ascending
+         */
+        public boolean isDescending() {
+            return descending;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java
 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java
new file mode 100644
index 0000000..40fb2f5
--- /dev/null
+++ 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java
@@ -0,0 +1,121 @@
+/**
+ * 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.camel.component.mail;
+
+import com.sun.mail.imap.SortTerm;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+import org.jvnet.mock_javamail.Mailbox;
+
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.Store;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.search.SearchTerm;
+
+import java.util.Date;
+
+import static org.apache.camel.component.mail.SearchTermBuilder.Op;
+
+/**
+ * This is a test that checks integration of the sort term in camel. The 
actual sorting logic is tested in the
+ * SortUtilTest.
+ */
+public class MailSortTermTest extends CamelTestSupport {
+
+    @Override
+    public void setUp() throws Exception {
+        prepareMailbox();
+        super.setUp();
+    }
+
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry jndi = super.createRegistry();
+        jndi.bind("sortAscendingDate", new SortTerm[]{SortTerm.DATE});
+        jndi.bind("sortDescendingDate", new SortTerm[]{SortTerm.REVERSE, 
SortTerm.DATE});
+        jndi.bind("searchTerm", new 
SearchTermBuilder().subject("Camel").build());
+        return jndi;
+    }
+
+    @Test
+    public void testSortTerm() throws Exception {
+        Mailbox mailbox = Mailbox.get("bill@localhost");
+        assertEquals(3, mailbox.size());
+
+        // This one has search term *not* set
+        MockEndpoint mockAsc = getMockEndpoint("mock:resultAscending");
+        mockAsc.expectedBodiesReceived("Earlier date", "Later date");
+
+        // This one has search term set
+        MockEndpoint mockDesc = getMockEndpoint("mock:resultDescending");
+        mockDesc.expectedBodiesReceived("Even later date", "Later date", 
"Earlier date");
+
+        // This one has search term set
+        MockEndpoint mockDescImap = 
getMockEndpoint("mock:resultDescendingImap");
+        mockDescImap.expectedBodiesReceived("Even later date", "Later date", 
"Earlier date");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    private void prepareMailbox() throws Exception {
+        // connect to mailbox
+        Mailbox.clearAll();
+        JavaMailSender sender = new DefaultJavaMailSender();
+        Store store = sender.getSession().getStore("pop3");
+        store.connect("localhost", 25, "bill", "secret");
+        Folder folder = store.getFolder("INBOX");
+        folder.open(Folder.READ_WRITE);
+        folder.expunge();
+
+        // inserts 3 messages, one with earlier, one with later sent date and 
one with invalid subject (not returned in search)
+        Message[] messages = new Message[3];
+        messages[0] = new MimeMessage(sender.getSession());
+        messages[0].setText("Earlier date");
+        messages[0].setSentDate(new Date(10000));
+        messages[0].setSubject("Camel");
+
+        messages[1] = new MimeMessage(sender.getSession());
+        messages[1].setText("Later date");
+        messages[1].setSentDate(new Date(20000));
+        messages[1].setSubject("Camel");
+
+        messages[2] = new MimeMessage(sender.getSession());
+        messages[2].setText("Even later date");
+        messages[2].setSentDate(new Date(30000));
+        messages[2].setSubject("Invalid");
+
+        folder.appendMessages(messages);
+        folder.close(true);
+    }
+
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        // Two tests also in conjunction with search since mail consumer does 
sort and search together
+        return new RouteBuilder() {
+            public void configure() throws Exception {
+                
from("pop3://bill@localhost?password=secret&searchTerm=#searchTerm&sortTerm=#sortAscendingDate").to("mock:resultAscending");
+                
from("pop3://bill@localhost?password=secret&sortTerm=#sortDescendingDate").to("mock:resultDescending");
+                
from("imap://bill@localhost?password=secret&sortTerm=#sortDescendingDate").to("mock:resultDescendingImap");
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java
 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java
new file mode 100644
index 0000000..a2a0b52
--- /dev/null
+++ 
b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java
@@ -0,0 +1,154 @@
+/**
+ * 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.camel.component.mail;
+
+import com.sun.mail.imap.SortTerm;
+import org.apache.camel.component.mail.MailSorter;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+import java.util.Date;
+
+import static org.mockito.Mockito.when;
+
+
+/**
+ * Tests mail sort util
+ */
+public class MailSorterTest extends CamelTestSupport {
+    private final static Message[] messages = new Message[3];
+    private final static Message tieBreaker;
+
+    /**
+     * All possible sort terms
+     */
+    private final static SortTerm[] possibleTerms = new SortTerm[]{
+            SortTerm.ARRIVAL,
+            SortTerm.CC,
+            SortTerm.DATE,
+            SortTerm.FROM,
+            SortTerm.SIZE,
+            SortTerm.TO,
+            SortTerm.SUBJECT
+    };
+
+    static {
+        try {
+            messages[0] = createMessage("to1", "cc1", "from1", new Date(1), 
new Date(1001), 1, "subject1");
+            messages[1] = createMessage("to2", "cc2", "from2", new Date(2), 
new Date(1002), 2, "subject2");
+            messages[2] = createMessage("to3", "cc3", "from3", new Date(3), 
new Date(1003), 3, "subject3");
+            // Message that creates a tie on all fields except for one
+            tieBreaker = createMessage("to3", "cc3", "from3", new Date(3), new 
Date(1003), 3, "subject0TieBreaker");
+        } catch (MessagingException e) {
+            // Rethrow as unchecked. Can not occur anyways
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Create a new message with the specified data
+     */
+    private static Message createMessage(String to, String cc, String from, 
Date received, Date sent, int size, String subject)
+            throws MessagingException {
+        final Message msg = Mockito.mock(Message.class);
+        when(msg.getFrom()).thenReturn(new Address[]{new 
InternetAddress(from)});
+        when(msg.getRecipients(Message.RecipientType.TO)).thenReturn(new 
Address[]{new InternetAddress(to)});
+        when(msg.getRecipients(Message.RecipientType.CC)).thenReturn(new 
Address[]{new InternetAddress(cc)});
+        when(msg.getSentDate()).thenReturn(sent);
+        when(msg.getReceivedDate()).thenReturn(received);
+        when(msg.getSize()).thenReturn(size);
+        when(msg.getSubject()).thenReturn(subject);
+        return msg;
+    }
+
+    @Test
+    public void testSortMessages() throws Exception {
+        Message[] expected = new Message[]{messages[0], messages[1], 
messages[2]};
+
+        // Sort using all the terms. Message order should be the same no 
matter what term is used
+        for (SortTerm term : possibleTerms) {
+            Message[] actual = messages.clone();
+            MailSorter.sortMessages(actual, new SortTerm[]{term});
+            try {
+                assertArrayEquals(actual, expected);
+            } catch (Exception ex) {
+                throw new Exception("Term: " + term.toString(), ex);
+            }
+        }
+    }
+
+    @Test
+    public void testSortMessagesReverse() throws Exception {
+        Message[] expected = new Message[]{messages[2], messages[1], 
messages[0]};
+
+        // Sort using all the terms. Message order should be the same no 
matter what term is used
+        for (SortTerm term : possibleTerms) {
+            Message[] actual = messages.clone();
+            MailSorter.sortMessages(actual, new SortTerm[]{SortTerm.REVERSE, 
term});
+            try {
+                assertArrayEquals(actual, expected);
+            } catch (AssertionError ex) {
+                throw new AssertionError("Term: " + term.toString(), ex);
+            }
+        }
+    }
+
+    @Test
+    public void testSortMessagesMulti() throws Exception {
+        Message[] expected = new Message[]{messages[0], messages[1], 
messages[2]};
+
+        // Sort using all the terms. Message order should be the same no 
matter what term is used. The second term
+        // should be ignored since it is already the decider.
+        for (SortTerm term1 : possibleTerms) {
+            for (SortTerm term2 : possibleTerms) {
+                Message[] actual = messages.clone();
+                MailSorter.sortMessages(actual, new SortTerm[]{term1, 
SortTerm.REVERSE, term2});
+                try {
+                    assertArrayEquals(actual, expected);
+                } catch (AssertionError ex) {
+                    throw new AssertionError(String.format("Terms: %s, %s", 
term1.toString(), term2.toString()), ex);
+                }
+            }
+
+        }
+    }
+
+    @Test
+    public void testSortMessagesWithTie() throws Exception {
+        Message[] given = new Message[]{messages[2], tieBreaker};
+
+        // Sort according to the whole list. Only the last element breaks the 
tie
+        Message[] actual1 = given.clone();
+        MailSorter.sortMessages(actual1, possibleTerms);
+        assertArrayEquals(actual1, new Message[]{tieBreaker, messages[2]});
+
+        // now reverse the last element (the tie breaker)
+        SortTerm[] reversed = new SortTerm[possibleTerms.length + 1];
+        System.arraycopy(possibleTerms, 0, reversed, 0, possibleTerms.length - 
1);
+        reversed[reversed.length - 2] = SortTerm.REVERSE;
+        reversed[reversed.length - 1] = possibleTerms[possibleTerms.length - 
1];
+        // And check again
+        Message[] actual2 = given.clone();
+        MailSorter.sortMessages(actual2, reversed);
+        assertArrayEquals(actual2, new Message[]{messages[2], tieBreaker});
+    }
+}
\ No newline at end of file

Reply via email to