<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/InstallationSource.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import java.net.URL;

interface InstallationSource {
    String determineVersion();

    URL resolveDownloadUrl();
}



<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/ElasticDownloader.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;

import static org.apache.commons.io.FileUtils.forceMkdir;
import static org.apache.commons.io.FileUtils.getFile;

class ElasticDownloader {
    private static final Logger logger = LoggerFactory.getLogger(ElasticDownloader.class);
    private static final String ELS_PACKAGE_STATUS_FILE_SUFFIX = "-downloaded";

    private final InstallationDescription installationDescription;

    ElasticDownloader(InstallationDescription installationDescription) {
        this.installationDescription = installationDescription;
    }

    Path download() throws IOException, InterruptedException {
        prepareDirectories();
        return downloadElastic();
    }

    private void prepareDirectories() throws IOException {
        forceMkdir(getDownloadDirectory());
    }

    private File getDownloadDirectory() {
        return getFile(installationDescription.getDownloadDirectory());
    }

    private Path downloadElastic() throws IOException {
        URL source = installationDescription.getDownloadUrl();
        File target = new File(getDownloadDirectory(), constructLocalFileName(source));
        File statusFile = new File(target.getParentFile(), target.getName() + ELS_PACKAGE_STATUS_FILE_SUFFIX);
        removeBrokenDownload(target, statusFile);
        if (!target.exists()) {
            proceedWithDownload(source, target, statusFile, installationDescription.getDownloaderConnectionTimeoutInMs(), installationDescription.getDownloaderReadTimeoutInMs());
        } else if (!statusFile.exists() && maybeDownloading(target)) {
            waitForDownload(target, statusFile);
        } else if (!statusFile.exists()) {
            throw new IOException("Broken download. File '" + target + "' exits but status '" + statusFile + "' file wash not created");
        } else {
            logger.info("Download skipped");
        }
        return target.toPath();
    }

    private String constructLocalFileName(URL url) {
        String path = url.getPath();
        if (path.isEmpty()) {
            return RandomStringUtils.randomAlphanumeric(10);
        }
        return FilenameUtils.getName(path);
    }

    private void removeBrokenDownload(File target, File statusFile) throws IOException {
        if (target.exists() && !statusFile.exists() && !maybeDownloading(target)) {
            logger.info("Removing broken download file {}", target);
            FileUtils.forceDelete(target);
        }
    }

    private boolean maybeDownloading(File target) {
        // Check based on assumption that if other thread or jvm is currently downloading file on disk should be modified
        // at least every 10 seconds as new data is being downloaded. This will not work on file system
        // without support for lastmodified field or on very slow internet connection
        return System.currentTimeMillis() - target.lastModified() < TimeUnit.SECONDS.toMillis(10L);
    }

    private void proceedWithDownload(URL source, File target, File statusFile, int connectionTimeout, int readTimeout) throws IOException {
        logger.info("Downloading {} to {} ...", source, target);
        copyURLToFile(source, target, connectionTimeout, readTimeout, installationDescription.getDownloadProxy());
        FileUtils.touch(statusFile);
        logger.info("Download complete");
    }

    private void copyURLToFile(URL source,
                               File destination,
                               int connectionTimeout,
                               int readTimeout,
                               Proxy proxy) throws IOException {
        final URLConnection connection = proxy != null ? source.openConnection(proxy) : source.openConnection();
        connection.setConnectTimeout(connectionTimeout);
        connection.setReadTimeout(readTimeout);
        FileUtils.copyInputStreamToFile(connection.getInputStream(), destination);
    }

    private void waitForDownload(File target, File statusFile) throws IOException {
        boolean downloaded;
        do {
            logger.info("File {} (size={}) is probably being downloaded by another thread/jvm. Waiting ...", target, target.length());
            downloaded = FileUtils.waitFor(statusFile, 30);
        } while (!downloaded && maybeDownloading(target));
        if (!downloaded) {
            throw new IOException("Broken download. Another party probably failed to download " + target);
        }
        logger.info("File was downloaded by another thread/jvm. Download skipped");
    }

}


<|start_filename|>test-base/src/main/groovy/pl/allegro/tech/embeddedelasticsearch/EmbeddedElasticCoreApiVersions5AndUnder.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.AMERICAN_PSYCHO
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.AUDIO_BOOK_INDEX_TYPE
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.BOOKS_INDEX_NAME
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.PAPER_BOOK_INDEX_TYPE
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.SHINING
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.toJson

abstract class EmbeddedElasticCoreApiVersions5AndUnder extends EmbeddedElasticCoreApiBaseSpec {

    def "should place document under right index and type"() {
        when:
        index(SHINING)
        index(AMERICAN_PSYCHO)

        then:
        with(fetchAllDocuments(BOOKS_INDEX_NAME, PAPER_BOOK_INDEX_TYPE)) { List<String> it ->
            it.size() == 1
            assertJsonsEquals(toJson(SHINING), it[0])
        }

        and:
        with(fetchAllDocuments(BOOKS_INDEX_NAME, AUDIO_BOOK_INDEX_TYPE)) { List<String> it ->
            it.size() == 1
            assertJsonsEquals(toJson(AMERICAN_PSYCHO), it[0])
        }
    }
}

<|start_filename|>core/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/ValidationSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import org.apache.commons.io.FileUtils
import spock.lang.Specification

import static java.util.concurrent.TimeUnit.MINUTES

class ValidationSpec extends Specification {

    static final ELASTIC_VERSION = "2.2.0"
    static final ELASTIC_DOWNLOAD_URL = new URL("https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/zip/elasticsearch/2.2.0/elasticsearch-2.2.0.zip")

    def "should throw exception on missing elastic version and download url"() {
        when:
            EmbeddedElastic.builder()
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .build()
                    .start()
        then:
            thrown(InvalidSetupException)
    }

    def "should construct embedded elastic with minimal required arguments and version"() {
        when:
            EmbeddedElastic.builder()
                    .withElasticVersion(ELASTIC_VERSION)
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .build()
                    .start()
                    .stop()
        then:
            noExceptionThrown()
    }

    def "should construct embedded elastic with minimal required arguments and url"() {
        when:
            EmbeddedElastic.builder()
                    .withDownloadUrl(ELASTIC_DOWNLOAD_URL)
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .build()
                    .start()
                    .stop()
        then:
            noExceptionThrown()
    }

    def "should construct embedded elastic with minimal required arguments and in resource location"() {
        when:
            EmbeddedElastic.builder()
                    .withInResourceLocation("elasticsearch-6.0.1.zip")
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .build()
                    .start()
                    .stop()
        then:
            noExceptionThrown()
    }

    def "should construct embedded elastic with minimal required arguments and custom download and install directory"() {
        when:
            def uniqId = UUID.randomUUID().toString();
            def installDir = new File(FileUtils.tempDirectory, "$uniqId-install")
            def downloadDir = new File(FileUtils.tempDirectory, "$uniqId-download")
            EmbeddedElastic.builder()
                    .withInResourceLocation("elasticsearch-6.0.1.zip")
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .withDownloadDirectory(downloadDir)
                    .withInstallationDirectory(installDir)
                    .build()
                    .start()
                    .stop()
            FileUtils.deleteDirectory(installDir)
            FileUtils.deleteDirectory(downloadDir)
        then:
            noExceptionThrown()
    }

    def "should throw exception on download url without specified elastic version inside"() {
        when:
            EmbeddedElastic.builder()
                    .withStartTimeout(TEST_START_TIMEOUT_IN_MINUTES, MINUTES)
                    .withDownloadUrl(new URL("http://some.invalid.link.example.com"))
                    .build()
                    .start()
                    .stop()
        then:
            thrown(IllegalArgumentException)
    }

    static final TEST_START_TIMEOUT_IN_MINUTES = 1;

}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/InvalidSetupException.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

public class InvalidSetupException extends IllegalArgumentException {

    public InvalidSetupException(String message) {
        super(message);
    }
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/PopularProperties.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

public interface PopularProperties {
    String HTTP_PORT = "http.port";
    String TRANSPORT_TCP_PORT = "transport.tcp.port";
    String CLUSTER_NAME = "cluster.name";
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/IndexRequest.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

public class IndexRequest {

    private final String indexName;
    private final String indexType;
    private final String id;
    private final String routing;
    private final String json;

    private IndexRequest(String indexName, String indexType, String json, String id, String routing) {
        this.indexName = indexName;
        this.indexType = indexType;
        this.id = id;
        this.routing = routing;
        this.json = json;
    }

    public String getIndexName() {
        return indexName;
    }

    public String getIndexType() {
        return indexType;
    }

    public String getId() {
        return id;
    }

    public String getRouting() {
        return routing;
    }

    public String getJson() {
        return json;
    }

    public static class IndexRequestBuilder {

        private String indexName;
        private String indexType;
        private String id;
        private String routing;
        private String json;

        public IndexRequestBuilder(final String indexName, final String indexType, final String json) {
            this.indexName = indexName;
            this.indexType = indexType;
            this.json = json;
        }

        public IndexRequestBuilder withIndexName(String indexName) {
            this.indexName = indexName;
            return this;
        }

        public IndexRequestBuilder withIndexType(String indexType) {
            this.indexType = indexType;
            return this;
        }

        public IndexRequestBuilder withId(String id) {
            this.id = id;
            return this;
        }

        public IndexRequestBuilder withRouting(String routing) {
            this.routing = routing;
            return this;
        }

        public IndexRequestBuilder withJson(String json) {
            this.json = json;
            return this;
        }

        public IndexRequest build() {
            return new IndexRequest(indexName, indexType, json, id, routing);
        }
    }
}


<|start_filename|>test-base/src/main/groovy/pl/allegro/tech/embeddedelasticsearch/PluginsInstallationBaseSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import groovy.json.JsonSlurper
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClients
import spock.lang.Specification

abstract class PluginsInstallationBaseSpec extends Specification {

    static final HTTP_PORT_VALUE = 9200

    def "should install plugin from url"() {
        given:
            final embeddedElastic = baseEmbeddedElastic()
                    .withPlugin(pluginByUrlUrl())
                    .build()

        when:
            embeddedElastic.start()

        then:
            fetchInstalledPluginsList() == [pluginByUrlName()].toSet()

        cleanup:
            embeddedElastic.stop()
    }

    def "should install plugin from name"() {
        given:
            final embeddedElastic = baseEmbeddedElastic()
                    .withPlugin(pluginByName())
                    .build()

        when:
            embeddedElastic.start()

        then:
            fetchInstalledPluginsList() == [pluginByNameName()].toSet()

        cleanup:
            embeddedElastic.stop()
    }

    def "should install multiple plugins"() {
        given:
            final embeddedElastic = baseEmbeddedElastic()
                    .withPlugin(pluginByUrlUrl())
                    .withPlugin(pluginByName())
                    .build()

        when:
            embeddedElastic.start()

        then:
            fetchInstalledPluginsList() == [pluginByUrlName(), pluginByNameName()].toSet()

        cleanup:
            embeddedElastic.stop()
    }

    abstract EmbeddedElastic.Builder baseEmbeddedElastic()

    Set<String> fetchInstalledPluginsList() {
        final request = new HttpGet("http://localhost:$HTTP_PORT_VALUE/_cat/plugins?format=json")
        final client = HttpClients.createDefault()
        final response = client.execute(request)
        final responseBody = response.entity.content.text
        final json = new JsonSlurper().parseText(responseBody)
        return json.collect { it.component }.toSet()
    }

    abstract String pluginByUrlUrl()

    abstract String pluginByUrlName()

    abstract String pluginByName()

    String pluginByNameName() { pluginByName() }

}


<|start_filename|>es17-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/PluginsInstallationSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import groovy.json.JsonSlurper
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClients

import static java.util.concurrent.TimeUnit.MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.START_TIMEOUT_IN_MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.TEST_ES_JAVA_OPTS

class PluginsInstallationSpec extends PluginsInstallationBaseSpec {

    static final HTTP_PORT_VALUE = 9200

    EmbeddedElastic.Builder baseEmbeddedElastic() {
        return EmbeddedElastic.builder()
                .withElasticVersion("1.7.5")
                .withEsJavaOpts(TEST_ES_JAVA_OPTS)
                .withStartTimeout(START_TIMEOUT_IN_MINUTES, MINUTES)
                .withSetting(PopularProperties.HTTP_PORT, HTTP_PORT_VALUE)
    }

    Set<String> fetchInstalledPluginsList() {
        final request = new HttpGet("http://localhost:$HTTP_PORT_VALUE/_cat/plugins?format=json")
        final client = HttpClients.createDefault()
        final response = client.execute(request)
        final responseBody = response.entity.content.text
        final json = new JsonSlurper().parseText(responseBody)
        return json.collect { it.component }.toSet()
    }

    @Override
    String pluginByUrlUrl() {
        return "http://download.elasticsearch.org/elasticsearch/elasticsearch-cloud-aws/elasticsearch-cloud-aws-2.7.1.zip"
    }

    @Override
    String pluginByUrlName() {
        return "cloud-aws"
    }

    @Override
    String pluginByName() {
        return "elasticsearch/elasticsearch-analysis-stempel/2.7.0"
    }

    @Override
    String pluginByNameName() {
        return "analysis-stempel"
    }
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/InstallationDescription.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ObjectUtils;

import java.io.File;
import java.net.Proxy;
import java.net.URL;
import java.util.List;

class InstallationDescription {

    private static final File DEFAULT_INSTALL_DIR = new File(System.getProperty("java.io.tmpdir"), "embedded-elasticsearch-temp-dir");
    private static final File DEFAULT_DOWNLOAD_DIR = DEFAULT_INSTALL_DIR;

    private final InstallationSource installationSource;
    private final List<Plugin> plugins;
    private final boolean cleanInstallationDirectoryOnStop;
    private final File installationDirectory;
    private final File downloadDirectory;
    private final int downloaderConnectionTimeoutInMs;
    private final int downloaderReadTimeoutInMs;
    private final Proxy downloadProxy;

    InstallationDescription(
            InstallationSource installationSource,
            File downloadDirectory,
            File installationDirectory,
            boolean cleanInstallationDirectoryOnStop,
            List<Plugin> plugins,
            int downloaderConnectionTimeoutInMs,
            int downloaderReadTimeoutInMs, Proxy downloadProxy) {
        this.installationSource = installationSource;
        this.plugins = plugins;
        this.cleanInstallationDirectoryOnStop = cleanInstallationDirectoryOnStop;
        this.installationDirectory = ObjectUtils.firstNonNull(installationDirectory, DEFAULT_INSTALL_DIR);
        this.downloadDirectory = ObjectUtils.firstNonNull(downloadDirectory, DEFAULT_DOWNLOAD_DIR);
        this.downloaderConnectionTimeoutInMs = downloaderConnectionTimeoutInMs;
        this.downloaderReadTimeoutInMs = downloaderReadTimeoutInMs;
        this.downloadProxy = downloadProxy;
    }

    String getVersion() {
        return installationSource.determineVersion();
    }

    URL getDownloadUrl() {
        return installationSource.resolveDownloadUrl();
    }

    List<Plugin> getPlugins() {
        return plugins;
    }

    boolean versionIs1x() {
        return getVersion().startsWith("1.");
    }

    boolean versionIs2x() {
        return getVersion().startsWith("2.");
    }

    boolean isCleanInstallationDirectoryOnStop() {
        return cleanInstallationDirectoryOnStop;
    }

    File getInstallationDirectory() {
        return installationDirectory;
    }

    File getDownloadDirectory() {
        return downloadDirectory;
    }

    int getDownloaderConnectionTimeoutInMs() {
        return downloaderConnectionTimeoutInMs;
    }

    int getDownloaderReadTimeoutInMs() {
        return downloaderReadTimeoutInMs;
    }

    Proxy getDownloadProxy() {
        return downloadProxy;
    }

    static class Plugin {
        private String expression;

        Plugin(String expression) {
            this.expression = expression;
        }

        String getExpression() {
            return expression;
        }

        @Override
        public String toString() {
            return expression;
        }

        String getPluginName() {
            if (expressionIsUrl()) {
                return FilenameUtils.getBaseName(expression).replaceAll("-[\\d].*", "");
            }
            return expression;
        }

        boolean expressionIsUrl() {
            return expression.startsWith("http");
        }
    }
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/InstanceSettings.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

class InstanceSettings {

    private final ObjectMapper yamlObjectMapper = new ObjectMapper(new YAMLFactory());
    private final Map<String, Object> settings;

    InstanceSettings() {
        this(Collections.emptyMap());
    }

    InstanceSettings(Map<String, Object> settings) {
        this.settings = Collections.unmodifiableMap(settings);
    }

    InstanceSettings withSetting(String key, Object value) {
        Map<String, Object> extendedSettings = new HashMap<>();
        extendedSettings.putAll(settings);
        extendedSettings.put(key, value);
        return new InstanceSettings(extendedSettings);
    }

    String toYaml() {
        try {
            return yamlObjectMapper.writeValueAsString(settings);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

}


<|start_filename|>test-base/src/main/groovy/pl/allegro/tech/embeddedelasticsearch/EmbeddedElasticConfiguration.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

/**
 * Common test configuration.
 */
public interface EmbeddedElasticConfiguration {
    /**
     * Default start timeout for test configurations.
     */
    int START_TIMEOUT_IN_MINUTES = 1;
    /**
     * Default Java options for test elasticsearch instances
     */
    String TEST_ES_JAVA_OPTS = "-Xms128m -Xmx512m";
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/IndicesDescription.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import java.util.Collection;
import java.util.Map;
import java.util.Optional;

class IndicesDescription {

    private final Map<String, Optional<IndexSettings>> indices;

    IndicesDescription(Map<String, Optional<IndexSettings>> indices) {
        this.indices = indices;
    }

    Collection<String> getIndicesNames() {
        return indices.keySet();
    }

    Optional<IndexSettings> getIndexSettings(String indexName) {
        return indices.get(indexName);
    }
}


<|start_filename|>es55-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/PluginsInstallationSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import static java.util.concurrent.TimeUnit.MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.START_TIMEOUT_IN_MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.TEST_ES_JAVA_OPTS

class PluginsInstallationSpec extends PluginsInstallationBaseSpec {

    static final HTTP_PORT_VALUE = 9200

    EmbeddedElastic.Builder baseEmbeddedElastic() {
        return EmbeddedElastic.builder()
                .withElasticVersion("5.5.1")
                .withEsJavaOpts(TEST_ES_JAVA_OPTS)
                .withStartTimeout(START_TIMEOUT_IN_MINUTES, MINUTES)
                .withSetting(PopularProperties.HTTP_PORT, HTTP_PORT_VALUE)
    }

    @Override
    String pluginByUrlUrl() {
        return "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-stempel/analysis-stempel-5.5.1.zip"
    }

    @Override
    String pluginByUrlName() {
        return "analysis-stempel"
    }

    @Override
    String pluginByName() {
        return "discovery-file"
    }
}


<|start_filename|>es50-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/CustomInstallationDirectorySpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import spock.lang.Specification
import spock.lang.Stepwise

import static EmbeddedElasticConfiguration.START_TIMEOUT_IN_MINUTES
import static PopularProperties.CLUSTER_NAME
import static PopularProperties.TRANSPORT_TCP_PORT
import static java.util.concurrent.TimeUnit.MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.TEST_ES_JAVA_OPTS

@Stepwise
class CustomInstallationDirectorySpec extends Specification {

    static final ELASTIC_VERSION = "5.4.0"
    static final TRANSPORT_TCP_PORT_VALUE = 9930
    static final CLUSTER_NAME_VALUE = "customDirectoryTestCluster"
    static final File INSTALLATION_DIRECTORY = new File(System.getProperty("java.io.tmpdir"), "embedded-elasticsearch-custom-dir")
    static final String ELASTIC_DIRECTORY_NAME = "elasticsearch-${ELASTIC_VERSION}"

    static EmbeddedElastic embeddedElastic = EmbeddedElastic.builder()
            .withElasticVersion(ELASTIC_VERSION)
            .withSetting(TRANSPORT_TCP_PORT, TRANSPORT_TCP_PORT_VALUE)
            .withSetting(CLUSTER_NAME, CLUSTER_NAME_VALUE)
            .withEsJavaOpts(TEST_ES_JAVA_OPTS)
            .withInstallationDirectory(INSTALLATION_DIRECTORY)
            .withCleanInstallationDirectoryOnStop(false)
            .withStartTimeout(START_TIMEOUT_IN_MINUTES, MINUTES)
            .build()

    void setupSpec() {
        INSTALLATION_DIRECTORY.deleteDir()
    }

    def cleanupSpec() {
        INSTALLATION_DIRECTORY.deleteDir()
    }

    def "should install embedded elastic on custom directory"() {
        when:
            embeddedElastic.start()

        then:
            INSTALLATION_DIRECTORY.exists()
            INSTALLATION_DIRECTORY.listFiles()
                    .findAll { it.isDirectory() }
                    .findAll { it.name == ELASTIC_DIRECTORY_NAME }.size() == 1
    }

    def "should not delete custom installation directory after stop"() {
        when:
            embeddedElastic.stop()

        then:
            INSTALLATION_DIRECTORY.exists()
            INSTALLATION_DIRECTORY.listFiles()
                    .findAll { it.isDirectory() }
                    .findAll { it.name == ELASTIC_DIRECTORY_NAME }.size() == 1
    }

}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/HttpStatusCodes.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

interface HttpStatusCodes {
    int OK = 200;
    int NOT_FOUND = 404;
}


<|start_filename|>es63-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/EmbeddedElasticSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import org.apache.http.HttpHost
import org.elasticsearch.action.get.GetRequest
import org.elasticsearch.action.search.SearchRequest
import org.elasticsearch.client.RestClient
import org.elasticsearch.client.RestHighLevelClient
import org.elasticsearch.index.query.QueryBuilders
import org.elasticsearch.search.builder.SearchSourceBuilder

import static java.util.concurrent.TimeUnit.MINUTES
import static pl.allegro.tech.embeddedelasticsearch.PopularProperties.HTTP_PORT
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.*

class EmbeddedElasticSpec extends EmbeddedElasticCoreApiBaseSpec {

    static final ELASTIC_VERSION = "6.3.0"
    static final HTTP_PORT_VALUE = 9999

    static EmbeddedElastic embeddedElastic = EmbeddedElastic.builder()
            .withElasticVersion(ELASTIC_VERSION)
            .withSetting(HTTP_PORT, HTTP_PORT_VALUE)
            .withEsJavaOpts("-Xms128m -Xmx512m")
            .withTemplate(CARS_TEMPLATE_NAME, CARS_TEMPLATE_6x)
            .withIndex(CARS_INDEX_NAME)
            .withIndex(BOOKS_INDEX_NAME, BOOKS_INDEX)
            .withStartTimeout(2, MINUTES)
            .build()
            .start()

    static RestHighLevelClient client = createClient()

    def setup() {
        embeddedElastic.recreateIndices()
    }

    def cleanupSpec() {
        client.close()
        embeddedElastic.stop()
    }

    static RestHighLevelClient createClient() {
        return new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", HTTP_PORT_VALUE)))
    }

    @Override
    List<String> fetchAllDocuments() {
        fetchAllDocuments(CARS_INDEX_NAME) + fetchAllDocuments(BOOKS_INDEX_NAME)
    }

    @Override
    List<String> fetchAllDocuments(String indexName) {
        final searchRequest = new SearchRequest(indexName)
                .source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));

        client.search(searchRequest)
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    List<String> fetchAllDocuments(String indexName, String typeName) {
        fetchAllDocuments(indexName)
    }

    @Override
    List<String> fetchAllDocuments(String indexName, String typeName, String routing) {
        final searchRequest = new SearchRequest(indexName)
                .types(typeName)
                .routing(routing)
                .source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()))

        client.search(searchRequest)
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    List<String> searchByTerm(String indexName, String typeName, String fieldName, String value) {
        final searchRequest = new SearchRequest()
                .source(new SearchSourceBuilder().query(QueryBuilders.termQuery(fieldName, value)));

        client.search(searchRequest)
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    String getById(String indexName, String typeName, String id) {
        final getRequest = new GetRequest(indexName, typeName, id)
        client.get(getRequest).sourceAsString
    }
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/TemplatesDescription.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import java.util.Collection;
import java.util.Map;

public class TemplatesDescription {
    private final Map<String, String> templates;

    TemplatesDescription(Map<String, String> templates) {
        this.templates = templates;
    }

    Collection<String> getTemplatesNames() {
        return templates.keySet();
    }

    String getTemplateSettings(String templateName) {
        return templates.get(templateName);
    }
}


<|start_filename|>es22-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/PluginsInstallationSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import static java.util.concurrent.TimeUnit.MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.START_TIMEOUT_IN_MINUTES
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.TEST_ES_JAVA_OPTS

class PluginsInstallationSpec extends PluginsInstallationBaseSpec {

    static final HTTP_PORT_VALUE = 9200

    def "should install plugin from github"() {
        given:
            final embeddedElastic = baseEmbeddedElastic()
                    .withPlugin("lmenezes/elasticsearch-kopf")
                    .build()

        when:
            embeddedElastic.start()

        then:
            fetchInstalledPluginsList() == ["kopf"].toSet()

        cleanup:
            embeddedElastic.stop()
    }

    EmbeddedElastic.Builder baseEmbeddedElastic() {
        return EmbeddedElastic.builder()
                .withElasticVersion("2.2.0")
                .withEsJavaOpts(TEST_ES_JAVA_OPTS)
                .withStartTimeout(START_TIMEOUT_IN_MINUTES, MINUTES)
                .withSetting(PopularProperties.HTTP_PORT, HTTP_PORT_VALUE)
    }

    @Override
    String pluginByUrlUrl() {
        return "https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/analysis-smartcn/2.2.0/analysis-smartcn-2.2.0.zip"
    }

    @Override
    String pluginByUrlName() {
        return "analysis-smartcn"
    }

    @Override
    String pluginByName() {
        return "analysis-stempel"
    }
}


<|start_filename|>es50-test/src/test/groovy/pl/allegro/tech/embeddedelasticsearch/EmbeddedElasticSpec.groovy<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch

import org.elasticsearch.client.Client
import org.elasticsearch.common.settings.Settings
import org.elasticsearch.common.transport.InetSocketTransportAddress
import org.elasticsearch.transport.client.PreBuiltTransportClient

import static EmbeddedElasticConfiguration.START_TIMEOUT_IN_MINUTES
import static PopularProperties.CLUSTER_NAME
import static PopularProperties.TRANSPORT_TCP_PORT
import static java.util.concurrent.TimeUnit.MINUTES
import static org.elasticsearch.index.query.QueryBuilders.termQuery
import static pl.allegro.tech.embeddedelasticsearch.EmbeddedElasticConfiguration.TEST_ES_JAVA_OPTS
import static pl.allegro.tech.embeddedelasticsearch.SampleIndices.*

class EmbeddedElasticSpec extends EmbeddedElasticCoreApiVersions5AndUnder {

    static final ELASTIC_VERSION = "5.0.0"
    static final TRANSPORT_TCP_PORT_VALUE = 9930
    static final CLUSTER_NAME_VALUE = "myTestCluster"

    static EmbeddedElastic embeddedElastic = EmbeddedElastic.builder()
            .withElasticVersion(ELASTIC_VERSION)
            .withSetting(TRANSPORT_TCP_PORT, TRANSPORT_TCP_PORT_VALUE)
            .withSetting(CLUSTER_NAME, CLUSTER_NAME_VALUE)
            .withEsJavaOpts(TEST_ES_JAVA_OPTS)
            .withTemplate(CARS_TEMPLATE_NAME, CARS_TEMPLATE)
            .withIndex(CARS_INDEX_NAME)
            .withIndex(BOOKS_INDEX_NAME, BOOKS_INDEX_MULTI_TYPE)
            .withStartTimeout(START_TIMEOUT_IN_MINUTES, MINUTES)
            .build()
            .start()

    static Client client = createClient()

    def setup() {
        embeddedElastic.recreateIndices()
    }

    def cleanupSpec() {
        client.close()
        embeddedElastic.stop()
    }

    static Client createClient() {
        Settings settings = Settings.builder().put("cluster.name", CLUSTER_NAME_VALUE).build()
        return new PreBuiltTransportClient(settings)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), TRANSPORT_TCP_PORT_VALUE))
    }

    @Override
    List<String> fetchAllDocuments() {
        client.prepareSearch().execute().actionGet().hits.hits.toList().collect { it.sourceAsString }
    }

    @Override
    List<String> fetchAllDocuments(String indexName) {
        client.prepareSearch(indexName)
                .execute().actionGet()
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    List<String> fetchAllDocuments(String indexName, String typeName) {
        client.prepareSearch(indexName)
                .setTypes(typeName)
                .execute().actionGet()
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    List<String> fetchAllDocuments(String indexName, String typeName, String routing) {
        client.prepareSearch(indexName)
                .setTypes(typeName)
                .setRouting(routing)
                .execute().actionGet()
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    List<String> searchByTerm(String indexName, String typeName, String fieldName, String value) {
        client.prepareSearch(indexName)
                .setTypes(typeName)
                .setQuery(termQuery(fieldName, value))
                .execute().actionGet()
                .hits.hits.toList()
                .collect { it.sourceAsString }
    }

    @Override
    String getById(String indexName, String typeName, String id) {
        client.prepareGet(indexName, typeName, id).execute().actionGet().sourceAsString
    }
}


<|start_filename|>core/src/main/java/pl/allegro/tech/embeddedelasticsearch/JavaHomeOption.java<|end_filename|>
package pl.allegro.tech.embeddedelasticsearch;

import java.util.function.Consumer;

/**
 * select java home to use when running elasticsearch
 */
public abstract class JavaHomeOption {

    public abstract boolean shouldBeSet();

    public abstract String getValue();

    public void ifNeedBeSet(Consumer<String> consumer) {
        if(shouldBeSet()) {
            consumer.accept(getValue());
        }
    }

    public static JavaHomeOption useSystem() {
        return new JavaHomeOption.UseSystem();
    }

    public static JavaHomeOption inheritTestSuite() {
        return new JavaHomeOption.Inherit();
    }

    public static JavaHomeOption path(String path) {
        return new JavaHomeOption.Path(path);
    }

    /**
     * do not set an option, use the system wide set default
     */
    public static class UseSystem extends JavaHomeOption {

        public boolean shouldBeSet() {
            return false;
        }

        public String getValue() {
            return null;
        }
    }

    /**
     * inherit java home from the process running the tests
     */
    public static class Inherit extends JavaHomeOption {

        @Override
        public boolean shouldBeSet() {
            return true;
        }

        @Override
        public String getValue() {
            return System.getProperty("java.home");
        }
    }

    /**
     * explicitly set java home using a path, e.g.
     *
     * new JavaHomeOption.Path("/usr/lib/jvm/java-8-openjdk-amd64/")
     */
    public static class Path extends JavaHomeOption {

        String value;

        public Path(String value) {
            this.value = value;
        }

        @Override
        public boolean shouldBeSet() {
            return true;
        }

        @Override
        public String getValue() {
            return value;
        }
    }

}
