1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.identitystore.service.indexer.elastic.client;
35
36 import com.fasterxml.jackson.databind.DeserializationFeature;
37 import com.fasterxml.jackson.databind.ObjectMapper;
38 import fr.paris.lutece.plugins.identitystore.service.indexer.elastic.search.model.inner.response.Response;
39 import fr.paris.lutece.plugins.identitystore.service.indexer.elastic.search.model.inner.response.Responses;
40 import fr.paris.lutece.portal.service.util.AppPropertiesService;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.hc.client5.http.ClientProtocolException;
43 import org.apache.hc.client5.http.classic.methods.HttpDelete;
44 import org.apache.hc.client5.http.classic.methods.HttpGet;
45 import org.apache.hc.client5.http.classic.methods.HttpHead;
46 import org.apache.hc.client5.http.classic.methods.HttpPost;
47 import org.apache.hc.client5.http.classic.methods.HttpPut;
48 import org.apache.hc.client5.http.config.RequestConfig;
49 import org.apache.hc.client5.http.impl.classic.AbstractHttpClientResponseHandler;
50 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
51 import org.apache.hc.client5.http.impl.classic.HttpClients;
52 import org.apache.hc.core5.http.ContentType;
53 import org.apache.hc.core5.http.Header;
54 import org.apache.hc.core5.http.HttpEntity;
55 import org.apache.hc.core5.http.HttpHeaders;
56 import org.apache.hc.core5.http.HttpResponse;
57 import org.apache.hc.core5.http.ParseException;
58 import org.apache.hc.core5.http.io.entity.EntityUtils;
59 import org.apache.hc.core5.http.io.entity.StringEntity;
60 import org.apache.hc.core5.http.message.BasicHeader;
61 import org.apache.hc.core5.util.Timeout;
62
63 import java.io.IOException;
64 import java.nio.charset.StandardCharsets;
65 import java.util.ArrayList;
66 import java.util.Base64;
67 import java.util.List;
68 import java.util.concurrent.atomic.AtomicBoolean;
69
70
71
72
73 public final class ElasticConnexion
74 {
75 private static final long RESPONSE_TIMEOUT = AppPropertiesService.getPropertyInt( "identitystore.elastic.client.response.timeout", 30 );
76 private static final long CONNECT_TIMEOUT = AppPropertiesService.getPropertyInt( "identitystore.elastic.client.connect.timeout", 30 );
77 private static final ObjectMapper _mapper = new ObjectMapper( ).disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
78 private static final int ERROR_CODE_START = 300;
79 private final AbstractHttpClientResponseHandler<String> _simpleResponseHandler = buildSimpleResponseHandler( );
80 private final AbstractHttpClientResponseHandler<Response> _searchResponseHandler = buildSearchResponseHandler( );
81 private final AbstractHttpClientResponseHandler<Responses> _mSearchResponseHandler = buildSearchesResponseHandler( );
82 private String _userLogin;
83 private String _userPassword;
84
85
86
87
88
89
90
91
92
93 public ElasticConnexion( final String userLogin, final String userPassword )
94 {
95 _userLogin = userLogin;
96 _userPassword = userPassword;
97 }
98
99
100
101
102 public ElasticConnexion( )
103 {
104 }
105
106
107
108
109
110
111
112
113 public String GET( final String strURI ) throws ElasticConnexionException
114 {
115 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
116 {
117 return _httpClient.execute( new HttpGet( strURI ), _simpleResponseHandler );
118 }
119 catch( final IOException e )
120 {
121 throw new ElasticConnexionException( "An error occurred during GET call to Elastic Search: ", e );
122 }
123 }
124
125
126
127
128
129
130
131
132 public String HEAD( final String strURI ) throws ElasticConnexionException
133 {
134 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
135 {
136 return _httpClient.execute( new HttpHead( strURI ), _simpleResponseHandler );
137 }
138 catch( final IOException e )
139 {
140 throw new ElasticConnexionException( "An error occurred during HEAD call to Elastic Search: ", e );
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153 public void PUT( final String strURI, final String strJSON ) throws ElasticConnexionException
154 {
155 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
156 {
157 final HttpPut request = new HttpPut( strURI );
158 request.setEntity( new StringEntity( strJSON, ContentType.APPLICATION_JSON, null, false ) );
159 final Integer code = _httpClient.execute( request, HttpResponse::getCode );
160 if ( code >= ERROR_CODE_START )
161 {
162 throw new ElasticConnexionException( "An error occurred during PUT call to Elastic Search with status code: " + code );
163 }
164 }
165 catch( final IOException e )
166 {
167 throw new ElasticConnexionException( "An error occurred during PUT call to Elastic Search: ", e );
168 }
169 }
170
171
172
173
174
175
176
177
178
179
180 public void POST( final String strURI, final String strJSON ) throws ElasticConnexionException
181 {
182 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
183 {
184 final HttpPost request = new HttpPost( strURI );
185 request.setEntity( new StringEntity( strJSON, ContentType.APPLICATION_JSON, null, false ) );
186 final Integer code = _httpClient.execute( request, HttpResponse::getCode );
187 if ( code >= ERROR_CODE_START )
188 {
189 throw new ElasticConnexionException( "An error occurred during POST call to Elastic Search with status code: " + code );
190 }
191 }
192 catch( final IOException e )
193 {
194 throw new ElasticConnexionException( "An error occurred during POST call to Elastic Search: ", e );
195 }
196 }
197
198
199
200
201
202
203
204
205
206
207 public Response SEARCH( final String strURI, final String strJSON ) throws ElasticConnexionException
208 {
209 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
210 {
211 final HttpGet request = new HttpGet( strURI );
212 request.setEntity( new StringEntity( strJSON, ContentType.APPLICATION_JSON, null, false ) );
213 final Response execute = _httpClient.execute( request, _searchResponseHandler );
214 if ( execute.getStatus( ) != null && execute.getStatus( ) >= ERROR_CODE_START )
215 {
216 throw new ElasticConnexionException( "An error occurred during SEARCH call to Elastic Search with status code: " + execute.getStatus( ) );
217 }
218 return execute;
219 }
220 catch( final IOException e )
221 {
222 throw new ElasticConnexionException( "An error occurred during SEARCH call to Elastic Search: ", e );
223 }
224 }
225
226
227
228
229
230
231
232
233
234
235 public Responses MSEARCH( final String strURI, final String strJSON ) throws ElasticConnexionException
236 {
237 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
238 {
239 final HttpGet request = new HttpGet( strURI );
240 request.setEntity( new StringEntity( strJSON, ContentType.APPLICATION_JSON, null, false ) );
241 final Responses execute = _httpClient.execute( request, _mSearchResponseHandler );
242 final AtomicBoolean failOccurred = new AtomicBoolean( false );
243 execute.getResponses( ).removeIf( response -> {
244 final boolean failed = response.getStatus( ) >= ERROR_CODE_START;
245 if ( failed )
246 {
247 failOccurred.set( true );
248 }
249 return failed;
250 } );
251 if ( failOccurred.get( ) )
252 {
253 throw new ElasticConnexionException( "An error occurred during MSEARCH call to Elastic Search. Could not get responses" );
254 }
255 return execute;
256 }
257 catch( final IOException e )
258 {
259 throw new ElasticConnexionException( "An error occurred during MSEARCH call to Elastic Search: ", e );
260 }
261 }
262
263
264
265
266
267
268
269
270 public void DELETE( final String strURI ) throws ElasticConnexionException
271 {
272 try ( final CloseableHttpClient _httpClient = this.buildHttpClient( ) )
273 {
274 final HttpDelete request = new HttpDelete( strURI );
275 final Integer code = _httpClient.execute( request, HttpResponse::getCode );
276 if ( code >= ERROR_CODE_START )
277 {
278 throw new ElasticConnexionException( "An error occurred during DELETE call to Elastic Search with status code: " + code );
279 }
280 }
281 catch( final IOException e )
282 {
283 throw new ElasticConnexionException( "An error occurred during DELETE call to Elastic Search: ", e );
284 }
285 }
286
287 private static AbstractHttpClientResponseHandler<String> buildSimpleResponseHandler( )
288 {
289 return new AbstractHttpClientResponseHandler<String>( )
290 {
291 @Override
292 public String handleEntity( HttpEntity httpEntity ) throws IOException
293 {
294
295 try
296 {
297 final String response = EntityUtils.toString( httpEntity );
298 EntityUtils.consume( httpEntity );
299 return response;
300 }
301 catch( ParseException var3 )
302 {
303 throw new ClientProtocolException( var3 );
304 }
305 }
306 };
307 }
308
309 private static AbstractHttpClientResponseHandler<Response> buildSearchResponseHandler( )
310 {
311 return new AbstractHttpClientResponseHandler<Response>( )
312 {
313 @Override
314 public Response handleEntity( HttpEntity httpEntity ) throws IOException
315 {
316 final Response response = _mapper.readValue( httpEntity.getContent( ), Response.class );
317 EntityUtils.consume( httpEntity );
318 return response;
319 }
320 };
321 }
322
323 private static AbstractHttpClientResponseHandler<Responses> buildSearchesResponseHandler( )
324 {
325 return new AbstractHttpClientResponseHandler<Responses>( )
326 {
327 @Override
328 public Responses handleEntity( HttpEntity httpEntity ) throws IOException
329 {
330 final Responses response = _mapper.readValue( httpEntity.getContent( ), Responses.class );
331 EntityUtils.consume( httpEntity );
332 return response;
333 }
334 };
335 }
336
337 private CloseableHttpClient buildHttpClient( )
338 {
339 final RequestConfig requestConfig = RequestConfig.custom( ).setResponseTimeout( Timeout.ofSeconds( RESPONSE_TIMEOUT ) )
340 .setConnectTimeout( Timeout.ofSeconds( CONNECT_TIMEOUT ) ).setConnectionRequestTimeout( Timeout.ofSeconds( CONNECT_TIMEOUT ) ).build( );
341 return HttpClients.custom( ).setDefaultHeaders( this.buildDefaultHeaders( ) ).setDefaultRequestConfig( requestConfig ).build( );
342 }
343
344 private List<Header> buildDefaultHeaders( )
345 {
346 final List<Header> defaultHeaders = new ArrayList<>( );
347 if ( StringUtils.isNoneEmpty( _userLogin, _userPassword ) )
348 {
349 final String auth = _userLogin + ":" + _userPassword;
350 final byte [ ] encodedAuth = Base64.getEncoder( ).encode( auth.getBytes( StandardCharsets.ISO_8859_1 ) );
351 final String authHeader = "Basic " + new String( encodedAuth );
352 defaultHeaders.add( new BasicHeader( HttpHeaders.AUTHORIZATION, authHeader ) );
353 }
354 return defaultHeaders;
355 }
356 }