This section describes how to get started with the low-level REST client from getting the artifact to using it in an application.
The low-level Java REST client is hosted on
Maven
Central. The minimum Java version required is 1.8
.
The low-level REST client is subject to the same release cycle as
Elasticsearch. Replace the version with the desired client version, first
released with 5.0.0-alpha4
. There is no relation between the client version
and the Elasticsearch version that the client can communicate with. The
low-level REST client is compatible with all Elasticsearch versions.
If you are looking for a SNAPSHOT version, the Elastic Maven Snapshot repository is available at https://snapshots.elastic.co/maven/.
Here is how you can configure the dependency using maven as a dependency manager.
Add the following to your pom.xml
file:
org.elasticsearch.client
elasticsearch-rest-client
{version}
The low-level Java REST client internally uses the Apache Http Async Client to send http requests. It depends on the following artifacts, namely the async http client and its own transitive dependencies:
-
org.apache.httpcomponents:httpasyncclient
-
org.apache.httpcomponents:httpcore-nio
-
org.apache.httpcomponents:httpclient
-
org.apache.httpcomponents:httpcore
-
commons-codec:commons-codec
-
commons-logging:commons-logging
In order to avoid version conflicts, the dependencies can be shaded and packaged within the client in a single JAR file (sometimes called an "uber JAR" or "fat JAR"). Shading a dependency consists of taking its content (resources files and Java class files) and renaming some of its packages before putting them in the same JAR file as the low-level Java REST client. Shading a JAR can be accomplished by 3rd-party plugins for Gradle and Maven.
Be advised that shading a JAR also has implications. Shading the Commons Logging layer, for instance, means that 3rd-party logging backends need to be shaded as well.
Here is a configuration using the Maven
Shade
plugin. Add the following to your pom.xml
file:
org.apache.maven.plugins
maven-shade-plugin
3.1.0
package
shade
org.apache.http
hidden.org.apache.http
org.apache.logging
hidden.org.apache.logging
org.apache.commons.codec
hidden.org.apache.commons.codec
org.apache.commons.logging
hidden.org.apache.commons.logging
Here is a configuration using the Gradle
ShadowJar plugin. Add the following to
your build.gradle
file:
shadowJar {
relocate 'org.apache.http', 'hidden.org.apache.http'
relocate 'org.apache.logging', 'hidden.org.apache.logging'
relocate 'org.apache.commons.codec', 'hidden.org.apache.commons.codec'
relocate 'org.apache.commons.logging', 'hidden.org.apache.commons.logging'
}
A RestClient
instance can be built through the corresponding
RestClientBuilder
class, created via RestClient#builder(HttpHost…)
static method. The only required argument is one or more hosts that the
client will communicate with, provided as instances of
HttpHost
as follows:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init]
The RestClient
class is thread-safe and ideally has the same lifecycle as
the application that uses it. It is important that it gets closed when no
longer needed so that all the resources used by it get properly released,
as well as the underlying http client instance and its threads:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-close]
RestClientBuilder
also allows to optionally set the following configuration
parameters while building the RestClient
instance:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init-default-headers]
-
Set the default headers that need to be sent with each request, to prevent having to specify them with each single request
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init-failure-listener]
-
Set a listener that gets notified every time a node fails, in case actions need to be taken. Used internally when sniffing on failure is enabled.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init-node-selector]
-
Set the node selector to be used to filter the nodes the client will send requests to among the ones that are set to the client itself. This is useful for instance to prevent sending requests to dedicated master nodes when sniffing is enabled. By default the client sends requests to every configured node.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init-request-config-callback]
-
Set a callback that allows to modify the default request configuration (e.g. request timeouts, authentication, or anything that the
org.apache.http.client.config.RequestConfig.Builder
allows to set)
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-init-client-config-callback]
-
Set a callback that allows to modify the http client configuration (e.g. encrypted communication over ssl, or anything that the
org.apache.http.impl.nio.client.HttpAsyncClientBuilder
allows to set)
Once the RestClient
has been created, requests can be sent by calling either
performRequest
or performRequestAsync
. performRequest
is synchronous and
will block the calling thread and return the Response
when the request is
successful or throw an exception if it fails. performRequestAsync
is
asynchronous and accepts a ResponseListener
argument that it calls with a
Response
when the request is successful or with an Exception
if it fails.
This is synchronous:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-sync]
-
The HTTP method (
GET
,POST
,HEAD
, etc) -
The endpoint on the server
And this is asynchronous:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-async]
-
The HTTP method (
GET
,POST
,HEAD
, etc) -
The endpoint on the server
-
Handle the response
-
Handle the failure
You can add request parameters to the request object:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-parameters]
You can set the body of the request to any HttpEntity
:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-body]
Important
|
The ContentType specified for the HttpEntity is important
because it will be used to set the Content-Type header so that Elasticsearch
can properly parse the content.
|
You can also set it to a String
which will default to
a ContentType
of application/json
.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-body-shorter]
The RequestOptions
class holds parts of the request that should be shared
between many requests in the same application. You can make a singleton
instance and share it between all requests:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-options-singleton]
-
Add any headers needed by all requests.
-
Customize the response consumer.
addHeader
is for headers that are required for authorization or to work with
a proxy in front of Elasticsearch. There is no need to set the Content-Type
header because the client will automatically set that from the HttpEntity
attached to the request.
You can set the NodeSelector
which controls which nodes will receive
requests. NodeSelector.SKIP_DEDICATED_MASTERS
is a good choice.
You can also customize the response consumer used to buffer the asynchronous responses. The default consumer will buffer up to 100MB of response on the JVM heap. If the response is larger then the request will fail. You could, for example, lower the maximum size which might be useful if you are running in a heap constrained environment like the example above.
Once you’ve created the singleton you can use it when making requests:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-options-set-singleton]
You can also customize these options on a per request basis. For example, this adds an extra header:
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-options-customize-header]
The client is quite happy to execute many actions in parallel. The following
example indexes many documents in parallel. In a real world scenario you’d
probably want to use the _bulk
API instead, but the example is illustrative.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-async-example]
-
Process the returned response
-
Handle the returned exception, due to communication error or a response with status code that indicates an error
The performRequestAsync
method returns a Cancellable
that exposes a single
public method called cancel
. Such method can be called to cancel the on-going
request. Cancelling a request will result in aborting the http request through
the underlying http client. On the server side, this does not automatically
translate to the execution of that request being cancelled, which needs to be
specifically implemented in the API itself.
The use of the Cancellable
instance is optional and you can safely ignore this
if you don’t need it. A typical usecase for this would be using this together with
frameworks like Rx Java or the Kotlin’s suspendCancellableCoRoutine
. Cancelling
no longer needed requests is a good way to avoid putting unnecessary
load on Elasticsearch.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-async-cancel]
-
Process the returned response, in case it was ready before the request got cancelled
-
Handle the returned exception, which will most likely be a
CancellationException
as the request got cancelled
The Response
object, either returned by the synchronous performRequest
methods or
received as an argument in ResponseListener#onSuccess(Response)
, wraps the
response object returned by the http client and exposes some additional information.
include-tagged::{doc-tests}/RestClientDocumentation.java[rest-client-response2]
-
Information about the performed request
-
The host that returned the response
-
The response status line, from which you can for instance retrieve the status code
-
The response headers, which can also be retrieved by name though
getHeader(String)
-
The response body enclosed in an
org.apache.http.HttpEntity
object
When performing a request, an exception is thrown (or received as an argument
in ResponseListener#onFailure(Exception)
in the following scenarios:
IOException
-
communication problem (e.g. SocketTimeoutException)
ResponseException
-
a response was returned, but its status code indicated an error (not
2xx
). AResponseException
originates from a valid http response, hence it exposes its correspondingResponse
object which gives access to the returned response.
Note
|
A ResponseException is not thrown for HEAD requests that return
a 404 status code because it is an expected HEAD response that simply
denotes that the resource is not found. All other HTTP methods (e.g., GET )
throw a ResponseException for 404 responses unless the ignore parameter
contains 404 . ignore is a special client parameter that doesn’t get sent
to Elasticsearch and contains a comma separated list of error status codes.
It allows to control whether some error status code should be treated as an
expected response rather than as an exception. This is useful for instance
with the get api as it can return 404 when the document is missing, in which
case the response body will not contain an error but rather the usual get api
response, just without the document as it was not found.
|
Note that the low-level client doesn’t expose any helper for json marshalling and un-marshalling. Users are free to use the library that they prefer for that purpose.
The underlying Apache Async Http Client ships with different
org.apache.http.HttpEntity
implementations that allow to provide the request body in different formats
(stream, byte array, string etc.). As for reading the response body, the
HttpEntity#getContent
method comes handy which returns an InputStream
reading from the previously buffered response body. As an alternative, it is
possible to provide a custom
org.apache.http.nio.protocol.HttpAsyncResponseConsumer
that controls how bytes are read and buffered.
The Java REST client uses the same logging library that the Apache Async Http
Client uses: Apache Commons Logging,
which comes with support for a number of popular logging implementations. The
java packages to enable logging for are org.elasticsearch.client
for the
client itself and org.elasticsearch.client.sniffer
for the sniffer.
The request tracer logging can also be enabled to log every request and
corresponding response in curl format. That comes handy when debugging, for
instance in case a request needs to be manually executed to check whether it
still yields the same response as it did. Enable trace logging for the tracer
package to have such log lines printed out. Do note that this type of logging is
expensive and should not be enabled at all times in production environments,
but rather temporarily used only when needed.
In order to enable trace logs for logback, we have to use jcl-over-slf4j bridging module.
-
Add the following to your Gradle setting:
dependencies { implementation('org.slf4j:slf4j-api:1.8.0-beta2') implementation('ch.qos.logback:logback-classic:1.3.0-alpha4') implementation('org.slf4j:jcl-over-slf4j:1.8.0-beta2') }
-
Exclude
commons-logging.jar
:dependencies { configurations.all { exclude group: "commons-logging", module: "commons-logging" } }
-
Add a tracer logger in Logback configuration:
<logger name="tracer" level="TRACE" additivity="false"> <appender-ref ref="your_appender_block_name" /> </logger>