
org.webpieces.elasticsearch.ElasticClient Maven / Gradle / Ivy
package org.webpieces.elasticsearch;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import javax.inject.Singleton;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.client.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.webpieces.elasticsearch.actions.Action;
import org.webpieces.elasticsearch.actions.AliasChange;
import org.webpieces.elasticsearch.actions.AtomicActionList;
import org.webpieces.elasticsearch.mapping.ElasticIndex;
import com.fasterxml.jackson.databind.ObjectMapper;
@Singleton
public class ElasticClient {
private static final Logger log = LoggerFactory.getLogger(ElasticClient.class);
private RestClient client;
private static final ObjectMapper objectMapper = new ObjectMapper();
public ElasticClient() {
log.info("constructing singleton");
}
/**
* Always connect in a method NOT in construction so that people don't accidentally connect to servers in
* tests (ie. every line in guice Module.configure is hit for tests even if you swap it out in a test so to avoid that,
* we do not create this in guice)
*/
public void connect(String ipAddress, int port) {
client = RestClient.builder(new HttpHost(ipAddress, port, "https")).build();
}
public void close() {
try {
client.close();
} catch (IOException e) {
throw new RuntimeException("Close failed");
}
}
public CompletableFuture loadDocument(String index, long id, Object document) {
Map params = Collections.emptyMap();
//url format is /{index}/{type}/{documentId}
//type goes away in future releases as well so don't put different types in the same index!!!(use new index)
return performRequest("PUT", "/"+index+"/_doc/"+id, params, document);
}
public CompletableFuture createAlias(String alias, String indexName) {
Map params = Collections.emptyMap();
AliasChange addAlias = new AliasChange();
addAlias.setIndex(indexName);
addAlias.setAlias(alias);
List actions = new ArrayList();
actions.add(new Action(addAlias , true));
AtomicActionList list = new AtomicActionList();
list.setActions(actions);
return performRequest("POST", "/_aliases", params, list);
}
public CompletableFuture getAliases(String index) {
Map params = Collections.emptyMap();
return performRequest("GET", "/"+index+"/_alias/*", params, null);
}
public CompletableFuture renameAlias(String previousIndex, String newIndex, String alias) {
Map params = Collections.emptyMap();
AliasChange removeAlias = new AliasChange();
removeAlias.setIndex(previousIndex);
removeAlias.setAlias(alias);
AliasChange addAlias = new AliasChange();
addAlias.setIndex(newIndex);
addAlias.setAlias(alias);
List actions = new ArrayList();
actions.add(new Action(removeAlias, false));
actions.add(new Action(addAlias , true));
AtomicActionList list = new AtomicActionList();
list.setActions(actions);
return performRequest("POST", "/_aliases", params, list);
}
public CompletableFuture deleteIndex(String name) {
Map params = Collections.emptyMap();
return performRequest("DELETE", "/"+name, params, null);
}
public CompletableFuture createIndex(String name, ElasticIndex index) {
Map params = Collections.emptyMap();
return performRequest("PUT", "/"+name, params, index);
}
public CompletableFuture performRequest(
String method, String endpoint, Map params, Object jsonObj, Header... headers) {
HttpEntity entity = null;
String jsonString = null;
if(jsonObj != null) {
try {
jsonString = objectMapper.writeValueAsString(jsonObj);
} catch (IOException e) {
throw new RuntimeException("failed to translate to json object to string: "+jsonObj, e);
}
entity = new NStringEntity(jsonString, ContentType.APPLICATION_JSON);
}
String jsonStr = jsonString;
CompletableFuture future = new CompletableFuture();
ResponseListener responseListener = new ToFutureListener(future);
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
Header[] allHeaders = new Header[headers.length + 1];
for(Header header: headers) {
builder.addHeader(header.getName(), header.getValue());
}
builder.addHeader("Authorization", "ApiKey V0xKeEdIRUIyMGdKdjF0QlZoWmc6X0NvaU9seGZSZHFxd283SjIyYXhvdw==");
Request requst = new Request(method, endpoint);
requst.setEntity(entity);
requst.setOptions(builder);
requst.addParameters(params);
client.performRequestAsync(requst, responseListener);
return future.handle( (r, e) -> {
if(e != null) {
CompletableFuture f = new CompletableFuture();
f.completeExceptionally(new RuntimeException("json failed to be processed by elastic search="+jsonStr, e));
return f;
}
return CompletableFuture.completedFuture(r);
}).thenCompose(Function.identity());
}
private static class ToFutureListener implements ResponseListener {
private CompletableFuture future;
public ToFutureListener(CompletableFuture future) {
this.future = future;
}
@Override
public void onSuccess(Response response) {
int status = response.getStatusLine().getStatusCode();
if(status != 201 && status != 200) {
throw new ElasticFailException("Invalid response, response="+response, response);
}
future.complete(response);
}
@Override
public void onFailure(Exception exception) {
future.completeExceptionally(exception);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy