| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Fesen HttpClient is a Java implementation of the org.opensearch.client.Client interface that talks to OpenSearch or Elasticsearch over their HTTP REST APIs instead of the native transport protocol. It lets applications written against the standard OpenSearch client API run against any HTTP-reachable cluster, including managed services that only expose REST endpoints.
The library is built on curl4j, a lightweight, pure-Java HTTP client, so it has no dependency on the OpenSearch/Elasticsearch transport module.
<dependency>
<groupId>org.codelibs.fesen.client</groupId>
<artifactId>fesen-httpclient</artifactId>
<version>3.7.0</version>
</dependency>implementation 'org.codelibs.fesen.client:fesen-httpclient:3.7.0'Check the Maven Central badge above for the latest published version.
import org.opensearch.client.Client;
import org.codelibs.fesen.client.HttpClient;
import org.opensearch.common.settings.Settings;
import org.opensearch.index.query.QueryBuilders;
import static org.opensearch.core.action.ActionListener.wrap;
Settings settings = Settings.builder()
.putList("http.hosts", "http://localhost:9200")
.put("http.compression", true)
.build();
Client client = new HttpClient(settings, null);
client.prepareSearch("my_index")
.setQuery(QueryBuilders.matchAllQuery())
.execute(wrap(response -> {
long totalHits = response.getHits().getTotalHits().value();
System.out.println("Found " + totalHits + " documents");
}, exception -> {
System.err.println("Search failed: " + exception.getMessage());
}));
// client.close() when the client is no longer neededThe second constructor argument is a ThreadPool. Passing null is fine; HttpClient creates its own internal thread pool from the thread_pool.http.* settings.
http.hosts is the only required setting. Everything else has a sensible default.
| Setting | Description | Default |
|---|---|---|
| http.hosts | List of node URLs (http:// is assumed if the scheme is omitted) | required |
| http.compression | Enable GZIP compression | true |
| http.heartbeat_interval | Node health-check interval, in milliseconds | 10000 |
| http.connection_timeout | Connection timeout, in milliseconds (0 = no timeout) | 0 |
| http.socket_timeout | Socket read timeout, in milliseconds (0 = no timeout) | 0 |
| Setting | Description |
|---|---|
| fesen.username | HTTP Basic authentication username |
| fesen.password | HTTP Basic authentication password |
| http.ssl.certificate_authorities | Path to a CA certificate file used to validate the server's TLS certificate |
| Setting | Description |
|---|---|
| http.proxy_host | Proxy server hostname |
| http.proxy_port | Proxy server port |
| http.proxy_username | Proxy authentication username |
| http.proxy_password | Proxy authentication password |
| Setting | Description | Default |
|---|---|---|
| thread_pool.http.size | Size of the internal HTTP thread pool | number of CPU cores |
| thread_pool.http.async | Run requests asynchronously through the thread pool | false |
Settings settings = Settings.builder()
.putList("http.hosts",
"https://node1:9200",
"https://node2:9200",
"https://node3:9200")
.put("http.heartbeat_interval", 5000L)
.put("fesen.username", "admin")
.put("fesen.password", "admin")
.put("http.ssl.certificate_authorities", "/path/to/ca.pem")
.build();
Client client = new HttpClient(settings, null);HttpClient.addRequestBuilder registers a function that is applied to every outgoing curl4j request, which is useful for adding headers such as API keys:
import org.codelibs.curl.CurlRequest;
HttpClient httpClient = new HttpClient(settings, null);
httpClient.addRequestBuilder(request -> request.header("X-Custom-Header", "MyValue"));import org.opensearch.action.index.IndexRequest;
import org.opensearch.common.xcontent.XContentType;
IndexRequest request = new IndexRequest("my_index")
.id("1")
.source("{\"name\": \"John Doe\", \"age\": 30}", XContentType.JSON);
client.index(request, wrap(
response -> System.out.println("Indexed: " + response.getId()),
exception -> System.err.println("Index failed: " + exception.getMessage())));import org.opensearch.action.get.GetRequest;
GetRequest request = new GetRequest("my_index", "1");
client.get(request, wrap(response -> {
if (response.isExists()) {
System.out.println("Document: " + response.getSourceAsString());
}
}, exception -> System.err.println("Get failed: " + exception.getMessage())));import org.opensearch.action.bulk.BulkRequest;
import org.opensearch.action.index.IndexRequest;
BulkRequest bulkRequest = new BulkRequest();
for (int i = 0; i < 1000; i++) {
bulkRequest.add(new IndexRequest("my_index")
.id(String.valueOf(i))
.source("{\"id\": " + i + "}", XContentType.JSON));
}
client.bulk(bulkRequest, wrap(
response -> System.out.println("Indexed " + response.getItems().length + " documents"),
exception -> System.err.println("Bulk request failed: " + exception.getMessage())));import org.opensearch.action.admin.indices.create.CreateIndexRequest;
CreateIndexRequest request = new CreateIndexRequest("my_new_index");
request.mapping("""
{
"properties": {
"name": {"type": "text"},
"created_at": {"type": "date"}
}
}
""", XContentType.JSON);
client.admin().indices().create(request, wrap(
response -> System.out.println("Created: " + response.index()),
exception -> System.err.println("Create failed: " + exception.getMessage())));import org.opensearch.action.admin.cluster.health.ClusterHealthRequest;
client.admin().cluster().health(new ClusterHealthRequest(), wrap(
response -> System.out.println("Status: " + response.getStatus()),
exception -> System.err.println("Health check failed: " + exception.getMessage())));Search, scroll, point-in-time, aggregations, and other read/write operations follow the same pattern using the corresponding request classes from org.opensearch.action.*. HttpClient covers close to the full REST API surface; see the action package described below for the complete list.
The integration test suite runs against the following versions using Testcontainers:
| Engine | Versions tested |
|---|---|
| OpenSearch | 1.3.20, 2.19.4, 3.8.0 |
| Elasticsearch | 7.17.29, 8.19.11 |
HttpClient inspects the cluster's root endpoint (GET /) to determine which engine and major version it is talking to, and adjusts request/response handling where the two diverge.
git clone https://github.com/codelibs/fesen-httpclient.git
cd fesen-httpclient
mvn clean compile # compile
mvn clean test # run the test suite (requires Docker; see below)
mvn clean package # build the jar
mvn clean install # install to the local repositoryThe test suite uses Testcontainers to start real OpenSearch/Elasticsearch instances in Docker, one version-specific test class per engine (OpenSearch1ClientTest, OpenSearch2ClientTest, OpenSearch3ClientTest, Elasticsearch7ClientTest, Elasticsearch8ClientTest). Docker must be running locally. To run a single engine's tests:
mvn test -Dtest=OpenSearch3ClientTestBefore committing, format the code and apply license headers:
mvn formatter:format
mvn license:formatA coverage report is produced automatically under target/site/jacoco during mvn package, or on demand with mvn jacoco:report.
Connection refused (org.codelibs.curl.CurlException: Connection refused) Verify that the cluster is running and reachable at the host and port configured in http.hosts.
Authentication failed (org.opensearch.OpenSearchStatusException: security_exception) Check the fesen.username/fesen.password credentials and the permissions of that user.
TLS handshake failure (javax.net.ssl.SSLHandshakeException) Check the http.ssl.certificate_authorities path and make sure the certificate is valid and trusted.
No available nodes (org.codelibs.fesen.client.node.NodeUnavailableException) All configured nodes are currently failing health checks. Verify network connectivity and cluster health, and consider adjusting http.heartbeat_interval.
Debug logging — for Log4j2:
logger.fesen.name = org.codelibs.fesen.client
logger.fesen.level = DEBUGContributions are welcome. For anything beyond a small fix, please open an issue first to discuss the change.
Licensed under the Apache License, Version 2.0. See LICENSE for details.
| Back | FazBrowse Home | New Git URL |