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", "btell...@linagora.com")).build(); + assertThat(headerCollection.getToAddressSet()) + .containsOnly(new EMailer("btell...@linagora.com", "btell...@linagora.com")); + } + + @Test + public void comaSeparatedAddressShouldBeBothAddedToTheAddressSet() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "btell...@linagora.com, be...@minet.net")).build(); + assertThat(headerCollection.getToAddressSet()) + .containsOnly( + new EMailer("btell...@linagora.com", "btell...@linagora.com"), + new EMailer("be...@minet.net", "be...@minet.net")); + } + + @Test + public void addressesOfTwoFieldsHavingTheSameNameShouldBeMerged() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "btell...@linagora.com")).add(new FieldImpl("To", "btell...@linagora.com, be...@minet.net")).build(); + assertThat(headerCollection.getToAddressSet()) + .containsOnly( + new EMailer("btell...@linagora.com", "btell...@linagora.com"), + new EMailer("be...@minet.net", "be...@minet.net")); + } + + @Test + public void displayNamesShouldBeRetreived() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "Christophe Hamerling <chamerl...@linagora.com>")).build(); + assertThat(headerCollection.getToAddressSet()) + .containsOnly(new EMailer("Christophe Hamerling", "chamerl...@linagora.com")); + } + + @Test + public void addressWithTwoDisplayNamesOnTheSameFieldShouldBeRetreived() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("From", "Christophe Hamerling <chamerl...@linagora.com>, Graham CROSMARIE <gcrosma...@linagora.com>")).build(); + assertThat(headerCollection.getFromAddressSet()) + .containsOnly(new EMailer("Christophe Hamerling", "chamerl...@linagora.com"), + new EMailer("Graham CROSMARIE", "gcrosma...@linagora.com")); + + } + + @Test + public void mixingAddressWithDisplayNamesWithOthersShouldBeAllowed() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("To", "Christophe Hamerling <chamerl...@linagora.com>, gcrosma...@linagora.com")).build(); + assertThat(headerCollection.getToAddressSet()) + .containsOnly(new EMailer("Christophe Hamerling", "chamerl...@linagora.com"), + new EMailer("gcrosma...@linagora.com", "gcrosma...@linagora.com")); + } + + @Test + public void displayNamesShouldBeRetreivedOnCc() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Cc", "Christophe Hamerling <chamerl...@linagora.com>")).build(); + assertThat(headerCollection.getCcAddressSet()) + .containsOnly(new EMailer("Christophe Hamerling", "chamerl...@linagora.com")); + } + + @Test + public void displayNamesShouldBeRetreivedOnBcc() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Christophe Hamerling <chamerl...@linagora.com>")).build(); + assertThat(headerCollection.getBccAddressSet()) + .containsOnly(new EMailer("Christophe Hamerling", "chamerl...@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 <tri...@mouse.com")).build(); + assertThat(headerCollection.getBccAddressSet()) + .containsOnly(new EMailer("Mickey", "tri...@mouse.com")); + } + + @Test + public void notComaSeparatedAddressSubpartShouldBeWellHandled() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Mickey <tri...@mouse.com> Miny<he...@polo.com>")).build(); + assertThat(headerCollection.getBccAddressSet()) + .containsOnly(new EMailer("Mickey", "tri...@mouse.com"), + new EMailer("Miny", "he...@polo.com")); + } + + @Test + public void notSeparatedAddressSubpartShouldBeWellHandled1() { + HeaderCollection headerCollection = HeaderCollection.builder().add(new FieldImpl("Bcc", "Mickey <tri...@mouse.com>Miny<he...@polo.com>")).build(); + assertThat(headerCollection.getBccAddressSet()) + .containsOnly(new EMailer("Mickey", "tri...@mouse.com"), + new EMailer("Miny", "he...@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-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org