You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2015/06/29 10:23:56 UTC

svn commit: r1688113 - in /james/mailbox/trunk/elasticsearch/src: main/java/org/apache/james/mailbox/elasticsearch/json/ test/ test/java/ test/java/org/ test/java/org/apache/ test/java/org/apache/james/ test/java/org/apache/james/mailbox/ test/java/org...

Author: btellier
Date: Mon Jun 29 08:23:55 2015
New Revision: 1688113

URL: http://svn.apache.org/r1688113
Log:
MAILBOX-234 Extract specific header values and store it

Added:
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java
    james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
    james/mailbox/trunk/elasticsearch/src/test/
    james/mailbox/trunk/elasticsearch/src/test/java/
    james/mailbox/trunk/elasticsearch/src/test/java/org/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/FieldImpl.java
    james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java?rev=1688113&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java Mon Jun 29 08:23:55 2015
@@ -0,0 +1,69 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.json;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.elasticsearch.common.base.MoreObjects;
+
+import java.util.Objects;
+
+public class EMailer {
+
+    private final String name;
+    private final String address;
+
+    public EMailer(String name, String address) {
+        this.name = name;
+        this.address = address;
+    }
+
+    @JsonProperty(JsonMessageConstants.EMailer.NAME)
+    public String getName() {
+        return name;
+    }
+
+    @JsonProperty(JsonMessageConstants.EMailer.ADDRESS)
+    public String getAddress() {
+        return address;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof EMailer) {
+            EMailer otherEMailer = (EMailer) o;
+            return Objects.equals(name, otherEMailer.name)
+                && Objects.equals(address, otherEMailer.address);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, address);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("name", name)
+            .add("address", address)
+            .toString();
+    }
+}

Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java?rev=1688113&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java Mon Jun 29 08:23:55 2015
@@ -0,0 +1,208 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.json;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import org.apache.james.mailbox.store.search.SearchUtil;
+import org.apache.james.mime4j.dom.address.Address;
+import org.apache.james.mime4j.dom.address.Group;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.field.address.LenientAddressParser;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.MimeUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class HeaderCollection {
+
+    public static class Builder {
+
+        private final Set<EMailer> toAddressSet;
+        private final Set<EMailer> fromAddressSet;
+        private final Set<EMailer> ccAddressSet;
+        private final Set<EMailer> bccAddressSet;
+        private final Set<String> subjectSet;
+        private final Multimap<String, String> headers;
+        private Optional<ZonedDateTime> sentDate;
+
+        private Builder() {
+            toAddressSet = new HashSet<>();
+            fromAddressSet = new HashSet<>();
+            ccAddressSet = new HashSet<>();
+            bccAddressSet = new HashSet<>();
+            subjectSet = new HashSet<>();
+            headers = ArrayListMultimap.create();
+            sentDate = Optional.empty();
+        }
+
+        public Builder add(Field field) {
+            Preconditions.checkNotNull(field);
+            String headerName = field.getName().toLowerCase();
+            String headerValue = field.getBody();
+            headers.put(headerName, headerValue);
+            handleSpecificHeader(headerName, headerValue);
+            return this;
+        }
+
+        public HeaderCollection build() {
+            return new HeaderCollection(
+                ImmutableSet.copyOf(toAddressSet),
+                ImmutableSet.copyOf(fromAddressSet),
+                ImmutableSet.copyOf(ccAddressSet),
+                ImmutableSet.copyOf(bccAddressSet),
+                ImmutableSet.copyOf(subjectSet),
+                ImmutableMultimap.copyOf(headers),
+                sentDate);
+        }
+
+        private void handleSpecificHeader(String headerName, String headerValue) {
+            switch (headerName) {
+                case TO:
+                case FROM:
+                case CC:
+                case BCC:
+                    manageAddressField(headerName, headerValue);
+                    break;
+                case SUBJECT:
+                    subjectSet.add(headerValue);
+                    break;
+                case DATE:
+                    sentDate = toISODate(headerValue);
+                    break;
+            }
+        }
+
+        private void manageAddressField(String headerName, String headerValue) {
+            LenientAddressParser.DEFAULT
+                .parseAddressList(MimeUtil.unfold(headerValue))
+                .stream()
+                .flatMap(this::convertAddressToMailboxStream)
+                .map((mailbox) -> new EMailer(SearchUtil.getDisplayAddress(mailbox) , mailbox.getAddress()))
+                .collect(Collectors.toCollection(() -> getAddressSet(headerName)));
+        }
+
+        private Stream<Mailbox> convertAddressToMailboxStream(Address address) {
+            if (address instanceof Mailbox) {
+                return Stream.of((Mailbox) address);
+            } else if (address instanceof Group) {
+                return ((Group) address).getMailboxes().stream();
+            }
+            return Stream.empty();
+        }
+
+        private Set<EMailer> getAddressSet(String headerName) {
+            switch (headerName) {
+                case TO:
+                    return toAddressSet;
+                case FROM:
+                    return fromAddressSet;
+                case CC:
+                    return ccAddressSet;
+                case BCC:
+                    return bccAddressSet;
+            }
+            throw new RuntimeException(headerName + " is not a address header name");
+        }
+
+        private Optional<ZonedDateTime> toISODate(String value) {
+            try {
+                return Optional.of(ZonedDateTime.parse(value, DateTimeFormatter.RFC_1123_DATE_TIME));
+            } catch (Exception e) {
+                LOGGER.info("Can not parse receive date " + value);
+                return Optional.empty();
+            }
+        }
+
+    }
+
+    public static final String TO = "to";
+    public static final String FROM = "from";
+    public static final String CC = "cc";
+    public static final String BCC = "bcc";
+    public static final String SUBJECT = "subject";
+    public static final String DATE = "date";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HeaderCollection.class);
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    private final ImmutableSet<EMailer> toAddressSet;
+    private final ImmutableSet<EMailer> fromAddressSet;
+    private final ImmutableSet<EMailer> ccAddressSet;
+    private final ImmutableSet<EMailer> bccAddressSet;
+    private final ImmutableSet<String> subjectSet;
+    private final ImmutableMultimap<String, String> headers;
+    private Optional<ZonedDateTime> sentDate;
+
+    private HeaderCollection(ImmutableSet<EMailer> toAddressSet, ImmutableSet<EMailer> fromAddressSet,
+        ImmutableSet<EMailer> ccAddressSet, ImmutableSet<EMailer> bccAddressSet, ImmutableSet<String> subjectSet,
+        ImmutableMultimap<String, String> headers, Optional<ZonedDateTime> sentDate) {
+        this.toAddressSet = toAddressSet;
+        this.fromAddressSet = fromAddressSet;
+        this.ccAddressSet = ccAddressSet;
+        this.bccAddressSet = bccAddressSet;
+        this.subjectSet = subjectSet;
+        this.headers = headers;
+        this.sentDate = sentDate;
+    }
+
+    public Set<EMailer> getToAddressSet() {
+        return toAddressSet;
+    }
+
+    public Set<EMailer> getFromAddressSet() {
+        return fromAddressSet;
+    }
+
+    public Set<EMailer> getCcAddressSet() {
+        return ccAddressSet;
+    }
+
+    public Set<EMailer> getBccAddressSet() {
+        return bccAddressSet;
+    }
+
+    public Set<String> getSubjectSet() {
+        return subjectSet;
+    }
+
+    public Optional<ZonedDateTime> getSentDate() {
+        return sentDate;
+    }
+
+    public Multimap<String, String> getHeaders() {
+        return headers;
+    }
+
+}

Added: james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/FieldImpl.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/FieldImpl.java?rev=1688113&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/FieldImpl.java (added)
+++ james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/FieldImpl.java Mon Jun 29 08:23:55 2015
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.json;
+
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.ByteSequence;
+
+import java.util.Objects;
+
+public class FieldImpl implements Field {
+    private final String name;
+    private final String body;
+
+    public FieldImpl(String name, String body) {
+        this.name = name;
+        this.body = body;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public ByteSequence getRaw() {
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, body);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof  FieldImpl) {
+            FieldImpl otherField = (FieldImpl) o;
+            return Objects.equals(name, otherField.name)
+                && Objects.equals(body, otherField.body);
+        }
+        return false;
+    }
+}

Added: james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java?rev=1688113&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java (added)
+++ james/mailbox/trunk/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java Mon Jun 29 08:23:55 2015
@@ -0,0 +1,151 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.elasticsearch.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.format.DateTimeFormatter;
+
+import org.junit.Test;
+
+public class HeaderCollectionTest {
+
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
+
+    @Test
+    public void simpleValueAddressHeaderShouldBeAddedToTheAddressSet() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "btellier@linagora.com")).build();
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("btellier@linagora.com", "btellier@linagora.com"));
+    }
+
+    @Test
+    public void comaSeparatedAddressShouldBeBothAddedToTheAddressSet() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "btellier@linagora.com, benwa@minet.net")).build();
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(
+                new EMailer("btellier@linagora.com", "btellier@linagora.com"),
+                new EMailer("benwa@minet.net", "benwa@minet.net"));
+    }
+
+    @Test
+    public void addressesOfTwoFieldsHavingTheSameNameShouldBeMerged() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "btellier@linagora.com")).add(new FieldImpl("To", "btellier@linagora.com, benwa@minet.net")).build();
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(
+                new EMailer("btellier@linagora.com", "btellier@linagora.com"),
+                new EMailer("benwa@minet.net", "benwa@minet.net"));
+    }
+
+    @Test
+    public void displayNamesShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>")).build();
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chamerling@linagora.com"));
+    }
+
+    @Test
+    public void addressWithTwoDisplayNamesOnTheSameFieldShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>")).build();
+        assertThat(headerCollection.getFromAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chamerling@linagora.com"),
+                new EMailer("Graham CROSMARIE", "gcrosmarie@linagora.com"));
+
+    }
+
+    @Test
+    public void mixingAddressWithDisplayNamesWithOthersShouldBeAllowed() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>, gcrosmarie@linagora.com")).build();
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chamerling@linagora.com"),
+                new EMailer("gcrosmarie@linagora.com", "gcrosmarie@linagora.com"));
+    }
+
+    @Test
+    public void displayNamesShouldBeRetreivedOnCc() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Cc", "Christophe Hamerling <ch...@linagora.com>")).build();
+        assertThat(headerCollection.getCcAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chamerling@linagora.com"));
+    }
+
+    @Test
+    public void displayNamesShouldBeRetreivedOnBcc() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Christophe Hamerling <ch...@linagora.com>")).build();
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chamerling@linagora.com"));
+    }
+
+    @Test
+    public void headerContaingNoAddressShouldBeConsideredBothAsNameAndAddress() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Not an address")).build();
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Not an address", "Not an address"));
+    }
+
+    @Test
+    public void unclosedAddressSubpartShouldBeWellHandled() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Mickey <tricky@mouse.com")).build();
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"));
+    }
+
+    @Test
+    public void notComaSeparatedAddressSubpartShouldBeWellHandled() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Mickey <tr...@mouse.com> Miny<he...@polo.com>")).build();
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"),
+                new EMailer("Miny", "hello@polo.com"));
+    }
+
+    @Test
+    public void notSeparatedAddressSubpartShouldBeWellHandled1() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Mickey <tr...@polo.com>")).build();
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"),
+                new EMailer("Miny", "hello@polo.com"));
+    }
+
+    @Test
+    public void dateShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Date", "Thu, 4 Jun 2015 06:08:41 +0200")).build();
+        assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
+            .isEqualTo("2015/06/04 06:08:41");
+    }
+
+    @Test
+    public void dateShouldBeAbsentOnInvalidHeader() {
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Date", "Not a date")).build();
+        assertThat(headerCollection.getSentDate().isPresent())
+            .isFalse();
+    }
+
+    @Test
+    public void subjectsShouldBeWellRetrieved() {
+        String subject = "A fantastic ElasticSearch module will be available soon for JAMES";
+        HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Subject", subject)).build();
+        assertThat(headerCollection.getSubjectSet()).containsOnly("A fantastic ElasticSearch module will be available soon for JAMES");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void nullFieldShouldThrow() {
+        HeaderCollection.builder().add(null).build();
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org