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