612a40efa95a8e90f956404c30ae476d69fcb0bd
[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(true)\r
105                     .clientCertFile(DataRouterConstants.DR_HOME_AUTH + certName)\r
106                     .clientCertPassword(deobfuscatedCertPassword)\r
107                     .trustStore(DataRouterConstants.DR_HOME_AUTH + keystore);\r
108     \r
109     this.searchUrl        = searchUrl;\r
110     this.documentEndpoint = documentEndpoint;\r
111     \r
112     this.logger           = logger;\r
113   }\r
114   \r
115   \r
116   /**\r
117    * Creates an index through the search db abstraction\r
118    * \r
119    * @param index          - The name of the index to be created.\r
120    * @param schemaLocation - The name of the schema file for the index.\r
121    */\r
122   public void createSearchIndex(String index, String schemaLocation) {\r
123      \r
124     // Create a mapping of the index name to schema location \r
125     indexSchemaMapping.put(index, schemaLocation);\r
126     \r
127     // Now, create the index.\r
128     createIndex(index, schemaLocation);\r
129   }\r
130   \r
131   public void createSearchIndex(String index, String schemaLocation, String endUrl) {\r
132     \r
133     // Create a mapping of the index name to schema location \r
134     indexSchemaMapping.put(index, schemaLocation);\r
135     \r
136     // Now, create the index.\r
137     createIndex(index, schemaLocation, endUrl);\r
138   }\r
139   \r
140   /**\r
141    * This method performs the actual work of creating a search index.\r
142    * \r
143    * @param index          - The name of the index to be created.\r
144    * @param schemaLocation - The name of the schema file for the index.\r
145    */\r
146   private void createIndex(String index, String schemaLocation) {\r
147     \r
148     logger.debug("Creating search index, index name: = " + index + ", schemaLocation = " + schemaLocation);\r
149     \r
150     MultivaluedMap<String, String> headers = new MultivaluedMapImpl();\r
151     headers.put("Accept", Arrays.asList("application/json"));\r
152     headers.put(Headers.FROM_APP_ID, Arrays.asList("DL"));\r
153     headers.put(Headers.TRANSACTION_ID, Arrays.asList(UUID.randomUUID().toString()));\r
154       \r
155     String url = concatSubUri(searchUrl, index);\r
156     try {\r
157 \r
158       OperationResult result = searchClient.put(url, loadFileData(schemaLocation), headers,\r
159                                                 MediaType.APPLICATION_JSON_TYPE, null);\r
160 \r
161       if (!HttpUtil.isHttpResponseClassSuccess(result.getResultCode())) {\r
162         logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, result.getFailureCause());\r
163       } else {\r
164         logger.info(DataRouterMsgs.SEARCH_INDEX_CREATE_SUCCESS, index);\r
165       }\r
166 \r
167     } catch (Exception e) {\r
168       logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, e.getLocalizedMessage());\r
169     }\r
170   }\r
171   \r
172   private void createIndex(String index, String schemaLocation, String endUrl) {\r
173     \r
174     logger.debug("Creating search index, index name: = " + index + ", schemaLocation = " + schemaLocation);\r
175     \r
176     MultivaluedMap<String, String> headers = new MultivaluedMapImpl();\r
177     headers.put("Accept", Arrays.asList("application/json"));\r
178     headers.put(Headers.FROM_APP_ID, Arrays.asList("DL"));\r
179     headers.put(Headers.TRANSACTION_ID, Arrays.asList(UUID.randomUUID().toString()));\r
180       \r
181     String url = concatSubUri(searchUrl, endUrl, index);\r
182     try {\r
183 \r
184       OperationResult result = searchClient.put(url, loadFileData(schemaLocation), headers,\r
185                                                 MediaType.APPLICATION_JSON_TYPE, null);\r
186       if (!HttpUtil.isHttpResponseClassSuccess(result.getResultCode())) {\r
187         logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, result.getFailureCause());\r
188       } else {\r
189         logger.info(DataRouterMsgs.SEARCH_INDEX_CREATE_SUCCESS, index);\r
190       }\r
191 \r
192     } catch (Exception e) {\r
193       logger.error(DataRouterMsgs.FAIL_TO_CREATE_SEARCH_INDEX, index, e.getLocalizedMessage());\r
194     }\r
195   }\r
196   \r
197   /**\r
198    * Retrieves a document from the search service.\r
199    * \r
200    * @param index - The index to retrieve the document from.\r
201    * @param id    - The unique identifier for the document.\r
202    * \r
203    * @return - The REST response returned from the Search Service.\r
204    */\r
205   public OperationResult getDocument(String index, String id) {\r
206     \r
207     Map<String, List<String>> headers = new HashMap<>();\r
208     headers.put(Headers.FROM_APP_ID, Arrays.asList("Data Router"));\r
209     headers.put(Headers.TRANSACTION_ID, Arrays.asList(MDC.get(MdcContext.MDC_REQUEST_ID)));\r
210     \r
211     String url = concatSubUri(searchUrl, index, documentEndpoint, id);\r
212     return searchClient.get(url, headers, MediaType.APPLICATION_JSON_TYPE);    \r
213   }\r
214   \r
215   \r
216   /**\r
217    * Creates or updates a document in the Search Service.\r
218    * \r
219    * @param index   - The index to create or update the document in.\r
220    * @param id      - The identifier for the document.\r
221    * @param payload - The document contents.\r
222    * @param headers - HTTP headers.\r
223    */\r
224   public void putDocument(String index, String id, String payload, Map<String, List<String>> headers) {\r
225         \r
226     // Try to post the document to the search service.\r
227     OperationResult result = doDocumentPut(index, id, payload, headers);\r
228     \r
229     // A 404 response from the Search Service may indicate that the index we are writing\r
230     // to does not actually exist.  We will try creating it now.\r
231     if(result.getResultCode() == Status.NOT_FOUND.getStatusCode()) {\r
232             \r
233       // Lookup the location of the schema that we want to create.\r
234       String indexSchemaLocation = indexSchemaMapping.get(index);\r
235       if(indexSchemaLocation != null) {\r
236         \r
237         // Try creating the index now...\r
238         logger.info(DataRouterMsgs.CREATE_MISSING_INDEX, index);\r
239         createIndex(index, indexSchemaLocation);\r
240         \r
241         // ...and retry the document post.\r
242         result = doDocumentPut(index, id, payload, headers);\r
243       }\r
244     }\r
245     \r
246     if(!resultSuccessful(result)) {\r
247       logger.error(DataRouterMsgs.FAIL_TO_CREATE_UPDATE_DOC, index, result.getFailureCause());\r
248     }\r
249   }\r
250   \r
251   \r
252   /**\r
253    * This method does the actual work of submitting a document PUT request to the Search Service.\r
254    * \r
255    * @param index   - The index to create or update the document in.\r
256    * @param id      - The identifier for the document.\r
257    * @param payload - The document contents.\r
258    * @param headers - HTTP headers.\r
259    * \r
260    * @return - The HTTP response returned by the Search Service.\r
261    */\r
262   private OperationResult doDocumentPut(String index, String id, String payload, Map<String, List<String>> headers) {\r
263     \r
264     String url = concatSubUri(searchUrl, index, documentEndpoint, id);\r
265     return searchClient.put(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE);\r
266   }\r
267   \r
268   \r
269   /**\r
270    * Creates a document in the Search Service.\r
271    * \r
272    * @param index   - The index to create the document in.\r
273    * @param payload - The document contents.\r
274    * @param headers - HTTP headers.\r
275    */\r
276   public void postDocument(String index, String payload, Map<String, List<String>> headers) {\r
277     \r
278     // Try to post the document to the search service.\r
279     OperationResult result = doDocumentPost(index, payload, headers);\r
280     \r
281     // A 404 response from the Search Service may indicate that the index we are writing\r
282     // to does not actually exist.  We will try creating it now.\r
283     if(result.getResultCode() == Status.NOT_FOUND.getStatusCode()) {\r
284       \r
285       // Lookup the location of the schema that we want to create.\r
286       String indexSchemaLocation = indexSchemaMapping.get(index);\r
287       if(indexSchemaLocation != null) {\r
288         \r
289         // Try creating the index now...\r
290         logger.info(DataRouterMsgs.CREATE_MISSING_INDEX, index);\r
291         createIndex(index, indexSchemaLocation);\r
292         \r
293         // ...and retry the document post.\r
294         result = doDocumentPost(index, payload, headers);\r
295       }\r
296     }\r
297     \r
298     if(!resultSuccessful(result)) {\r
299       logger.error(DataRouterMsgs.FAIL_TO_CREATE_UPDATE_DOC, index, result.getFailureCause());\r
300     }\r
301   }\r
302   \r
303   \r
304   /**\r
305    * This method does the actual work of submitting a document PUT request to the Search Service.\r
306    * \r
307    * @param index   - The index to create or update the document in.\r
308    * @param payload - The document contents.\r
309    * @param headers - HTTP headers.\r
310    * \r
311    * @return - The HTTP response returned by the Search Service.\r
312    */\r
313   private OperationResult doDocumentPost(String index, String payload, Map<String, List<String>> headers) {\r
314     \r
315     String url = concatSubUri(searchUrl, index, documentEndpoint);\r
316     return searchClient.post(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE);\r
317   }\r
318   \r
319   \r
320   /**\r
321    * Removes a document from the Search Service.\r
322    * \r
323    * @param index   - The index to create the document in.\r
324    * @param documentId      - The identifier for the document.\r
325    * @param headers - HTTP headers.\r
326    */\r
327   public void deleteDocument(String index, String documentId, Map<String, List<String>> headers) {\r
328     \r
329     String url = concatSubUri(searchUrl, index, documentEndpoint, documentId);\r
330     searchClient.delete(url, headers, null);\r
331   }\r
332   \r
333   \r
334   /**\r
335    * Convenience method to load up all the data from a file into a string\r
336    * \r
337    * @param filename the filename to read from disk\r
338    * @return the data contained within the file\r
339    * @throws Exception\r
340    */\r
341   protected String loadFileData(String filename) throws Exception {\r
342     StringBuilder data = new StringBuilder();\r
343 \r
344     try (InputStreamReader inputStreamReader = new InputStreamReader(EntityEventPolicy.class.getClassLoader()\r
345         .getResourceAsStream("/" + filename), StandardCharsets.UTF_8); BufferedReader in = new BufferedReader(\r
346         inputStreamReader)\r
347     ) {\r
348 \r
349       String line;\r
350       while ((line = in.readLine()) != null) {\r
351         data.append(line);\r
352       }\r
353     } catch (Exception e) {\r
354       throw new Exception("Failed to read from file = " + filename + ".", e);\r
355     }\r
356 \r
357     return data.toString();\r
358   }\r
359   \r
360   \r
361   /**\r
362    * Helper utility to concatenate substrings of a URI together to form a proper URI.\r
363    * \r
364    * @param suburis the list of substrings to concatenate together\r
365    * @return the concatenated list of substrings\r
366    */\r
367   public static String concatSubUri(String... suburis) {\r
368     String finalUri = "";\r
369 \r
370     for (String suburi : suburis) {\r
371 \r
372       if (suburi != null) {\r
373         // Remove any leading / since we only want to append /\r
374         suburi = suburi.replaceFirst("^/*", "");\r
375 \r
376         // Add a trailing / if one isn't already there\r
377         finalUri += suburi.endsWith("/") ? suburi : suburi + "/";\r
378       }\r
379     }\r
380 \r
381     return finalUri;\r
382   }\r
383   \r
384   \r
385   /**\r
386    * Helper utility to check the response code of an HTTP response.\r
387    * \r
388    * @param aResult - The response that we want to check.\r
389    * \r
390    * @return - true if the response contains a success code,\r
391    *           false otherwise.\r
392    */\r
393   private boolean resultSuccessful(OperationResult aResult) {\r
394     \r
395     return (aResult.getResultCode() >= 200) && (aResult.getResultCode() < 300);\r
396   }\r
397 }\r