diff --git a/pom.xml b/pom.xml
index 81567db6..b53d6b9c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,7 +110,7 @@
com.fasterxml.jackson.core
jackson-core
- 2.14.2
+ 2.16.0
jakarta.json
diff --git a/src/main/java/org/phoebus/channelfinder/ElasticConfig.java b/src/main/java/org/phoebus/channelfinder/ElasticConfig.java
index d2dd0c24..f20a1204 100644
--- a/src/main/java/org/phoebus/channelfinder/ElasticConfig.java
+++ b/src/main/java/org/phoebus/channelfinder/ElasticConfig.java
@@ -17,6 +17,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -29,8 +30,16 @@
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.Header;
import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
import org.phoebus.channelfinder.entity.Property;
import org.phoebus.channelfinder.entity.Tag;
import org.springframework.beans.factory.annotation.Value;
@@ -60,14 +69,19 @@ public class ElasticConfig implements ServletContextListener {
private ElasticsearchClient searchClient;
private ElasticsearchClient indexClient;
- private static final AtomicBoolean esInitialized = new AtomicBoolean();
- @Value("${elasticsearch.cluster.name:elasticsearch}")
- private String clusterName;
@Value("${elasticsearch.network.host:localhost}")
private String host;
+ @Value("${elasticsearch.host_urls:http://localhost:9200}")
+ private String[] httpHostUrls;
@Value("${elasticsearch.http.port:9200}")
private int port;
+ @Value("${elasticsearch.authorization.header:}")
+ private String authorizationHeader;
+ @Value("${elasticsearch.authorization.username:}")
+ private String username;
+ @Value("${elasticsearch.authorization.password:}")
+ private String password;
@Value("${elasticsearch.create.indices:true}")
private String createIndices;
@@ -98,11 +112,23 @@ public int getES_QUERY_SIZE() {
.addMixIn(Property.class, Property.OnlyProperty.class);
private static ElasticsearchClient createClient(ElasticsearchClient currentClient, ObjectMapper objectMapper,
- String host, int port, String createIndices, ElasticConfig config) {
+ HttpHost[] httpHosts, String createIndices, ElasticConfig config) {
ElasticsearchClient client;
if (currentClient == null) {
// Create the low-level client
- RestClient httpClient = RestClient.builder(new HttpHost(host, port)).build();
+ RestClientBuilder clientBuilder = RestClient.builder(httpHosts);
+ // Configure authentication
+ if (!config.authorizationHeader.isEmpty()) {
+ clientBuilder.setDefaultHeaders(new Header[] {new BasicHeader("Authorization", config.authorizationHeader)});
+ if (!config.username.isEmpty() || !config.password.isEmpty()) {
+ logger.warning("elasticsearch.authorization_header is set, ignoring elasticsearch.username and elasticsearch.password.");
+ }
+ } else if (!config.username.isEmpty() || !config.password.isEmpty()) {
+ final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(config.username, config.password));
+ clientBuilder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
+ }
+ RestClient httpClient = clientBuilder.build();
// Create the Java API Client with the same low level client
ElasticsearchTransport transport = new RestClientTransport(httpClient, new JacksonJsonpMapper(objectMapper));
@@ -111,22 +137,40 @@ private static ElasticsearchClient createClient(ElasticsearchClient currentClien
} else {
client = currentClient;
}
- esInitialized.set(!Boolean.parseBoolean(createIndices));
- if (esInitialized.compareAndSet(false, true)) {
+ if (Boolean.parseBoolean(createIndices)) {
config.elasticIndexValidation(client);
}
return client;
}
+
+ private HttpHost[] getHttpHosts() {
+ boolean hostIsDefault = host.equals("localhost");
+ boolean hostUrlsIsDefault = httpHostUrls.length == 1 && httpHostUrls[0].equals("http://localhost:9200");
+ boolean portIsDefault = (port == 9200);
+ if (hostUrlsIsDefault && (!hostIsDefault || !portIsDefault)) {
+ logger.warning("Specifying elasticsearch.network.host and elasticsearch.http.port is deprecated, please consider using elasticsearch.host_urls instead.");
+ return new HttpHost[] {new HttpHost(host, port)};
+ } else {
+ if (!hostIsDefault) {
+ logger.warning("Only one of elasticsearch.host_urls and elasticsearch.network.host can be set, ignoring elasticsearch.network.host.");
+ }
+ if (!portIsDefault) {
+ logger.warning("Only one of elasticsearch.host_urls and elasticsearch.http.port can be set, ignoring elasticsearch.http.port.");
+ }
+ return Arrays.stream(httpHostUrls).map(HttpHost::create).toArray(HttpHost[]::new);
+ }
+ }
+
@Bean({ "searchClient" })
public ElasticsearchClient getSearchClient() {
- searchClient = createClient(searchClient, objectMapper, host, port, createIndices, this);
+ searchClient = createClient(searchClient, objectMapper, getHttpHosts(), createIndices, this);
return searchClient;
}
@Bean({ "indexClient" })
public ElasticsearchClient getIndexClient() {
- indexClient = createClient(indexClient, objectMapper, host, port, createIndices, this);
+ indexClient = createClient(indexClient, objectMapper, getHttpHosts(), createIndices, this);
return indexClient;
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index f692013f..6b15becc 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -65,43 +65,34 @@ tag-groups=cf-tags,USER
############################## Elastic Network And HTTP ###############################
-# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
-# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
-# communication. (the range means that if the port is busy, it will automatically
-# try the next port).
+# Comma-separated list of URLs for the Elasticsearch hosts. All hosts listed
+# here must belong to the same Elasticsearch cluster.
+elasticsearch.host_urls=http://localhost:9200
-# Set the bind address specifically (IPv4 or IPv6):
-#
-# network.bind_host: 169.254.42.56
-
-# Set the address other nodes will use to communicate with this node. If not
-# set, it is automatically derived. It must point to an actual IP address.
-#
-# network.publish_host: 192.168.0.1
-
-# Set both 'bind_host' and 'publish_host':
-#
-elasticsearch.network.host: localhost
-
-# Set a custom port for the node to node communication (9300 by default):
-#
-#elasticsearch.transport.tcp.port: 9300
-
-# Enable compression for all communication between nodes (disabled by default):
-#
-#transport.tcp.compress: true
+# Old way of configuring the Elasticsearch host. Deprecated in favor of
+# elasticsearch.host_urls.
+elasticsearch.network.host=localhost
-# Set a custom port to listen for HTTP traffic:
-#
-elasticsearch.http.port: 9200
-
-# Set a custom allowed content length:
-#
-#http.max_content_length: 100mb
+# Old way of configuring the Elasticsearch HTTP port. Deprecated in favor of
+# elasticsearch.host_urls.
+elasticsearch.http.port=9200
-# Disable HTTP completely:
+# Value of the Authorization header that is sent with requests to the
+# Elasticsearch sever. This can be used for authentication using tokens or API
+# keys.
#
-#http.enabled: false
+# For example, for token authentication, set this to ?Bearer abcd1234?, where
+# ?abcd1234? is the token. For API key authentication, set this to the Base64
+# encoded version of the concatenation of the API key ID and the API key
+# secret, separated by a colon. See
+# https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/8.12/_other_authentication_methods.html
+# for details.
+elasticsearch.authorization.header =
+
+# Username and password for authentication with the Elasticsearch server. This
+# is only used if elasticsearch.authorization.header is not set.
+elasticsearch.authorization.username =
+elasticsearch.authorization.password =
# Elasticsearch index names and types used by channelfinder, ensure that any changes here should be replicated in the mapping_definitions.sh
elasticsearch.tag.index = cf_tags
@@ -112,7 +103,7 @@ elasticsearch.channel.index = channelfinder
elasticsearch.query.size = 10000
# Create the Channel Finder indices if they do not exist
-elasticsearch.create.indices: true
+elasticsearch.create.indices=true
############################## Service Info ###############################
# ChannelFinder version as defined in the pom file
diff --git a/src/test/resources/application_test.properties b/src/test/resources/application_test.properties
index 78b60d53..2297af05 100644
--- a/src/test/resources/application_test.properties
+++ b/src/test/resources/application_test.properties
@@ -61,43 +61,9 @@ tag-groups=cf-tags
############################## Elastic Network And HTTP ###############################
-# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
-# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
-# communication. (the range means that if the port is busy, it will automatically
-# try the next port).
-
-# Set the bind address specifically (IPv4 or IPv6):
-#
-# network.bind_host: 169.254.42.56
-
-# Set the address other nodes will use to communicate with this node. If not
-# set, it is automatically derived. It must point to an actual IP address.
-#
-# network.publish_host: 192.168.0.1
-
-# Set both 'bind_host' and 'publish_host':
-#
-elasticsearch.network.host: localhost
-
-# Set a custom port for the node to node communication (9300 by default):
-#
-#elasticsearch.transport.tcp.port: 9300
-
-# Enable compression for all communication between nodes (disabled by default):
-#
-#transport.tcp.compress: true
-
-# Set a custom port to listen for HTTP traffic:
-#
-elasticsearch.http.port: 9200
-
-# Set a custom allowed content length:
-#
-#http.max_content_length: 100mb
-
-# Disable HTTP completely:
-#
-#http.enabled: false
+# Comma-separated list of URLs for the Elasticsearch hosts. All hosts listed
+# here must belong to the same Elasticsearch cluster.
+elasticsearch.host_urls=http://localhost:9200
# Elasticsearch index names and types used by channelfinder, ensure that any changes here should be replicated in the mapping_definitions.sh
elasticsearch.tag.index = test_${random.int[1,1000]}_cf_tags
@@ -108,7 +74,7 @@ elasticsearch.channel.index = test_${random.int[1,1000]}_channelfinder
elasticsearch.query.size = 10000
# Create the Channel Finder indices if they do not exist
-elasticsearch.create.indices: true
+elasticsearch.create.indices = true
############################## Service Info ###############################
# ChannelFinder version as defined in the pom file