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