001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.search.lastn; 021 022import ca.uhn.fhir.context.ConfigurationException; 023import ca.uhn.fhir.i18n.Msg; 024import co.elastic.clients.elasticsearch.ElasticsearchClient; 025import co.elastic.clients.json.jackson.JacksonJsonpMapper; 026import co.elastic.clients.transport.ElasticsearchTransport; 027import co.elastic.clients.transport.rest_client.RestClientTransport; 028import jakarta.annotation.Nullable; 029import org.apache.commons.lang3.StringUtils; 030import org.apache.http.Header; 031import org.apache.http.HttpHost; 032import org.apache.http.auth.AuthScope; 033import org.apache.http.auth.UsernamePasswordCredentials; 034import org.apache.http.client.CredentialsProvider; 035import org.apache.http.impl.client.BasicCredentialsProvider; 036import org.apache.http.message.BasicHeader; 037import org.elasticsearch.client.Node; 038import org.elasticsearch.client.RestClient; 039import org.elasticsearch.client.RestClientBuilder; 040 041import java.util.Arrays; 042import java.util.List; 043import java.util.stream.Collectors; 044 045public class ElasticsearchRestClientFactory { 046 047 public static ElasticsearchClient createElasticsearchHighLevelRestClient( 048 String protocol, String hosts, @Nullable String theUsername, @Nullable String thePassword) { 049 050 if (hosts.contains("://")) { 051 throw new ConfigurationException( 052 Msg.code(1173) 053 + "Elasticsearch URLs cannot include a protocol, that is a separate property. Remove http:// or https:// from this URL."); 054 } 055 String[] hostArray = hosts.split(","); 056 List<Node> clientNodes = Arrays.stream(hostArray) 057 .map(String::trim) 058 .filter(s -> s.contains(":")) 059 .map(h -> { 060 int colonIndex = h.indexOf(":"); 061 String host = h.substring(0, colonIndex); 062 int port = Integer.parseInt(h.substring(colonIndex + 1)); 063 return new Node(new HttpHost(host, port, protocol)); 064 }) 065 .collect(Collectors.toList()); 066 if (hostArray.length != clientNodes.size()) { 067 throw new ConfigurationException( 068 Msg.code(1174) 069 + "Elasticsearch URLs have to contain ':' as a host:port separator. Example: localhost:9200,localhost:9201,localhost:9202"); 070 } 071 072 RestClientBuilder clientBuilder = RestClient.builder(clientNodes.toArray(new Node[0])); 073 if (StringUtils.isNotBlank(theUsername) && StringUtils.isNotBlank(thePassword)) { 074 final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 075 credentialsProvider.setCredentials( 076 AuthScope.ANY, new UsernamePasswordCredentials(theUsername, thePassword)); 077 clientBuilder.setHttpClientConfigCallback( 078 httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); 079 } 080 081 Header[] defaultHeaders = new Header[] {new BasicHeader("Content-Type", "application/json")}; 082 clientBuilder.setDefaultHeaders(defaultHeaders); 083 084 RestClient restClient = clientBuilder.build(); 085 086 // Create the transport with a Jackson mapper 087 ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); 088 089 // And create the API client 090 return new ElasticsearchClient(transport); 091 } 092}