diff --git a/dev-tools/src/main/resources/ant/integration-tests.xml b/dev-tools/src/main/resources/ant/integration-tests.xml
index 9c8df5d4c8f..23df37338de 100644
--- a/dev-tools/src/main/resources/ant/integration-tests.xml
+++ b/dev-tools/src/main/resources/ant/integration-tests.xml
@@ -297,6 +297,11 @@
+
+
+
+
+
diff --git a/qa/pom.xml b/qa/pom.xml
index f8b1f38f6bd..a3ac686cee0 100644
--- a/qa/pom.xml
+++ b/qa/pom.xml
@@ -147,6 +147,7 @@
smoke-test-plugins
smoke-test-multinode
+ smoke-test-client
diff --git a/qa/smoke-test-client/pom.xml b/qa/smoke-test-client/pom.xml
new file mode 100644
index 00000000000..1daec63add0
--- /dev/null
+++ b/qa/smoke-test-client/pom.xml
@@ -0,0 +1,130 @@
+
+
+
+ elasticsearch-qa
+ org.elasticsearch.qa
+ 2.1.0-SNAPSHOT
+
+ 4.0.0
+
+
+
+ smoke-test-client
+ QA: Smoke Test Client
+ Test the Java Client against a running cluster
+ pom
+
+
+ true
+
+
+
+
+ org.elasticsearch
+ elasticsearch
+ test
+
+
+ log4j
+ log4j
+ test
+
+
+
+
+
+
+ src/test/resources
+
+
+
+ ${elasticsearch.tools.directory}/shared-test-resources
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-remote-resources-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ integ-setup-dependencies
+ pre-integration-test
+
+ copy
+
+
+ ${skip.integ.tests}
+ true
+ ${integ.deps}/plugins
+
+
+
+
+ org.elasticsearch.distribution.zip
+ elasticsearch
+ ${elasticsearch.version}
+ zip
+ true
+ ${integ.deps}
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+
+ integ-setup
+ pre-integration-test
+
+ run
+
+
+ ${skip.integ.tests}
+
+
+
+
+
+
+
+
+
+ integ-teardown
+ post-integration-test
+
+ run
+
+
+ ${skip.integ.tests}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java
new file mode 100644
index 00000000000..0ccd28f47f1
--- /dev/null
+++ b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.elasticsearch.smoketest;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.client.transport.TransportClient;
+import org.elasticsearch.common.logging.ESLogger;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.common.transport.TransportAddress;
+import org.elasticsearch.node.internal.InternalSettingsPreparer;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.file.Path;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiOfLength;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * {@link ESSmokeClientTestCase} is an abstract base class to run integration
+ * tests against an external Elasticsearch Cluster.
+ *
+ * You can define a list of transport addresses from where you can reach your cluster
+ * by setting "tests.cluster" system property. It defaults to "localhost:9300".
+ *
+ * All tests can be run from maven using mvn install as maven will start an external cluster first.
+ *
+ * If you want to debug this module from your IDE, then start an external cluster by yourself
+ * then run JUnit. If you changed the default port, set "tests.cluster=localhost:PORT" when running
+ * your test.
+ */
+public abstract class ESSmokeClientTestCase extends LuceneTestCase {
+
+ /**
+ * Key used to eventually switch to using an external cluster and provide its transport addresses
+ */
+ public static final String TESTS_CLUSTER = "tests.cluster";
+
+ /**
+ * Defaults to localhost:9300
+ */
+ public static final String TESTS_CLUSTER_DEFAULT = "localhost:9300";
+
+ protected static ESLogger logger = ESLoggerFactory.getLogger(ESSmokeClientTestCase.class.getName());
+
+ private static final AtomicInteger counter = new AtomicInteger();
+ private static Client client;
+ private static String clusterAddresses;
+ protected String index;
+
+ private static Client startClient(Path tempDir, TransportAddress... transportAddresses) {
+ Settings clientSettings = Settings.settingsBuilder()
+ .put("name", "qa_smoke_client_" + counter.getAndIncrement())
+ .put(InternalSettingsPreparer.IGNORE_SYSTEM_PROPERTIES_SETTING, true) // prevents any settings to be replaced by system properties.
+ .put("client.transport.ignore_cluster_name", true)
+ .put("path.home", tempDir)
+ .put("node.mode", "network").build(); // we require network here!
+
+ TransportClient.Builder transportClientBuilder = TransportClient.builder().settings(clientSettings);
+ TransportClient client = transportClientBuilder.build().addTransportAddresses(transportAddresses);
+
+ logger.info("--> Elasticsearch Java TransportClient started");
+
+ Exception clientException = null;
+ try {
+ ClusterHealthResponse health = client.admin().cluster().prepareHealth().get();
+ logger.info("--> connected to [{}] cluster which is running [{}] node(s).",
+ health.getClusterName(), health.getNumberOfNodes());
+ } catch (Exception e) {
+ clientException = e;
+ }
+
+ assumeNoException("Sounds like your cluster is not running at " + clusterAddresses, clientException);
+
+ return client;
+ }
+
+ private static Client startClient() throws UnknownHostException {
+ String[] stringAddresses = clusterAddresses.split(",");
+ TransportAddress[] transportAddresses = new TransportAddress[stringAddresses.length];
+ int i = 0;
+ for (String stringAddress : stringAddresses) {
+ String[] split = stringAddress.split(":");
+ if (split.length < 2) {
+ throw new IllegalArgumentException("address [" + clusterAddresses + "] not valid");
+ }
+ try {
+ transportAddresses[i++] = new InetSocketTransportAddress(InetAddress.getByName(split[0]), Integer.valueOf(split[1]));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("port is not valid, expected number but was [" + split[1] + "]");
+ }
+ }
+ return startClient(createTempDir(), transportAddresses);
+ }
+
+ public static Client getClient() {
+ if (client == null) {
+ try {
+ client = startClient();
+ } catch (UnknownHostException e) {
+ logger.error("can not start the client", e);
+ }
+ assertThat(client, notNullValue());
+ }
+ return client;
+ }
+
+ @BeforeClass
+ public static void initializeSettings() throws UnknownHostException {
+ clusterAddresses = System.getProperty(TESTS_CLUSTER);
+ if (clusterAddresses == null || clusterAddresses.isEmpty()) {
+ clusterAddresses = TESTS_CLUSTER_DEFAULT;
+ logger.info("[{}] not set. Falling back to [{}]", TESTS_CLUSTER, TESTS_CLUSTER_DEFAULT);
+ }
+ }
+
+ @AfterClass
+ public static void stopTransportClient() {
+ if (client != null) {
+ client.close();
+ client = null;
+ }
+ }
+
+ @Before
+ public void defineIndexName() {
+ doClean();
+ index = "qa-smoke-test-client-" + randomAsciiOfLength(10).toLowerCase(Locale.getDefault());
+ }
+
+ @After
+ public void cleanIndex() {
+ doClean();
+ }
+
+ private void doClean() {
+ if (client != null) {
+ try {
+ client.admin().indices().prepareDelete(index).get();
+ } catch (Exception e) {
+ // We ignore this cleanup exception
+ }
+ }
+ }
+
+}
diff --git a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/SmokeTestClientIT.java b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/SmokeTestClientIT.java
new file mode 100644
index 00000000000..dbf51f0f739
--- /dev/null
+++ b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/SmokeTestClientIT.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.elasticsearch.smoketest;
+
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.Client;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class SmokeTestClientIT extends ESSmokeClientTestCase {
+
+ /**
+ * Check that we are connected to a cluster named "elasticsearch".
+ */
+ @Test
+ public void testSimpleClient() {
+ Client client = getClient();
+
+ // START SNIPPET: java-doc-admin-cluster-health
+ ClusterHealthResponse health = client.admin().cluster().prepareHealth().setWaitForYellowStatus().get();
+ // END SNIPPET: java-doc-admin-cluster-health
+ assertThat(health.getClusterName(), is("elasticsearch"));
+ }
+
+ /**
+ * Create an index and index some docs
+ */
+ @Test
+ public void testPutDocument() {
+ Client client = getClient();
+
+ // START SNIPPET: java-doc-index-doc-simple
+ client.prepareIndex(index, "doc", "1") // Index, Type, Id
+ .setSource("foo", "bar") // Simple document: { "foo" : "bar" }
+ .get(); // Execute and wait for the result
+ // END SNIPPET: java-doc-index-doc-simple
+
+ // START SNIPPET: java-doc-admin-indices-refresh
+ // Prepare a refresh action on a given index, execute and wait for the result
+ client.admin().indices().prepareRefresh(index).get();
+ // END SNIPPET: java-doc-admin-indices-refresh
+
+ // START SNIPPET: java-doc-search-simple
+ SearchResponse searchResponse = client.prepareSearch(index).get();
+ assertThat(searchResponse.getHits().getTotalHits(), is(1L));
+ // END SNIPPET: java-doc-search-simple
+ }
+}
+