package sk.stuba.fiit.perconik.elasticsearch.preferences; import java.io.Serializable; import java.net.InetSocketAddress; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; import com.google.common.base.Function; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; import org.eclipse.core.runtime.Platform; import org.eclipse.osgi.service.datalocation.Location; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; import sk.stuba.fiit.perconik.utilities.configuration.AbstractOptionAccessor; import sk.stuba.fiit.perconik.utilities.configuration.AbstractOptions; import sk.stuba.fiit.perconik.utilities.configuration.AbstractOptionsWriter; import sk.stuba.fiit.perconik.utilities.configuration.Configurables; import sk.stuba.fiit.perconik.utilities.configuration.OptionAccessor; import sk.stuba.fiit.perconik.utilities.configuration.OptionParser; import sk.stuba.fiit.perconik.utilities.configuration.Options; import sk.stuba.fiit.perconik.utilities.configuration.OptionsReader; import sk.stuba.fiit.perconik.utilities.configuration.OptionsWriter; import static java.util.Arrays.asList; import static java.util.UUID.randomUUID; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.isNull; import static com.google.common.base.Predicates.not; import static com.google.common.collect.ImmutableMap.builder; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.filterValues; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; import static sk.stuba.fiit.perconik.elasticsearch.plugin.Activator.PLUGIN_ID; import static sk.stuba.fiit.perconik.elasticsearch.preferences.ElasticsearchOptionParsers.byteSizeParser; import static sk.stuba.fiit.perconik.elasticsearch.preferences.ElasticsearchOptionParsers.timeParser; import static sk.stuba.fiit.perconik.preferences.AbstractPreferences.Keys.join; import static sk.stuba.fiit.perconik.preferences.AbstractPreferences.Keys.separator; import static sk.stuba.fiit.perconik.utilities.MoreStrings.trimLeading; import static sk.stuba.fiit.perconik.utilities.configuration.Configurables.newReader; import static sk.stuba.fiit.perconik.utilities.configuration.OptionParsers.arrayListParser; import static sk.stuba.fiit.perconik.utilities.configuration.OptionParsers.booleanParser; import static sk.stuba.fiit.perconik.utilities.configuration.OptionParsers.inetSocketAddressParser; import static sk.stuba.fiit.perconik.utilities.configuration.OptionParsers.pathParser; import static sk.stuba.fiit.perconik.utilities.configuration.OptionParsers.stringParser; import static sk.stuba.fiit.perconik.utilities.io.MorePaths.path; public interface ElasticsearchOptions extends Options { public static final class Schema { static final String qualifier = join(PLUGIN_ID, "preferences"); public static final OptionAccessor<String> nodeName = option(stringParser(), join(qualifier, "name"), "perconik-client-" + randomUUID()); public static final OptionAccessor<String> clusterName = option(stringParser(), join(qualifier, "cluster", "name"), "perconik"); public static final OptionAccessor<ArrayList<InetSocketAddress>> clientTransportAddresses = option(arrayListParser(inetSocketAddressParser(), ",", "", ""), join(qualifier, "client", "transport", "addresses"), newArrayList(new InetSocketAddress("127.0.0.1", 9300))); public static final OptionAccessor<TimeValue> clientTransportNodesSamplerInterval = option(timeParser(), join(qualifier, "client", "transport", "nodes_sampler_interval"), timeValueSeconds(5)); public static final OptionAccessor<TimeValue> clientTransportPingTimeout = option(timeParser(), join(qualifier, "client", "transport", "ping_timeout"), timeValueSeconds(5)); public static final OptionAccessor<Boolean> clientTransportIgnoreClusterName = option(booleanParser(), join(qualifier, "client", "transport", "ignore_cluster_name"), false); public static final OptionAccessor<TimeValue> transportTcpConnectTimeout = option(timeParser(), join(qualifier, "transport", "tcp", "connect_timeout"), timeValueSeconds(30)); public static final OptionAccessor<Boolean> transportTcpCompress = option(booleanParser(), join(qualifier, "transport", "tcp", "compress"), false); public static final OptionAccessor<Boolean> networkTcpNoDelay = option(booleanParser(), join(qualifier, "network", "tcp", "no_delay"), true); public static final OptionAccessor<Boolean> networkTcpKeepAlive = option(booleanParser(), join(qualifier, "network", "tcp", "keep_alive"), true); public static final OptionAccessor<Boolean> networkTcpReuseAddress = option(booleanParser(), join(qualifier, "network", "tcp", "reuse_address"), false); public static final OptionAccessor<ByteSizeValue> networkTcpSendBufferSize = option(byteSizeParser(), join(qualifier, "network", "tcp", "send_buffer_size"), null); public static final OptionAccessor<ByteSizeValue> networkTcpReceiveBufferSize = option(byteSizeParser(), join(qualifier, "network", "tcp", "receive_buffer_size"), null); public static final OptionAccessor<Path> pathLogs = option(pathParser(), join(qualifier, "path", "logs"), defaultPath("elasticsearch", "logs")); public static final OptionAccessor<Path> pathWork = option(pathParser(), join(qualifier, "path", "work"), defaultPath("elasticsearch", "work")); public static final OptionAccessor<Boolean> displayErrors = option(booleanParser(), join(qualifier, "display_errors"), true); public static final OptionAccessor<Boolean> logNotices = option(booleanParser(), join(qualifier, "log_notices"), false); public static final OptionAccessor<Boolean> logErrors = option(booleanParser(), join(qualifier, "log_errors"), true); static final ImmutableMap<String, OptionAccessor<?>> accessors; static { ImmutableMap.Builder<String, OptionAccessor<?>> builder = builder(); for (OptionAccessor<?> accessor: Configurables.accessors(Schema.class)) { builder.put(accessor.getKey(), accessor); } accessors = builder.build(); } private Schema() {} public static ImmutableCollection<OptionAccessor<?>> accessors() { return accessors.values(); } public static ImmutableSet<String> keys() { return accessors.keySet(); } static Path defaultPath(final String ... segments) { try { Location configuration = Platform.getConfigurationLocation(); return path(configuration.getURL().toURI()).resolve(path("..", segments)).normalize(); } catch (Exception e) { return path(".").resolve(path(asList(segments))).normalize(); } } static <T> OptionAccessor<T> option(final OptionParser<T> parser, final String key, @Nullable final T defaultValue) { return new CustomOptionAccessor<>(parser.type(), parser, key, defaultValue); } static final class CustomOptionAccessor<T> extends AbstractOptionAccessor<T> { private final OptionParser<T> parser; CustomOptionAccessor(final TypeToken<T> type, final OptionParser<T> parser, final String key, @Nullable final T defaultValue) { super(type, key, defaultValue); this.parser = parser; } @Override protected OptionsReader reader(final Options options) { return newReader(options); } @Override protected OptionsWriter writer(final Options options) { return new CustomOptionsWriter(options); } @Override protected OptionParser<? extends T> parser() { return this.parser; } } static final class CustomOptionsWriter extends AbstractOptionsWriter { private final Options options; CustomOptionsWriter(final Options options) { this.options = checkNotNull(options); } @Override protected Options options() { return this.options; } @Override public Object putRaw(final String key, @Nullable final Object value) { return super.putRaw(key, CustomOptionsConverter.INSTANCE.apply(value)); } } static enum CustomOptionsConverter implements Function<Object, String> { INSTANCE; public String apply(@Nullable final Object input) { if (input == null) { return null; } if (input instanceof Collection) { Collection<?> collection = (Collection<?>) input; StringBuilder builder = new StringBuilder(24 * collection.size()); for (Object element: collection) { String value = element.toString(); if (element instanceof InetSocketAddress) { value = trimLeading(value, "/"); } builder.append(value).append(','); } return builder.deleteCharAt(builder.length() - 1).toString(); } return input.toString(); } } static Map<String, Object> toMap(final Options options) { return filterValues(Configurables.values(accessors(), options, new LinkedHashMap<String, Object>()), not(isNull())); } static Settings toSettings(final Options options) { Set<String> ignore = ImmutableSet.of(displayErrors.getKey(), logNotices.getKey(), logErrors.getKey()); ImmutableSettings.Builder builder = settingsBuilder(); int prefix = (qualifier + separator).length(); for (Entry<String, Object> option: toMap(options).entrySet()) { String key = option.getKey(); if (!ignore.contains(key)) { builder.put(key.substring(prefix), CustomOptionsConverter.INSTANCE.apply(option.getValue())); } } return builder.build(); } } public static final class View extends AbstractOptions implements ElasticsearchOptions, Serializable { private static final long serialVersionUID = 0L; private final Options options; private View(final Options options) { this.options = checkNotNull(options); } public static ElasticsearchOptions of(final Options options) { return new View(options); } public Map<String, Object> toMap() { return Schema.toMap(this.options); } public Settings toSettings() { return Schema.toSettings(this.options); } } public Settings toSettings(); }