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 2019/05/28 04:00:29 UTC
[james-project] 02/11: JAMES-2767 replace old mailbox-es module by
the new one
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 710ed2148d6b58e5f8e566b9e0feccc9d5432aef
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu May 23 11:03:52 2019 +0700
JAMES-2767 replace old mailbox-es module by the new one
---
mailbox/elasticsearch-v6/pom.xml | 196 --
.../v6/ElasticSearchMailboxConfiguration.java | 180 --
.../mailbox/elasticsearch/v6/IndexAttachments.java | 24 -
.../v6/MailboxElasticSearchConstants.java | 35 -
.../elasticsearch/v6/MailboxIndexCreationUtil.java | 56 -
.../elasticsearch/v6/MailboxMappingFactory.java | 339 ---
.../ElasticSearchListeningMessageSearchIndex.java | 197 --
.../mailbox/elasticsearch/v6/json/EMailer.java | 75 -
.../mailbox/elasticsearch/v6/json/EMailers.java | 52 -
.../elasticsearch/v6/json/HeaderCollection.java | 224 --
.../elasticsearch/v6/json/IndexableMessage.java | 471 ----
.../v6/json/JsonMessageConstants.java | 77 -
.../v6/json/MessageToElasticSearchJson.java | 86 -
.../elasticsearch/v6/json/MessageUpdateJson.java | 79 -
.../mailbox/elasticsearch/v6/json/MimePart.java | 303 ---
.../v6/json/MimePartContainerBuilder.java | 50 -
.../elasticsearch/v6/json/MimePartParser.java | 129 -
.../v6/json/RootMimePartContainerBuilder.java | 96 -
.../elasticsearch/v6/json/SerializableMessage.java | 25 -
.../mailbox/elasticsearch/v6/json/Subjects.java | 50 -
.../elasticsearch/v6/query/CriterionConverter.java | 313 ---
.../v6/query/DateResolutionFormater.java | 73 -
.../elasticsearch/v6/query/QueryConverter.java | 79 -
.../elasticsearch/v6/query/SortConverter.java | 82 -
.../v6/search/ElasticSearchSearcher.java | 139 --
.../elasticsearch-v6/src/reporting-site/site.xml | 29 -
.../v6/ElasticSearchIntegrationTest.java | 217 --
.../v6/ElasticSearchMailboxConfigurationTest.java | 227 --
...asticSearchListeningMessageSearchIndexTest.java | 270 ---
.../elasticsearch/v6/json/EMailersTest.java | 66 -
.../mailbox/elasticsearch/v6/json/FieldImpl.java | 65 -
.../v6/json/HeaderCollectionTest.java | 334 ---
.../v6/json/IndexableMessageTest.java | 578 -----
.../v6/json/MessageToElasticSearchJsonTest.java | 388 ---
.../elasticsearch/v6/json/MimePartTest.java | 50 -
.../elasticsearch/v6/json/SubjectsTest.java | 66 -
.../v6/query/DateResolutionFormaterTest.java | 99 -
.../elasticsearch/v6/query/SearchQueryTest.java | 77 -
.../src/test/resources/eml/bodyMakeTikaToFail.eml | 1272 ----------
.../test/resources/eml/emailWith3Attachments.eml | 50 -
.../src/test/resources/eml/mailWithHeaders.eml | 14 -
.../src/test/resources/logback-test.xml | 24 -
mailbox/elasticsearch/.gitignore | 6 -
mailbox/elasticsearch/README.txt | 1 -
mailbox/elasticsearch/jsonStructure.adoc | 64 -
mailbox/elasticsearch/pom.xml | 29 +-
.../ElasticSearchMailboxConfiguration.java | 113 +-
.../mailbox/elasticsearch/IndexAttachments.java | 36 +-
.../MailboxElasticSearchConstants.java | 38 +-
.../elasticsearch/MailboxIndexCreationUtil.java | 14 +-
.../elasticsearch/MailboxMappingFactory.java | 404 ++--
.../ElasticSearchListeningMessageSearchIndex.java | 47 +-
.../james/mailbox/elasticsearch/json/EMailer.java | 6 +-
.../james/mailbox/elasticsearch/json/EMailers.java | 2 +-
.../elasticsearch/json/JsonMessageConstants.java | 6 -
.../elasticsearch/json/MessageUpdateJson.java | 21 +-
...{Serializable.java => SerializableMessage.java} | 2 +-
.../james/mailbox/elasticsearch/json/Subjects.java | 2 +-
.../elasticsearch/query/CriterionConverter.java | 17 +-
.../mailbox/elasticsearch/query/SortConverter.java | 25 +-
.../search/ElasticSearchSearcher.java | 77 +-
.../ElasticSearchIntegrationTest.java | 18 +-
.../ElasticSearchMailboxConfigurationTest.java | 44 +-
...asticSearchListeningMessageSearchIndexTest.java | 7 +-
.../mailbox/elasticsearch}/json/EMailerTest.java | 2 +-
.../src/test/resources/eml/bodyMakeTikaToFail.eml | 2544 ++++++++++----------
.../test/resources/eml/emailWith3Attachments.eml | 100 +-
mailbox/pom.xml | 1 -
68 files changed, 1755 insertions(+), 9127 deletions(-)
diff --git a/mailbox/elasticsearch-v6/pom.xml b/mailbox/elasticsearch-v6/pom.xml
deleted file mode 100644
index f7615c9..0000000
--- a/mailbox/elasticsearch-v6/pom.xml
+++ /dev/null
@@ -1,196 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.james</groupId>
- <artifactId>apache-james-mailbox</artifactId>
- <version>3.4.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <artifactId>apache-james-mailbox-elasticsearch-v6</artifactId>
- <name>Apache James :: Mailbox :: ElasticSearch :: v6</name>
- <description>Apache James Mailbox IMAP search implementation using ElasticSearch v6</description>
-
- <dependencies>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-backends-es-v6</artifactId>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-backends-es-v6</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-api</artifactId>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-event-memory</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-memory</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-memory</artifactId>
- <scope>test</scope>
- <type>test-jar</type>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-store</artifactId>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-store</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-tika</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>apache-james-mailbox-tika</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>james-server-testing</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>${james.groupId}</groupId>
- <artifactId>james-server-util</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.datatype</groupId>
- <artifactId>jackson-datatype-guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.datatype</groupId>
- <artifactId>jackson-datatype-jdk8</artifactId>
- </dependency>
- <dependency>
- <groupId>com.github.steveash.guavate</groupId>
- <artifactId>guavate</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.jayway.awaitility</groupId>
- <artifactId>awaitility</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.sun.mail</groupId>
- <artifactId>javax.mail</artifactId>
- </dependency>
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>net.javacrumbs.json-unit</groupId>
- <artifactId>json-unit-assertj</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>nl.jqno.equalsverifier</groupId>
- <artifactId>equalsverifier</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-params</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>testcontainers</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <reuseForks>true</reuseForks>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
deleted file mode 100644
index 7e362a7..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.commons.configuration.Configuration;
-import org.apache.james.backends.es.v6.IndexName;
-import org.apache.james.backends.es.v6.ReadAliasName;
-import org.apache.james.backends.es.v6.WriteAliasName;
-import org.apache.james.util.OptionalUtils;
-
-public class ElasticSearchMailboxConfiguration {
-
- public static class Builder {
- private Optional<IndexName> indexMailboxName;
- private Optional<ReadAliasName> readAliasMailboxName;
- private Optional<WriteAliasName> writeAliasMailboxName;
- private Optional<IndexAttachments> indexAttachment;
-
- Builder() {
- indexMailboxName = Optional.empty();
- readAliasMailboxName = Optional.empty();
- writeAliasMailboxName = Optional.empty();
- indexAttachment = Optional.empty();
- }
-
- Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
- this.indexMailboxName = indexMailboxName;
- return this;
- }
-
- Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
- this.readAliasMailboxName = readAliasMailboxName;
- return this;
- }
-
- Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
- this.writeAliasMailboxName = writeAliasMailboxName;
- return this;
- }
-
-
- Builder indexAttachment(IndexAttachments indexAttachment) {
- this.indexAttachment = Optional.of(indexAttachment);
- return this;
- }
-
-
-
- public ElasticSearchMailboxConfiguration build() {
- return new ElasticSearchMailboxConfiguration(
- indexMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX),
- readAliasMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS),
- writeAliasMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS),
- indexAttachment.orElse(IndexAttachments.YES));
- }
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- private static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
- private static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
- private static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
- private static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
- private static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
- private static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
- private static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
- private static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
-
- static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
- return builder()
- .indexMailboxName(computeMailboxIndexName(configuration))
- .readAliasMailboxName(computeMailboxReadAlias(configuration))
- .writeAliasMailboxName(computeMailboxWriteAlias(configuration))
- .indexAttachment(provideIndexAttachments(configuration))
- .build();
- }
-
- static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
- return OptionalUtils.or(
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_MAILBOX_NAME))
- .map(IndexName::new),
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_NAME))
- .map(IndexName::new));
- }
-
- static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
- return OptionalUtils.or(
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME))
- .map(WriteAliasName::new),
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_NAME))
- .map(WriteAliasName::new));
- }
-
- static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
- return OptionalUtils.or(
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME))
- .map(ReadAliasName::new),
- Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_NAME))
- .map(ReadAliasName::new));
- }
-
-
- private static IndexAttachments provideIndexAttachments(Configuration configuration) {
- if (configuration.getBoolean(ELASTICSEARCH_INDEX_ATTACHMENTS, DEFAULT_INDEX_ATTACHMENTS)) {
- return IndexAttachments.YES;
- }
- return IndexAttachments.NO;
- }
-
-
- private final IndexName indexMailboxName;
- private final ReadAliasName readAliasMailboxName;
- private final WriteAliasName writeAliasMailboxName;
- private final IndexAttachments indexAttachment;
-
- private ElasticSearchMailboxConfiguration(IndexName indexMailboxName, ReadAliasName readAliasMailboxName,
- WriteAliasName writeAliasMailboxName, IndexAttachments indexAttachment) {
- this.indexMailboxName = indexMailboxName;
- this.readAliasMailboxName = readAliasMailboxName;
- this.writeAliasMailboxName = writeAliasMailboxName;
- this.indexAttachment = indexAttachment;
- }
-
-
- IndexName getIndexMailboxName() {
- return indexMailboxName;
- }
-
- ReadAliasName getReadAliasMailboxName() {
- return readAliasMailboxName;
- }
-
- WriteAliasName getWriteAliasMailboxName() {
- return writeAliasMailboxName;
- }
-
- IndexAttachments getIndexAttachment() {
- return indexAttachment;
- }
-
- @Override
- public final boolean equals(Object o) {
- if (o instanceof ElasticSearchMailboxConfiguration) {
- ElasticSearchMailboxConfiguration that = (ElasticSearchMailboxConfiguration) o;
-
- return Objects.equals(this.indexAttachment, that.indexAttachment)
- && Objects.equals(this.indexMailboxName, that.indexMailboxName)
- && Objects.equals(this.readAliasMailboxName, that.readAliasMailboxName)
- && Objects.equals(this.writeAliasMailboxName, that.writeAliasMailboxName);
- }
- return false;
- }
-
- @Override
- public final int hashCode() {
- return Objects.hash(indexMailboxName, readAliasMailboxName, writeAliasMailboxName, indexAttachment, writeAliasMailboxName);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java
deleted file mode 100644
index f827bd8..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-public enum IndexAttachments {
- NO, YES
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
deleted file mode 100644
index 07a7644..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import org.apache.james.backends.es.v6.IndexName;
-import org.apache.james.backends.es.v6.ReadAliasName;
-import org.apache.james.backends.es.v6.WriteAliasName;
-
-public interface MailboxElasticSearchConstants {
-
- interface InjectionNames {
- String MAILBOX = "mailbox";
- }
-
- WriteAliasName DEFAULT_MAILBOX_WRITE_ALIAS = new WriteAliasName("mailboxWriteAlias");
- ReadAliasName DEFAULT_MAILBOX_READ_ALIAS = new ReadAliasName("mailboxReadAlias");
- IndexName DEFAULT_MAILBOX_INDEX = new IndexName("mailbox_v1");
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
deleted file mode 100644
index 8a41671..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import java.io.IOException;
-
-import org.apache.james.backends.es.v6.ElasticSearchConfiguration;
-import org.apache.james.backends.es.v6.IndexCreationFactory;
-import org.apache.james.backends.es.v6.IndexName;
-import org.apache.james.backends.es.v6.NodeMappingFactory;
-import org.apache.james.backends.es.v6.ReadAliasName;
-import org.apache.james.backends.es.v6.WriteAliasName;
-import org.elasticsearch.client.RestHighLevelClient;
-
-public class MailboxIndexCreationUtil {
-
- public static RestHighLevelClient prepareClient(RestHighLevelClient client,
- ReadAliasName readAlias,
- WriteAliasName writeAlias,
- IndexName indexName,
- ElasticSearchConfiguration configuration) throws IOException {
- return NodeMappingFactory.applyMapping(
- new IndexCreationFactory(configuration)
- .useIndex(indexName)
- .addAlias(readAlias)
- .addAlias(writeAlias)
- .createIndexAndAliases(client),
- indexName,
- MailboxMappingFactory.getMappingContent());
- }
-
- public static RestHighLevelClient prepareDefaultClient(RestHighLevelClient client, ElasticSearchConfiguration configuration) throws IOException {
- return prepareClient(client,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX,
- configuration);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
deleted file mode 100644
index af0bbe1..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import static org.apache.james.backends.es.v6.IndexCreationFactory.CASE_INSENSITIVE;
-import static org.apache.james.backends.es.v6.IndexCreationFactory.KEEP_MAIL_AND_URL;
-import static org.apache.james.backends.es.v6.IndexCreationFactory.SNOWBALL_KEEP_MAIL_AND_URL;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.ANALYZER;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.BOOLEAN;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.FIELDS;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.FORMAT;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.IGNORE_ABOVE;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.KEYWORD;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.LONG;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.NESTED;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.NORMALIZER;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.PROPERTIES;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.RAW;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.SEARCH_ANALYZER;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.SNOWBALL;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.SPLIT_EMAIL;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.TYPE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.BCC;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.CC;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.DATE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.FROM;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.HAS_ATTACHMENT;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.HTML_BODY;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_ANSWERED;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_DELETED;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_DRAFT;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_FLAGGED;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_RECENT;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_UNREAD;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MAILBOX_ID;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MEDIA_TYPE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MESSAGE_ID;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MIME_MESSAGE_ID;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MODSEQ;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SENT_DATE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SIZE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SUBJECT;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SUBTYPE;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TEXT;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TEXT_BODY;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TO;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.UID;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.USERS;
-import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.USER_FLAGS;
-import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
-
-import java.io.IOException;
-
-import org.apache.james.backends.es.v6.NodeMappingFactory;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.EMailer;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-
-public class MailboxMappingFactory {
-
- private static final int MAXIMUM_TERM_LENGTH = 4096;
- private static final String STANDARD = "standard";
- private static final String STORE = "store";
-
- public static XContentBuilder getMappingContent() {
- try {
- return jsonBuilder()
- .startObject()
-
- .startObject(PROPERTIES)
-
- .startObject(MESSAGE_ID)
- .field(TYPE, KEYWORD)
- .field(STORE, true)
- .endObject()
-
- .startObject(UID)
- .field(TYPE, LONG)
- .field(STORE, true)
- .endObject()
-
- .startObject(MODSEQ)
- .field(TYPE, LONG)
- .endObject()
-
- .startObject(SIZE)
- .field(TYPE, LONG)
- .endObject()
-
- .startObject(IS_ANSWERED)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(IS_DELETED)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(IS_DRAFT)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(IS_FLAGGED)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(IS_RECENT)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(IS_UNREAD)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(DATE)
- .field(TYPE, NodeMappingFactory.DATE)
- .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
- .endObject()
-
- .startObject(SENT_DATE)
- .field(TYPE, NodeMappingFactory.DATE)
- .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
- .endObject()
-
- .startObject(MEDIA_TYPE)
- .field(TYPE, KEYWORD)
- .endObject()
-
- .startObject(SUBTYPE)
- .field(TYPE, KEYWORD)
- .endObject()
-
- .startObject(USER_FLAGS)
- .field(TYPE, KEYWORD)
- .endObject()
-
- .startObject(FROM)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(SUBJECT)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(TO)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(CC)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(BCC)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .endObject()
- .endObject()
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(MAILBOX_ID)
- .field(TYPE, KEYWORD)
- .field(STORE, true)
- .endObject()
-
- .startObject(MIME_MESSAGE_ID)
- .field(TYPE, KEYWORD)
- .endObject()
-
- .startObject(USERS)
- .field(TYPE, KEYWORD)
- .endObject()
-
- .startObject(TEXT_BODY)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .endObject()
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(HTML_BODY)
- .field(TYPE, TEXT)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, TEXT)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .endObject()
- .startObject(RAW)
- .field(TYPE, KEYWORD)
- .field(NORMALIZER, CASE_INSENSITIVE)
- .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
- .endObject()
- .endObject()
- .endObject()
-
- .startObject(HAS_ATTACHMENT)
- .field(TYPE, BOOLEAN)
- .endObject()
-
- .startObject(TEXT)
- .field(TYPE, TEXT)
- .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, TEXT)
- .field(ANALYZER, SNOWBALL)
- .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
- .endObject()
- .endObject()
- .endObject()
- .endObject()
- .endObject();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
deleted file mode 100644
index 0847f45..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/****************************************************************
- * 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.v6.events;
-
-import static org.elasticsearch.index.query.QueryBuilders.termQuery;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.apache.james.backends.es.v6.ElasticSearchIndexer;
-import org.apache.james.backends.es.v6.UpdatedRepresentation;
-import org.apache.james.mailbox.MailboxManager.MessageCapabilities;
-import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.elasticsearch.v6.MailboxElasticSearchConstants;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
-import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
-import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
-import org.apache.james.mailbox.events.Group;
-import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.UpdatedFlags;
-import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
-import org.apache.james.mailbox.store.SessionProvider;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.github.fge.lambdas.Throwing;
-import com.github.steveash.guavate.Guavate;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSearchIndex {
- public static class ElasticSearchListeningMessageSearchIndexGroup extends Group {}
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchListeningMessageSearchIndex.class);
- private static final String ID_SEPARATOR = ":";
- private static final Group GROUP = new ElasticSearchListeningMessageSearchIndexGroup();
-
- private final ElasticSearchIndexer elasticSearchIndexer;
- private final ElasticSearchSearcher searcher;
- private final MessageToElasticSearchJson messageToElasticSearchJson;
-
- @Inject
- public ElasticSearchListeningMessageSearchIndex(MailboxSessionMapperFactory factory,
- @Named(MailboxElasticSearchConstants.InjectionNames.MAILBOX) ElasticSearchIndexer indexer,
- ElasticSearchSearcher searcher, MessageToElasticSearchJson messageToElasticSearchJson,
- SessionProvider sessionProvider) {
- super(factory, sessionProvider);
- this.elasticSearchIndexer = indexer;
- this.messageToElasticSearchJson = messageToElasticSearchJson;
- this.searcher = searcher;
- }
-
- @Override
- public Group getDefaultGroup() {
- return GROUP;
- }
-
- @Override
- public EnumSet<SearchCapabilities> getSupportedCapabilities(EnumSet<MessageCapabilities> messageCapabilities) {
- return EnumSet.of(
- SearchCapabilities.MultimailboxSearch,
- SearchCapabilities.Text,
- SearchCapabilities.FullText,
- SearchCapabilities.Attachment,
- SearchCapabilities.AttachmentFileName,
- SearchCapabilities.PartialEmailMatch);
- }
-
- @Override
- public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) {
- Preconditions.checkArgument(session != null, "'session' is mandatory");
- Optional<Integer> noLimit = Optional.empty();
- return searcher
- .search(ImmutableList.of(mailbox.getMailboxId()), searchQuery, noLimit)
- .map(SearchResult::getMessageUid)
- .iterator();
- }
-
- @Override
- public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
- Preconditions.checkArgument(session != null, "'session' is mandatory");
-
- if (mailboxIds.isEmpty()) {
- return ImmutableList.of();
- }
-
- return searcher.search(mailboxIds, searchQuery, Optional.empty())
- .peek(this::logIfNoMessageId)
- .map(SearchResult::getMessageId)
- .map(Optional::get)
- .distinct()
- .limit(limit)
- .collect(Guavate.toImmutableList());
- }
-
- @Override
- public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws IOException {
- LOGGER.info("Indexing mailbox {}-{} of user {} on message {}",
- mailbox.getName(),
- mailbox.getMailboxId(),
- session.getUser().asString(),
- message.getUid());
-
- String jsonContent = generateIndexedJson(mailbox, message, session);
- elasticSearchIndexer.index(indexIdFor(mailbox, message.getUid()), jsonContent);
- }
-
- private String generateIndexedJson(Mailbox mailbox, MailboxMessage message, MailboxSession session) throws JsonProcessingException {
- try {
- return messageToElasticSearchJson.convertToJson(message, ImmutableList.of(session.getUser()));
- } catch (Exception e) {
- LOGGER.warn("Indexing mailbox {}-{} of user {} on message {} without attachments ",
- mailbox.getName(),
- mailbox.getMailboxId().serialize(),
- session.getUser().asString(),
- message.getUid(),
- e);
- return messageToElasticSearchJson.convertToJsonWithoutAttachment(message, ImmutableList.of(session.getUser()));
- }
- }
-
- @Override
- public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) throws IOException {
- elasticSearchIndexer
- .delete(expungedUids.stream()
- .map(uid -> indexIdFor(mailbox, uid))
- .collect(Guavate.toImmutableList()));
- }
-
- @Override
- public void deleteAll(MailboxSession session, Mailbox mailbox) {
- elasticSearchIndexer
- .deleteAllMatchingQuery(
- termQuery(
- JsonMessageConstants.MAILBOX_ID,
- mailbox.getMailboxId().serialize()));
- }
-
- @Override
- public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) throws IOException {
- elasticSearchIndexer
- .update(updatedFlagsList.stream()
- .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
- updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
- .sneakyThrow())
- .collect(Guavate.toImmutableList()));
- }
-
- private UpdatedRepresentation createUpdatedDocumentPartFromUpdatedFlags(Mailbox mailbox, UpdatedFlags updatedFlags) throws JsonProcessingException {
- return new UpdatedRepresentation(
- indexIdFor(mailbox, updatedFlags.getUid()),
- messageToElasticSearchJson
- .getUpdatedJsonMessagePart(updatedFlags.getNewFlags(), updatedFlags.getModSeq()));
- }
-
- private String indexIdFor(Mailbox mailbox, MessageUid uid) {
- return String.join(ID_SEPARATOR, mailbox.getMailboxId().serialize(), String.valueOf(uid.asLong()));
- }
-
- private void logIfNoMessageId(SearchResult searchResult) {
- if (!searchResult.getMessageId().isPresent()) {
- LOGGER.error("No messageUid for {} in mailbox {}", searchResult.getMessageUid(), searchResult.getMailboxId());
- }
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
deleted file mode 100644
index ab0194e..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.util.Objects;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Joiner;
-import com.google.common.base.MoreObjects;
-
-public class EMailer implements SerializableMessage {
-
- 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 String serialize() {
- return Joiner.on(" ").join(name, address);
- }
-
- @Override
- public final 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 final int hashCode() {
- return Objects.hash(name, address);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("name", name)
- .add("address", address)
- .toString();
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
deleted file mode 100644
index 06a4667..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.base.Preconditions;
-
-public class EMailers implements SerializableMessage {
-
- public static EMailers from(Set<EMailer> emailers) {
- Preconditions.checkNotNull(emailers, "'emailers' is mandatory");
- return new EMailers(emailers);
- }
-
- private final Set<EMailer> emailers;
-
- private EMailers(Set<EMailer> emailers) {
- this.emailers = emailers;
- }
-
- @JsonValue
- public Set<EMailer> getEmailers() {
- return emailers;
- }
-
- @Override
- public String serialize() {
- return emailers.stream()
- .map(EMailer::serialize)
- .collect(Collectors.joining(" "));
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java
deleted file mode 100644
index 83f2b52..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.time.ZonedDateTime;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.james.mailbox.store.search.SearchUtil;
-import org.apache.james.mailbox.store.search.comparator.SentDateComparator;
-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 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;
-
-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<EMailer> replyToAddressSet;
- private final Set<String> subjectSet;
- private final Multimap<String, String> headers;
- private Optional<ZonedDateTime> sentDate;
- private Optional<String> messageID;
-
- private Builder() {
- toAddressSet = new HashSet<>();
- fromAddressSet = new HashSet<>();
- ccAddressSet = new HashSet<>();
- bccAddressSet = new HashSet<>();
- replyToAddressSet = new HashSet<>();
- subjectSet = new HashSet<>();
- headers = ArrayListMultimap.create();
- sentDate = Optional.empty();
- messageID = Optional.empty();
- }
-
- public Builder add(Field field) {
- Preconditions.checkNotNull(field);
- String headerName = field.getName().toLowerCase(Locale.US);
- String rawHeaderValue = field.getBody();
- String sanitizedValue = MimeUtil.unscrambleHeaderValue(rawHeaderValue);
-
- if (!headerName.contains(".")) {
- headers.put(headerName, sanitizedValue);
- }
- handleSpecificHeader(headerName, sanitizedValue, rawHeaderValue);
- return this;
- }
-
- public HeaderCollection build() {
- return new HeaderCollection(
- ImmutableSet.copyOf(toAddressSet),
- ImmutableSet.copyOf(fromAddressSet),
- ImmutableSet.copyOf(ccAddressSet),
- ImmutableSet.copyOf(bccAddressSet),
- ImmutableSet.copyOf(replyToAddressSet),
- ImmutableSet.copyOf(subjectSet),
- ImmutableMultimap.copyOf(headers),
- sentDate, messageID);
- }
-
- private void handleSpecificHeader(String headerName, String headerValue, String rawHeaderValue) {
- switch (headerName) {
- case TO:
- case FROM:
- case CC:
- case BCC:
- case REPLY_TO:
- manageAddressField(headerName, rawHeaderValue);
- break;
- case SUBJECT:
- subjectSet.add(headerValue);
- break;
- case DATE:
- sentDate = SentDateComparator.toISODate(headerValue);
- break;
- case MESSAGE_ID:
- messageID = Optional.ofNullable(headerValue);
- break;
- }
- }
-
- private void manageAddressField(String headerName, String rawHeaderValue) {
- LenientAddressParser.DEFAULT
- .parseAddressList(rawHeaderValue)
- .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;
- case REPLY_TO:
- return replyToAddressSet;
- }
- throw new RuntimeException(headerName + " is not a address header name");
- }
- }
-
- 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 REPLY_TO = "reply-to";
- public static final String SUBJECT = "subject";
- public static final String DATE = "date";
- public static final String MESSAGE_ID = "message-id";
-
- 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<EMailer> replyToAddressSet;
- private final ImmutableSet<String> subjectSet;
- private final ImmutableMultimap<String, String> headers;
- private final Optional<ZonedDateTime> sentDate;
- private final Optional<String> messageID;
-
- private HeaderCollection(ImmutableSet<EMailer> toAddressSet, ImmutableSet<EMailer> fromAddressSet,
- ImmutableSet<EMailer> ccAddressSet, ImmutableSet<EMailer> bccAddressSet, ImmutableSet<EMailer> replyToAddressSet, ImmutableSet<String> subjectSet,
- ImmutableMultimap<String, String> headers, Optional<ZonedDateTime> sentDate, Optional<String> messageID) {
- this.toAddressSet = toAddressSet;
- this.fromAddressSet = fromAddressSet;
- this.ccAddressSet = ccAddressSet;
- this.bccAddressSet = bccAddressSet;
- this.replyToAddressSet = replyToAddressSet;
- this.subjectSet = subjectSet;
- this.headers = headers;
- this.sentDate = sentDate;
- this.messageID = messageID;
- }
-
- 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<EMailer> getReplyToAddressSet() {
- return replyToAddressSet;
- }
-
- public Set<String> getSubjectSet() {
- return subjectSet;
- }
-
- public Optional<ZonedDateTime> getSentDate() {
- return sentDate;
- }
-
- public Multimap<String, String> getHeaders() {
- return headers;
- }
-
- public Optional<String> getMessageID() {
- return messageID;
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java
deleted file mode 100644
index 221aa91..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.james.core.User;
-import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
-import org.apache.james.mailbox.elasticsearch.v6.query.DateResolutionFormater;
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.mailbox.store.mail.model.Property;
-import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
-import org.apache.james.mailbox.store.search.SearchUtil;
-import org.apache.james.mime4j.MimeException;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.github.steveash.guavate.Guavate;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
-
-public class IndexableMessage {
-
- public static class Builder {
- private static ZonedDateTime getSanitizedInternalDate(MailboxMessage message, ZoneId zoneId) {
- if (message.getInternalDate() == null) {
- return ZonedDateTime.now();
- }
- return ZonedDateTime.ofInstant(
- Instant.ofEpochMilli(message.getInternalDate().getTime()),
- zoneId);
- }
-
- private IndexAttachments indexAttachments;
- private MailboxMessage message;
- private TextExtractor textExtractor;
- private List<User> users;
-
- private ZoneId zoneId;
-
- private Builder() {
- }
-
- public IndexableMessage build() {
- Preconditions.checkNotNull(message.getMailboxId());
- Preconditions.checkNotNull(users);
- Preconditions.checkNotNull(textExtractor);
- Preconditions.checkNotNull(indexAttachments);
- Preconditions.checkNotNull(zoneId);
- Preconditions.checkState(!users.isEmpty());
-
- try {
- return instanciateIndexedMessage();
- } catch (IOException | MimeException e) {
- throw new RuntimeException(e);
- }
- }
-
- public Builder extractor(TextExtractor textExtractor) {
- this.textExtractor = textExtractor;
- return this;
- }
-
- public Builder indexAttachments(IndexAttachments indexAttachments) {
- this.indexAttachments = indexAttachments;
- return this;
- }
-
- public Builder message(MailboxMessage message) {
- this.message = message;
- return this;
- }
-
- public Builder users(List<User> users) {
- this.users = users;
- return this;
- }
-
- public Builder zoneId(ZoneId zoneId) {
- this.zoneId = zoneId;
- return this;
- }
-
- private boolean computeHasAttachment(MailboxMessage message) {
- return message.getProperties()
- .stream()
- .anyMatch(property -> property.equals(HAS_ATTACHMENT_PROPERTY));
- }
-
- private IndexableMessage instanciateIndexedMessage() throws IOException, MimeException {
- String messageId = SearchUtil.getSerializedMessageIdIfSupportedByUnderlyingStorageOrNull(message);
- MimePart parsingResult = new MimePartParser(message, textExtractor).parse();
-
- List<String> stringifiedUsers = users.stream()
- .map(User::asString)
- .collect(Guavate.toImmutableList());
-
- Optional<String> bodyText = parsingResult.locateFirstTextBody();
- Optional<String> bodyHtml = parsingResult.locateFirstHtmlBody();
-
- boolean hasAttachment = computeHasAttachment(message);
- List<MimePart> attachments = setFlattenedAttachments(parsingResult, indexAttachments);
-
- HeaderCollection headerCollection = parsingResult.getHeaderCollection();
- ZonedDateTime internalDate = getSanitizedInternalDate(message, zoneId);
-
- Multimap<String, String> headers = headerCollection.getHeaders();
- Subjects subjects = Subjects.from(headerCollection.getSubjectSet());
- EMailers from = EMailers.from(headerCollection.getFromAddressSet());
- EMailers to = EMailers.from(headerCollection.getToAddressSet());
- EMailers replyTo = EMailers.from(headerCollection.getReplyToAddressSet());
- EMailers cc = EMailers.from(headerCollection.getCcAddressSet());
- EMailers bcc = EMailers.from(headerCollection.getBccAddressSet());
- String sentDate = DateResolutionFormater.DATE_TIME_FOMATTER.format(headerCollection.getSentDate().orElse(internalDate));
- Optional<String> mimeMessageID = headerCollection.getMessageID();
-
- String text = Stream.of(from.serialize(),
- to.serialize(),
- cc.serialize(),
- bcc.serialize(),
- subjects.serialize(),
- bodyText.orElse(null),
- bodyHtml.orElse(null))
- .filter(str -> !Strings.isNullOrEmpty(str))
- .collect(Collectors.joining(" "));
-
- long uid = message.getUid().asLong();
- String mailboxId = message.getMailboxId().serialize();
- long modSeq = message.getModSeq();
- long size = message.getFullContentOctets();
- String date = DateResolutionFormater.DATE_TIME_FOMATTER.format(getSanitizedInternalDate(message, zoneId));
- String mediaType = message.getMediaType();
- String subType = message.getSubType();
- boolean isAnswered = message.isAnswered();
- boolean isDeleted = message.isDeleted();
- boolean isDraft = message.isDraft();
- boolean isFlagged = message.isFlagged();
- boolean isRecent = message.isRecent();
- boolean isUnRead = !message.isSeen();
- String[] userFlags = message.createFlags().getUserFlags();
- List<Property> properties = message.getProperties();
-
- return new IndexableMessage(
- attachments,
- bcc,
- bodyHtml,
- bodyText,
- cc,
- date,
- from,
- hasAttachment,
- headers,
- isAnswered,
- isDeleted,
- isDraft,
- isFlagged,
- isRecent,
- isUnRead,
- mailboxId,
- mediaType,
- messageId,
- modSeq,
- properties,
- replyTo,
- sentDate,
- size,
- subjects,
- subType,
- text,
- to,
- uid,
- userFlags,
- stringifiedUsers,
- mimeMessageID);
- }
-
- private List<MimePart> setFlattenedAttachments(MimePart parsingResult, IndexAttachments indexAttachments) {
- if (IndexAttachments.YES.equals(indexAttachments)) {
- return parsingResult.getAttachmentsStream()
- .collect(Guavate.toImmutableList());
- } else {
- return ImmutableList.of();
- }
- }
- }
-
- public static final SimpleProperty HAS_ATTACHMENT_PROPERTY = new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true");
-
- public static Builder builder() {
- return new Builder();
- }
-
- private final List<MimePart> attachments;
- private final EMailers bcc;
- private final Optional<String> bodyHtml;
- private final Optional<String> bodyText;
- private final EMailers cc;
- private final String date;
- private final EMailers from;
- private final boolean hasAttachment;
- private final Multimap<String, String> headers;
- private final boolean isAnswered;
- private final boolean isDeleted;
- private final boolean isDraft;
- private final boolean isFlagged;
- private final boolean isRecent;
- private final boolean isUnRead;
- private final String mailboxId;
- private final String mediaType;
- private final String messageId;
- private final long modSeq;
- private final List<Property> properties;
- private final EMailers replyTo;
- private final String sentDate;
- private final long size;
- private final Subjects subjects;
- private final String subType;
- private final String text;
- private final EMailers to;
- private final long uid;
- private final String[] userFlags;
- private final List<String> users;
- private final Optional<String> mimeMessageID;
-
- private IndexableMessage(
- List<MimePart> attachments,
- EMailers bcc,
- Optional<String> bodyHtml,
- Optional<String> bodyText,
- EMailers cc,
- String date,
- EMailers from,
- boolean hasAttachment,
- Multimap<String, String> headers,
- boolean isAnswered,
- boolean isDeleted,
- boolean isDraft,
- boolean isFlagged,
- boolean isRecent,
- boolean isUnRead,
- String mailboxId,
- String mediaType,
- String messageId,
- long modSeq,
- List<Property> properties,
- EMailers replyTo,
- String sentDate,
- long size,
- Subjects subjects,
- String subType,
- String text,
- EMailers to,
- long uid,
- String[] userFlags,
- List<String> users,
- Optional<String> mimeMessageID) {
- this.attachments = attachments;
- this.bcc = bcc;
- this.bodyHtml = bodyHtml;
- this.bodyText = bodyText;
- this.cc = cc;
- this.date = date;
- this.from = from;
- this.hasAttachment = hasAttachment;
- this.headers = headers;
- this.isAnswered = isAnswered;
- this.isDeleted = isDeleted;
- this.isDraft = isDraft;
- this.isFlagged = isFlagged;
- this.isRecent = isRecent;
- this.isUnRead = isUnRead;
- this.mailboxId = mailboxId;
- this.mediaType = mediaType;
- this.messageId = messageId;
- this.modSeq = modSeq;
- this.properties = properties;
- this.replyTo = replyTo;
- this.sentDate = sentDate;
- this.size = size;
- this.subjects = subjects;
- this.subType = subType;
- this.text = text;
- this.to = to;
- this.uid = uid;
- this.userFlags = userFlags;
- this.users = users;
- this.mimeMessageID = mimeMessageID;
- }
-
- @JsonProperty(JsonMessageConstants.ATTACHMENTS)
- public List<MimePart> getAttachments() {
- return attachments;
- }
-
- @JsonProperty(JsonMessageConstants.BCC)
- public EMailers getBcc() {
- return bcc;
- }
-
- @JsonProperty(JsonMessageConstants.HTML_BODY)
- public Optional<String> getBodyHtml() {
- return bodyHtml;
- }
-
- @JsonProperty(JsonMessageConstants.TEXT_BODY)
- public Optional<String> getBodyText() {
- return bodyText;
- }
-
- @JsonProperty(JsonMessageConstants.CC)
- public EMailers getCc() {
- return cc;
- }
-
- @JsonProperty(JsonMessageConstants.DATE)
- public String getDate() {
- return date;
- }
-
- @JsonProperty(JsonMessageConstants.FROM)
- public EMailers getFrom() {
- return from;
- }
-
- @JsonProperty(JsonMessageConstants.HAS_ATTACHMENT)
- public boolean getHasAttachment() {
- return hasAttachment;
- }
-
- @JsonProperty(JsonMessageConstants.HEADERS)
- public Multimap<String, String> getHeaders() {
- return headers;
- }
-
- @JsonProperty(JsonMessageConstants.MAILBOX_ID)
- public String getMailboxId() {
- return mailboxId;
- }
-
- @JsonProperty(JsonMessageConstants.MEDIA_TYPE)
- public String getMediaType() {
- return mediaType;
- }
-
- @JsonProperty(JsonMessageConstants.MESSAGE_ID)
- public String getMessageId() {
- return messageId;
- }
-
- @JsonProperty(JsonMessageConstants.MODSEQ)
- public long getModSeq() {
- return modSeq;
- }
-
- @JsonProperty(JsonMessageConstants.PROPERTIES)
- public List<Property> getProperties() {
- return properties;
- }
-
- @JsonProperty(JsonMessageConstants.REPLY_TO)
- public EMailers getReplyTo() {
- return replyTo;
- }
-
- @JsonProperty(JsonMessageConstants.SENT_DATE)
- public String getSentDate() {
- return sentDate;
- }
-
- @JsonProperty(JsonMessageConstants.SIZE)
- public long getSize() {
- return size;
- }
-
- @JsonProperty(JsonMessageConstants.SUBJECT)
- public Subjects getSubjects() {
- return subjects;
- }
-
- @JsonProperty(JsonMessageConstants.SUBTYPE)
- public String getSubType() {
- return subType;
- }
-
- @JsonProperty(JsonMessageConstants.TEXT)
- public String getText() {
- return text;
- }
-
- @JsonProperty(JsonMessageConstants.TO)
- public EMailers getTo() {
- return to;
- }
-
- @JsonProperty(JsonMessageConstants.UID)
- public Long getUid() {
- return uid;
- }
-
- @JsonProperty(JsonMessageConstants.USER_FLAGS)
- public String[] getUserFlags() {
- return userFlags;
- }
-
- @JsonProperty(JsonMessageConstants.USERS)
- public List<String> getUsers() {
- return users;
- }
-
- @JsonProperty(JsonMessageConstants.IS_ANSWERED)
- public boolean isAnswered() {
- return isAnswered;
- }
-
- @JsonProperty(JsonMessageConstants.IS_DELETED)
- public boolean isDeleted() {
- return isDeleted;
- }
-
- @JsonProperty(JsonMessageConstants.IS_DRAFT)
- public boolean isDraft() {
- return isDraft;
- }
-
- @JsonProperty(JsonMessageConstants.IS_FLAGGED)
- public boolean isFlagged() {
- return isFlagged;
- }
-
- @JsonProperty(JsonMessageConstants.IS_RECENT)
- public boolean isRecent() {
- return isRecent;
- }
-
- @JsonProperty(JsonMessageConstants.IS_UNREAD)
- public boolean isUnRead() {
- return isUnRead;
- }
-
- @JsonProperty(JsonMessageConstants.MIME_MESSAGE_ID)
- public Optional<String> getMimeMessageID() {
- return mimeMessageID;
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
deleted file mode 100644
index 21ce86c..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-public interface JsonMessageConstants {
-
- /*
- Properties defined by JMAP
- */
- String MESSAGE_ID = "messageId";
- String UID = "uid";
- String MAILBOX_ID = "mailboxId";
- String USERS = "users";
- String IS_UNREAD = "isUnread";
- String IS_FLAGGED = "isFlagged";
- String IS_ANSWERED = "isAnswered";
- String IS_DRAFT = "isDraft";
- String HEADERS = "headers";
- String FROM = "from";
- String TO = "to";
- String CC = "cc";
- String BCC = "bcc";
- String REPLY_TO = "replyTo";
- String SUBJECT = "subject";
- String DATE = "date";
- String SIZE = "size";
- String TEXT_BODY = "textBody";
- String HTML_BODY = "htmlBody";
- String SENT_DATE = "sentDate";
- String ATTACHMENTS = "attachments";
- String TEXT = "text";
- String MIME_MESSAGE_ID = "mimeMessageID";
-
- /*
- James properties we can easily get
- */
- String PROPERTIES = "properties";
- String MODSEQ = "modSeq";
- String USER_FLAGS = "userFlags";
- String IS_RECENT = "isRecent";
- String IS_DELETED = "isDeleted";
- String MEDIA_TYPE = "mediaType";
- String SUBTYPE = "subtype";
- String HAS_ATTACHMENT = "hasAttachment";
-
- interface EMailer {
- String NAME = "name";
- String ADDRESS = "address";
- }
-
- interface Attachment {
- String TEXT_CONTENT = "textContent";
- String MEDIA_TYPE = "mediaType";
- String SUBTYPE = "subtype";
- String CONTENT_DISPOSITION = "contentDisposition";
- String FILENAME = "fileName";
- String FILE_EXTENSION = "fileExtension";
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java
deleted file mode 100644
index 95d9c5d..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.time.ZoneId;
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.mail.Flags;
-
-import org.apache.james.core.User;
-import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.guava.GuavaModule;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
-import com.google.common.base.Preconditions;
-
-public class MessageToElasticSearchJson {
-
- private final ObjectMapper mapper;
- private final TextExtractor textExtractor;
- private final ZoneId zoneId;
- private final IndexAttachments indexAttachments;
-
- public MessageToElasticSearchJson(TextExtractor textExtractor, ZoneId zoneId, IndexAttachments indexAttachments) {
- this.textExtractor = textExtractor;
- this.zoneId = zoneId;
- this.indexAttachments = indexAttachments;
- this.mapper = new ObjectMapper();
- this.mapper.registerModule(new GuavaModule());
- this.mapper.registerModule(new Jdk8Module());
- }
-
- @Inject
- public MessageToElasticSearchJson(TextExtractor textExtractor, IndexAttachments indexAttachments) {
- this(textExtractor, ZoneId.systemDefault(), indexAttachments);
- }
-
- public String convertToJson(MailboxMessage message, List<User> users) throws JsonProcessingException {
- Preconditions.checkNotNull(message);
-
- return mapper.writeValueAsString(IndexableMessage.builder()
- .message(message)
- .users(users)
- .extractor(textExtractor)
- .zoneId(zoneId)
- .indexAttachments(indexAttachments)
- .build());
- }
-
- public String convertToJsonWithoutAttachment(MailboxMessage message, List<User> users) throws JsonProcessingException {
- return mapper.writeValueAsString(IndexableMessage.builder()
- .message(message)
- .users(users)
- .extractor(textExtractor)
- .zoneId(zoneId)
- .indexAttachments(IndexAttachments.NO)
- .build());
- }
-
- public String getUpdatedJsonMessagePart(Flags flags, long modSeq) throws JsonProcessingException {
- Preconditions.checkNotNull(flags);
- return mapper.writeValueAsString(new MessageUpdateJson(flags, modSeq));
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
deleted file mode 100644
index 3e42114..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-/****************************************************************
- * 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.v6.json;
-
-import javax.mail.Flags;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class MessageUpdateJson {
-
- private final Flags flags;
- private final long modSeq;
-
- public MessageUpdateJson(Flags flags, long modSeq) {
- this.flags = flags;
- this.modSeq = modSeq;
- }
-
- @JsonProperty(JsonMessageConstants.IS_ANSWERED)
- public boolean isAnswered() {
- return flags.contains(Flags.Flag.ANSWERED);
- }
-
- @JsonProperty(JsonMessageConstants.IS_DELETED)
- public boolean isDeleted() {
- return flags.contains(Flags.Flag.DELETED);
- }
-
- @JsonProperty(JsonMessageConstants.IS_DRAFT)
- public boolean isDraft() {
- return flags.contains(Flags.Flag.DRAFT);
- }
-
- @JsonProperty(JsonMessageConstants.IS_FLAGGED)
- public boolean isFlagged() {
- return flags.contains(Flags.Flag.FLAGGED);
- }
-
- @JsonProperty(JsonMessageConstants.IS_RECENT)
- public boolean isRecent() {
- return flags.contains(Flags.Flag.RECENT);
- }
-
- @JsonProperty(JsonMessageConstants.IS_UNREAD)
- public boolean isUnRead() {
- return !flags.contains(Flags.Flag.SEEN);
- }
-
-
- @JsonProperty(JsonMessageConstants.USER_FLAGS)
- public String[] getUserFlags() {
- return flags.getUserFlags();
- }
-
- @JsonProperty(JsonMessageConstants.MODSEQ)
- public long getModSeq() {
- return modSeq;
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java
deleted file mode 100644
index 3d700cb..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.james.mailbox.extractor.ParsedContent;
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
-import org.apache.james.mime4j.stream.Field;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-
-public class MimePart {
-
- public static class Builder implements MimePartContainerBuilder {
-
- private final HeaderCollection.Builder headerCollectionBuilder;
- private Optional<InputStream> bodyContent;
- private final List<MimePart> children;
- private Optional<String> mediaType;
- private Optional<String> subType;
- private Optional<String> fileName;
- private Optional<String> fileExtension;
- private Optional<String> contentDisposition;
- private Optional<Charset> charset;
- private TextExtractor textExtractor;
-
- private Builder() {
- children = Lists.newArrayList();
- headerCollectionBuilder = HeaderCollection.builder();
- this.bodyContent = Optional.empty();
- this.mediaType = Optional.empty();
- this.subType = Optional.empty();
- this.fileName = Optional.empty();
- this.fileExtension = Optional.empty();
- this.contentDisposition = Optional.empty();
- this.charset = Optional.empty();
- this.textExtractor = new DefaultTextExtractor();
- }
-
- @Override
- public Builder addToHeaders(Field field) {
- headerCollectionBuilder.add(field);
- return this;
- }
-
- @Override
- public Builder addBodyContent(InputStream bodyContent) {
- this.bodyContent = Optional.of(bodyContent);
- return this;
- }
-
- @Override
- public Builder addChild(MimePart mimePart) {
- children.add(mimePart);
- return this;
- }
-
- @Override
- public Builder addFileName(String fileName) {
- this.fileName = Optional.ofNullable(fileName);
- this.fileExtension = this.fileName.map(FilenameUtils::getExtension);
- return this;
- }
-
- @Override
- public Builder addMediaType(String mediaType) {
- this.mediaType = Optional.ofNullable(mediaType);
- return this;
- }
-
- @Override
- public Builder addSubType(String subType) {
- this.subType = Optional.ofNullable(subType);
- return this;
- }
-
- @Override
- public Builder addContentDisposition(String contentDisposition) {
- this.contentDisposition = Optional.ofNullable(contentDisposition);
- return this;
- }
-
- @Override
- public MimePartContainerBuilder using(TextExtractor textExtractor) {
- Preconditions.checkArgument(textExtractor != null, "Provided text extractor should not be null");
- this.textExtractor = textExtractor;
- return this;
- }
-
- @Override
- public MimePartContainerBuilder charset(Charset charset) {
- this.charset = Optional.of(charset);
- return this;
- }
-
- @Override
- public MimePart build() {
- Optional<ParsedContent> parsedContent = parseContent(textExtractor);
- return new MimePart(
- headerCollectionBuilder.build(),
- parsedContent.flatMap(ParsedContent::getTextualContent),
- mediaType,
- subType,
- fileName,
- fileExtension,
- contentDisposition,
- children);
- }
-
- private Optional<ParsedContent> parseContent(TextExtractor textExtractor) {
- if (bodyContent.isPresent()) {
- try {
- return Optional.of(extractText(textExtractor, bodyContent.get()));
- } catch (Throwable e) {
- LOGGER.warn("Failed parsing attachment", e);
- }
- }
- return Optional.empty();
- }
-
- private ParsedContent extractText(TextExtractor textExtractor, InputStream bodyContent) throws Exception {
- if (isTextBody()) {
- return new ParsedContent(
- Optional.ofNullable(IOUtils.toString(bodyContent, charset.orElse(StandardCharsets.UTF_8))),
- ImmutableMap.of());
- }
- return textExtractor.extractContent(
- bodyContent,
- computeContentType().orElse(null));
- }
-
- private Boolean isTextBody() {
- return mediaType.map("text"::equals).orElse(false);
- }
-
- private Optional<String> computeContentType() {
- if (mediaType.isPresent() && subType.isPresent()) {
- return Optional.of(mediaType.get() + "/" + subType.get());
- } else {
- return Optional.empty();
- }
- }
-
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MimePart.class);
-
- private final HeaderCollection headerCollection;
- private final Optional<String> bodyTextContent;
- private final Optional<String> mediaType;
- private final Optional<String> subType;
- private final Optional<String> fileName;
- private final Optional<String> fileExtension;
- private final Optional<String> contentDisposition;
- private final List<MimePart> attachments;
-
- private MimePart(HeaderCollection headerCollection, Optional<String> bodyTextContent, Optional<String> mediaType,
- Optional<String> subType, Optional<String> fileName, Optional<String> fileExtension,
- Optional<String> contentDisposition, List<MimePart> attachments) {
- this.headerCollection = headerCollection;
- this.mediaType = mediaType;
- this.subType = subType;
- this.fileName = fileName;
- this.fileExtension = fileExtension;
- this.contentDisposition = contentDisposition;
- this.attachments = attachments;
- this.bodyTextContent = bodyTextContent;
- }
-
- @JsonIgnore
- public List<MimePart> getAttachments() {
- return attachments;
- }
-
- @JsonIgnore
- public HeaderCollection getHeaderCollection() {
- return headerCollection;
- }
-
- @JsonProperty(JsonMessageConstants.HEADERS)
- public Multimap<String, String> getHeaders() {
- return headerCollection.getHeaders();
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.FILENAME)
- public Optional<String> getFileName() {
- return fileName;
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.FILE_EXTENSION)
- public Optional<String> getFileExtension() {
- return fileExtension;
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.MEDIA_TYPE)
- public Optional<String> getMediaType() {
- return mediaType;
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.SUBTYPE)
- public Optional<String> getSubType() {
- return subType;
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.CONTENT_DISPOSITION)
- public Optional<String> getContentDisposition() {
- return contentDisposition;
- }
-
- @JsonProperty(JsonMessageConstants.Attachment.TEXT_CONTENT)
- public Optional<String> getTextualBody() {
- return bodyTextContent;
- }
-
- @JsonIgnore
- public Optional<String> locateFirstTextBody() {
- return firstBody(textAttachments()
- .filter(this::isPlainSubType));
- }
-
- @JsonIgnore
- public Optional<String> locateFirstHtmlBody() {
- return firstBody(textAttachments()
- .filter(this::isHtmlSubType));
- }
-
- private Optional<String> firstBody(Stream<MimePart> mimeParts) {
- return mimeParts
- .map((mimePart) -> mimePart.bodyTextContent)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .findFirst();
- }
-
- private Stream<MimePart> textAttachments() {
- return Stream.concat(
- Stream.of(this),
- attachments.stream())
- .filter(this::isTextMediaType);
- }
-
- private boolean isTextMediaType(MimePart mimePart) {
- return mimePart.getMediaType()
- .filter("text"::equals)
- .isPresent();
- }
-
- private boolean isPlainSubType(MimePart mimePart) {
- return mimePart.getSubType()
- .filter("plain"::equals)
- .isPresent();
- }
-
- private boolean isHtmlSubType(MimePart mimePart) {
- return mimePart.getSubType()
- .filter("html"::equals)
- .isPresent();
- }
-
- @JsonIgnore
- public Stream<MimePart> getAttachmentsStream() {
- return attachments.stream()
- .flatMap((mimePart) -> Stream.concat(Stream.of(mimePart), mimePart.getAttachmentsStream()));
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java
deleted file mode 100644
index 5a12008..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.io.InputStream;
-import java.nio.charset.Charset;
-
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mime4j.stream.Field;
-
-public interface MimePartContainerBuilder {
-
- MimePart build();
-
- MimePartContainerBuilder using(TextExtractor textExtractor);
-
- MimePartContainerBuilder addToHeaders(Field field);
-
- MimePartContainerBuilder addBodyContent(InputStream bodyContent);
-
- MimePartContainerBuilder addChild(MimePart mimePart);
-
- MimePartContainerBuilder addFileName(String fileName);
-
- MimePartContainerBuilder charset(Charset charset);
-
- MimePartContainerBuilder addMediaType(String mediaType);
-
- MimePartContainerBuilder addSubType(String subType);
-
- MimePartContainerBuilder addContentDisposition(String contentDisposition);
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java
deleted file mode 100644
index 0f2a8ff..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.Optional;
-
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.store.mail.model.Message;
-import org.apache.james.mime4j.MimeException;
-import org.apache.james.mime4j.message.DefaultBodyDescriptorBuilder;
-import org.apache.james.mime4j.message.MaximalBodyDescriptor;
-import org.apache.james.mime4j.stream.EntityState;
-import org.apache.james.mime4j.stream.MimeConfig;
-import org.apache.james.mime4j.stream.MimeTokenStream;
-
-import com.google.common.base.Preconditions;
-
-public class MimePartParser {
-
- private final Message message;
- private final TextExtractor textExtractor;
- private final MimeTokenStream stream;
- private final Deque<MimePartContainerBuilder> builderStack;
- private MimePart result;
- private MimePartContainerBuilder currentlyBuildMimePart;
-
- public MimePartParser(Message message, TextExtractor textExtractor) {
- this.message = message;
- this.textExtractor = textExtractor;
- this.builderStack = new LinkedList<>();
- this.currentlyBuildMimePart = new RootMimePartContainerBuilder();
- this.stream = new MimeTokenStream(
- MimeConfig.PERMISSIVE,
- new DefaultBodyDescriptorBuilder());
- }
-
- public MimePart parse() throws IOException, MimeException {
- stream.parse(message.getFullContent());
- for (EntityState state = stream.getState(); state != EntityState.T_END_OF_STREAM; state = stream.next()) {
- processMimePart(stream, state);
- }
- return result;
- }
-
- private void processMimePart(MimeTokenStream stream, EntityState state) {
- switch (state) {
- case T_START_MULTIPART:
- case T_START_MESSAGE:
- stackCurrent();
- break;
- case T_START_HEADER:
- currentlyBuildMimePart = MimePart.builder();
- break;
- case T_FIELD:
- currentlyBuildMimePart.addToHeaders(stream.getField());
- break;
- case T_BODY:
- manageBodyExtraction(stream);
- closeMimePart();
- break;
- case T_END_MULTIPART:
- case T_END_MESSAGE:
- unstackToCurrent();
- closeMimePart();
- break;
- default:
- break;
- }
- }
-
- private void stackCurrent() {
- builderStack.push(currentlyBuildMimePart);
- currentlyBuildMimePart = null;
- }
-
- private void unstackToCurrent() {
- currentlyBuildMimePart = builderStack.pop();
- }
-
- private void closeMimePart() {
- MimePart bodyMimePart = currentlyBuildMimePart.using(textExtractor).build();
- if (!builderStack.isEmpty()) {
- builderStack.peek().addChild(bodyMimePart);
- } else {
- Preconditions.checkState(result == null);
- result = bodyMimePart;
- }
- }
-
- private void manageBodyExtraction(MimeTokenStream stream) {
- extractMimePartBodyDescription(stream);
- currentlyBuildMimePart.addBodyContent(stream.getDecodedInputStream());
- }
-
- private void extractMimePartBodyDescription(MimeTokenStream stream) {
- MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) stream.getBodyDescriptor();
-
- currentlyBuildMimePart.addMediaType(descriptor.getMediaType())
- .addSubType(descriptor.getSubType())
- .addContentDisposition(descriptor.getContentDispositionType())
- .addFileName(descriptor.getContentDispositionFilename());
-
- Optional.ofNullable(descriptor.getCharset())
- .map(Charset::forName)
- .ifPresent(currentlyBuildMimePart::charset);
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java
deleted file mode 100644
index 415d96f..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.io.InputStream;
-import java.nio.charset.Charset;
-
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mime4j.stream.Field;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RootMimePartContainerBuilder implements MimePartContainerBuilder {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(RootMimePartContainerBuilder.class);
-
- private MimePart rootMimePart;
-
- @Override
- public MimePart build() {
- return rootMimePart;
- }
-
- @Override public MimePartContainerBuilder using(TextExtractor textExtractor) {
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addToHeaders(Field field) {
- LOGGER.warn("Trying to add headers to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addBodyContent(InputStream bodyContent) {
- LOGGER.warn("Trying to add body content to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addChild(MimePart mimePart) {
- if (rootMimePart == null) {
- rootMimePart = mimePart;
- } else {
- LOGGER.warn("Trying to add several children to the Root MimePart container");
- }
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addFileName(String fileName) {
- LOGGER.warn("Trying to add fineName to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addMediaType(String mediaType) {
- LOGGER.warn("Trying to add media type to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addSubType(String subType) {
- LOGGER.warn("Trying to add sub type to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder addContentDisposition(String contentDisposition) {
- LOGGER.warn("Trying to add content disposition to the Root MimePart container");
- return this;
- }
-
- @Override
- public MimePartContainerBuilder charset(Charset charset) {
- LOGGER.warn("Trying to add content charset to the Root MimePart container");
- return this;
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java
deleted file mode 100644
index b5d903e..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-public interface SerializableMessage {
-
- String serialize();
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
deleted file mode 100644
index a6a29f4..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.util.Set;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-
-public class Subjects implements SerializableMessage {
-
- public static Subjects from(Set<String> subjects) {
- Preconditions.checkNotNull(subjects, "'subjects' is mandatory");
- return new Subjects(subjects);
- }
-
- private final Set<String> subjects;
-
- private Subjects(Set<String> subjects) {
- this.subjects = subjects;
- }
-
- @JsonValue
- public Set<String> getSubjects() {
- return subjects;
- }
-
- @Override
- public String serialize() {
- return Joiner.on(" ").join(subjects);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
deleted file mode 100644
index 4e8c44d..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import static org.apache.james.backends.es.v6.NodeMappingFactory.RAW;
-import static org.apache.james.backends.es.v6.NodeMappingFactory.SPLIT_EMAIL;
-import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
-import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
-import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
-import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
-import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
-import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
-import static org.elasticsearch.index.query.QueryBuilders.termQuery;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.stream.Collector;
-import java.util.stream.Stream;
-
-import javax.mail.Flags;
-
-import org.apache.james.mailbox.elasticsearch.v6.json.HeaderCollection;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.Criterion;
-import org.apache.james.mailbox.model.SearchQuery.HeaderOperator;
-import org.apache.lucene.search.join.ScoreMode;
-import org.elasticsearch.index.query.BoolQueryBuilder;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-
-public class CriterionConverter {
-
- private final Map<Class<?>, Function<Criterion, QueryBuilder>> criterionConverterMap;
- private final Map<Class<?>, BiFunction<String, HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
-
- public CriterionConverter() {
- criterionConverterMap = new HashMap<>();
- headerOperatorConverterMap = new HashMap<>();
-
- registerCriterionConverters();
- registerHeaderOperatorConverters();
- }
-
- private void registerCriterionConverters() {
- registerCriterionConverter(SearchQuery.FlagCriterion.class, this::convertFlag);
- registerCriterionConverter(SearchQuery.UidCriterion.class, this::convertUid);
- registerCriterionConverter(SearchQuery.ConjunctionCriterion.class, this::convertConjunction);
- registerCriterionConverter(SearchQuery.HeaderCriterion.class, this::convertHeader);
- registerCriterionConverter(SearchQuery.TextCriterion.class, this::convertTextCriterion);
- registerCriterionConverter(SearchQuery.CustomFlagCriterion.class, this::convertCustomFlagCriterion);
-
- registerCriterionConverter(SearchQuery.AllCriterion.class,
- criterion -> matchAllQuery());
-
- registerCriterionConverter(SearchQuery.ModSeqCriterion.class,
- criterion -> createNumericFilter(JsonMessageConstants.MODSEQ, criterion.getOperator()));
-
- registerCriterionConverter(SearchQuery.SizeCriterion.class,
- criterion -> createNumericFilter(JsonMessageConstants.SIZE, criterion.getOperator()));
-
- registerCriterionConverter(SearchQuery.InternalDateCriterion.class,
- criterion -> dateRangeFilter(JsonMessageConstants.DATE, criterion.getOperator()));
-
- registerCriterionConverter(SearchQuery.AttachmentCriterion.class, this::convertAttachmentCriterion);
- registerCriterionConverter(SearchQuery.MimeMessageIDCriterion.class, this::convertMimeMessageIDCriterion);
- }
-
- @SuppressWarnings("unchecked")
- private <T extends Criterion> void registerCriterionConverter(Class<T> type, Function<T, QueryBuilder> f) {
- criterionConverterMap.put(type, (Function<Criterion, QueryBuilder>) f);
- }
-
- private void registerHeaderOperatorConverters() {
-
- registerHeaderOperatorConverter(
- SearchQuery.ExistsOperator.class,
- (headerName, operator) ->
- existsQuery(JsonMessageConstants.HEADERS + "." + headerName));
-
- registerHeaderOperatorConverter(
- SearchQuery.AddressOperator.class,
- (headerName, operator) -> manageAddressFields(headerName, operator.getAddress()));
-
- registerHeaderOperatorConverter(
- SearchQuery.DateOperator.class,
- (headerName, operator) -> dateRangeFilter(JsonMessageConstants.SENT_DATE, operator));
-
- registerHeaderOperatorConverter(
- SearchQuery.ContainsOperator.class,
- (headerName, operator) -> matchQuery(JsonMessageConstants.HEADERS + "." + headerName,
- operator.getValue()));
- }
-
- @SuppressWarnings("unchecked")
- private <T extends HeaderOperator> void registerHeaderOperatorConverter(Class<T> type, BiFunction<String, T, QueryBuilder> f) {
- headerOperatorConverterMap.put(type, (BiFunction<String, HeaderOperator, QueryBuilder>) f);
- }
-
- public QueryBuilder convertCriterion(Criterion criterion) {
- return criterionConverterMap.get(criterion.getClass()).apply(criterion);
- }
-
- private QueryBuilder convertAttachmentCriterion(SearchQuery.AttachmentCriterion criterion) {
- return termQuery(JsonMessageConstants.HAS_ATTACHMENT, criterion.getOperator().isSet());
- }
-
- private QueryBuilder convertMimeMessageIDCriterion(SearchQuery.MimeMessageIDCriterion criterion) {
- return termQuery(JsonMessageConstants.MIME_MESSAGE_ID, criterion.getMessageID());
- }
-
- private QueryBuilder convertCustomFlagCriterion(SearchQuery.CustomFlagCriterion criterion) {
- QueryBuilder termQueryBuilder = termQuery(JsonMessageConstants.USER_FLAGS, criterion.getFlag());
- if (criterion.getOperator().isSet()) {
- return termQueryBuilder;
- } else {
- return boolQuery().mustNot(termQueryBuilder);
- }
- }
-
- private QueryBuilder convertTextCriterion(SearchQuery.TextCriterion textCriterion) {
- switch (textCriterion.getType()) {
- case BODY:
- return boolQuery()
- .should(matchQuery(JsonMessageConstants.TEXT_BODY, textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.TEXT_BODY + "." + SPLIT_EMAIL,
- textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.HTML_BODY + "." + SPLIT_EMAIL,
- textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()));
- case TEXT:
- return boolQuery()
- .should(matchQuery(JsonMessageConstants.TEXT, textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.TEXT + "." + SPLIT_EMAIL,
- textCriterion.getOperator().getValue()));
- case FULL:
- return boolQuery()
- .should(matchQuery(JsonMessageConstants.TEXT_BODY, textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.TEXT_BODY + "." + SPLIT_EMAIL,
- textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.HTML_BODY + "." + SPLIT_EMAIL,
- textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()))
- .should(matchQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.TEXT_CONTENT,
- textCriterion.getOperator().getValue()));
- case ATTACHMENTS:
- return boolQuery()
- .should(matchQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.TEXT_CONTENT,
- textCriterion.getOperator().getValue()));
- case ATTACHMENT_FILE_NAME:
- return boolQuery()
- .should(termQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.FILENAME,
- textCriterion.getOperator().getValue()));
- }
- throw new RuntimeException("Unknown SCOPE for text criterion");
- }
-
- private QueryBuilder dateRangeFilter(String field, SearchQuery.DateOperator dateOperator) {
- return boolQuery().filter(
- convertDateOperator(field,
- dateOperator.getType(),
- DateResolutionFormater.DATE_TIME_FOMATTER.format(
- DateResolutionFormater.computeLowerDate(
- DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
- dateOperator.getDateResultion())),
- DateResolutionFormater.DATE_TIME_FOMATTER.format(
- DateResolutionFormater.computeUpperDate(
- DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
- dateOperator.getDateResultion()))));
- }
-
- private BoolQueryBuilder convertConjunction(SearchQuery.ConjunctionCriterion criterion) {
- return convertToBoolQuery(criterion.getCriteria().stream().map(this::convertCriterion),
- convertConjunctionType(criterion.getType()));
- }
-
- private BiFunction<BoolQueryBuilder, QueryBuilder, BoolQueryBuilder> convertConjunctionType(SearchQuery.Conjunction type) {
- switch (type) {
- case AND:
- return BoolQueryBuilder::must;
- case OR:
- return BoolQueryBuilder::should;
- case NOR:
- return BoolQueryBuilder::mustNot;
- default:
- throw new RuntimeException("Unexpected conjunction criteria " + type);
- }
- }
-
- private BoolQueryBuilder convertToBoolQuery(Stream<QueryBuilder> stream, BiFunction<BoolQueryBuilder, QueryBuilder, BoolQueryBuilder> addCriterionToBoolQuery) {
- return stream.collect(Collector.of(QueryBuilders::boolQuery,
- addCriterionToBoolQuery::apply,
- addCriterionToBoolQuery::apply));
- }
-
- private QueryBuilder convertFlag(SearchQuery.FlagCriterion flagCriterion) {
- SearchQuery.BooleanOperator operator = flagCriterion.getOperator();
- Flags.Flag flag = flagCriterion.getFlag();
- if (flag.equals(Flags.Flag.DELETED)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_DELETED, operator.isSet()));
- }
- if (flag.equals(Flags.Flag.ANSWERED)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_ANSWERED, operator.isSet()));
- }
- if (flag.equals(Flags.Flag.DRAFT)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_DRAFT, operator.isSet()));
- }
- if (flag.equals(Flags.Flag.SEEN)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_UNREAD, !operator.isSet()));
- }
- if (flag.equals(Flags.Flag.RECENT)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_RECENT, operator.isSet()));
- }
- if (flag.equals(Flags.Flag.FLAGGED)) {
- return boolQuery().filter(termQuery(JsonMessageConstants.IS_FLAGGED, operator.isSet()));
- }
- throw new RuntimeException("Unknown flag used in Flag search criterion");
- }
-
- private QueryBuilder createNumericFilter(String fieldName, SearchQuery.NumericOperator operator) {
- switch (operator.getType()) {
- case EQUALS:
- return boolQuery().filter(rangeQuery(fieldName).gte(operator.getValue()).lte(operator.getValue()));
- case GREATER_THAN:
- return boolQuery().filter(rangeQuery(fieldName).gte(operator.getValue()));
- case LESS_THAN:
- return boolQuery().filter(rangeQuery(fieldName).lte(operator.getValue()));
- default:
- throw new RuntimeException("A non existing numeric operator was triggered");
- }
- }
-
- private BoolQueryBuilder convertUid(SearchQuery.UidCriterion uidCriterion) {
- if (uidCriterion.getOperator().getRange().length == 0) {
- return boolQuery();
- }
- return boolQuery().filter(
- convertToBoolQuery(
- Arrays.stream(uidCriterion.getOperator().getRange())
- .map(this::uidRangeFilter), BoolQueryBuilder::should));
- }
-
- private QueryBuilder uidRangeFilter(SearchQuery.UidRange numericRange) {
- return rangeQuery(JsonMessageConstants.UID)
- .lte(numericRange.getHighValue().asLong())
- .gte(numericRange.getLowValue().asLong());
- }
-
- private QueryBuilder convertHeader(SearchQuery.HeaderCriterion headerCriterion) {
- return headerOperatorConverterMap.get(headerCriterion.getOperator().getClass())
- .apply(
- headerCriterion.getHeaderName().toLowerCase(Locale.US),
- headerCriterion.getOperator());
- }
-
- private QueryBuilder manageAddressFields(String headerName, String value) {
- return nestedQuery(getFieldNameFromHeaderName(headerName),
- boolQuery()
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)),
- ScoreMode.Avg);
- }
-
- private String getFieldNameFromHeaderName(String headerName) {
- switch (headerName.toLowerCase(Locale.US)) {
- case HeaderCollection.TO:
- return JsonMessageConstants.TO;
- case HeaderCollection.CC:
- return JsonMessageConstants.CC;
- case HeaderCollection.BCC:
- return JsonMessageConstants.BCC;
- case HeaderCollection.FROM:
- return JsonMessageConstants.FROM;
- }
- throw new RuntimeException("Header not recognized as Addess Header : " + headerName);
- }
-
- private QueryBuilder convertDateOperator(String field, SearchQuery.DateComparator dateComparator, String lowDateString, String upDateString) {
- switch (dateComparator) {
- case BEFORE:
- return rangeQuery(field).lte(upDateString);
- case AFTER:
- return rangeQuery(field).gte(lowDateString);
- case ON:
- return rangeQuery(field).lte(upDateString).gte(lowDateString);
- }
- throw new RuntimeException("Unknown date operator");
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java
deleted file mode 100644
index 731b564..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
-import java.util.Date;
-
-import org.apache.james.mailbox.model.SearchQuery;
-
-public class DateResolutionFormater {
-
- public static DateTimeFormatter DATE_TIME_FOMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
-
- public static ZonedDateTime computeUpperDate(ZonedDateTime date, SearchQuery.DateResolution resolution) {
- return computeLowerDate(date, resolution).plus(1, convertDateResolutionField(resolution));
- }
-
- public static ZonedDateTime computeLowerDate(ZonedDateTime date, SearchQuery.DateResolution resolution) {
- switch (resolution) {
- case Year:
- return date.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
- case Month:
- return date.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
- default:
- return date.truncatedTo(convertDateResolutionField(resolution));
- }
- }
-
- private static TemporalUnit convertDateResolutionField(SearchQuery.DateResolution resolution) {
- switch (resolution) {
- case Year:
- return ChronoUnit.YEARS;
- case Month:
- return ChronoUnit.MONTHS;
- case Day:
- return ChronoUnit.DAYS;
- case Hour:
- return ChronoUnit.HOURS;
- case Minute:
- return ChronoUnit.MINUTES;
- case Second:
- return ChronoUnit.SECONDS;
- default:
- throw new RuntimeException("Unknown Date resolution used");
- }
- }
-
- public static ZonedDateTime convertDateToZonedDateTime(Date date) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
- }
-}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java
deleted file mode 100644
index c06239b..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
-import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Inject;
-
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.elasticsearch.index.query.BoolQueryBuilder;
-import org.elasticsearch.index.query.QueryBuilder;
-
-import com.github.steveash.guavate.Guavate;
-import com.google.common.collect.ImmutableList;
-
-public class QueryConverter {
-
-
- private final CriterionConverter criterionConverter;
-
- @Inject
- public QueryConverter(CriterionConverter criterionConverter) {
- this.criterionConverter = criterionConverter;
- }
-
- public QueryBuilder from(Collection<MailboxId> mailboxIds, SearchQuery query) {
- BoolQueryBuilder boolQueryBuilder = boolQuery()
- .must(generateQueryBuilder(query));
-
- mailboxesQuery(mailboxIds).map(boolQueryBuilder::filter);
- return boolQueryBuilder;
- }
-
- private QueryBuilder generateQueryBuilder(SearchQuery searchQuery) {
- List<SearchQuery.Criterion> criteria = searchQuery.getCriterias();
- if (criteria.isEmpty()) {
- return criterionConverter.convertCriterion(SearchQuery.all());
- } else if (criteria.size() == 1) {
- return criterionConverter.convertCriterion(criteria.get(0));
- } else {
- return criterionConverter.convertCriterion(new SearchQuery.ConjunctionCriterion(SearchQuery.Conjunction.AND, criteria));
- }
- }
-
- private Optional<QueryBuilder> mailboxesQuery(Collection<MailboxId> mailboxIds) {
- if (mailboxIds.isEmpty()) {
- return Optional.empty();
- }
- ImmutableList<String> ids = mailboxIds.stream()
- .map(MailboxId::serialize)
- .collect(Guavate.toImmutableList());
- return Optional.of(termsQuery(JsonMessageConstants.MAILBOX_ID, ids));
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
deleted file mode 100644
index 16a52bc..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import org.apache.james.backends.es.v6.NodeMappingFactory;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.elasticsearch.search.sort.FieldSortBuilder;
-import org.elasticsearch.search.sort.NestedSortBuilder;
-import org.elasticsearch.search.sort.SortBuilders;
-import org.elasticsearch.search.sort.SortMode;
-import org.elasticsearch.search.sort.SortOrder;
-
-public class SortConverter {
-
- private static final String PATH_SEPARATOR = ".";
-
- public static FieldSortBuilder convertSort(SearchQuery.Sort sort) {
- return getSortClause(sort.getSortClause())
- .order(getOrder(sort))
- .sortMode(SortMode.MIN);
- }
-
- private static FieldSortBuilder getSortClause(SearchQuery.Sort.SortClause clause) {
- switch (clause) {
- case Arrival :
- return SortBuilders.fieldSort(JsonMessageConstants.DATE);
- case MailboxCc :
- return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
- + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.CC));
- case MailboxFrom :
- return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
- + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
- case MailboxTo :
- return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
- + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
- case BaseSubject :
- return SortBuilders.fieldSort(JsonMessageConstants.SUBJECT + PATH_SEPARATOR + NodeMappingFactory.RAW);
- case Size :
- return SortBuilders.fieldSort(JsonMessageConstants.SIZE);
- case SentDate :
- return SortBuilders.fieldSort(JsonMessageConstants.SENT_DATE);
- case Uid :
- return SortBuilders.fieldSort(JsonMessageConstants.UID);
- case DisplayFrom:
- return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
- + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
- case DisplayTo:
- return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
- + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
- case Id:
- return SortBuilders.fieldSort(JsonMessageConstants.MESSAGE_ID);
- default:
- throw new RuntimeException("Sort is not implemented");
- }
- }
-
- private static SortOrder getOrder(SearchQuery.Sort sort) {
- if (sort.isReverse()) {
- return SortOrder.DESC;
- } else {
- return SortOrder.ASC;
- }
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
deleted file mode 100644
index 16ba761..0000000
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/****************************************************************
- * 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.v6.search;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.apache.james.backends.es.v6.AliasName;
-import org.apache.james.backends.es.v6.NodeMappingFactory;
-import org.apache.james.backends.es.v6.ReadAliasName;
-import org.apache.james.backends.es.v6.search.ScrollIterable;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
-import org.apache.james.mailbox.elasticsearch.v6.query.QueryConverter;
-import org.apache.james.mailbox.elasticsearch.v6.query.SortConverter;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.store.search.MessageSearchIndex;
-import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.elasticsearch.common.document.DocumentField;
-import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.builder.SearchSourceBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ImmutableList;
-
-public class ElasticSearchSearcher {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchSearcher.class);
- private static final TimeValue TIMEOUT = TimeValue.timeValueMinutes(1);
- private static final ImmutableList STORED_FIELDS = ImmutableList.of(JsonMessageConstants.MAILBOX_ID,
- JsonMessageConstants.UID, JsonMessageConstants.MESSAGE_ID);
-
- private final RestHighLevelClient client;
- private final QueryConverter queryConverter;
- private final int size;
- private final MailboxId.Factory mailboxIdFactory;
- private final MessageId.Factory messageIdFactory;
- private final AliasName aliasName;
-
- public ElasticSearchSearcher(RestHighLevelClient client, QueryConverter queryConverter, int size,
- MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory,
- ReadAliasName aliasName) {
- this.client = client;
- this.queryConverter = queryConverter;
- this.size = size;
- this.mailboxIdFactory = mailboxIdFactory;
- this.messageIdFactory = messageIdFactory;
- this.aliasName = aliasName;
- }
-
- public Stream<MessageSearchIndex.SearchResult> search(Collection<MailboxId> mailboxIds, SearchQuery query,
- Optional<Integer> limit) {
- SearchRequest searchRequest = prepareSearch(mailboxIds, query, limit);
- Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequest).stream()
- .flatMap(this::transformResponseToUidStream);
-
- return limit.map(pairStream::limit)
- .orElse(pairStream);
- }
-
- private SearchRequest prepareSearch(Collection<MailboxId> users, SearchQuery query, Optional<Integer> limit) {
- SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
- .query(queryConverter.from(users, query))
- .size(computeRequiredSize(limit))
- .storedFields(STORED_FIELDS);
-
- query.getSorts()
- .stream()
- .map(SortConverter::convertSort)
- .forEach(searchSourceBuilder::sort);
-
- return new SearchRequest(aliasName.getValue())
- .types(NodeMappingFactory.DEFAULT_MAPPING_NAME)
- .scroll(TIMEOUT)
- .source(searchSourceBuilder);
- }
-
- private int computeRequiredSize(Optional<Integer> limit) {
- return limit.map(value -> Math.min(value.intValue(), size))
- .orElse(size);
- }
-
- private Stream<MessageSearchIndex.SearchResult> transformResponseToUidStream(SearchResponse searchResponse) {
- return Arrays.stream(searchResponse.getHits().getHits())
- .map(this::extractContentFromHit)
- .filter(Optional::isPresent)
- .map(Optional::get);
- }
-
- private Optional<MessageSearchIndex.SearchResult> extractContentFromHit(SearchHit hit) {
- DocumentField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
- DocumentField uid = hit.field(JsonMessageConstants.UID);
- Optional<DocumentField> id = retrieveMessageIdField(hit);
- if (mailboxId != null && uid != null) {
- Number uidAsNumber = uid.getValue();
- return Optional.of(
- new MessageSearchIndex.SearchResult(
- id.map(field -> messageIdFactory.fromString(field.getValue())),
- mailboxIdFactory.fromString(mailboxId.getValue()),
- MessageUid.of(uidAsNumber.longValue())));
- } else {
- LOGGER.warn("Can not extract UID, MessageID and/or MailboxId for search result {}", hit.getId());
- return Optional.empty();
- }
- }
-
- private Optional<DocumentField> retrieveMessageIdField(SearchHit hit) {
- if (hit.getFields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
- return Optional.ofNullable(hit.field(JsonMessageConstants.MESSAGE_ID));
- }
- return Optional.empty();
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/reporting-site/site.xml b/mailbox/elasticsearch-v6/src/reporting-site/site.xml
deleted file mode 100644
index d919164..0000000
--- a/mailbox/elasticsearch-v6/src/reporting-site/site.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
- 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.
--->
-<project name="${project.name}">
-
- <body>
-
- <menu ref="parent" />
- <menu ref="reports" />
-
- </body>
-
-</project>
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
deleted file mode 100644
index c838692..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.time.ZoneId;
-
-import org.apache.james.backends.es.v6.DockerElasticSearchRule;
-import org.apache.james.backends.es.v6.ElasticSearchConfiguration;
-import org.apache.james.backends.es.v6.ElasticSearchIndexer;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.elasticsearch.v6.events.ElasticSearchListeningMessageSearchIndex;
-import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
-import org.apache.james.mailbox.elasticsearch.v6.query.CriterionConverter;
-import org.apache.james.mailbox.elasticsearch.v6.query.QueryConverter;
-import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
-import org.apache.james.mailbox.inmemory.InMemoryId;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
-import org.apache.james.mailbox.model.ComposedMessageId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.store.search.AbstractMessageSearchIndexTest;
-import org.apache.james.mailbox.tika.TikaConfiguration;
-import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
-import org.apache.james.mailbox.tika.TikaHttpClientImpl;
-import org.apache.james.mailbox.tika.TikaTextExtractor;
-import org.apache.james.metrics.api.NoopMetricFactory;
-import org.apache.james.mime4j.dom.Message;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-
-import com.google.common.base.Strings;
-
-public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest {
-
- private static final int BATCH_SIZE = 1;
- private static final int SEARCH_SIZE = 1;
-
- @ClassRule
- public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
-
- @Rule
- public DockerElasticSearchRule elasticSearch = new DockerElasticSearchRule();
- private TikaTextExtractor textExtractor;
-
- @Override
- public void setUp() throws Exception {
- textExtractor = new TikaTextExtractor(new NoopMetricFactory(),
- new TikaHttpClientImpl(TikaConfiguration.builder()
- .host(tika.getIp())
- .port(tika.getPort())
- .timeoutInMillis(tika.getTimeoutInMillis())
- .build()));
- super.setUp();
- }
-
- @Override
- protected void await() {
- elasticSearch.awaitForElasticSearch();
- }
-
- @Override
- protected void initializeMailboxManager() throws IOException {
- RestHighLevelClient client = MailboxIndexCreationUtil.prepareDefaultClient(
- elasticSearch.clientProvider().get(),
- ElasticSearchConfiguration.builder()
- .addHost(elasticSearch.getDockerElasticSearch().getHttpHost())
- .build());
-
- InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
-
- InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder()
- .preProvisionnedFakeAuthenticator()
- .fakeAuthorizator()
- .inVmEventBus()
- .defaultAnnotationLimits()
- .defaultMessageParser()
- .listeningSearchIndex(preInstanciationStage -> new ElasticSearchListeningMessageSearchIndex(
- preInstanciationStage.getMapperFactory(),
- new ElasticSearchIndexer(client,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
- BATCH_SIZE),
- new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE,
- new InMemoryId.Factory(), messageIdFactory,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS),
- new MessageToElasticSearchJson(textExtractor, ZoneId.of("Europe/Paris"), IndexAttachments.YES),
- preInstanciationStage.getSessionProvider()))
- .noPreDeletionHooks()
- .storeQuotaManager()
- .build();
-
- storeMailboxManager = resources.getMailboxManager();
- messageIdManager = resources.getMessageIdManager();
- messageSearchIndex = resources.getSearchIndex();
- }
-
- @Test
- public void termsBetweenElasticSearchAndLuceneLimitDueTuNonAsciiCharsShouldBeTruncated() throws Exception {
- MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
- MailboxSession session = MailboxSessionUtil.create(USERNAME);
- MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
-
- String recipient = "benwa@linagora.com";
- ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
- Message.Builder.of()
- .setTo(recipient)
- .setBody(Strings.repeat("0à 2345678é", 3200), StandardCharsets.UTF_8)),
- session);
-
- elasticSearch.awaitForElasticSearch();
-
- assertThat(messageManager.search(new SearchQuery(SearchQuery.address(SearchQuery.AddressType.To, recipient)), session))
- .containsExactly(composedMessageId.getUid());
- }
-
- @Test
- public void tooLongTermsShouldNotMakeIndexingFail() throws Exception {
- MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
- MailboxSession session = MailboxSessionUtil.create(USERNAME);
- MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
-
- String recipient = "benwa@linagora.com";
- ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
- Message.Builder.of()
- .setTo(recipient)
- .setBody(Strings.repeat("0123456789", 3300), StandardCharsets.UTF_8)),
- session);
-
- elasticSearch.awaitForElasticSearch();
-
- assertThat(messageManager.search(new SearchQuery(SearchQuery.address(SearchQuery.AddressType.To, recipient)), session))
- .containsExactly(composedMessageId.getUid());
- }
-
- @Test
- public void fieldsExceedingLuceneLimitShouldNotBeIgnored() throws Exception {
- MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
- MailboxSession session = MailboxSessionUtil.create(USERNAME);
- MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
-
- String recipient = "benwa@linagora.com";
- ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
- Message.Builder.of()
- .setTo(recipient)
- .setBody(Strings.repeat("0123456789 ", 5000), StandardCharsets.UTF_8)),
- session);
-
- elasticSearch.awaitForElasticSearch();
-
- assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains("0123456789")), session))
- .containsExactly(composedMessageId.getUid());
- }
-
- @Test
- public void fieldsWithTooLongTermShouldStillBeIndexed() throws Exception {
- MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
- MailboxSession session = MailboxSessionUtil.create(USERNAME);
- MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
-
- String recipient = "benwa@linagora.com";
- ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
- Message.Builder.of()
- .setTo(recipient)
- .setBody(Strings.repeat("0123456789 ", 5000) + " matchMe", StandardCharsets.UTF_8)),
- session);
-
- elasticSearch.awaitForElasticSearch();
-
- assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains("matchMe")), session))
- .containsExactly(composedMessageId.getUid());
- }
-
- @Test
- public void reasonableLongTermShouldNotBeIgnored() throws Exception {
- MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
- MailboxSession session = MailboxSessionUtil.create(USERNAME);
- MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
-
- String recipient = "benwa@linagora.com";
- String reasonableLongTerm = "dichlorodiphényltrichloroéthane";
- ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
- Message.Builder.of()
- .setTo(recipient)
- .setBody(reasonableLongTerm, StandardCharsets.UTF_8)),
- session);
-
- elasticSearch.awaitForElasticSearch();
-
- assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains(reasonableLongTerm)), session))
- .containsExactly(composedMessageId.getUid());
- }
-}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
deleted file mode 100644
index c04eb16..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/****************************************************************
- * 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.v6;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.commons.configuration.PropertiesConfiguration;
-import org.apache.james.backends.es.v6.IndexName;
-import org.apache.james.backends.es.v6.ReadAliasName;
-import org.apache.james.backends.es.v6.WriteAliasName;
-import org.junit.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-public class ElasticSearchMailboxConfigurationTest {
- @Test
- public void elasticSearchMailboxConfigurationShouldRespectBeanContract() {
- EqualsVerifier.forClass(ElasticSearchMailboxConfiguration.class)
- .verify();
- }
-
- @Test
- public void getIndexMailboxNameShouldReturnOldConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.index.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexMailboxName())
- .isEqualTo(new IndexName(name));
- }
-
- @Test
- public void getIndexMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.index.name", "other");
- configuration.addProperty("elasticsearch.index.mailbox.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexMailboxName())
- .isEqualTo(new IndexName(name));
- }
-
- @Test
- public void getIndexMailboxNameShouldReturnConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.index.mailbox.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexMailboxName())
- .isEqualTo(new IndexName(name));
- }
-
- @Test
- public void getIndexMailboxNameShouldReturnDefaultValueWhenMissing() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexMailboxName())
- .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX);
- }
-
- @Test
- public void getReadAliasMailboxNameShouldReturnOldConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.read.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
- .isEqualTo(new ReadAliasName(name));
- }
-
- @Test
- public void getReadAliasMailboxNameShouldReturnConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.read.mailbox.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
- .isEqualTo(new ReadAliasName(name));
- }
-
- @Test
- public void getReadAliasMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.read.mailbox.name", name);
- configuration.addProperty("elasticsearch.alias.read.name", "other");
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
- .isEqualTo(new ReadAliasName(name));
- }
-
- @Test
- public void getReadAliasMailboxNameShouldReturnDefaultValueWhenMissing() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
- .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS);
- }
-
- @Test
- public void getWriteAliasMailboxNameShouldReturnOldConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.write.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
- .isEqualTo(new WriteAliasName(name));
- }
-
- @Test
- public void getWriteAliasMailboxNameShouldReturnConfiguredValue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.write.mailbox.name", name);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
- .isEqualTo(new WriteAliasName(name));
- }
-
- @Test
- public void getWriteAliasMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- String name = "name";
- configuration.addProperty("elasticsearch.alias.write.mailbox.name", name);
- configuration.addProperty("elasticsearch.alias.write.name", "other");
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
- .isEqualTo(new WriteAliasName(name));
- }
-
- @Test
- public void getWriteAliasMailboxNameShouldReturnDefaultValueWhenMissing() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
- .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS);
- }
-
- @Test
- public void getIndexAttachmentShouldReturnConfiguredValueWhenTrue() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.indexAttachments", true);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexAttachment())
- .isEqualTo(IndexAttachments.YES);
- }
-
- @Test
- public void getIndexAttachmentShouldReturnConfiguredValueWhenFalse() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.indexAttachments", false);
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexAttachment())
- .isEqualTo(IndexAttachments.NO);
- }
-
- @Test
- public void getIndexAttachmentShouldReturnDefaultValueWhenMissing() {
- PropertiesConfiguration configuration = new PropertiesConfiguration();
- configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
-
- ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
-
- assertThat(elasticSearchConfiguration.getIndexAttachment())
- .isEqualTo(IndexAttachments.YES);
- }
-
-}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
deleted file mode 100644
index 2596d5c..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/****************************************************************
- * 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.v6.events;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.refEq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-
-import javax.mail.Flags;
-
-import org.apache.james.backends.es.v6.ElasticSearchIndexer;
-import org.apache.james.backends.es.v6.UpdatedRepresentation;
-import org.apache.james.core.User;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.MailboxSessionUtil;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
-import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
-import org.apache.james.mailbox.events.Group;
-import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.TestId;
-import org.apache.james.mailbox.model.UpdatedFlags;
-import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
-import org.apache.james.mailbox.store.SessionProvider;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.action.bulk.BulkResponse;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.core.JsonGenerationException;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-public class ElasticSearchListeningMessageSearchIndexTest {
- private static final long MODSEQ = 18L;
- private static final MessageUid MESSAGE_UID = MessageUid.of(1);
- private static final TestId MAILBOX_ID = TestId.of(12);
- private static final String ELASTIC_SEARCH_ID = "12:1";
- private static final String EXPECTED_JSON_CONTENT = "json content";
- private static final String USERNAME = "username";
-
- private ElasticSearchIndexer elasticSearchIndexer;
- private MessageToElasticSearchJson messageToElasticSearchJson;
- private ElasticSearchListeningMessageSearchIndex testee;
- private MailboxSession session;
- private List<User> users;
- private Mailbox mailbox;
-
- @Before
- public void setup() {
- MailboxSessionMapperFactory mapperFactory = mock(MailboxSessionMapperFactory.class);
- messageToElasticSearchJson = mock(MessageToElasticSearchJson.class);
- ElasticSearchSearcher elasticSearchSearcher = mock(ElasticSearchSearcher.class);
- SessionProvider mockSessionProvider = mock(SessionProvider.class);
-
- elasticSearchIndexer = mock(ElasticSearchIndexer.class);
-
- testee = new ElasticSearchListeningMessageSearchIndex(mapperFactory, elasticSearchIndexer, elasticSearchSearcher,
- messageToElasticSearchJson, mockSessionProvider);
- session = MailboxSessionUtil.create(USERNAME);
- users = ImmutableList.of(User.fromUsername(USERNAME));
-
- mailbox = mock(Mailbox.class);
- when(mailbox.getMailboxId()).thenReturn(MAILBOX_ID);
- }
-
- @Test
- public void deserializeElasticSearchListeningMessageSearchIndexGroup() throws Exception {
- assertThat(Group.deserialize("org.apache.james.mailbox.elasticsearch.v6.events.ElasticSearchListeningMessageSearchIndex$ElasticSearchListeningMessageSearchIndexGroup"))
- .isEqualTo(new ElasticSearchListeningMessageSearchIndex.ElasticSearchListeningMessageSearchIndexGroup());
- }
-
- @Test
- public void addShouldIndex() throws Exception {
- //Given
- MailboxMessage message = mockedMessage(MESSAGE_UID);
-
- when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
- .thenReturn(EXPECTED_JSON_CONTENT);
-
- //When
- testee.add(session, mailbox, message);
-
- //Then
- verify(elasticSearchIndexer).index(eq(ELASTIC_SEARCH_ID), eq(EXPECTED_JSON_CONTENT));
- }
-
- @Test
- public void addShouldIndexEmailBodyWhenNotIndexableAttachment() throws Exception {
- //Given
- MailboxMessage message = mockedMessage(MESSAGE_UID);
-
- when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
- .thenThrow(JsonProcessingException.class);
-
- when(messageToElasticSearchJson.convertToJsonWithoutAttachment(eq(message), eq(users)))
- .thenReturn(EXPECTED_JSON_CONTENT);
-
- //When
- testee.add(session, mailbox, message);
-
- //Then
- verify(elasticSearchIndexer).index(eq(ELASTIC_SEARCH_ID), eq(EXPECTED_JSON_CONTENT));
- }
-
- private MailboxMessage mockedMessage(MessageUid uid) {
- MailboxMessage message = mock(MailboxMessage.class);
- when(message.getUid()).thenReturn(uid);
- return message;
- }
-
- @Test
- public void addShouldPropagateExceptionWhenExceptionOccurs() throws Exception {
- //Given
- MailboxMessage message = mockedMessage(MESSAGE_UID);
-
- when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
- .thenThrow(JsonProcessingException.class);
-
- // When
- JsonGenerator jsonGenerator = null;
- when(messageToElasticSearchJson.convertToJsonWithoutAttachment(eq(message), eq(users)))
- .thenThrow(new JsonGenerationException("expected error", jsonGenerator));
-
- //Then
- assertThatThrownBy(() -> testee.add(session, mailbox, message)).isInstanceOf(JsonGenerationException.class);
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void deleteShouldWork() throws IOException {
- //Given
- BulkResponse expectedBulkResponse = mock(BulkResponse.class);
- when(elasticSearchIndexer.delete(any(List.class)))
- .thenReturn(Optional.of(expectedBulkResponse));
-
- //When
- testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID));
-
- //Then
- verify(elasticSearchIndexer).delete(eq(Lists.newArrayList(ELASTIC_SEARCH_ID)));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void deleteShouldWorkWhenMultipleMessageIds() throws IOException {
- //Given
- MessageUid messageId2 = MessageUid.of(2);
- MessageUid messageId3 = MessageUid.of(3);
- MessageUid messageId4 = MessageUid.of(4);
- MessageUid messageId5 = MessageUid.of(5);
-
- BulkResponse expectedBulkResponse = mock(BulkResponse.class);
- when(elasticSearchIndexer.delete(any(List.class)))
- .thenReturn(Optional.of(expectedBulkResponse));
-
- //When
- testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID, messageId2, messageId3, messageId4, messageId5));
-
- //Then
- verify(elasticSearchIndexer).delete(eq(Lists.newArrayList(ELASTIC_SEARCH_ID, "12:2", "12:3", "12:4", "12:5")));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void deleteShouldPropagateExceptionWhenExceptionOccurs() throws IOException {
- //Given
- when(elasticSearchIndexer.delete(any(List.class)))
- .thenThrow(new ElasticsearchException(""));
-
- // Then
- assertThatThrownBy(() -> testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID)))
- .isInstanceOf(ElasticsearchException.class);
- }
-
- @Test
- public void updateShouldWork() throws Exception {
- //Given
- Flags flags = new Flags();
-
- UpdatedFlags updatedFlags = UpdatedFlags.builder()
- .uid(MESSAGE_UID)
- .modSeq(MODSEQ)
- .oldFlags(flags)
- .newFlags(flags)
- .build();
-
- when(messageToElasticSearchJson.getUpdatedJsonMessagePart(any(Flags.class), any(Long.class)))
- .thenReturn("json updated content");
-
- //When
- testee.update(session, mailbox, Lists.newArrayList(updatedFlags));
-
- //Then
- ImmutableList<UpdatedRepresentation> expectedUpdatedRepresentations = ImmutableList.of(new UpdatedRepresentation(ELASTIC_SEARCH_ID, "json updated content"));
- verify(elasticSearchIndexer).update(expectedUpdatedRepresentations);
- }
-
- @Test
- public void updateShouldPropagateExceptionWhenExceptionOccurs() throws Exception {
- //Given
- Flags flags = new Flags();
- UpdatedFlags updatedFlags = UpdatedFlags.builder()
- .uid(MESSAGE_UID)
- .modSeq(MODSEQ)
- .oldFlags(flags)
- .newFlags(flags)
- .build();
- when(messageToElasticSearchJson.getUpdatedJsonMessagePart(any(), anyLong())).thenReturn("update doc");
-
- //When
- when(elasticSearchIndexer.update(any())).thenThrow(new ElasticsearchException(""));
-
- //Then
- assertThatThrownBy(() -> testee.update(session, mailbox, Lists.newArrayList(updatedFlags))).isInstanceOf(ElasticsearchException.class);
- }
-
- @Test
- public void deleteAllShouldWork() {
- //Given
- testee.deleteAll(session, mailbox);
-
- //Then
- QueryBuilder expectedQueryBuilder = QueryBuilders.termQuery("mailboxId", "12");
- verify(elasticSearchIndexer).deleteAllMatchingQuery(refEq(expectedQueryBuilder));
- }
-
- @Test
- public void deleteAllShouldNotPropagateExceptionWhenExceptionOccurs() {
- //Given
- doThrow(RuntimeException.class)
- .when(elasticSearchIndexer).deleteAllMatchingQuery(any());
-
- //Then
- assertThatThrownBy(() -> testee.deleteAll(session, mailbox)).isInstanceOf(RuntimeException.class);
- }
-
-}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java
deleted file mode 100644
index c89a0ed..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.junit.Test;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-
-public class EMailersTest {
-
- @Test
- public void fromShouldThrowWhenSetIsNull() {
- assertThatThrownBy(() -> EMailers.from(null))
- .isInstanceOf(NullPointerException.class)
- .hasMessage("'emailers' is mandatory");
- }
-
- @Test
- public void serializeShouldReturnEmptyWhenEmptySet() {
- EMailers eMailers = EMailers.from(ImmutableSet.of());
-
- assertThat(eMailers.serialize()).isEmpty();
- }
-
- @Test
- public void serializeShouldNotJoinWhenOneElement() {
- EMailer emailer = new EMailer("name", "address");
- EMailers eMailers = EMailers.from(ImmutableSet.of(emailer));
-
- assertThat(eMailers.serialize()).isEqualTo(emailer.serialize());
- }
-
- @Test
- public void serializeShouldJoinWhenMultipleElements() {
- EMailer emailer = new EMailer("name", "address");
- EMailer emailer2 = new EMailer("name2", "address2");
- EMailer emailer3 = new EMailer("name3", "address3");
-
- String expected = Joiner.on(" ").join(emailer.serialize(), emailer2.serialize(), emailer3.serialize());
-
- EMailers eMailers = EMailers.from(ImmutableSet.of(emailer, emailer2, emailer3));
-
- assertThat(eMailers.serialize()).isEqualTo(expected);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java
deleted file mode 100644
index 0525330..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import java.util.Objects;
-
-import org.apache.james.mime4j.stream.Field;
-import org.apache.james.mime4j.util.ByteSequence;
-
-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;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getBody() {
- return body;
- }
-
- @Override
- 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;
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java
deleted file mode 100644
index 649c61c..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.time.format.DateTimeFormatter;
-import java.util.stream.Stream;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.ArgumentsProvider;
-import org.junit.jupiter.params.provider.ArgumentsSource;
-
-class HeaderCollectionTest {
-
- static class UTF8FromHeaderTestSource implements ArgumentsProvider {
-
- @Override
- public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
- return Stream.of(
- Arguments.of("=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>", "Frédéric MARTIN"),
- Arguments.of("=?UTF-8?Q?=C3=9Csteli=C4=9Fhan_Ma=C5=9Frapa?= <us...@domain.tld>", "ÃœsteliÄŸhan MaÅŸrapa"),
- Arguments.of("=?UTF-8?Q?Ke=C5=9Ffet_Turizm?= <ke...@domain.tld>", "KeÅŸfet Turizm"),
- Arguments.of("=?UTF-8?Q?MODAL=C4=B0F?= <mo...@domain.tld>", "MODALÄ°F"));
- }
- }
-
- private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
-
- @Test
- void simpleValueAddressHeaderShouldBeAddedToTheAddressSet() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("To", "ben.tellier@linagora.com"))
- .build();
-
- assertThat(headerCollection.getToAddressSet())
- .containsOnly(new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"));
- }
-
- @Test
- void comaSeparatedAddressShouldBeBothAddedToTheAddressSet() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("To", "ben.tellier@linagora.com, btellier@minet.net"))
- .build();
-
- assertThat(headerCollection.getToAddressSet())
- .containsOnly(
- new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"),
- new EMailer("btellier@minet.net", "btellier@minet.net"));
- }
-
- @Test
- void addressesOfTwoFieldsHavingTheSameNameShouldBeMerged() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("To", "ben.tellier@linagora.com"))
- .add(new FieldImpl("To", "ben.tellier@linagora.com, btellier@minet.net"))
- .build();
-
- assertThat(headerCollection.getToAddressSet())
- .containsOnly(
- new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"),
- new EMailer("btellier@minet.net", "btellier@minet.net"));
- }
-
- @Test
- void displayNamesShouldBeRetreived() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getToAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
- }
-
- @ParameterizedTest
- @ArgumentsSource(UTF8FromHeaderTestSource.class)
- void displayNamesShouldBeRetrievedWhenEncodedWord(String encodedFromHeader, String nameOfFromAddress) {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("From", encodedFromHeader))
- .build();
-
- assertThat(headerCollection.getFromAddressSet())
- .extracting(EMailer::getName)
- .contains(nameOfFromAddress);
- }
-
- @Test
- void getHeadersShouldDecodeValues() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("From", "=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getHeaders().get("from"))
- .containsExactly("Frédéric MARTIN <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>");
- }
-
- @Test
- void getHeadersShouldIgnoreHeadersWithDots() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("a.b.c", "value"))
- .build();
-
- assertThat(headerCollection.getHeaders().get("a.b.c"))
- .isEmpty();
- }
-
- @Test
- void addressWithTwoDisplayNamesOnTheSameFieldShouldBeRetrieved() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gr...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getFromAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
- new EMailer("Graham CROSMARIE", "grah.crosmarie@linagora.com"));
- }
-
- @Test
- void foldedFromHeaderShouldBeSupported() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>,\r\n" +
- " Graham CROSMARIE <gr...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getFromAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
- new EMailer("Graham CROSMARIE", "grah.crosmarie@linagora.com"));
- }
-
- @Test
- void foldedHeaderShouldBeSupported() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>,\r\n" +
- " Graham CROSMARIE <gr...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getHeaders().get("from"))
- .containsOnly("Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gr...@linagora.com>");
- }
-
- @Test
- void mixingAddressWithDisplayNamesWithOthersShouldBeAllowed() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>, grah.crosmarie@linagora.com"))
- .build();
-
- assertThat(headerCollection.getToAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
- new EMailer("grah.crosmarie@linagora.com", "grah.crosmarie@linagora.com"));
- }
-
- @Test
- void displayNamesShouldBeRetreivedOnCc() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Cc", "Christophe Hamerling <ch...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getCcAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
- }
-
- @Test
- void displayNamesShouldBeRetreivedOnReplyTo() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Reply-To", "Christophe Hamerling <ch...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getReplyToAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
- }
-
- @Test
- void displayNamesShouldBeRetreivedOnBcc() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Bcc", "Christophe Hamerling <ch...@linagora.com>"))
- .build();
-
- assertThat(headerCollection.getBccAddressSet())
- .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
- }
-
- @Test
- 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
- 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
- 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
- void notSeparatedAddressSubpartShouldBeWellHandled() {
- 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
- 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
- void partialYearShouldBeCompleted() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Date", "Thu, 4 Jun 15 06:08:41 +0200"))
- .build();
-
- assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
- .isEqualTo("2015/06/04 06:08:41");
- }
-
- @Test
- void nonStandardDatesShouldBeRetreived() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Date", "Thu, 4 Jun 2015 06:08:41 +0200 (UTC)"))
- .build();
-
- assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
- .isEqualTo("2015/06/04 06:08:41");
- }
-
- @Test
- void dateShouldBeAbsentOnInvalidHeader() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Date", "Not a date"))
- .build();
-
- assertThat(headerCollection.getSentDate().isPresent())
- .isFalse();
- }
-
- @Test
- 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
- void getMessageIDShouldReturnMessageIdValue() {
- String messageID = "<ab...@123>";
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Message-ID", messageID))
- .build();
-
- assertThat(headerCollection.getMessageID())
- .contains(messageID);
- }
-
- @Test
- void getMessageIDShouldReturnLatestEncounteredMessageIdValue() {
- String messageID = "<ab...@123>";
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Message-ID", "<ot...@toto.com>"))
- .add(new FieldImpl("Message-ID", messageID))
- .build();
-
- assertThat(headerCollection.getMessageID())
- .contains(messageID);
- }
-
- @Test
- void getMessageIDShouldReturnEmptyWhenNoMessageId() {
- HeaderCollection headerCollection = HeaderCollection.builder()
- .add(new FieldImpl("Other", "value"))
- .build();
-
- assertThat(headerCollection.getMessageID())
- .isEmpty();
- }
-
- @Test
- void nullFieldShouldThrow() {
- assertThatThrownBy(() -> HeaderCollection.builder().add(null).build())
- .isInstanceOf(NullPointerException.class);
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java
deleted file mode 100644
index c9e2e19..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java
+++ /dev/null
@@ -1,578 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.time.ZoneId;
-import java.util.Optional;
-
-import javax.mail.Flags;
-
-import org.apache.james.core.User;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
-import org.apache.james.mailbox.extractor.ParsedContent;
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.inmemory.InMemoryMessageId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestId;
-import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
-import org.apache.james.mailbox.tika.TikaConfiguration;
-import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
-import org.apache.james.mailbox.tika.TikaHttpClientImpl;
-import org.apache.james.mailbox.tika.TikaTextExtractor;
-import org.apache.james.metrics.api.NoopMetricFactory;
-import org.assertj.core.api.iterable.Extractor;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class IndexableMessageTest {
- private static final MessageUid MESSAGE_UID = MessageUid.of(154);
-
- @ClassRule
- public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
-
- private TikaTextExtractor textExtractor;
-
- @Before
- public void setUp() throws Exception {
- textExtractor = new TikaTextExtractor(new NoopMetricFactory(), new TikaHttpClientImpl(TikaConfiguration.builder()
- .host(tika.getIp())
- .port(tika.getPort())
- .timeoutInMillis(tika.getTimeoutInMillis())
- .build()));
- }
-
- @Test
- public void textShouldBeEmptyWhenNoMatchingHeaders() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEmpty();
- }
-
- @Test
- public void textShouldContainsFromWhenFrom() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("From: First user <us...@james.org>\nFrom: Second user <us...@james.org>".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("Second user user2@james.org First user user@james.org");
- }
-
- @Test
- public void textShouldContainsToWhenTo() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("To: First to <us...@james.org>\nTo: Second to <us...@james.org>".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("First to user@james.org Second to user2@james.org");
- }
-
- @Test
- public void textShouldContainsCcWhenCc() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("Cc: First cc <us...@james.org>\nCc: Second cc <us...@james.org>".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("First cc user@james.org Second cc user2@james.org");
- }
-
- @Test
- public void textShouldContainsBccWhenBcc() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("Bcc: First bcc <us...@james.org>\nBcc: Second bcc <us...@james.org>".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("Second bcc user2@james.org First bcc user@james.org");
- }
-
- @Test
- public void textShouldContainsSubjectsWhenSubjects() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("Subject: subject1\nSubject: subject2".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("subject1 subject2");
- }
-
- @Test
- public void textShouldContainsBodyWhenBody() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(new ByteArrayInputStream("\nMy body".getBytes()));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("My body");
- }
-
- @Test
- public void textShouldContainsAllFieldsWhenAllSet() throws Exception {
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- assertThat(indexableMessage.getText()).isEqualTo("Ad Min admin@opush.test " +
- "a@test a@test B b@test " +
- "c@test c@test " +
- "dD d@test " +
- "my subject " +
- "Mail content\n" +
- "\n" +
- "-- \n" +
- "Ad Min\n");
- }
-
- @Test
- public void hasAttachmentsShouldReturnTrueWhenPropertyIsPresentAndTrue() throws IOException {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
- when(mailboxMessage.getProperties()).thenReturn(ImmutableList.of(IndexableMessage.HAS_ATTACHMENT_PROPERTY));
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getHasAttachment()).isTrue();
- }
-
- @Test
- public void hasAttachmentsShouldReturnFalseWhenPropertyIsPresentButFalse() throws IOException {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
- when(mailboxMessage.getProperties())
- .thenReturn(ImmutableList.of(new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false")));
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- // Then
- assertThat(indexableMessage.getHasAttachment()).isFalse();
- }
-
- @Test
- public void hasAttachmentsShouldReturnFalseWhenPropertyIsAbsent() throws IOException {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
- when(mailboxMessage.getProperties())
- .thenReturn(ImmutableList.of());
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- // Then
- assertThat(indexableMessage.getHasAttachment()).isFalse();
- }
-
- @Test
- public void attachmentsShouldNotBeenIndexedWhenAsked() throws Exception {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.NO)
- .build();
-
- // Then
- assertThat(indexableMessage.getAttachments()).isEmpty();
- }
-
- @Test
- public void attachmentsShouldBeenIndexedWhenAsked() throws Exception {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/emailWith3Attachments.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(new DefaultTextExtractor())
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getAttachments()).isNotEmpty();
- }
-
- @Test
- public void otherAttachmentsShouldBeenIndexedWhenOneOfThemCannotBeParsed() throws Exception {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/emailWith3Attachments.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- TextExtractor textExtractor = mock(TextExtractor.class);
- when(textExtractor.extractContent(any(), any()))
- .thenReturn(new ParsedContent(Optional.of("first attachment content"), ImmutableMap.of()))
- .thenThrow(new RuntimeException("second cannot be parsed"))
- .thenReturn(new ParsedContent(Optional.of("third attachment content"), ImmutableMap.of()));
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(textExtractor)
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getAttachments())
- .extracting(new TextualBodyExtractor())
- .contains("first attachment content", TextualBodyExtractor.NO_TEXTUAL_BODY, "third attachment content");
- }
-
- private static class TextualBodyExtractor implements Extractor<MimePart, String> {
-
- public static final String NO_TEXTUAL_BODY = "The textual body is not present";
-
- @Override
- public String extract(MimePart input) {
- return input.getTextualBody().orElse(NO_TEXTUAL_BODY);
- }
- }
-
- @Test
- public void messageShouldBeIndexedEvenIfTikaParserThrowsAnError() throws Exception {
- //Given
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(InMemoryMessageId.of(42));
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- // When
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(textExtractor)
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getText()).contains("subject should be parsed");
- }
-
- @Test
- public void shouldHandleCorrectlyMessageIdHavingSerializeMethodThatReturnNull() throws Exception {
- MessageId invalidMessageIdThatReturnNull = mock(MessageId.class);
- when(invalidMessageIdThatReturnNull.serialize())
- .thenReturn(null);
-
- // When
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(invalidMessageIdThatReturnNull);
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(textExtractor)
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getMessageId()).isNull();
- }
-
- @Test
- public void shouldHandleCorrectlyNullMessageId() throws Exception {
-
- // When
- MailboxMessage mailboxMessage = mock(MailboxMessage.class);
- TestId mailboxId = TestId.of(1);
- when(mailboxMessage.getMailboxId())
- .thenReturn(mailboxId);
- when(mailboxMessage.getMessageId())
- .thenReturn(null);
- when(mailboxMessage.getFullContent())
- .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
- when(mailboxMessage.createFlags())
- .thenReturn(new Flags());
- when(mailboxMessage.getUid())
- .thenReturn(MESSAGE_UID);
-
- IndexableMessage indexableMessage = IndexableMessage.builder()
- .message(mailboxMessage)
- .users(ImmutableList.of(User.fromUsername("username")))
- .extractor(textExtractor)
- .zoneId(ZoneId.of("Europe/Paris"))
- .indexAttachments(IndexAttachments.YES)
- .build();
-
- // Then
- assertThat(indexableMessage.getMessageId()).isNull();
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java
deleted file mode 100644
index 9c840be..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
-import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
-import static net.javacrumbs.jsonunit.core.Option.IGNORING_VALUES;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.time.ZoneId;
-import java.util.Date;
-
-import javax.mail.Flags;
-import javax.mail.util.SharedByteArrayInputStream;
-
-import org.apache.james.core.User;
-import org.apache.james.mailbox.FlagsBuilder;
-import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
-import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestId;
-import org.apache.james.mailbox.model.TestMessageId;
-import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
-import org.apache.james.mailbox.tika.TikaConfiguration;
-import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
-import org.apache.james.mailbox.tika.TikaHttpClientImpl;
-import org.apache.james.mailbox.tika.TikaTextExtractor;
-import org.apache.james.metrics.api.NoopMetricFactory;
-import org.apache.james.util.ClassLoaderUtils;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class MessageToElasticSearchJsonTest {
- private static final int SIZE = 25;
- private static final int BODY_START_OCTET = 100;
- private static final TestId MAILBOX_ID = TestId.of(18L);
- private static final MessageId MESSAGE_ID = TestMessageId.of(184L);
- private static final long MOD_SEQ = 42L;
- private static final MessageUid UID = MessageUid.of(25);
- private static final String USERNAME = "username";
- private static final User USER = User.fromUsername(USERNAME);
-
- private TextExtractor textExtractor;
-
- private Date date;
- private PropertyBuilder propertyBuilder;
-
- @ClassRule
- public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
-
- @Before
- public void setUp() throws Exception {
- textExtractor = new TikaTextExtractor(new NoopMetricFactory(), new TikaHttpClientImpl(TikaConfiguration.builder()
- .host(tika.getIp())
- .port(tika.getPort())
- .timeoutInMillis(tika.getTimeoutInMillis())
- .build()));
- // 2015/06/07 00:00:00 0200 (Paris time zone)
- date = new Date(1433628000000L);
- propertyBuilder = new PropertyBuilder();
- propertyBuilder.setMediaType("plain");
- propertyBuilder.setSubType("text");
- propertyBuilder.setTextualLineCount(18L);
- propertyBuilder.setContentDescription("An e-mail");
- }
-
- @Test
- public void convertToJsonShouldThrowWhenNoUser() {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- new SharedByteArrayInputStream("message".getBytes(StandardCharsets.UTF_8)),
- new Flags(),
- propertyBuilder,
- MAILBOX_ID);
- ImmutableList<User> users = ImmutableList.of();
-
- assertThatThrownBy(() -> messageToElasticSearchJson.convertToJson(spamMail, users))
- .isInstanceOf(IllegalStateException.class);
- }
-
- @Test
- public void spamEmailShouldBeWellConvertedToJson() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/spamMail.eml"),
- new Flags(),
- propertyBuilder,
- MAILBOX_ID);
- spamMail.setUid(UID);
- spamMail.setModSeq(MOD_SEQ);
- assertThatJson(messageToElasticSearchJson.convertToJson(spamMail, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/spamMail.json"));
- }
-
- @Test
- public void htmlEmailShouldBeWellConvertedToJson() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage htmlMail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/htmlMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("social", "pocket-money").build(),
- propertyBuilder,
- MAILBOX_ID);
- htmlMail.setModSeq(MOD_SEQ);
- htmlMail.setUid(UID);
- assertThatJson(messageToElasticSearchJson.convertToJson(htmlMail, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/htmlMail.json"));
- }
-
- @Test
- public void pgpSignedEmailShouldBeWellConvertedToJson() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage pgpSignedMail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/pgpSignedMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- pgpSignedMail.setModSeq(MOD_SEQ);
- pgpSignedMail.setUid(UID);
- assertThatJson(messageToElasticSearchJson.convertToJson(pgpSignedMail, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/pgpSignedMail.json"));
- }
-
- @Test
- public void simpleEmailShouldBeWellConvertedToJson() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage mail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/mail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- mail.setModSeq(MOD_SEQ);
- mail.setUid(UID);
- assertThatJson(messageToElasticSearchJson.convertToJson(mail,
- ImmutableList.of(User.fromUsername("user1"), User.fromUsername("user2"))))
- .when(IGNORING_ARRAY_ORDER).when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/mail.json"));
- }
-
- @Test
- public void recursiveEmailShouldBeWellConvertedToJson() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage recursiveMail = new SimpleMailboxMessage(MESSAGE_ID,
- date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- recursiveMail.setModSeq(MOD_SEQ);
- recursiveMail.setUid(UID);
- assertThatJson(messageToElasticSearchJson.convertToJson(recursiveMail, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER).when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
- }
-
- @Test
- public void emailWithNoInternalDateShouldUseNowDate() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
- null,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- mailWithNoInternalDate.setModSeq(MOD_SEQ);
- mailWithNoInternalDate.setUid(UID);
- assertThatJson(messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER)
- .when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
- }
-
- @Test
- public void emailWithAttachmentsShouldConvertAttachmentsWhenIndexAttachmentsIsTrue() throws IOException {
- // Given
- MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
- null,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- mailWithNoInternalDate.setModSeq(MOD_SEQ);
- mailWithNoInternalDate.setUid(UID);
-
- // When
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.YES);
- String convertToJson = messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER));
-
- // Then
- assertThatJson(convertToJson)
- .when(IGNORING_ARRAY_ORDER)
- .when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
- }
-
- @Test
- public void emailWithAttachmentsShouldNotConvertAttachmentsWhenIndexAttachmentsIsFalse() throws IOException {
- // Given
- MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
- null,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- mailWithNoInternalDate.setModSeq(MOD_SEQ);
- mailWithNoInternalDate.setUid(UID);
-
- // When
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.NO);
- String convertToJson = messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER));
-
- // Then
- assertThatJson(convertToJson)
- .when(IGNORING_ARRAY_ORDER)
- .when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMailWithoutAttachments.json"));
- }
-
- @Test
- public void emailWithNoMailboxIdShouldThrow() {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"), IndexAttachments.YES);
- MailboxMessage mailWithNoMailboxId = new SimpleMailboxMessage(MESSAGE_ID, date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- null);
- mailWithNoMailboxId.setModSeq(MOD_SEQ);
- mailWithNoMailboxId.setUid(UID);
-
- assertThatThrownBy(() ->
- messageToElasticSearchJson.convertToJson(mailWithNoMailboxId, ImmutableList.of(USER)))
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void getUpdatedJsonMessagePartShouldBehaveWellOnEmptyFlags() throws Exception {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.YES);
- assertThatJson(messageToElasticSearchJson.getUpdatedJsonMessagePart(new Flags(), MOD_SEQ))
- .isEqualTo("{\"modSeq\":42,\"isAnswered\":false,\"isDeleted\":false,\"isDraft\":false,\"isFlagged\":false,\"isRecent\":false,\"userFlags\":[],\"isUnread\":true}");
- }
-
- @Test
- public void getUpdatedJsonMessagePartShouldBehaveWellOnNonEmptyFlags() throws Exception {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.YES);
- assertThatJson(messageToElasticSearchJson.getUpdatedJsonMessagePart(new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.FLAGGED).add("user").build(), MOD_SEQ))
- .isEqualTo("{\"modSeq\":42,\"isAnswered\":false,\"isDeleted\":true,\"isDraft\":false,\"isFlagged\":true,\"isRecent\":false,\"userFlags\":[\"user\"],\"isUnread\":true}");
- }
-
- @Test(expected = NullPointerException.class)
- public void getUpdatedJsonMessagePartShouldThrowIfFlagsIsNull() throws Exception {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.YES);
- messageToElasticSearchJson.getUpdatedJsonMessagePart(null, MOD_SEQ);
- }
-
- @Test
- public void spamEmailShouldBeWellConvertedToJsonWithApacheTika() throws IOException {
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- textExtractor,
- ZoneId.of("Europe/Paris"),
- IndexAttachments.YES);
- MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID, date,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/nonTextual.eml"),
- new Flags(),
- propertyBuilder,
- MAILBOX_ID);
- spamMail.setUid(UID);
- spamMail.setModSeq(MOD_SEQ);
-
- assertThatJson(messageToElasticSearchJson.convertToJson(spamMail, ImmutableList.of(USER)))
- .when(IGNORING_ARRAY_ORDER)
- .isEqualTo(
- ClassLoaderUtils.getSystemResourceAsString("eml/nonTextual.json", StandardCharsets.UTF_8));
- }
-
- @Test
- public void convertToJsonWithoutAttachmentShouldConvertEmailBoby() throws IOException {
- // Given
- MailboxMessage message = new SimpleMailboxMessage(MESSAGE_ID,
- null,
- SIZE,
- BODY_START_OCTET,
- ClassLoaderUtils.getSystemResourceAsSharedStream("eml/emailWithNonIndexableAttachment.eml"),
- new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
- propertyBuilder,
- MAILBOX_ID);
- message.setModSeq(MOD_SEQ);
- message.setUid(UID);
-
- // When
- MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
- new DefaultTextExtractor(),
- ZoneId.of("Europe/Paris"),
- IndexAttachments.NO);
- String convertToJsonWithoutAttachment = messageToElasticSearchJson.convertToJsonWithoutAttachment(message, ImmutableList.of(USER));
-
- // Then
- assertThatJson(convertToJsonWithoutAttachment)
- .when(IGNORING_ARRAY_ORDER)
- .when(IGNORING_VALUES)
- .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/emailWithNonIndexableAttachmentWithoutAttachment.json"));
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java
deleted file mode 100644
index 89daa82..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.ByteArrayInputStream;
-import java.nio.charset.StandardCharsets;
-
-import org.junit.Test;
-
-public class MimePartTest {
-
- @Test
- public void buildShouldWorkWhenTextualContentFromParserIsEmpty() {
- MimePart.builder()
- .addBodyContent(new ByteArrayInputStream(new byte[] {}))
- .addMediaType("text")
- .addSubType("plain")
- .build();
- }
-
- @Test
- public void buildShouldWorkWhenTextualContentFromParserIsNonEmpty() {
- String body = "text";
- MimePart mimePart = MimePart.builder()
- .addBodyContent(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)))
- .addMediaType("text")
- .addSubType("plain")
- .build();
-
- assertThat(mimePart.getTextualBody()).contains(body);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java
deleted file mode 100644
index 38f6581..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * 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.v6.json;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.junit.Test;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-
-public class SubjectsTest {
-
- @Test
- public void fromShouldThrowWhenSetIsNull() {
- assertThatThrownBy(() -> Subjects.from(null))
- .isInstanceOf(NullPointerException.class)
- .hasMessage("'subjects' is mandatory");
- }
-
- @Test
- public void serializeShouldReturnEmptyWhenEmptySet() {
- Subjects subjects = Subjects.from(ImmutableSet.of());
-
- assertThat(subjects.serialize()).isEmpty();
- }
-
- @Test
- public void serializeShouldNotJoinWhenOneElement() {
- String expected = "subject";
- Subjects subjects = Subjects.from(ImmutableSet.of(expected));
-
- assertThat(subjects.serialize()).isEqualTo(expected);
- }
-
- @Test
- public void serializeShouldJoinWhenMultipleElements() {
- String subject = "subject";
- String subject2 = "subject2";
- String subject3 = "subject3";
-
- String expected = Joiner.on(" ").join(subject, subject2, subject3);
-
- Subjects subjects = Subjects.from(ImmutableSet.of(subject, subject2, subject3));
-
- assertThat(subjects.serialize()).isEqualTo(expected);
- }
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java
deleted file mode 100644
index db48dad..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.text.ParseException;
-import java.time.ZonedDateTime;
-
-import org.apache.james.mailbox.model.SearchQuery;
-import org.junit.Test;
-
-
-public class DateResolutionFormaterTest {
-
- private final String dateString = "2014-01-02T15:15:15Z";
-
- @Test
- public void calculateUpperDateShouldReturnDateUpToTheNextMinuteUsingMinuteUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Minute)
- )
- ).isEqualTo("2014-01-02T15:16:00Z");
- }
-
- @Test
- public void calculateUpperDateShouldReturnDateUpToTheNextHourUsingHourUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Hour)
- )
- ).isEqualTo("2014-01-02T16:00:00Z");
- }
-
- @Test
- public void calculateUpperDateShouldReturnDateUpToTheNextMonthUsingMonthUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Month)
- )
- ).isEqualTo("2014-02-01T00:00:00Z");
- }
-
- @Test
- public void calculateUpperDateShouldReturnDateUpToTheNextYearUsingYearUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Year)
- )
- ).isEqualTo("2015-01-01T00:00:00Z");
- }
-
- @Test
- public void calculateUpperDateShouldReturnDateUpToTheNextDayUsingDayUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Day)
- )
- ).isEqualTo("2014-01-03T00:00:00Z");
- }
-
- @Test
- public void calculateLowerDateShouldReturnDateUpToThePreviousMinuteUsingMinuteUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeLowerDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Minute)
- )
- ).isEqualTo("2014-01-02T15:15:00Z");
- }
-
- @Test
- public void calculateLowerDateShouldReturnDateUpToThePreviousDayUsingDayUnit() throws ParseException {
- assertThat(
- ISO_OFFSET_DATE_TIME.format(
- DateResolutionFormater.computeLowerDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Day)
- )
- ).isEqualTo("2014-01-02T00:00:00Z");
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java
deleted file mode 100644
index bfa4245..0000000
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************
- * 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.v6.query;
-
-import java.util.Date;
-
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.DateResolution;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class SearchQueryTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void sentDateOnShouldThrowOnNullDate() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateOn(null, DateResolution.Day);
- }
-
- @Test
- public void sentDateOnShouldThrowOnNullResolution() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateOn(new Date(), null);
- }
-
- @Test
- public void sentDateAfterShouldThrowOnNullDate() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateAfter(null, DateResolution.Day);
- }
-
- @Test
- public void sentDateAfterShouldThrowOnNullResolution() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateAfter(new Date(), null);
- }
-
- @Test
- public void sentDateBeforeShouldThrowOnNullDate() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateBefore(null, DateResolution.Day);
- }
-
- @Test
- public void sentDateBeforeShouldThrowOnNullResolution() {
- expectedException.expect(NullPointerException.class);
-
- SearchQuery.sentDateOn(new Date(), null);
- }
-
-}
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml
deleted file mode 100644
index e4e7ede..0000000
--- a/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml
+++ /dev/null
@@ -1,1272 +0,0 @@
-Date: Mon, 30 Jan 2017 11:51:35 +0100
-From: Raphael OUAZANA <ra...@linagora.com>
-To: OUAZANA Raphael <ra...@linagora.com>
-Subject: subject should be parsed
-Message-ID: <25...@linagora.com>
-X-Sender: raph.ouazana@linagora.com
-User-Agent: Roundcube Webmail/1.1.4
-
-Return-Path: <ip...@obm.lng.org>
-Received: from lenny.obm.lng.org (localhost [127.0.0.1])
- by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
- Wed, 21 Mar 2012 14:51:59 +0100
-X-Sieve: CMU Sieve 2.3
-Received: from [192.168.2.48] (unknown [192.168.2.48])
- by lenny.obm.lng.org (Postfix) with ESMTP id E495D32929
- for <us...@obm.lng.org>; Wed, 21 Mar 2012 14:51:58 +0100 (CET)
-Message-ID: <4F...@obm.lng.org>
-Date: Wed, 21 Mar 2012 14:52:14 +0100
- From: iphone <ip...@obm.lng.org>
-User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US;
-rv:1.9.2.28) Gecko/20120306 Lightning/1.0b2 Thunderbird/3.1.20
-MIME-Version: 1.0
-To: usera <us...@obm.lng.org>
-Subject: Fwd: Re: email with sign
-Content-Type: multipart/mixed;
- boundary="------------080809000000030101030405"
-
-This is a multi-part message in MIME format.
---------------080809000000030101030405
-Content-Type: text/plain; charset=ISO-8859-1; format=flowed
-Content-Transfer-Encoding: 7bit
-
-new email text part
-
---------------080809000000030101030405
-Content-Type: message/rfc822;
- name="Re: email with sign.eml"
-Content-Transfer-Encoding: 7bit
-Content-Disposition: attachment;
- filename="Re: email with sign.eml"
-
-Return-Path: <us...@obm.lng.org>
-Received: from lenny.obm.lng.org (localhost [127.0.0.1])
- by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
- Wed, 21 Mar 2012 14:19:29 +0100
-X-Sieve: CMU Sieve 2.3
-Received: from [192.168.2.48] (unknown [192.168.2.48])
- by lenny.obm.lng.org (Postfix) with ESMTP id 47EC832D51
- for <ip...@obm.lng.org>; Wed, 21 Mar 2012 14:19:29 +0100 (CET)
-Message-ID: <4F...@obm.lng.org>
-Date: Wed, 21 Mar 2012 14:19:44 +0100
- From: usera <us...@obm.lng.org>
-User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US;
-rv:1.9.2.28) Gecko/20120306 Thunderbird/3.1.20
-MIME-Version: 1.0
-To: iphone <ip...@obm.lng.org>
-Subject: Re: email with sign
-References: <4F...@obm.lng.org>
-In-Reply-To: <4F...@obm.lng.org>
-Content-Type: multipart/alternative;
- boundary="------------050702060806040107070701"
-
-This is a multi-part message in MIME format.
---------------050702060806040107070701
-Content-Type: text/plain; charset=ISO-8859-1; format=flowed
-Content-Transfer-Encoding: 7bit
-
-On 03/21/2012 01:59 PM, iphone wrote:
-> email with sign text part
-> --
-new email text part
-
---------------050702060806040107070701
-Content-Type: multipart/related;
- boundary="------------090109030206070103090500"
-
-
---------------090109030206070103090500
-Content-Type: text/html; charset=ISO-8859-1
-Content-Transfer-Encoding: 7bit
-
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
- <head>
- <meta content="text/html; charset=ISO-8859-1"
- http-equiv="Content-Type">
- </head>
- <body text="#000000" bgcolor="#ffffff">
- On 03/21/2012 01:59 PM, iphone wrote:
- <blockquote cite="mid:4F69D0C6.501@obm.lng.org" type="cite">
- <meta http-equiv="content-type" content="text/html;
- charset=ISO-8859-1">
- email with sign text part<br>
- <div class="moz-signature">-- <br>
- <img src="cid:part1.05020706.03070506@obm.lng.org"
-border="0"></div>
- </blockquote>
- new email text part<br>
- </body>
-
-</html>
-
---------------090109030206070103090500
-Content-Type: image/jpeg
-Content-Transfer-Encoding: base64
-Content-ID: <pa...@obm.lng.org>
-
-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
-RvvR7cp9+nyYupWotquN7t17lQ1W/iOqfiKi4Sl3iAJbUmI2CNhIVz3Yykxd66HItw0joyzq
-lWSbK0tavBjTt3sSJ8Z5BaZQtzgKWNjmWEKSoKO3KgdwJoC46n1W1brNapdnipvUm9SG49qa
-bfDbchS21OhRcwdqA22tZUAThPIE4FQrfSXEtUqRa9awFWW7tPstNxoXGuKJXGbeW0WS20Fq
-3CM+MFtJy2Rg5TmTvGjus6XsdsttyFunWBbLttltxG9jbjbSmubKNqNim1uIKE7QAo7duBiM
-hdH85zVNu1VfNQNT7zGuCJTy2IHAZUy3Elx22G0FxRQEmY45uUpZJJHIEbQPub0raPtUORIv
-dzTH6uuaXjFiyZKGWo0l1hTjiksjh5UypPjAArCkoU4AFK+k9KmkWHZEa43EMyY7zyXkxosl
-9tlpEuRGDriwyA2nfGcClKwhBB8dSSlaud9IXRzrONGv9o0il+ajVMGdEnyVR44ZaTImzZKA
-SuQlbezrriVKS27vHclBxi4o6KNto1zA8PZ7V22XA39T/wA148u4yN+N/j7fCO3Hi54Wcjdh
-IFsh6005L1IrT0ec6qeHHGk5iPJZccbBLjaHikNLWkA7kJUVDarIGDjLqDVmn7BPhQLtcBHk
-zXWmmGw0tZUp15DLYO0EJ3OOJSCrA7z3JURUtNdFMGxdIb+qY5sRS5NlzgrwCz4RLskrK0qm
-klZbBcXtCUpVghJUUjB+ekLRGor/AKvdl2i5QrexJYta1SZUMyktOW6c5KQ3ww80r9Ip1GVA
-nxWlDkVJUAL/AGy5Qrm285CfDoYfcjujaUlDiFFKkkEAjmP3ggjIINbdVvRNpm2+dqefNbDJ
-u95VLaZ3BWxtDDMdJ5EjxwxxMeTfzwcirJQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKU
-pQClKUApSlAKUpQClKUApSlAKUpQClKUBjkuLajuutsOPrQgqS02UhSyB9UbiBk93MgecioX
-w9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/+
-+hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqU
-BA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6l
-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
-IVsZZfbzsWknIyCD5fMTUxSld2LBjwrbjior4KjglOUncnYpSlalRSlKAUpSgIzUNqVc2GSy
-/wBXkxnOKw4U7gFYIII8oIJH76iPBGqvtOz/AHNz5lWlSgkFSiAB5Sax9ZjekNf1xQFa8Eaq
-+07P9zc+ZTwRqr7Ts/3Nz5lWXrMb0hr+uKdZjekNf1xUUTZWvBGqvtOz/c3PmV+Ks2qFbc3K
-znaoKH8jd7wQR/rPOBVm6zG9Ia/rinWY3pDX9cUoWVrwRqr7Ts/3Nz5lPA+qfLc7Rj2RHM/2
-lWXrMb0hr+uKdZj+kNf1xShZq2C2ptVuTFDqnVlanHFn+ktRyo/vJrWvlmdmXCJdIExMO4RU
-rQ2txritqQvG5Kk5SSOQPIg5AqYBBGQcivlxaG07nFpQnzqOBUkEJ1XWH23YvhDv5mnVdYfb
-di+EO/mal+txPSmPeCnW4npTHvBQER1XWH23YvhDv5mnVdYfbdi+EO/mal+txPSmPeCnW4np
-THvBQGpYbYq2syC9JMqVKeL8l4oCQte0JGEjuASlKQOfIDmTzqRr8SpK0hSVBQPcQciv2gFK
-UoBSlKAUpSgFKUoBSlKAVB631Ta9IWZNzuvHWl2Q3FjsR0b3ZDzhwhtCcjKifOQOR51OVTul
-rSErV9ltibdKYjXKz3eNd4RfBLS3WSSELxzCSFEZGccuRoWjV8m9atXQ34Fwm3m33HTDdvcC
-JCr0lthsAjkpLoWppafJlKzg8jitmTq3SkW0MXiTqaysW2QcMTHJ7SWXT5krKtp/capet9J6
-41lYogui9ORJtsvsW6wYbDrzjDqGSSWn3VIBVuyTlLYxgcjVfY6ILqxZDIUiG7qJV9nXdh+J
-eX4KbYZO0KbZWlhZWnCRnchOefnqC6jB9WdRt+qrTcNRtWWBIYll63eEW5DEyOtC2uJw+SA5
-xSM/09mzybt3Kp2uWaA0FrK2dI1t1bqm+wLu8zpQ2eU+2FJddkdcLwUE7QnYEEJ3ZBJGdvOu
-p1JSaSfApSlCopSlAKUpQGtdp8a12uVcprqWo0VlbzqyeSUpBJP8BX84umnpRvnSHrFy4TVP
-M2kKPg6NkhCW88lEfrEcya949O1luuouh7VNlsiVLuMq3uIYQk4K1d+0ftAI/fXhRWmpUmxM
-M3SzTrZMYSG1syo6m1ZSMEpyOdUndHVpau+5XrYtwPNvsuFLiFBSfYa9KW/6UZ07p+128aF6
-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
---------------090109030206070103090500--
-
---------------050702060806040107070701--
-
---------------080809000000030101030405--
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml
deleted file mode 100644
index 7cacfcb..0000000
--- a/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml
+++ /dev/null
@@ -1,50 +0,0 @@
-To: Laura ROYET <la...@linagora.com>
-From: Laura Royet <la...@linagora.com>
-Subject: test
-Message-ID: <cb...@linagora.com>
-Date: Wed, 11 Jan 2017 11:52:35 +0100
-User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101
- Thunderbird/45.6.0
-MIME-Version: 1.0
-Content-Type: multipart/mixed;
- boundary="------------36566F1E9D791340FFB75FF8"
-
-This is a multi-part message in MIME format.
---------------36566F1E9D791340FFB75FF8
-Content-Type: text/plain; charset=utf-8; format=flowed
-Content-Transfer-Encoding: 7bit
-
-
-
---
-Laura Royet
-
-
---------------36566F1E9D791340FFB75FF8
-Content-Type: application/vnd.oasis.opendocument.text;
- name="attachment1.odt"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="attachment1.odt"
-
-UEsDBBQAAAgAAGJVK0pexjIMJwAAACcAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQu
-dC54bWxQSwUGAAAAABEAEQBwBAAAjyUAAAAA
---------------36566F1E9D791340FFB75FF8
-Content-Type: application/vnd.oasis.opendocument.text;
- name="attachment2-nonIndexableAttachment.html"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="attachment2-nonIndexableAttachment.odt"
-
-PCFET0NUWVBFIGh0bWw+CjxodG1sIGNsYXNzPSJtb3ppbGxhIiBsYW5nPSJlbiI+PGhlYWQ+
-CI+PC9kaXY+PC9kaXY+PC9ib2R5PjwvaHRtbD4=
---------------36566F1E9D791340FFB75FF8
-Content-Type: application/vnd.oasis.opendocument.text;
- name="attachment3.odt"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="attachment3.odt"
-
-UEsDBBQAAAgAAG9VK0pexjIMJwAAACcAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQu
-AAAAEgkAABNRVRBLUlORi9tYW5pZmVzdC54bWxQSwUGAAAAABEAEQBwBAAApyUAAAAA
---------------36566F1E9D791340FFB75FF8--
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml
deleted file mode 100644
index 2aff55d..0000000
--- a/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml
+++ /dev/null
@@ -1,14 +0,0 @@
-Content-Type: text/plain; Charset=UTF-8
-Date: Fri, 17 Sep 2010 17:12:26 +0200
-Subject: my subject
-To: a@test, B <b...@test>
-Cc: c@test
-Bcc: dD <d...@test>
-MIME-Version: 1.0
-Message-Id: <20...@lenny>
-From: Ad Min <ad...@opush.test>
-
-Mail content
-
---
-Ad Min
diff --git a/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml b/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml
deleted file mode 100644
index b0c305c..0000000
--- a/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<configuration>
-
- <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
- <resetJUL>true</resetJUL>
- </contextListener>
-
- <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
- <immediateFlush>false</immediateFlush>
- </encoder>
- </appender>
-
- <root level="ERROR">
- <appender-ref ref="CONSOLE" />
- </root>
-
-
- <logger name="org.apache.james" level="WARN" >
- <appender-ref ref="CONSOLE" />
- </logger>
-
-</configuration>
diff --git a/mailbox/elasticsearch/.gitignore b/mailbox/elasticsearch/.gitignore
deleted file mode 100644
index 7eb3e0a..0000000
--- a/mailbox/elasticsearch/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-data
-.classpath
-.project
-.settings/
-target/
-*.iml
diff --git a/mailbox/elasticsearch/README.txt b/mailbox/elasticsearch/README.txt
deleted file mode 100644
index 02e6575..0000000
--- a/mailbox/elasticsearch/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-= ElasticSearch search implementation
diff --git a/mailbox/elasticsearch/jsonStructure.adoc b/mailbox/elasticsearch/jsonStructure.adoc
deleted file mode 100644
index 87fda5f..0000000
--- a/mailbox/elasticsearch/jsonStructure.adoc
+++ /dev/null
@@ -1,64 +0,0 @@
-= Structure of indexed documents
-
-== Message
-
-A message is transformed in JSON in a way that looks like JMAP.
-
-A message has the following fields :
- - An *id* : corresponds to the message uid. This fields is a long.
- - A *mailboxId* : identifies in which mailbox this message belongs. This field is a string.
- - A *date* : corresponds to the internal date ( date James parse it and APPENDED it to a mailbox ). This field is a date this the following format : "yyyy-MM-dd HH:mm:ssZZ" ( Z is the offset with GMT ), corresponding to the date_time_no_millis ElasticSearch date format. Note that if the internal date is null, current time will be used instead.
- - A *size* : Number of bytes composing this message. This field is a long.
- - A *hasAttachments* : Tells you if this message has attachments. This field is a boolean.
- - A *mediaType* : First part of the contentType for the Maim MIME part. This field is a string.
- - A *subType* : Second part of the contentType for the Maim MIME par. This field is a string.
- - A *modSeq* : Corresponds to IMAP modification sequence of this mail. This field is a long.
- - *headers* : A multimap of header name to (decoded) list of header value for all headers in the message. This field is a nested document using header names as key and list of strings as value ( corresponding to the list of the corresponding header values ).
- - *attachments* : An array of Attachments Json object. See below.
- - *properties* : An Array of property. See below.
- - *textBody* : Text body. Corresponds to what a user should see in an e-mail client. If this message is not multipart, we will use the Body. If it is multipart, we will use the first textual mime part body found. This field is a string.
-
-Flags properties are directly attached to the Json message structure. You will found :
- - *isAnswered* : Is this message answered ? This field is a boolean.
- - *isDeleted* : Is this message deleted ? This field is a boolean.
- - *isDraft* : Is this message a draft ? This field is a boolean.
- - *isFlagged* : Is this message flagged ? This field is a boolean.
- - *isRecent* : Is this message recent ? This field is a boolean.
- - *isUnread* : Is this message unread ? This field is a boolean.
- - *userFlags* : List of the user flags attached to this e-mail. This field is a array of strings.
-
-Some informations are extracted from the headers :
- - *from* : Emailers at the origin of the e-mail. Array of Emailer.
- - *to* : Emailers for who this Email was sent. Array of Emailer.
- - *cc* : Emailers notified about this e-mail. Array of Emailer.
- - *bcc* : Emailers blindly notified about this e-mail. Array of Emailer.
- - *sentDate* : Date this message was sent. This field is a date this the following format : "yyyy-MM-dd HH:mm:ssZZ" ( Z is the offset with GMT ), corresponding to the date_time_no_millis ElasticSearch date format. If null, non parsable or absent from headers, this field is set to the internal date.
- - *subject* : Subject for this message. As several Subject headers can be found, this field is an array of string.
-
-== Emailer
-
-An Emailer represents someone sending an e-mail.
-
-It has the following properties :
-
- - A *name* : The Fully qualified name of the Emailer. This field is a string.
- - An *email* : The email of the emailer. This field is a string.
-
-== Attachments
-
-An attachment is composed of the following fields :
- - A *mediaType* : Similar to the message property. This is a string field.
- - A *subType* : Similar to the message property. This is a string field.
- - A *contentDisposition* : Content-Disposition header field extracted. This is a string field.
- - A *fileName* : Name of the attached file. This is a string field.
- - A *fileExtension* : Extension part of the file name. This is a string field.
-
-== Properties
-
-A property is a string value computed by James when storing the message.
-
-A property contains :
-
- - A *namespace* : identifies the domain of this property. This field is a string.
- - A *name* : identifies this property. This field is a string.
- - A *value* : value of the property. This field is a string.
\ No newline at end of file
diff --git a/mailbox/elasticsearch/pom.xml b/mailbox/elasticsearch/pom.xml
index ba50010..bafe65f 100644
--- a/mailbox/elasticsearch/pom.xml
+++ b/mailbox/elasticsearch/pom.xml
@@ -17,7 +17,9 @@
specific language governing permissions and limitations
under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -91,13 +93,13 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
- <artifactId>james-server-util</artifactId>
- <type>test-jar</type>
+ <artifactId>james-server-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.james</groupId>
- <artifactId>james-server-testing</artifactId>
+ <groupId>${james.groupId}</groupId>
+ <artifactId>james-server-util</artifactId>
+ <type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
@@ -149,20 +151,13 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
+ <groupId>nl.jqno.equalsverifier</groupId>
+ <artifactId>equalsverifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.elasticsearch</groupId>
- <artifactId>elasticsearch</artifactId>
- <version>2.2.1</version>
- </dependency>
- <dependency>
- <groupId>org.elasticsearch</groupId>
- <artifactId>elasticsearch</artifactId>
- <version>2.2.1</version>
- <type>test-jar</type>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -198,4 +193,4 @@
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfiguration.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfiguration.java
index 3ce4cd5..ca1c9d9 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfiguration.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfiguration.java
@@ -1,21 +1,21 @@
-/*
- * 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.
- */
+/****************************************************************
+ * 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;
@@ -36,42 +36,30 @@ public class ElasticSearchMailboxConfiguration {
private Optional<WriteAliasName> writeAliasMailboxName;
private Optional<IndexAttachments> indexAttachment;
- public Builder() {
+ Builder() {
indexMailboxName = Optional.empty();
readAliasMailboxName = Optional.empty();
writeAliasMailboxName = Optional.empty();
indexAttachment = Optional.empty();
}
- public Builder indexMailboxName(IndexName indexMailboxName) {
- return indexMailboxName(Optional.of(indexMailboxName));
- }
-
- public Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
+ Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
this.indexMailboxName = indexMailboxName;
return this;
}
- public Builder readAliasMailboxName(ReadAliasName readAliasMailboxName) {
- return readAliasMailboxName(Optional.of(readAliasMailboxName));
- }
-
- public Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
+ Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
this.readAliasMailboxName = readAliasMailboxName;
return this;
}
- public Builder writeAliasMailboxName(WriteAliasName writeAliasMailboxName) {
- return writeAliasMailboxName(Optional.of(writeAliasMailboxName));
- }
-
- public Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
+ Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
this.writeAliasMailboxName = writeAliasMailboxName;
return this;
}
- public Builder indexAttachment(IndexAttachments indexAttachment) {
+ Builder indexAttachment(IndexAttachments indexAttachment) {
this.indexAttachment = Optional.of(indexAttachment);
return this;
}
@@ -91,35 +79,16 @@ public class ElasticSearchMailboxConfiguration {
return new Builder();
}
- public static final String ELASTICSEARCH_HOSTS = "elasticsearch.hosts";
- public static final String ELASTICSEARCH_MASTER_HOST = "elasticsearch.masterHost";
- public static final String ELASTICSEARCH_PORT = "elasticsearch.port";
- public static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
- public static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
- public static final String ELASTICSEARCH_NB_REPLICA = "elasticsearch.nb.replica";
- public static final String ELASTICSEARCH_NB_SHARDS = "elasticsearch.nb.shards";
- public static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
- public static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
- public static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
- public static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
- public static final String ELASTICSEARCH_INDEX_QUOTA_RATIO_NAME = "elasticsearch.index.quota.ratio.name";
- public static final String ELASTICSEARCH_ALIAS_READ_QUOTA_RATIO_NAME = "elasticsearch.alias.read.quota.ratio.name";
- public static final String ELASTICSEARCH_ALIAS_WRITE_QUOTA_RATIO_NAME = "elasticsearch.alias.write.quota.ratio.name";
- public static final String ELASTICSEARCH_RETRY_CONNECTION_MIN_DELAY = "elasticsearch.retryConnection.minDelay";
- public static final String ELASTICSEARCH_RETRY_CONNECTION_MAX_RETRIES = "elasticsearch.retryConnection.maxRetries";
- public static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
-
- public static final int DEFAULT_CONNECTION_MAX_RETRIES = 7;
- public static final int DEFAULT_CONNECTION_MIN_DELAY = 3000;
- public static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
- public static final int DEFAULT_NB_SHARDS = 5;
- public static final int DEFAULT_NB_REPLICA = 1;
- public static final int DEFAULT_PORT = 9300;
- public static final Optional<Integer> DEFAULT_PORT_AS_OPTIONAL = Optional.of(DEFAULT_PORT);
-
- public static final ElasticSearchMailboxConfiguration DEFAULT_CONFIGURATION = builder().build();
-
- public static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
+ private static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
+ private static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
+ private static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
+ private static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
+ private static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
+ private static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
+ private static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
+ private static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
+
+ static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
return builder()
.indexMailboxName(computeMailboxIndexName(configuration))
.readAliasMailboxName(computeMailboxReadAlias(configuration))
@@ -128,7 +97,7 @@ public class ElasticSearchMailboxConfiguration {
.build();
}
- public static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
+ static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
return OptionalUtils.or(
Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_MAILBOX_NAME))
.map(IndexName::new),
@@ -136,7 +105,7 @@ public class ElasticSearchMailboxConfiguration {
.map(IndexName::new));
}
- public static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
+ static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
return OptionalUtils.or(
Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME))
.map(WriteAliasName::new),
@@ -144,7 +113,7 @@ public class ElasticSearchMailboxConfiguration {
.map(WriteAliasName::new));
}
- public static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
+ static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
return OptionalUtils.or(
Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME))
.map(ReadAliasName::new),
@@ -161,8 +130,6 @@ public class ElasticSearchMailboxConfiguration {
}
-
-
private final IndexName indexMailboxName;
private final ReadAliasName readAliasMailboxName;
private final WriteAliasName writeAliasMailboxName;
@@ -177,19 +144,19 @@ public class ElasticSearchMailboxConfiguration {
}
- public IndexName getIndexMailboxName() {
+ IndexName getIndexMailboxName() {
return indexMailboxName;
}
- public ReadAliasName getReadAliasMailboxName() {
+ ReadAliasName getReadAliasMailboxName() {
return readAliasMailboxName;
}
- public WriteAliasName getWriteAliasMailboxName() {
+ WriteAliasName getWriteAliasMailboxName() {
return writeAliasMailboxName;
}
- public IndexAttachments getIndexAttachment() {
+ IndexAttachments getIndexAttachment() {
return indexAttachment;
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/IndexAttachments.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/IndexAttachments.java
index 9d80a4a..9b94a0f 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/IndexAttachments.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/IndexAttachments.java
@@ -1,21 +1,21 @@
-/*
- * 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.
- */
+/****************************************************************
+ * 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;
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxElasticSearchConstants.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxElasticSearchConstants.java
index 0f7f11d..b480c5c 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxElasticSearchConstants.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxElasticSearchConstants.java
@@ -1,27 +1,26 @@
-/*
- * 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.
- */
+/****************************************************************
+ * 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;
import org.apache.james.backends.es.IndexName;
import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.TypeName;
import org.apache.james.backends.es.WriteAliasName;
public interface MailboxElasticSearchConstants {
@@ -33,5 +32,4 @@ public interface MailboxElasticSearchConstants {
WriteAliasName DEFAULT_MAILBOX_WRITE_ALIAS = new WriteAliasName("mailboxWriteAlias");
ReadAliasName DEFAULT_MAILBOX_READ_ALIAS = new ReadAliasName("mailboxReadAlias");
IndexName DEFAULT_MAILBOX_INDEX = new IndexName("mailbox_v1");
- TypeName MESSAGE_TYPE = new TypeName("message");
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxIndexCreationUtil.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxIndexCreationUtil.java
index d234e0d..4b76dad 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxIndexCreationUtil.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxIndexCreationUtil.java
@@ -19,22 +19,23 @@
package org.apache.james.mailbox.elasticsearch;
+import java.io.IOException;
+
import org.apache.james.backends.es.ElasticSearchConfiguration;
import org.apache.james.backends.es.IndexCreationFactory;
import org.apache.james.backends.es.IndexName;
import org.apache.james.backends.es.NodeMappingFactory;
import org.apache.james.backends.es.ReadAliasName;
import org.apache.james.backends.es.WriteAliasName;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RestHighLevelClient;
public class MailboxIndexCreationUtil {
- public static Client prepareClient(Client client,
+ public static RestHighLevelClient prepareClient(RestHighLevelClient client,
ReadAliasName readAlias,
WriteAliasName writeAlias,
IndexName indexName,
- ElasticSearchConfiguration configuration) {
-
+ ElasticSearchConfiguration configuration) throws IOException {
return NodeMappingFactory.applyMapping(
new IndexCreationFactory(configuration)
.useIndex(indexName)
@@ -42,15 +43,14 @@ public class MailboxIndexCreationUtil {
.addAlias(writeAlias)
.createIndexAndAliases(client),
indexName,
- MailboxElasticSearchConstants.MESSAGE_TYPE,
MailboxMappingFactory.getMappingContent());
}
- public static Client prepareDefaultClient(Client client, ElasticSearchConfiguration configuration) {
+ public static RestHighLevelClient prepareDefaultClient(RestHighLevelClient client, ElasticSearchConfiguration configuration) throws IOException {
return prepareClient(client,
MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX,
- configuration);
+ configuration);
}
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxMappingFactory.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxMappingFactory.java
index d6acd0b..7b0823c 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxMappingFactory.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/MailboxMappingFactory.java
@@ -27,16 +27,15 @@ import static org.apache.james.backends.es.NodeMappingFactory.BOOLEAN;
import static org.apache.james.backends.es.NodeMappingFactory.FIELDS;
import static org.apache.james.backends.es.NodeMappingFactory.FORMAT;
import static org.apache.james.backends.es.NodeMappingFactory.IGNORE_ABOVE;
-import static org.apache.james.backends.es.NodeMappingFactory.INDEX;
+import static org.apache.james.backends.es.NodeMappingFactory.KEYWORD;
import static org.apache.james.backends.es.NodeMappingFactory.LONG;
import static org.apache.james.backends.es.NodeMappingFactory.NESTED;
-import static org.apache.james.backends.es.NodeMappingFactory.NOT_ANALYZED;
+import static org.apache.james.backends.es.NodeMappingFactory.NORMALIZER;
import static org.apache.james.backends.es.NodeMappingFactory.PROPERTIES;
import static org.apache.james.backends.es.NodeMappingFactory.RAW;
import static org.apache.james.backends.es.NodeMappingFactory.SEARCH_ANALYZER;
import static org.apache.james.backends.es.NodeMappingFactory.SNOWBALL;
import static org.apache.james.backends.es.NodeMappingFactory.SPLIT_EMAIL;
-import static org.apache.james.backends.es.NodeMappingFactory.STRING;
import static org.apache.james.backends.es.NodeMappingFactory.TYPE;
import static org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants.BCC;
import static org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants.CC;
@@ -71,288 +70,263 @@ import java.io.IOException;
import org.apache.james.backends.es.NodeMappingFactory;
import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants.EMailer;
-import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants.Property;
import org.elasticsearch.common.xcontent.XContentBuilder;
public class MailboxMappingFactory {
private static final int MAXIMUM_TERM_LENGTH = 4096;
private static final String STANDARD = "standard";
+ private static final String STORE = "store";
public static XContentBuilder getMappingContent() {
try {
return jsonBuilder()
.startObject()
- .startObject(MailboxElasticSearchConstants.MESSAGE_TYPE.getValue())
- .startObject(PROPERTIES)
+ .startObject(PROPERTIES)
- .startObject(MESSAGE_ID)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(MESSAGE_ID)
+ .field(TYPE, KEYWORD)
+ .field(STORE, true)
+ .endObject()
- .startObject(UID)
- .field(TYPE, LONG)
- .endObject()
+ .startObject(UID)
+ .field(TYPE, LONG)
+ .field(STORE, true)
+ .endObject()
- .startObject(MODSEQ)
- .field(TYPE, LONG)
- .endObject()
+ .startObject(MODSEQ)
+ .field(TYPE, LONG)
+ .endObject()
- .startObject(SIZE)
- .field(TYPE, LONG)
- .endObject()
+ .startObject(SIZE)
+ .field(TYPE, LONG)
+ .endObject()
- .startObject(IS_ANSWERED)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_ANSWERED)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(IS_DELETED)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_DELETED)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(IS_DRAFT)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_DRAFT)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(IS_FLAGGED)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_FLAGGED)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(IS_RECENT)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_RECENT)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(IS_UNREAD)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(IS_UNREAD)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(DATE)
- .field(TYPE, NodeMappingFactory.DATE)
- .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
- .endObject()
+ .startObject(DATE)
+ .field(TYPE, NodeMappingFactory.DATE)
+ .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+ .endObject()
- .startObject(SENT_DATE)
- .field(TYPE, NodeMappingFactory.DATE)
- .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
- .endObject()
+ .startObject(SENT_DATE)
+ .field(TYPE, NodeMappingFactory.DATE)
+ .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+ .endObject()
- .startObject(MEDIA_TYPE)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(MEDIA_TYPE)
+ .field(TYPE, KEYWORD)
+ .endObject()
- .startObject(SUBTYPE)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(SUBTYPE)
+ .field(TYPE, KEYWORD)
+ .endObject()
- .startObject(USER_FLAGS)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(USER_FLAGS)
+ .field(TYPE, KEYWORD)
+ .endObject()
- .startObject(FROM)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .startObject(FROM)
+ .field(TYPE, NESTED)
+ .startObject(PROPERTIES)
+ .startObject(EMailer.NAME)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .endObject()
+ .startObject(EMailer.ADDRESS)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
.endObject()
.endObject()
+ .endObject()
- .startObject(SUBJECT)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .startObject(SUBJECT)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
+ .endObject()
- .startObject(TO)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .startObject(TO)
+ .field(TYPE, NESTED)
+ .startObject(PROPERTIES)
+ .startObject(EMailer.NAME)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .endObject()
+ .startObject(EMailer.ADDRESS)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
.endObject()
.endObject()
+ .endObject()
- .startObject(CC)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .startObject(CC)
+ .field(TYPE, NESTED)
+ .startObject(PROPERTIES)
+ .startObject(EMailer.NAME)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .endObject()
+ .startObject(EMailer.ADDRESS)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
.endObject()
.endObject()
+ .endObject()
- .startObject(BCC)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(EMailer.NAME)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .startObject(BCC)
+ .field(TYPE, NESTED)
+ .startObject(PROPERTIES)
+ .startObject(EMailer.NAME)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
- .startObject(EMailer.ADDRESS)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .endObject()
+ .endObject()
+ .startObject(EMailer.ADDRESS)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
.endObject()
.endObject()
.endObject()
.endObject()
+ .endObject()
- .startObject(MAILBOX_ID)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(MAILBOX_ID)
+ .field(TYPE, KEYWORD)
+ .field(STORE, true)
+ .endObject()
- .startObject(MIME_MESSAGE_ID)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(MIME_MESSAGE_ID)
+ .field(TYPE, KEYWORD)
+ .endObject()
- .startObject(USERS)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(USERS)
+ .field(TYPE, KEYWORD)
+ .endObject()
- .startObject(PROPERTIES)
- .field(TYPE, NESTED)
- .startObject(PROPERTIES)
- .startObject(Property.NAMESPACE)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
- .startObject(Property.NAME)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
- .startObject(Property.VALUE)
- .field(TYPE, STRING)
- .field(INDEX, NOT_ANALYZED)
- .endObject()
+ .startObject(TEXT_BODY)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(SPLIT_EMAIL)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
.endObject()
- .endObject()
-
- .startObject(TEXT_BODY)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .endObject()
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
- .endObject()
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
+ .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
.endObject()
.endObject()
+ .endObject()
- .startObject(HTML_BODY)
- .field(TYPE, STRING)
- .field(ANALYZER, KEEP_MAIL_AND_URL)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, STRING)
- .field(ANALYZER, STANDARD)
- .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
- .endObject()
- .startObject(RAW)
- .field(TYPE, STRING)
- .field(ANALYZER, CASE_INSENSITIVE)
- .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
- .endObject()
+ .startObject(HTML_BODY)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(SPLIT_EMAIL)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, STANDARD)
+ .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+ .endObject()
+ .startObject(RAW)
+ .field(TYPE, KEYWORD)
+ .field(NORMALIZER, CASE_INSENSITIVE)
+ .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
.endObject()
.endObject()
+ .endObject()
- .startObject(HAS_ATTACHMENT)
- .field(TYPE, BOOLEAN)
- .endObject()
+ .startObject(HAS_ATTACHMENT)
+ .field(TYPE, BOOLEAN)
+ .endObject()
- .startObject(TEXT)
- .field(TYPE, STRING)
- .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
- .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
- .startObject(FIELDS)
- .startObject(SPLIT_EMAIL)
- .field(TYPE, STRING)
- .field(ANALYZER, SNOWBALL)
- .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
- .endObject()
+ .startObject(TEXT)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
+ .startObject(FIELDS)
+ .startObject(SPLIT_EMAIL)
+ .field(TYPE, TEXT)
+ .field(ANALYZER, SNOWBALL)
+ .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
.endObject()
.endObject()
.endObject()
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
index cf00439..1dd808c 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
@@ -20,6 +20,7 @@ package org.apache.james.mailbox.elasticsearch.events;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
+import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
@@ -40,7 +41,6 @@ import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
import org.apache.james.mailbox.elasticsearch.json.MessageToElasticSearchJson;
import org.apache.james.mailbox.elasticsearch.search.ElasticSearchSearcher;
import org.apache.james.mailbox.events.Group;
-import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.Mailbox;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageId;
@@ -98,9 +98,9 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
}
@Override
- public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
+ public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) {
Preconditions.checkArgument(session != null, "'session' is mandatory");
- Optional<Long> noLimit = Optional.empty();
+ Optional<Integer> noLimit = Optional.empty();
return searcher
.search(ImmutableList.of(mailbox.getMailboxId()), searchQuery, noLimit)
.map(SearchResult::getMessageUid)
@@ -108,8 +108,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
}
@Override
- public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit)
- throws MailboxException {
+ public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
Preconditions.checkArgument(session != null, "'session' is mandatory");
if (mailboxIds.isEmpty()) {
@@ -126,7 +125,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
}
@Override
- public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws JsonProcessingException {
+ public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws IOException {
LOGGER.info("Indexing mailbox {}-{} of user {} on message {}",
mailbox.getName(),
mailbox.getMailboxId(),
@@ -152,35 +151,37 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
}
@Override
- public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) {
- elasticSearchIndexer.delete(expungedUids.stream()
- .map(uid -> indexIdFor(mailbox, uid))
- .collect(Guavate.toImmutableList()));
+ public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) throws IOException {
+ elasticSearchIndexer
+ .delete(expungedUids.stream()
+ .map(uid -> indexIdFor(mailbox, uid))
+ .collect(Guavate.toImmutableList()));
}
@Override
public void deleteAll(MailboxSession session, Mailbox mailbox) {
- elasticSearchIndexer.deleteAllMatchingQuery(
- termQuery(
- JsonMessageConstants.MAILBOX_ID,
- mailbox.getMailboxId().serialize()));
+ elasticSearchIndexer
+ .deleteAllMatchingQuery(
+ termQuery(
+ JsonMessageConstants.MAILBOX_ID,
+ mailbox.getMailboxId().serialize()));
}
@Override
- public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) {
- elasticSearchIndexer.update(updatedFlagsList.stream()
- .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
- updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
- .sneakyThrow())
- .collect(Guavate.toImmutableList()));
+ public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) throws IOException {
+ elasticSearchIndexer
+ .update(updatedFlagsList.stream()
+ .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
+ updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
+ .sneakyThrow())
+ .collect(Guavate.toImmutableList()));
}
private UpdatedRepresentation createUpdatedDocumentPartFromUpdatedFlags(Mailbox mailbox, UpdatedFlags updatedFlags) throws JsonProcessingException {
return new UpdatedRepresentation(
indexIdFor(mailbox, updatedFlags.getUid()),
- messageToElasticSearchJson.getUpdatedJsonMessagePart(
- updatedFlags.getNewFlags(),
- updatedFlags.getModSeq()));
+ messageToElasticSearchJson
+ .getUpdatedJsonMessagePart(updatedFlags.getNewFlags(), updatedFlags.getModSeq()));
}
private String indexIdFor(Mailbox mailbox, MessageUid uid) {
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java
index 6fff269..e0a8d86 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailer.java
@@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
-public class EMailer implements Serializable {
+public class EMailer implements SerializableMessage {
private final String name;
private final String address;
@@ -51,7 +51,7 @@ public class EMailer implements Serializable {
}
@Override
- public boolean equals(Object o) {
+ public final boolean equals(Object o) {
if (o instanceof EMailer) {
EMailer otherEMailer = (EMailer) o;
return Objects.equals(name, otherEMailer.name)
@@ -61,7 +61,7 @@ public class EMailer implements Serializable {
}
@Override
- public int hashCode() {
+ public final int hashCode() {
return Objects.hash(name, address);
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailers.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailers.java
index 7679310..45c2b0f 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailers.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/EMailers.java
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Preconditions;
-public class EMailers implements Serializable {
+public class EMailers implements SerializableMessage {
public static EMailers from(Set<EMailer> emailers) {
Preconditions.checkNotNull(emailers, "'emailers' is mandatory");
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/JsonMessageConstants.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/JsonMessageConstants.java
index 91e731b..3697610 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/JsonMessageConstants.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/JsonMessageConstants.java
@@ -74,10 +74,4 @@ public interface JsonMessageConstants {
String FILE_EXTENSION = "fileExtension";
}
- interface Property {
- String NAMESPACE = "namespace";
- String NAME = "name";
- String VALUE = "value";
- }
-
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MessageUpdateJson.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MessageUpdateJson.java
index d0b7faf..f059317 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MessageUpdateJson.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MessageUpdateJson.java
@@ -1,5 +1,24 @@
+/****************************************************************
+ * 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 javax.mail.Flags;
@@ -43,7 +62,7 @@ public class MessageUpdateJson {
@JsonProperty(JsonMessageConstants.IS_UNREAD)
public boolean isUnRead() {
- return ! flags.contains(Flags.Flag.SEEN);
+ return !flags.contains(Flags.Flag.SEEN);
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Serializable.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/SerializableMessage.java
similarity index 97%
rename from mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Serializable.java
rename to mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/SerializableMessage.java
index 92915df..abf2c34 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Serializable.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/SerializableMessage.java
@@ -19,7 +19,7 @@
package org.apache.james.mailbox.elasticsearch.json;
-public interface Serializable {
+public interface SerializableMessage {
String serialize();
}
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Subjects.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Subjects.java
index 214d312..6989aa1 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Subjects.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/Subjects.java
@@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
-public class Subjects implements Serializable {
+public class Subjects implements SerializableMessage {
public static Subjects from(Set<String> subjects) {
Preconditions.checkNotNull(subjects, "'subjects' is mandatory");
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
index 5499a50..3dd0885 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/CriterionConverter.java
@@ -45,14 +45,15 @@ import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
import org.apache.james.mailbox.model.SearchQuery;
import org.apache.james.mailbox.model.SearchQuery.Criterion;
import org.apache.james.mailbox.model.SearchQuery.HeaderOperator;
+import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
public class CriterionConverter {
- private final Map<Class<?>, Function<SearchQuery.Criterion, QueryBuilder>> criterionConverterMap;
- private final Map<Class<?>, BiFunction<String, SearchQuery.HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
+ private final Map<Class<?>, Function<Criterion, QueryBuilder>> criterionConverterMap;
+ private final Map<Class<?>, BiFunction<String, HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
public CriterionConverter() {
criterionConverterMap = new HashMap<>();
@@ -117,7 +118,7 @@ public class CriterionConverter {
headerOperatorConverterMap.put(type, (BiFunction<String, HeaderOperator, QueryBuilder>) f);
}
- public QueryBuilder convertCriterion(SearchQuery.Criterion criterion) {
+ public QueryBuilder convertCriterion(Criterion criterion) {
return criterionConverterMap.get(criterion.getClass()).apply(criterion);
}
@@ -275,10 +276,12 @@ public class CriterionConverter {
}
private QueryBuilder manageAddressFields(String headerName, String value) {
- return nestedQuery(getFieldNameFromHeaderName(headerName), boolQuery()
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
- .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)));
+ return nestedQuery(getFieldNameFromHeaderName(headerName),
+ boolQuery()
+ .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
+ .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
+ .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)),
+ ScoreMode.Avg);
}
private String getFieldNameFromHeaderName(String headerName) {
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/SortConverter.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/SortConverter.java
index a6bad82..29323b7 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/SortConverter.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/query/SortConverter.java
@@ -23,18 +23,19 @@ import org.apache.james.backends.es.NodeMappingFactory;
import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
import org.apache.james.mailbox.model.SearchQuery;
import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.NestedSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortMode;
import org.elasticsearch.search.sort.SortOrder;
public class SortConverter {
- private static final String MIN = "min";
private static final String PATH_SEPARATOR = ".";
public static FieldSortBuilder convertSort(SearchQuery.Sort sort) {
return getSortClause(sort.getSortClause())
.order(getOrder(sort))
- .sortMode(MIN);
+ .sortMode(SortMode.MIN);
}
private static FieldSortBuilder getSortClause(SearchQuery.Sort.SortClause clause) {
@@ -42,14 +43,14 @@ public class SortConverter {
case Arrival :
return SortBuilders.fieldSort(JsonMessageConstants.DATE);
case MailboxCc :
- return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
- .setNestedPath(JsonMessageConstants.CC);
+ return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+ + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.CC));
case MailboxFrom :
- return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
- .setNestedPath(JsonMessageConstants.FROM);
+ return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+ + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
case MailboxTo :
- return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
- .setNestedPath(JsonMessageConstants.TO);
+ return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+ + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
case BaseSubject :
return SortBuilders.fieldSort(JsonMessageConstants.SUBJECT + PATH_SEPARATOR + NodeMappingFactory.RAW);
case Size :
@@ -59,11 +60,11 @@ public class SortConverter {
case Uid :
return SortBuilders.fieldSort(JsonMessageConstants.UID);
case DisplayFrom:
- return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
- .setNestedPath(JsonMessageConstants.FROM);
+ return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
+ + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
case DisplayTo:
- return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
- .setNestedPath(JsonMessageConstants.TO);
+ return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
+ + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
case Id:
return SortBuilders.fieldSort(JsonMessageConstants.MESSAGE_ID);
default:
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
index 3b36de0..0aea2e1 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
@@ -19,101 +19,103 @@
package org.apache.james.mailbox.elasticsearch.search;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.james.backends.es.AliasName;
+import org.apache.james.backends.es.NodeMappingFactory;
import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.TypeName;
import org.apache.james.backends.es.search.ScrollIterable;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.elasticsearch.json.JsonMessageConstants;
import org.apache.james.mailbox.elasticsearch.query.QueryConverter;
import org.apache.james.mailbox.elasticsearch.query.SortConverter;
-import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.SearchQuery;
import org.apache.james.mailbox.store.search.MessageSearchIndex;
-import org.apache.james.util.streams.Iterators;
-import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.SearchHitField;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ElasticSearchSearcher {
+import com.google.common.collect.ImmutableList;
- public static final int DEFAULT_SEARCH_SIZE = 100;
+public class ElasticSearchSearcher {
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchSearcher.class);
- private static final TimeValue TIMEOUT = new TimeValue(60000);
+ private static final TimeValue TIMEOUT = TimeValue.timeValueMinutes(1);
+ private static final ImmutableList STORED_FIELDS = ImmutableList.of(JsonMessageConstants.MAILBOX_ID,
+ JsonMessageConstants.UID, JsonMessageConstants.MESSAGE_ID);
- private final Client client;
+ private final RestHighLevelClient client;
private final QueryConverter queryConverter;
private final int size;
private final MailboxId.Factory mailboxIdFactory;
private final MessageId.Factory messageIdFactory;
private final AliasName aliasName;
- private final TypeName typeName;
- public ElasticSearchSearcher(Client client, QueryConverter queryConverter, int size,
+ public ElasticSearchSearcher(RestHighLevelClient client, QueryConverter queryConverter, int size,
MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory,
- ReadAliasName aliasName, TypeName typeName) {
+ ReadAliasName aliasName) {
this.client = client;
this.queryConverter = queryConverter;
this.size = size;
this.mailboxIdFactory = mailboxIdFactory;
this.messageIdFactory = messageIdFactory;
this.aliasName = aliasName;
- this.typeName = typeName;
}
public Stream<MessageSearchIndex.SearchResult> search(Collection<MailboxId> mailboxIds, SearchQuery query,
- Optional<Long> limit) throws MailboxException {
- SearchRequestBuilder searchRequestBuilder = getSearchRequestBuilder(client, mailboxIds, query, limit);
- Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequestBuilder).stream()
+ Optional<Integer> limit) {
+ SearchRequest searchRequest = prepareSearch(mailboxIds, query, limit);
+ Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequest).stream()
.flatMap(this::transformResponseToUidStream);
return limit.map(pairStream::limit)
.orElse(pairStream);
}
- private SearchRequestBuilder getSearchRequestBuilder(Client client, Collection<MailboxId> users,
- SearchQuery query, Optional<Long> limit) {
- return query.getSorts()
+ private SearchRequest prepareSearch(Collection<MailboxId> users, SearchQuery query, Optional<Integer> limit) {
+ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
+ .query(queryConverter.from(users, query))
+ .size(computeRequiredSize(limit))
+ .storedFields(STORED_FIELDS);
+
+ query.getSorts()
.stream()
- .reduce(
- client.prepareSearch(aliasName.getValue())
- .setTypes(typeName.getValue())
- .setScroll(TIMEOUT)
- .addFields(JsonMessageConstants.UID, JsonMessageConstants.MAILBOX_ID, JsonMessageConstants.MESSAGE_ID)
- .setQuery(queryConverter.from(users, query))
- .setSize(computeRequiredSize(limit)),
- (searchBuilder, sort) -> searchBuilder.addSort(SortConverter.convertSort(sort)),
- (partialResult1, partialResult2) -> partialResult1);
+ .map(SortConverter::convertSort)
+ .forEach(searchSourceBuilder::sort);
+
+ return new SearchRequest(aliasName.getValue())
+ .types(NodeMappingFactory.DEFAULT_MAPPING_NAME)
+ .scroll(TIMEOUT)
+ .source(searchSourceBuilder);
}
- private int computeRequiredSize(Optional<Long> limit) {
+ private int computeRequiredSize(Optional<Integer> limit) {
return limit.map(value -> Math.min(value.intValue(), size))
.orElse(size);
}
private Stream<MessageSearchIndex.SearchResult> transformResponseToUidStream(SearchResponse searchResponse) {
- return Iterators.toStream(searchResponse.getHits().iterator())
+ return Arrays.stream(searchResponse.getHits().getHits())
.map(this::extractContentFromHit)
.filter(Optional::isPresent)
.map(Optional::get);
}
private Optional<MessageSearchIndex.SearchResult> extractContentFromHit(SearchHit hit) {
- SearchHitField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
- SearchHitField uid = hit.field(JsonMessageConstants.UID);
- Optional<SearchHitField> id = retrieveMessageIdField(hit);
+ DocumentField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
+ DocumentField uid = hit.field(JsonMessageConstants.UID);
+ Optional<DocumentField> id = retrieveMessageIdField(hit);
if (mailboxId != null && uid != null) {
Number uidAsNumber = uid.getValue();
return Optional.of(
@@ -127,12 +129,11 @@ public class ElasticSearchSearcher {
}
}
- private Optional<SearchHitField> retrieveMessageIdField(SearchHit hit) {
- if (hit.fields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
+ private Optional<DocumentField> retrieveMessageIdField(SearchHit hit) {
+ if (hit.getFields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
return Optional.ofNullable(hit.field(JsonMessageConstants.MESSAGE_ID));
- } else {
- return Optional.empty();
}
+ return Optional.empty();
}
}
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
index 3a96813..d32d51b 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
@@ -21,10 +21,9 @@ package org.apache.james.mailbox.elasticsearch;
import static org.assertj.core.api.Assertions.assertThat;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
import org.apache.james.backends.es.DockerElasticSearchRule;
import org.apache.james.backends.es.ElasticSearchConfiguration;
@@ -50,8 +49,7 @@ import org.apache.james.mailbox.tika.TikaHttpClientImpl;
import org.apache.james.mailbox.tika.TikaTextExtractor;
import org.apache.james.metrics.api.NoopMetricFactory;
import org.apache.james.mime4j.dom.Message;
-import org.apache.james.util.concurrent.NamedThreadFactory;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RestHighLevelClient;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@@ -87,15 +85,14 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
}
@Override
- protected void initializeMailboxManager() {
- Client client = MailboxIndexCreationUtil.prepareDefaultClient(
+ protected void initializeMailboxManager() throws IOException {
+ RestHighLevelClient client = MailboxIndexCreationUtil.prepareDefaultClient(
elasticSearch.clientProvider().get(),
ElasticSearchConfiguration.builder()
- .addHost(elasticSearch.getTcpHost())
+ .addHost(elasticSearch.getDockerElasticSearch().getHttpHost())
.build());
InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
- ThreadFactory threadFactory = NamedThreadFactory.withClassName(getClass());
InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder()
.preProvisionnedFakeAuthenticator()
@@ -106,14 +103,11 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
.listeningSearchIndex(preInstanciationStage -> new ElasticSearchListeningMessageSearchIndex(
preInstanciationStage.getMapperFactory(),
new ElasticSearchIndexer(client,
- Executors.newSingleThreadExecutor(threadFactory),
MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
- MailboxElasticSearchConstants.MESSAGE_TYPE,
BATCH_SIZE),
new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE,
new InMemoryId.Factory(), messageIdFactory,
- MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
- MailboxElasticSearchConstants.MESSAGE_TYPE),
+ MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS),
new MessageToElasticSearchJson(textExtractor, ZoneId.of("Europe/Paris"), IndexAttachments.YES),
preInstanciationStage.getSessionProvider()))
.noPreDeletionHooks()
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfigurationTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfigurationTest.java
index dca02ad..1d0b94f 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfigurationTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchMailboxConfigurationTest.java
@@ -1,21 +1,21 @@
-/*
- * 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.
- */
+/****************************************************************
+ * 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;
@@ -27,8 +27,16 @@ import org.apache.james.backends.es.ReadAliasName;
import org.apache.james.backends.es.WriteAliasName;
import org.junit.Test;
+import nl.jqno.equalsverifier.EqualsVerifier;
+
public class ElasticSearchMailboxConfigurationTest {
@Test
+ public void elasticSearchMailboxConfigurationShouldRespectBeanContract() {
+ EqualsVerifier.forClass(ElasticSearchMailboxConfiguration.class)
+ .verify();
+ }
+
+ @Test
public void getIndexMailboxNameShouldReturnOldConfiguredValue() {
PropertiesConfiguration configuration = new PropertiesConfiguration();
String name = "name";
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndexTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndexTest.java
index f409654..2a1efb5 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndexTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndexTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.io.IOException;
import java.util.List;
import java.util.Optional;
@@ -159,7 +160,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
@Test
@SuppressWarnings("unchecked")
- public void deleteShouldWork() {
+ public void deleteShouldWork() throws IOException {
//Given
BulkResponse expectedBulkResponse = mock(BulkResponse.class);
when(elasticSearchIndexer.delete(any(List.class)))
@@ -174,7 +175,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
@Test
@SuppressWarnings("unchecked")
- public void deleteShouldWorkWhenMultipleMessageIds() {
+ public void deleteShouldWorkWhenMultipleMessageIds() throws IOException {
//Given
MessageUid messageId2 = MessageUid.of(2);
MessageUid messageId3 = MessageUid.of(3);
@@ -194,7 +195,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
@Test
@SuppressWarnings("unchecked")
- public void deleteShouldPropagateExceptionWhenExceptionOccurs() {
+ public void deleteShouldPropagateExceptionWhenExceptionOccurs() throws IOException {
//Given
when(elasticSearchIndexer.delete(any(List.class)))
.thenThrow(new ElasticsearchException(""));
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/EMailerTest.java
similarity index 96%
rename from mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java
rename to mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/EMailerTest.java
index 1a907fb..521bf5c 100644
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/EMailerTest.java
@@ -17,7 +17,7 @@
* under the License. *
****************************************************************/
-package org.apache.james.mailbox.elasticsearch.v6.json;
+package org.apache.james.mailbox.elasticsearch.json;
import org.junit.jupiter.api.Test;
diff --git a/mailbox/elasticsearch/src/test/resources/eml/bodyMakeTikaToFail.eml b/mailbox/elasticsearch/src/test/resources/eml/bodyMakeTikaToFail.eml
index 8732157..e4e7ede 100644
--- a/mailbox/elasticsearch/src/test/resources/eml/bodyMakeTikaToFail.eml
+++ b/mailbox/elasticsearch/src/test/resources/eml/bodyMakeTikaToFail.eml
@@ -1,1272 +1,1272 @@
-Date: Mon, 30 Jan 2017 11:51:35 +0100
-From: Raphael OUAZANA <ra...@linagora.com>
-To: OUAZANA Raphael <ra...@linagora.com>
-Subject: subject should be parsed
-Message-ID: <25...@linagora.com>
-X-Sender: raph.ouazana@linagora.com
-User-Agent: Roundcube Webmail/1.1.4
-
-Return-Path: <ip...@obm.lng.org>
-Received: from lenny.obm.lng.org (localhost [127.0.0.1])
- by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
- Wed, 21 Mar 2012 14:51:59 +0100
-X-Sieve: CMU Sieve 2.3
-Received: from [192.168.2.48] (unknown [192.168.2.48])
- by lenny.obm.lng.org (Postfix) with ESMTP id E495D32929
- for <us...@obm.lng.org>; Wed, 21 Mar 2012 14:51:58 +0100 (CET)
-Message-ID: <4F...@obm.lng.org>
-Date: Wed, 21 Mar 2012 14:52:14 +0100
- From: iphone <ip...@obm.lng.org>
-User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US;
-rv:1.9.2.28) Gecko/20120306 Lightning/1.0b2 Thunderbird/3.1.20
-MIME-Version: 1.0
-To: usera <us...@obm.lng.org>
-Subject: Fwd: Re: email with sign
-Content-Type: multipart/mixed;
- boundary="------------080809000000030101030405"
-
-This is a multi-part message in MIME format.
---------------080809000000030101030405
-Content-Type: text/plain; charset=ISO-8859-1; format=flowed
-Content-Transfer-Encoding: 7bit
-
-new email text part
-
---------------080809000000030101030405
-Content-Type: message/rfc822;
- name="Re: email with sign.eml"
-Content-Transfer-Encoding: 7bit
-Content-Disposition: attachment;
- filename="Re: email with sign.eml"
-
-Return-Path: <us...@obm.lng.org>
-Received: from lenny.obm.lng.org (localhost [127.0.0.1])
- by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
- Wed, 21 Mar 2012 14:19:29 +0100
-X-Sieve: CMU Sieve 2.3
-Received: from [192.168.2.48] (unknown [192.168.2.48])
- by lenny.obm.lng.org (Postfix) with ESMTP id 47EC832D51
- for <ip...@obm.lng.org>; Wed, 21 Mar 2012 14:19:29 +0100 (CET)
-Message-ID: <4F...@obm.lng.org>
-Date: Wed, 21 Mar 2012 14:19:44 +0100
- From: usera <us...@obm.lng.org>
-User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US;
-rv:1.9.2.28) Gecko/20120306 Thunderbird/3.1.20
-MIME-Version: 1.0
-To: iphone <ip...@obm.lng.org>
-Subject: Re: email with sign
-References: <4F...@obm.lng.org>
-In-Reply-To: <4F...@obm.lng.org>
-Content-Type: multipart/alternative;
- boundary="------------050702060806040107070701"
-
-This is a multi-part message in MIME format.
---------------050702060806040107070701
-Content-Type: text/plain; charset=ISO-8859-1; format=flowed
-Content-Transfer-Encoding: 7bit
-
-On 03/21/2012 01:59 PM, iphone wrote:
-> email with sign text part
-> --
-new email text part
-
---------------050702060806040107070701
-Content-Type: multipart/related;
- boundary="------------090109030206070103090500"
-
-
---------------090109030206070103090500
-Content-Type: text/html; charset=ISO-8859-1
-Content-Transfer-Encoding: 7bit
-
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
- <head>
- <meta content="text/html; charset=ISO-8859-1"
- http-equiv="Content-Type">
- </head>
- <body text="#000000" bgcolor="#ffffff">
- On 03/21/2012 01:59 PM, iphone wrote:
- <blockquote cite="mid:4F69D0C6.501@obm.lng.org" type="cite">
- <meta http-equiv="content-type" content="text/html;
- charset=ISO-8859-1">
- email with sign text part<br>
- <div class="moz-signature">-- <br>
- <img src="cid:part1.05020706.03070506@obm.lng.org"
-border="0"></div>
- </blockquote>
- new email text part<br>
- </body>
-
-</html>
-
---------------090109030206070103090500
-Content-Type: image/jpeg
-Content-Transfer-Encoding: base64
-Content-ID: <pa...@obm.lng.org>
-
-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
-RvvR7cp9+nyYupWotquN7t17lQ1W/iOqfiKi4Sl3iAJbUmI2CNhIVz3Yykxd66HItw0joyzq
-lWSbK0tavBjTt3sSJ8Z5BaZQtzgKWNjmWEKSoKO3KgdwJoC46n1W1brNapdnipvUm9SG49qa
-bfDbchS21OhRcwdqA22tZUAThPIE4FQrfSXEtUqRa9awFWW7tPstNxoXGuKJXGbeW0WS20Fq
-3CM+MFtJy2Rg5TmTvGjus6XsdsttyFunWBbLttltxG9jbjbSmubKNqNim1uIKE7QAo7duBiM
-hdH85zVNu1VfNQNT7zGuCJTy2IHAZUy3Elx22G0FxRQEmY45uUpZJJHIEbQPub0raPtUORIv
-dzTH6uuaXjFiyZKGWo0l1hTjiksjh5UypPjAArCkoU4AFK+k9KmkWHZEa43EMyY7zyXkxosl
-9tlpEuRGDriwyA2nfGcClKwhBB8dSSlaud9IXRzrONGv9o0il+ajVMGdEnyVR44ZaTImzZKA
-SuQlbezrriVKS27vHclBxi4o6KNto1zA8PZ7V22XA39T/wA148u4yN+N/j7fCO3Hi54Wcjdh
-IFsh6005L1IrT0ec6qeHHGk5iPJZccbBLjaHikNLWkA7kJUVDarIGDjLqDVmn7BPhQLtcBHk
-zXWmmGw0tZUp15DLYO0EJ3OOJSCrA7z3JURUtNdFMGxdIb+qY5sRS5NlzgrwCz4RLskrK0qm
-klZbBcXtCUpVghJUUjB+ekLRGor/AKvdl2i5QrexJYta1SZUMyktOW6c5KQ3ww80r9Ip1GVA
-nxWlDkVJUAL/AGy5Qrm285CfDoYfcjujaUlDiFFKkkEAjmP3ggjIINbdVvRNpm2+dqefNbDJ
-u95VLaZ3BWxtDDMdJ5EjxwxxMeTfzwcirJQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKU
-pQClKUApSlAKUpQClKUApSlAKUpQClKUBjkuLajuutsOPrQgqS02UhSyB9UbiBk93MgecioX
-w9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/+
-+hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqU
-BA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6l
-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
... 1935 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org