Coverage Summary for Class: KitodoRestClient (org.kitodo.data.elasticsearch)
Class |
Class, %
|
Method, %
|
Line, %
|
KitodoRestClient |
100%
(1/1)
|
81%
(17/21)
|
69,7%
(53/76)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.data.elasticsearch;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.HttpMethod;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.MainResponse;
import org.elasticsearch.rest.RestStatus;
import org.kitodo.config.ConfigMain;
import org.kitodo.data.elasticsearch.api.RestClientInterface;
import org.kitodo.data.elasticsearch.exceptions.CustomResponseException;
/**
* Implementation of ElasticSearch REST Client for Index Module.
*/
public abstract class KitodoRestClient implements RestClientInterface {
private static final Logger logger = LogManager.getLogger(KitodoRestClient.class);
protected String indexBase;
protected RestClient client;
protected RestHighLevelClient highLevelClient;
public static final List<String> MAPPING_TYPES = Arrays.asList("batch", "docket", "filter", "process", "project",
"property", "ruleset", "task", "template", "workflow");
/**
* Create REST client.
*
*/
protected void initiateClient() {
String host = ConfigMain.getParameter("elasticsearch.host", "localhost");
int port = ConfigMain.getIntParameter("elasticsearch.port", 9200);
String protocol = ConfigMain.getParameter("elasticsearch.protocol", "http");
String path = ConfigMain.getParameter("elasticsearch.path", "/");
initiateClient(host, port, protocol, path);
}
/**
* Create REST client with other without basic authentication.
*
* @param host
* default host is localhost
* @param port
* default port ist 9200
* @param protocol
* default protocol is http
* @param path
* default path is /
*/
private void initiateClient(String host, Integer port, String protocol, String path) {
if (ConfigMain.getBooleanParameter("elasticsearch.useAuthentication")) {
initiateClientWithAuth(host, port, protocol, path);
} else {
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocol));
if (!path.isEmpty() && !path.equals("/")) {
builder.setPathPrefix(path);
}
client = builder.build();
highLevelClient = new RestHighLevelClient(builder);
}
}
/**
* Create REST client with basic authentication.
*
* @param host
* default host is localhost
* @param port
* default port ist 9200
* @param protocol
* default protocol is http
* @param path
* default path is /
*/
private void initiateClientWithAuth(String host, Integer port, String protocol, String path) {
String user = ConfigMain.getParameter("elasticsearch.user", "elastic");
String password = ConfigMain.getParameter("elasticsearch.password", "changeme");
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, password));
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocol)).setHttpClientConfigCallback(
httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
if (!path.isEmpty() && !path.equals("/")) {
builder.setPathPrefix(path);
}
client = builder.build();
highLevelClient = new RestHighLevelClient(builder);
}
/**
* Get information about client server.
*
* @return information about the server
*/
public String getServerInformation() throws IOException {
Request request = new Request(HttpMethod.GET, "/");
request.addParameter("pretty", "true");
Response response = client.performRequest(request);
return EntityUtils.toString(response.getEntity());
}
/**
* Get information about client server.
*
* @return information about the server
*/
public String getServerInfo() throws IOException {
MainResponse response = highLevelClient.info(RequestOptions.DEFAULT);
return response.toString();
}
/**
* Get mapping.
*
* @param mappingType
* the name of table in database as String
* @return mapping
*/
public String getMapping(String mappingType) throws IOException {
Request request = new Request(HttpMethod.GET, "/" + indexBase + "_" + mappingType + "/_mapping");
request.addParameter("pretty", "true");
Response response = client.performRequest(request);
return EntityUtils.toString(response.getEntity());
}
/**
* Create new indexes without mappings.
*/
public void createIndexes() throws IOException, CustomResponseException {
for (String mappingType : MAPPING_TYPES) {
createIndex(null, mappingType);
}
}
/**
* Create new index with mapping.
*
* @param query
* contains mapping
* @param mappingType
* the name of table in database as String
* @return true or false - can be used for displaying information to user if
* success
*/
public boolean createIndex(String query, String mappingType) throws IOException, CustomResponseException {
if (query == null) {
query = "{\"settings\" : {\"index\" : {\"number_of_shards\" : 1,\"number_of_replicas\" : 0}}}";
}
HttpEntity entity = new NStringEntity(query, ContentType.APPLICATION_JSON);
Request request = new Request(HttpMethod.PUT, "/" + indexBase + "_" + mappingType);
request.setEntity(entity);
Response indexResponse = client.performRequest(request);
int statusCode = processStatusCode(indexResponse.getStatusLine());
return statusCode == 200 || statusCode == 201;
}
/**
* Check if all indexes already exist. Needed for frontend.
*
* @return false if any index doesn't exist, true else
*/
public boolean typeIndexesExist() throws IOException, CustomResponseException {
for (String mappingType : MAPPING_TYPES) {
Response indexResponse = client.performRequest(new Request(HttpMethod.GET, "/" + indexBase + "_" + mappingType));
int statusCode = processStatusCode(indexResponse.getStatusLine());
if (statusCode != 200 && statusCode != 201) {
return false;
}
}
return true;
}
/**
* Delete index of given mappingType. Currently, only used in test cases.
* @param mappingType mapping type
*/
public void deleteIndex(String mappingType) throws IOException {
client.performRequest(new Request(HttpMethod.DELETE, "/" + indexBase + "_" + mappingType));
}
/**
* Delete all indexes. Used for cleaning after tests!
*/
public void deleteAllIndexes() throws IOException {
for (String mappingType : MAPPING_TYPES) {
client.performRequest(new Request(HttpMethod.DELETE, "/" + indexBase + "_" + mappingType));
}
}
/**
* Getter for indexBase.
* The indexBase is the prefix of all indexes - equal to the name of database, default kitodo.
*
* @return indexBase name
*/
public String getIndexBase() {
return indexBase;
}
/**
* Update refresh interval of search indices to 1 ms.
*
* @throws IOException when index cannot be reached
*/
public void setRefreshInterval() throws IOException {
this.highLevelClient.indices().putSettings(Requests.updateSettingsRequest()
.settings(Collections.singletonMap("refresh_interval", "1ms")), RequestOptions.DEFAULT);
}
/**
* Setter for indexBase. The indexBase is the prefix of all indexes
*
* @param indexBase
* - equal to the name of database, default kitodo
*/
public void setIndexBase(String indexBase) {
this.indexBase = indexBase;
}
protected void handleResponseException(ResponseException e) throws CustomResponseException {
if (e.getResponse().getStatusLine().getStatusCode() == 404) {
if (logger.isTraceEnabled()) {
logger.trace(e.getMessage(), e);
} else {
logger.debug(e.getMessage().replaceAll("\\p{Space}+", " "));
}
} else {
throw new CustomResponseException(e);
}
}
protected int processStatusCode(StatusLine statusLine) throws CustomResponseException {
int statusCode = statusLine.getStatusCode();
processStatusCode(statusCode, statusLine);
return statusCode;
}
protected int processStatusCode(RestStatus restStatus) throws CustomResponseException {
int statusCode = restStatus.getStatus();
processStatusCode(statusCode, restStatus);
return statusCode;
}
private void processStatusCode(int statusCode, Object restStatus) throws CustomResponseException {
if (statusCode >= 400 && statusCode <= 499) {
throw new CustomResponseException("Client error: " + restStatus.toString());
} else if (statusCode >= 500 && statusCode <= 599) {
throw new CustomResponseException("Server error: " + restStatus.toString());
}
}
}