blob: d2ba429da67b6b279b20085446517bb08389e5d8 [file] [log] [blame]
/****************************************************************
* 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.modules.mailbox;
import static org.apache.james.mailbox.opensearch.search.ElasticSearchSearcher.DEFAULT_SEARCH_SIZE;
import java.io.FileNotFoundException;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.james.backends.opensearch.ElasticSearchConfiguration;
import org.apache.james.backends.opensearch.ElasticSearchIndexer;
import org.apache.james.backends.opensearch.ReactorElasticSearchClient;
import org.apache.james.backends.opensearch.RoutingKey;
import org.apache.james.events.EventListener;
import org.apache.james.lifecycle.api.StartUpCheck;
import org.apache.james.lifecycle.api.Startable;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.opensearch.ElasticSearchMailboxConfiguration;
import org.apache.james.mailbox.opensearch.IndexAttachments;
import org.apache.james.mailbox.opensearch.MailboxElasticSearchConstants;
import org.apache.james.mailbox.opensearch.MailboxIdRoutingKeyFactory;
import org.apache.james.mailbox.opensearch.MailboxIndexCreationUtil;
import org.apache.james.mailbox.opensearch.events.ElasticSearchListeningMessageSearchIndex;
import org.apache.james.mailbox.opensearch.query.QueryConverter;
import org.apache.james.mailbox.opensearch.search.ElasticSearchSearcher;
import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex;
import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex.SearchOverride;
import org.apache.james.mailbox.store.search.MessageSearchIndex;
import org.apache.james.utils.ClassName;
import org.apache.james.utils.GuiceGenericLoader;
import org.apache.james.utils.InitializationOperation;
import org.apache.james.utils.InitilizationOperationBuilder;
import org.apache.james.utils.NamingScheme;
import org.apache.james.utils.PropertiesProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.fge.lambdas.Throwing;
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.ProvidesIntoSet;
public class ElasticSearchMailboxModule extends AbstractModule {
static class MailboxIndexCreator implements Startable {
private final ElasticSearchConfiguration configuration;
private final ElasticSearchMailboxConfiguration mailboxConfiguration;
private final ReactorElasticSearchClient client;
@Inject
MailboxIndexCreator(ElasticSearchConfiguration configuration,
ElasticSearchMailboxConfiguration mailboxConfiguration,
ReactorElasticSearchClient client) {
this.configuration = configuration;
this.mailboxConfiguration = mailboxConfiguration;
this.client = client;
}
void createIndex() {
MailboxIndexCreationUtil.prepareClient(client,
mailboxConfiguration.getReadAliasMailboxName(),
mailboxConfiguration.getWriteAliasMailboxName(),
mailboxConfiguration.getIndexMailboxName(),
configuration);
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchMailboxModule.class);
public static final String ELASTICSEARCH_CONFIGURATION_NAME = "elasticsearch";
@Override
protected void configure() {
install(new ElasticSearchQuotaSearcherModule());
bind(ElasticSearchListeningMessageSearchIndex.class).in(Scopes.SINGLETON);
bind(MessageSearchIndex.class).to(ElasticSearchListeningMessageSearchIndex.class);
bind(ListeningMessageSearchIndex.class).to(ElasticSearchListeningMessageSearchIndex.class);
bind(new TypeLiteral<RoutingKey.Factory<MailboxId>>() {}).to(MailboxIdRoutingKeyFactory.class);
Multibinder.newSetBinder(binder(), EventListener.ReactiveGroupEventListener.class)
.addBinding()
.to(ElasticSearchListeningMessageSearchIndex.class);
Multibinder.newSetBinder(binder(), StartUpCheck.class)
.addBinding()
.to(ElasticSearchStartUpCheck.class);
}
@Provides
Set<SearchOverride> provideSearchOverrides(GuiceGenericLoader loader, ElasticSearchConfiguration configuration) {
return configuration.getSearchOverrides()
.stream()
.map(ClassName::new)
.map(Throwing.function(loader.<SearchOverride>withNamingSheme(NamingScheme.IDENTITY)::instantiate))
.peek(routes -> LOGGER.info("Loading Search override {}", routes.getClass().getCanonicalName()))
.collect(ImmutableSet.toImmutableSet());
}
@Provides
@Singleton
@Named(MailboxElasticSearchConstants.InjectionNames.MAILBOX)
private ElasticSearchIndexer createMailboxElasticSearchIndexer(ReactorElasticSearchClient client,
ElasticSearchMailboxConfiguration configuration) {
return new ElasticSearchIndexer(
client,
configuration.getWriteAliasMailboxName());
}
@Provides
@Singleton
private ElasticSearchSearcher createMailboxElasticSearchSearcher(ReactorElasticSearchClient client,
QueryConverter queryConverter,
ElasticSearchMailboxConfiguration configuration,
RoutingKey.Factory<MailboxId> routingKeyFactory) {
return new ElasticSearchSearcher(
client,
queryConverter,
DEFAULT_SEARCH_SIZE,
configuration.getReadAliasMailboxName(), routingKeyFactory);
}
@Provides
@Singleton
private ElasticSearchConfiguration getElasticSearchConfiguration(PropertiesProvider propertiesProvider) throws ConfigurationException {
try {
Configuration configuration = propertiesProvider.getConfiguration(ELASTICSEARCH_CONFIGURATION_NAME);
return ElasticSearchConfiguration.fromProperties(configuration);
} catch (FileNotFoundException e) {
LOGGER.warn("Could not find " + ELASTICSEARCH_CONFIGURATION_NAME + " configuration file. Using {}:{} as contact point",
ElasticSearchConfiguration.LOCALHOST, ElasticSearchConfiguration.DEFAULT_PORT);
return ElasticSearchConfiguration.DEFAULT_CONFIGURATION;
}
}
@Provides
@Singleton
private ElasticSearchMailboxConfiguration getElasticSearchMailboxConfiguration(PropertiesProvider propertiesProvider) throws ConfigurationException {
try {
Configuration configuration = propertiesProvider.getConfiguration(ELASTICSEARCH_CONFIGURATION_NAME);
return ElasticSearchMailboxConfiguration.fromProperties(configuration);
} catch (FileNotFoundException e) {
LOGGER.warn("Could not find " + ELASTICSEARCH_CONFIGURATION_NAME + " configuration file. Providing a default ElasticSearchMailboxConfiguration");
return ElasticSearchMailboxConfiguration.DEFAULT_CONFIGURATION;
}
}
@Provides
@Singleton
public IndexAttachments provideIndexAttachments(ElasticSearchMailboxConfiguration configuration) {
return configuration.getIndexAttachment();
}
@ProvidesIntoSet
InitializationOperation createIndex(MailboxIndexCreator instance) {
return InitilizationOperationBuilder
.forClass(MailboxIndexCreator.class)
.init(instance::createIndex);
}
}