align router code with 1.2.1 version of rest
[aai/data-router.git] / src / main / java / org / onap / aai / datarouter / util / SearchServiceAgent.java
1 /**\r
2  * ============LICENSE_START=======================================================\r
3  * org.onap.aai\r
4  * ================================================================================\r
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.\r
6  * Copyright © 2017-2018 Amdocs\r
7  * ================================================================================\r
8  * Licensed under the Apache License, Version 2.0 (the "License");\r
9  * you may not use this file except in compliance with the License.\r
10  * You may obtain a copy of the License at\r
11  *\r
12  *       http://www.apache.org/licenses/LICENSE-2.0\r
13  *\r
14  * Unless required by applicable law or agreed to in writing, software\r
15  * distributed under the License is distributed on an "AS IS" BASIS,\r
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
17  * See the License for the specific language governing permissions and\r
18  * limitations under the License.\r
19  * ============LICENSE_END=========================================================\r
20  */\r
21 package org.onap.aai.datarouter.util;\r
22 \r
23 import java.io.BufferedReader;\r
24 import java.io.InputStreamReader;\r
25 import java.nio.charset.StandardCharsets;\r
26 import java.util.Arrays;\r
27 import java.util.HashMap;\r
28 import java.util.List;\r
29 import java.util.Map;\r
30 import java.util.UUID;\r
31 \r
32 import javax.ws.rs.core.MediaType;\r
33 import javax.ws.rs.core.MultivaluedMap;\r
34 import javax.ws.rs.core.Response.Status;\r
35 \r
36 import org.eclipse.jetty.util.security.Password;\r
37 import org.onap.aai.cl.api.Logger;\r
38 import org.onap.aai.cl.mdc.MdcContext;\r
39 import org.onap.aai.datarouter.logging.DataRouterMsgs;\r
40 import org.onap.aai.datarouter.policy.EntityEventPolicy;\r
41 import org.onap.aai.restclient.client.Headers;\r
42 import org.onap.aai.restclient.client.OperationResult;\r
43 import org.onap.aai.restclient.client.RestClient;\r
44 import org.onap.aai.restclient.enums.RestAuthenticationMode;\r
45 import org.onap.aai.restclient.rest.HttpUtil;\r
46 import org.slf4j.MDC;\r
47 \r
48 import com.sun.jersey.core.util.MultivaluedMapImpl;\r
49 \r
50 public class SearchServiceAgent {\r
51 \r
52   private Logger logger;\r
53   \r
54   private RestClient searchClient = null;\r
55   private Map<String, String> indexSchemaMapping = new HashMap<>();\r
56   \r
57   private String searchUrl = null;\r
58   private String documentEndpoint = null;\r
59   \r
60   \r
61   /**\r
62    * Creates a new instance of the search service agent.\r
63    * \r
64    * @param certName         - Certificate to use for talking to the Search Service.\r
65    * @param keystore         - Keystore to use for talking to the Search Service.\r
66    * @param keystorePwd      - Keystore password for talking to the Search Service.\r
67    * @param searchUrl        - URL at which the Search Service can be reached.\r
68    * @param documentEndpoint - Endpoint for accessing document resources on the Search Service.\r
69    * @param logger           - Logger to use for system logs.\r
70    */\r
71   public SearchServiceAgent(String certName, \r
72                             String keystore, \r
73                             String keystorePwd,\r
74                             String searchUrl,\r
75                             String documentEndpoint,\r
76                             Logger logger) {\r
77     \r
78     initialize(certName, keystore, keystorePwd, searchUrl, documentEndpoint, logger);\r
79   }\r
80   \r
81   \r
82   /**\r
83    * Performs all one-time initialization required for the search agent.\r
84    * \r
85    * @param certName         - Certificate to use for talking to the Search Service.\r
86    * @param keystore         - Keystore to use for talking to the Search Service.\r
87    * @param keystorePwd      - Keystore password for talking to the Search Service.\r
88    * @param searchUrl        - URL at which the Search Service can be reached.\r
89    * @param documentEndpoint - Endpoint for accessing document resources on the Search Service.\r
90    * @param logger           - Logger to use for system logs.\r
91    */\r
92   private void initialize(String certName, \r
93                           String keystore, \r
94                           String keystorePwd, \r
95                           String searchUrl, \r
96                           String documentEndpoint, \r
97                           Logger logger) {\r
98     \r
99     String deobfuscatedCertPassword = keystorePwd.startsWith("OBF:")?Password.deobfuscate(keystorePwd):keystorePwd;\r
100     // Create REST client for search service\r
101     searchClient = new RestClient()\r
102                     .authenticationMode(RestAuthenticationMode.SSL_CERT)\r
103                     .validateServerHostname(false)\r
104                     .validateServerCertChain(false)\r
105                     .clientCertFile(DataRouterConstants.DR_HOME_AUTH + certName)\r
106                     .clientCertPassword(deobfuscatedCertPassword);                    \r
107     \r
108     this.searchUrl        = searchUrl;\r
109     this.documentEndpoint = documentEndpoint;\r
110     \r
111     this.logger           = logger;\r
112   }\r
113   \r
114   \r
115   /**\r
116    * Creates an index through the search db abstraction\r
117    * \r
118    * @param index          - The name of the index to be created.\r
119    * @param schemaLocation - The name of the schema file for the index.\r
120    */\r
121   public void createSearchIndex(String index, String schemaLocation) {\r
122      \r
123     // Create a mapping of the index name to schema location \r
124     indexSchemaMapping.put(index, schemaLocation);\r
125     \r
126     // Now, create the index.\r
127     createIndex(index, schemaLocation);\r
128   }\r
129   \r
130   public void createSearchIndex(String index, String schemaLocation, String endUrl) {\r
131     \r
132     // Create a mapping of the index name to schema location \r
133     indexSchemaMapping.put(index, schemaLocation);\r
134     \r
135     // Now, create the index.\r
136     createIndex(index, schemaLocation, endUrl);\r
137   }\r
138   \r
139   /**\r
140    * This method performs the actual work of creating a search index.\r
141    * \r
142    * @param index          - The name of the index to be created.\r
143    * @param schemaLocation - The name of the schema file for the index.\r
144    */\r
145   private void createIndex(String index, String schemaLocation) {\r
146     \r
147     logger.debug("Creating search index, index name: = " + index + ", schemaLocation = " + schemaLocation);\r
148     \r
149     MultivaluedMap<String, String> headers = new MultivaluedMapImpl();\r
150     headers.put("Accept", Arrays.asList("application/json"));\r
151     headers.put(Headers.FROM_APP_ID, Arrays.asList("DL"));\r
152     headers.put(Headers.TRANSACTION_ID, Arrays.asList(UUID.randomUUID().toString()));\r
153       \r
154     String url = concatSubUri(searchUrl, index);\r
155     try {\r
156 \r
157       OperationResult result = searchClient.put(url, loadFileData(schemaLocation), headers,\r
158                                                 MediaType.APPLICATION_JSON_TYPE, null);\r
159 \r
160       if (!HttpUtil.isHttpResponseClassSuccess(result.getResultCode())) {\r
161         logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, result.getFailureCause());\r
162       } else {\r
163         logger.info(DataRouterMsgs.SEARCH_INDEX_CREATE_SUCCESS, index);\r
164       }\r
165 \r
166     } catch (Exception e) {\r
167       logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, e.getLocalizedMessage());\r
168     }\r
169   }\r
170   \r
171   private void createIndex(String index, String schemaLocation, String endUrl) {\r
172     \r
173     logger.debug("Creating search index, index name: = " + index + ", schemaLocation = " + schemaLocation);\r
174     \r
175     MultivaluedMap<String, String> headers = new MultivaluedMapImpl();\r
176     headers.put("Accept", Arrays.asList("application/json"));\r
177     headers.put(Headers.FROM_APP_ID, Arrays.asList("DL"));\r
178     headers.put(Headers.TRANSACTION_ID, Arrays.asList(UUID.randomUUID().toString()));\r
179       \r
180     String url = concatSubUri(searchUrl, endUrl, index);\r
181     try {\r
182 \r
183       OperationResult result = searchClient.put(url, loadFileData(schemaLocation), headers,\r
184                                                 MediaType.APPLICATION_JSON_TYPE, null);\r
185       if (!HttpUtil.isHttpResponseClassSuccess(result.getResultCode())) {\r
186         logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, result.getFailureCause());\r
187       } else {\r
188         logger.info(DataRouterMsgs.SEARCH_INDEX_CREATE_SUCCESS, index);\r
189       }\r
190 \r
191     } catch (Exception e) {\r
192       logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, e.getLocalizedMessage());\r
193     }\r
194   }\r
195   \r
196   /**\r
197    * Retrieves a document from the search service.\r
198    * \r
199    * @param index - The index to retrieve the document from.\r
200    * @param id    - The unique identifier for the document.\r
201    * \r
202    * @return - The REST response returned from the Search Service.\r
203    */\r
204   public OperationResult getDocument(String index, String id) {\r
205     \r
206     Map<String, List<String>> headers = new HashMap<>();\r
207     headers.put(Headers.FROM_APP_ID, Arrays.asList("Data Router"));\r
208     headers.put(Headers.TRANSACTION_ID, Arrays.asList(MDC.get(MdcContext.MDC_REQUEST_ID)));\r
209     \r
210     String url = concatSubUri(searchUrl, index, documentEndpoint, id);\r
211     return searchClient.get(url, headers, MediaType.APPLICATION_JSON_TYPE);    \r
212   }\r
213   \r
214   \r
215   /**\r
216    * Creates or updates a document in the Search Service.\r
217    * \r
218    * @param index   - The index to create or update the document in.\r
219    * @param id      - The identifier for the document.\r
220    * @param payload - The document contents.\r
221    * @param headers - HTTP headers.\r
222    */\r
223   public void putDocument(String index, String id, String payload, Map<String, List<String>> headers) {\r
224         \r
225     // Try to post the document to the search service.\r
226     OperationResult result = doDocumentPut(index, id, payload, headers);\r
227     \r
228     // A 404 response from the Search Service may indicate that the index we are writing\r
229     // to does not actually exist.  We will try creating it now.\r
230     if(result.getResultCode() == Status.NOT_FOUND.getStatusCode()) {\r
231             \r
232       // Lookup the location of the schema that we want to create.\r
233       String indexSchemaLocation = indexSchemaMapping.get(index);\r
234       if(indexSchemaLocation != null) {\r
235         \r
236         // Try creating the index now...\r
237         logger.info(DataRouterMsgs.CREATE_MISSING_INDEX, index);\r
238         createIndex(index, indexSchemaLocation);\r
239         \r
240         // ...and retry the document post.\r
241         result = doDocumentPut(index, id, payload, headers);\r
242       }\r
243     }\r
244     \r
245     if(!resultSuccessful(result)) {\r
246       logger.error(DataRouterMsgs.FAIL_TO_CREATE_UPDATE_DOC, index, result.getFailureCause());\r
247     }\r
248   }\r
249   \r
250   \r
251   /**\r
252    * This method does the actual work of submitting a document PUT request to the Search Service.\r
253    * \r
254    * @param index   - The index to create or update the document in.\r
255    * @param id      - The identifier for the document.\r
256    * @param payload - The document contents.\r
257    * @param headers - HTTP headers.\r
258    * \r
259    * @return - The HTTP response returned by the Search Service.\r
260    */\r
261   private OperationResult doDocumentPut(String index, String id, String payload, Map<String, List<String>> headers) {\r
262     \r
263     String url = concatSubUri(searchUrl, index, documentEndpoint, id);\r
264     return searchClient.put(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE);\r
265   }\r
266   \r
267   \r
268   /**\r
269    * Creates a document in the Search Service.\r
270    * \r
271    * @param index   - The index to create the document in.\r
272    * @param payload - The document contents.\r
273    * @param headers - HTTP headers.\r
274    */\r
275   public void postDocument(String index, String payload, Map<String, List<String>> headers) {\r
276     \r
277     // Try to post the document to the search service.\r
278     OperationResult result = doDocumentPost(index, payload, headers);\r
279     \r
280     // A 404 response from the Search Service may indicate that the index we are writing\r
281     // to does not actually exist.  We will try creating it now.\r
282     if(result.getResultCode() == Status.NOT_FOUND.getStatusCode()) {\r
283       \r
284       // Lookup the location of the schema that we want to create.\r
285       String indexSchemaLocation = indexSchemaMapping.get(index);\r
286       if(indexSchemaLocation != null) {\r
287         \r
288         // Try creating the index now...\r
289         logger.info(DataRouterMsgs.CREATE_MISSING_INDEX, index);\r
290         createIndex(index, indexSchemaLocation);\r
291         \r
292         // ...and retry the document post.\r
293         result = doDocumentPost(index, payload, headers);\r
294       }\r
295     }\r
296     \r
297     if(!resultSuccessful(result)) {\r
298       logger.error(DataRouterMsgs.FAIL_TO_CREATE_UPDATE_DOC, index, result.getFailureCause());\r
299     }\r
300   }\r
301   \r
302   \r
303   /**\r
304    * This method does the actual work of submitting a document PUT request to the Search Service.\r
305    * \r
306    * @param index   - The index to create or update the document in.\r
307    * @param payload - The document contents.\r
308    * @param headers - HTTP headers.\r
309    * \r
310    * @return - The HTTP response returned by the Search Service.\r
311    */\r
312   private OperationResult doDocumentPost(String index, String payload, Map<String, List<String>> headers) {\r
313     \r
314     String url = concatSubUri(searchUrl, index, documentEndpoint);\r
315     return searchClient.post(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE);\r
316   }\r
317   \r
318   \r
319   /**\r
320    * Removes a document from the Search Service.\r
321    * \r
322    * @param index   - The index to create the document in.\r
323    * @param documentId      - The identifier for the document.\r
324    * @param headers - HTTP headers.\r
325    */\r
326   public void deleteDocument(String index, String documentId, Map<String, List<String>> headers) {\r
327     \r
328     String url = concatSubUri(searchUrl, index, documentEndpoint, documentId);\r
329     searchClient.delete(url, headers, null);\r
330   }\r
331   \r
332   \r
333   /**\r
334    * Convenience method to load up all the data from a file into a string\r
335    * \r
336    * @param filename the filename to read from disk\r
337    * @return the data contained within the file\r
338    * @throws Exception\r
339    */\r
340   protected String loadFileData(String filename) throws Exception {\r
341     StringBuilder data = new StringBuilder();\r
342 \r
343     try (InputStreamReader inputStreamReader = new InputStreamReader(EntityEventPolicy.class.getClassLoader()\r
344         .getResourceAsStream("/" + filename), StandardCharsets.UTF_8); BufferedReader in = new BufferedReader(\r
345         inputStreamReader)\r
346     ) {\r
347 \r
348       String line;\r
349       while ((line = in.readLine()) != null) {\r
350         data.append(line);\r
351       }\r
352     } catch (Exception e) {\r
353       throw new Exception("Failed to read from file = " + filename + ".", e);\r
354     }\r
355 \r
356     return data.toString();\r
357   }\r
358   \r
359   \r
360   /**\r
361    * Helper utility to concatenate substrings of a URI together to form a proper URI.\r
362    * \r
363    * @param suburis the list of substrings to concatenate together\r
364    * @return the concatenated list of substrings\r
365    */\r
366   public static String concatSubUri(String... suburis) {\r
367     String finalUri = "";\r
368 \r
369     for (String suburi : suburis) {\r
370 \r
371       if (suburi != null) {\r
372         // Remove any leading / since we only want to append /\r
373         suburi = suburi.replaceFirst("^/*", "");\r
374 \r
375         // Add a trailing / if one isn't already there\r
376         finalUri += suburi.endsWith("/") ? suburi : suburi + "/";\r
377       }\r
378     }\r
379 \r
380     return finalUri;\r
381   }\r
382   \r
383   \r
384   /**\r
385    * Helper utility to check the response code of an HTTP response.\r
386    * \r
387    * @param aResult - The response that we want to check.\r
388    * \r
389    * @return - true if the response contains a success code,\r
390    *           false otherwise.\r
391    */\r
392   private boolean resultSuccessful(OperationResult aResult) {\r
393     \r
394     return (aResult.getResultCode() >= 200) && (aResult.getResultCode() < 300);\r
395   }\r
396 }\r