17

I'm following the tutorial from elastic search java api client here: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html

My code is as following.

// Create the low-level client
RestClient restClient = RestClient.builder(
 new HttpHost("localhost", 9200)).build();

// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
 restClient, new JacksonJsonpMapper());

// And create the API client
ElasticsearchClient client = new ElasticsearchClient(transport);

try {
 SearchResponse<Object> search = client.search(s -> s
   .index("*:*"),
   Object.class);
} catch (IOException e) {
 System.out.println(e.getMessage());
}

This code is throwing out the following exception:

co.elastic.clients.transport.TransportException: [es/search] Missing [X-Elastic-Product] header. Please check that you are connecting to an Elasticsearch instance, and that any networking filters are preserving that header.

I've tried manually putting this header via the setDefaultHeaders method like this:

RestClientBuilder builder = RestClient.builder(
 new HttpHost("localhost", 9200, "http"));
Header[] defaultHeaders = new Header[]{new BasicHeader("X-Elastic-Product", "Elasticsearch")};
builder.setDefaultHeaders(defaultHeaders);
RestClient restClient = builder.build();

But the error is the same.

I've tried both version 7.16 and 8.0.0, same result.

Isvoran Andrei
  • 409
  • 7
  • 16
  • are you sure you are also running an Elasticsearch server (not API) version >= 7.16 ? – VeryNiceArgumentException Jun 06 '22 at 10:20
  • 2
    I ran into the same problem... have you found any solution? I am using he new java client 8.4 on an elasticsearch server 7.10. I've set compatibility headers. Indexing of a document works fine. When I perform a get request for a given index and id this problem arise – Saverio Mirko Viola Oct 12 '22 at 08:43

2 Answers2

17

The default headers the RestClientBuilder allows you to specify are the request headers, not the response headers. The error you are getting is because older Elasticsearch [server] versions do not include the X-Elastic-Product=Elasticsearch header in any of the API responses, but the recent distributions do (7.14+?), so the newer versions of elasticsearch-java (i.e. client) expects them.

I am in the same boat — I use 8.4.2 of elasticsearch-java with an Elasticsearch server version of 7.2.0.

I ran into two format-based compatibility issues:

  1. The client passing a Content-Type not known to the server, and so its request getting rejected with a 406
  2. The client validating if the response has X-Elastic-Product=Elasticsearch header

Fortunately, the RestClientBuilder allows you to customize the underlying http client through: setHttpClientConfigCallback. The callback looks like this, so basically you can intercept the request and responses, manipulate headers, and thereby get around these issues:

    public interface HttpClientConfigCallback {
        /**
         * Allows to customize the {@link CloseableHttpAsyncClient} being created and used by the {@link RestClient}.
         * Commonly used to customize the default {@link org.apache.http.client.CredentialsProvider} for authentication
         * or the {@link SchemeIOSessionStrategy} for communication through ssl without losing any other useful default
         * value that the {@link RestClientBuilder} internally sets, like connection pooling.
         */
        HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder);
    }

Specifically, here's what worked for me:

var httpClientConfigCallback = httpClientBuilder ->
        httpClientBuilder
            .setDefaultCredentialsProvider(credentialsProvider)
            // this request & response header manipulation helps get around newer (>=7.16) versions
            // of elasticsearch-java client not working with older (<7.14) versions of Elasticsearch
            // server
            .setDefaultHeaders(
                List.of(
                    new BasicHeader(
                        HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())))
            .addInterceptorLast(
                (HttpResponseInterceptor)
                    (response, context) ->
                        response.addHeader("X-Elastic-Product", "Elasticsearch"));
var restClient =
        RestClient.builder(elasticsearchHosts)
            .setHttpClientConfigCallback(httpClientConfigCallback)
            .build();

Note that there could still be behavioral differences between the aforementioned product and API versions as they are way too apart. The above only fixes format-based incompatibilities. For this reason, it's always best to use at least the same major versions of these components, if not the exact versions.

mystarrocks
  • 4,040
  • 2
  • 36
  • 61
2

Below is what worked for me. I hope it helps someone out there

@Configuration
public class config {
    @Bean
    public RestClient getRestClient() {
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("", ""));
        return RestClient.builder(new HttpHost("localhost", 9500))
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    httpClientBuilder.disableAuthCaching();
                    httpClientBuilder.setDefaultHeaders(List.of(
                            new BasicHeader(
                                    HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)));
                    httpClientBuilder.addInterceptorLast((HttpResponseInterceptor)
                            (response, context) ->
                                    response.addHeader("X-Elastic-Product", "Elasticsearch"));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }).build();
    }

    @Bean
    public ElasticsearchTransport getElasticsearchTransport() {
        return new RestClientTransport(getRestClient(), new JacksonJsonpMapper());
    }

    @Bean
    public ElasticsearchClient getElasticsearchClient() {
        return new ElasticsearchClient(getElasticsearchTransport());
    }

}
Jaba
  • 39
  • 3