View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.ext.elasticsearch.client;
20  
21  import co.elastic.clients.elasticsearch.ElasticsearchClient;
22  import co.elastic.clients.json.jackson.JacksonJsonpMapper;
23  import co.elastic.clients.transport.rest_client.RestClientTransport;
24  import com.fasterxml.jackson.databind.SerializationFeature;
25  import com.fasterxml.jackson.databind.json.JsonMapper;
26  import java.nio.charset.StandardCharsets;
27  import java.util.Base64;
28  import java.util.List;
29  import org.apache.http.Header;
30  import org.apache.http.HttpHeaders;
31  import org.apache.http.HttpHost;
32  import org.apache.http.auth.AuthScope;
33  import org.apache.http.auth.UsernamePasswordCredentials;
34  import org.apache.http.client.CredentialsProvider;
35  import org.apache.http.impl.client.BasicCredentialsProvider;
36  import org.apache.http.message.BasicHeader;
37  import org.elasticsearch.client.RestClient;
38  import org.elasticsearch.client.RestClientBuilder;
39  import org.springframework.beans.factory.DisposableBean;
40  import org.springframework.beans.factory.FactoryBean;
41  
42  /**
43   * Spring {@link FactoryBean} for getting the {@link ElasticsearchClient} singleton instance.
44   */
45  public class ElasticsearchClientFactoryBean implements FactoryBean<ElasticsearchClient>, DisposableBean {
46  
47      private final List<HttpHost> hosts;
48  
49      private String username;
50  
51      private String password;
52  
53      private String serviceToken;
54  
55      private String apiKeyId;
56  
57      private String apiKeySecret;
58  
59      private RestClient restClient;
60  
61      private ElasticsearchClient client;
62  
63      public ElasticsearchClientFactoryBean(final List<HttpHost> hosts) {
64          this.hosts = hosts;
65      }
66  
67      public void setUsername(final String username) {
68          this.username = username;
69      }
70  
71      public void setPassword(final String password) {
72          this.password = password;
73      }
74  
75      public String getServiceToken() {
76          return serviceToken;
77      }
78  
79      public void setServiceToken(final String serviceToken) {
80          this.serviceToken = serviceToken;
81      }
82  
83      public String getApiKeyId() {
84          return apiKeyId;
85      }
86  
87      public void setApiKeyId(final String apiKeyId) {
88          this.apiKeyId = apiKeyId;
89      }
90  
91      public String getApiKeySecret() {
92          return apiKeySecret;
93      }
94  
95      public void setApiKeySecret(final String apiKeySecret) {
96          this.apiKeySecret = apiKeySecret;
97      }
98  
99      @Override
100     public ElasticsearchClient getObject() throws Exception {
101         synchronized (this) {
102             if (client == null) {
103                 RestClientBuilder builder = RestClient.builder(hosts.toArray(HttpHost[]::new));
104                 if (username != null && password != null) {
105                     CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
106                     credentialsProvider.setCredentials(
107                             AuthScope.ANY, new UsernamePasswordCredentials(username, password));
108                     builder.setHttpClientConfigCallback(b -> b.setDefaultCredentialsProvider(credentialsProvider));
109                 } else if (serviceToken != null) {
110                     builder.setDefaultHeaders(
111                             new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + serviceToken) });
112                 } else if (apiKeyId != null && apiKeySecret != null) {
113                     String apiKeyAuth = Base64.getEncoder().encodeToString(
114                             (apiKeyId + ":" + apiKeySecret).getBytes(StandardCharsets.UTF_8));
115                     builder.setDefaultHeaders(
116                             new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + apiKeyAuth) });
117                 }
118 
119                 restClient = builder.build();
120                 client = new ElasticsearchClient(new RestClientTransport(
121                         restClient,
122                         new JacksonJsonpMapper(JsonMapper.builder().
123                                 findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build())));
124             }
125         }
126         return client;
127     }
128 
129     @Override
130     public Class<?> getObjectType() {
131         return ElasticsearchClient.class;
132     }
133 
134     @Override
135     public void destroy() throws Exception {
136         if (restClient != null) {
137             restClient.close();
138         }
139     }
140 }