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