5a6554398e2db7ae2005636145121eb66089df74
[ccsdk/features.git] /
1 /*******************************************************************************
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt
4  * =================================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  ******************************************************************************/
18 package org.onap.ccsdk.features.sdnr.wt.devicemanager.base.database;
19
20 import java.io.IOException;
21 import java.nio.charset.StandardCharsets;
22 import java.nio.file.Files;
23 import java.nio.file.Paths;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Set;
27 import javax.annotation.Nonnull;
28 import javax.annotation.Nullable;
29 import org.elasticsearch.ElasticsearchException;
30 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
31 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
32 import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
33 import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
34 import org.elasticsearch.action.delete.DeleteResponse;
35 import org.elasticsearch.action.get.GetResponse;
36 import org.elasticsearch.action.index.IndexRequestBuilder;
37 import org.elasticsearch.action.index.IndexResponse;
38 import org.elasticsearch.action.search.SearchResponse;
39 import org.elasticsearch.client.Client;
40 import org.elasticsearch.common.bytes.BytesReference;
41 import org.elasticsearch.common.settings.Settings;
42 import org.elasticsearch.index.query.QueryBuilder;
43 import org.elasticsearch.index.query.QueryBuilders;
44 import org.elasticsearch.search.SearchHit;
45 import org.json.JSONObject;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * @author Herbert
51  *
52  */
53 public class HtDatabaseClientAbstract implements HtDataBase, AutoCloseable {
54
55     private final Logger log = LoggerFactory.getLogger(HtDatabaseClientAbstract.class);
56
57     private final Client client;
58     private String esIndexAlias;
59
60     /**
61      * Simple database initialization. Query all ES configuration information from cluster node.
62      *
63      * @param esIndex Database index
64      * @param database database node descriptor
65      */
66     public HtDatabaseClientAbstract(String esIndex, @Nonnull HtDatabaseNode database) {
67
68         this.esIndexAlias = esIndex;
69         this.client = database.getClient();
70     }
71
72
73     /*----------------------------------
74      * some constructing functions, used by public constructors
75      */
76
77
78     /*----------------------------------
79      * Getter / Setter
80      */
81
82     @Override
83     public String getNetworkIndex() {
84         return esIndexAlias;
85     }
86
87     @Override
88     public void setNetworkIndex(String es_index) {
89         this.esIndexAlias = es_index;
90     }
91
92     @Override
93     public Client getClient() {
94         return client;
95     }
96
97     /*----------------------------------
98      * Functions
99      */
100
101     /**
102      * Close function
103      */
104     @Override
105     public void close() {
106         client.close();
107     }
108
109     /**
110      * Verify if index already created
111      *
112      * @return boolean accordingly
113      */
114     public boolean isExistsIndex() {
115
116         if (esIndexAlias == null) {
117             throw new IllegalArgumentException("Missing Index");
118         }
119
120         log.debug("Check status of ES index: {}", esIndexAlias);
121
122         final IndicesExistsResponse indexStatus =
123                 client.admin().indices().prepareExists(esIndexAlias).execute().actionGet();
124
125         return indexStatus.isExists();
126
127     }
128
129
130     /**
131      * Create and write the mapping and setting of the index
132      *
133      * @param jsonIndexMappingSetting with mapping and setting definition Object or null for no
134      *        configuration
135      */
136     public void doCreateIndexWithMapping(JSONObject jsonIndexMappingSetting) {
137
138         if (esIndexAlias == null) {
139             throw new IllegalArgumentException("Missing Index");
140         }
141
142         try {
143             // Create index with mapping and setting
144             String esIndexName = esIndexAlias + "_v1";
145             log.debug("Create not existing ES index: {} with alias:{}", esIndexName, esIndexAlias);
146
147             doCreateIndexWithMappingsAndSettings(esIndexName, jsonIndexMappingSetting);
148
149             // Set Alias
150             log.debug("Set alias {} to index {}", esIndexAlias, esIndexName);
151             IndicesAliasesResponse setAliasResponse =
152                     client.admin().indices().prepareAliases().addAlias(esIndexName, esIndexAlias).execute().actionGet();
153             log.debug("CreateIndex response {}", setAliasResponse);
154
155         } catch (ElasticsearchException e) {
156             log.warn("ElasticsearchException: {}", e.getDetailedMessage());
157         }
158     }
159
160
161     /**
162      * Assign each mapping in the mappings section as separate mapping entry
163      *
164      * @param createIndexRequestBuilder builder for command to ES
165      * @param jsonIndexMappingSetting json with mapping information
166      */
167     private void doCreateIndexWithMappingsAndSettings(String esIndexName, JSONObject jsonIndexMappingSetting) {
168
169         CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(esIndexName);
170         if (createIndexRequestBuilder == null) {
171             throw new HtDatabaseClientException("No client. Can not create index.", esIndexAlias);
172         }
173
174         if (jsonIndexMappingSetting != null) {
175             try {
176                 doAddMappings(createIndexRequestBuilder, jsonIndexMappingSetting);
177                 doAddSetting(createIndexRequestBuilder, jsonIndexMappingSetting);
178                 log.debug(" doCreateIndexWithMapping");
179             } catch (RuntimeException e) {
180                 log.info("Exception during adding mappings or settings to CreateIndexRequestBuilder. ", e);
181             }
182         }
183
184         CreateIndexResponse createResponse = createIndexRequestBuilder.execute().actionGet();
185         log.debug("CreateIndex response {}", createResponse);
186     }
187
188     /**
189      * Add one or more mappings to command
190      *
191      * @param createIndexRequestBuilder to add parameters
192      * @param jsonIndexMappingSetting contains mapping and setting information
193      */
194     private void doAddMappings(CreateIndexRequestBuilder createIndexRequestBuilder,
195             JSONObject jsonIndexMappingSetting) {
196
197         // If there are json information .. verify if they contain mappings
198         JSONObject jsonMapping = jsonIndexMappingSetting.optJSONObject("mappings");
199
200         // Handle optional mappings if requested
201         if (jsonMapping != null) {
202             log.debug("Set mapping for index {} {}", esIndexAlias, jsonMapping);
203
204             // For any reason the function below was not working without iterator
205             Set<String> keys = getStringKeySet(jsonMapping);
206             if (log.isDebugEnabled()) {
207                 log.debug("Found length: {} keys: {}", jsonMapping.length(), keys.size());
208             }
209
210             for (String docType : keys) {
211                 JSONObject jsonObject = jsonMapping.getJSONObject(docType);
212                 if (jsonObject != null) {
213                     String jsonObjectString = jsonObject.toString();
214                     log.debug("Doctype:{} mapping:{}", docType, jsonObjectString);
215                     createIndexRequestBuilder.addMapping(docType, jsonObjectString);
216                     log.debug("Mapping created Doctype:{}", docType);
217                 } else {
218                     log.debug("No jsonObject for docType {}", docType);
219                 }
220             }
221         } else {
222             log.debug("No mapping requested for index {}", esIndexAlias);
223         }
224     }
225
226     /**
227      * Add one setting to command
228      *
229      * @param createIndexRequestBuilder to add parameters
230      * @param jsonIndexMappingSetting contains mapping and setting information
231      */
232     private void doAddSetting(CreateIndexRequestBuilder createIndexRequestBuilder, JSONObject jsonIndexMappingSetting) {
233         // Handle optional settings if requested
234         log.debug("Handle settings");
235         JSONObject jsonSettings = jsonIndexMappingSetting.optJSONObject("settings");
236         if (jsonSettings != null) {
237             log.debug("Set setting for index {} {}", esIndexAlias, jsonSettings);
238             createIndexRequestBuilder.setSettings(Settings.settingsBuilder().loadFromSource(jsonSettings.toString()));
239         } else {
240             log.debug("No settings requested for index {}", esIndexAlias);
241         }
242     }
243
244     /**
245      * Create Index with alias according to definition, but no mapping
246      */
247     public void doCreateIndex() {
248         doCreateIndexWithMapping(null);
249     }
250
251     /**
252      * Write one object into Database
253      *
254      * @param esId Database index
255      * @param dataTypeName Name of datatype
256      * @param json String in JSON format.
257      * @return esId of the object
258      */
259     @Override
260     public String doWriteJsonString(String dataTypeName, IsEsObject esId, String json) {
261         return doWriteByteArray(dataTypeName, esId, json.getBytes());
262     }
263
264     /**
265      * Write one object into Database
266      *
267      * @param esId Database index
268      * @param dataTypeName Name of datatype
269      * @param json String in JSON format.
270      * @return esId of the object
271      */
272
273     @Override
274     public String doWriteByteArray(String dataTypeName, IsEsObject esId, byte[] json) {
275         return doWriteRaw(dataTypeName, esId.getEsId(), json);
276     }
277
278     /**
279      * Write one object into Database
280      *
281      * @param dataTypeName Name of datatype
282      * @param id id of the object or null
283      * @param json Object as json
284      * @return esId of the Object
285      */
286     public String doWriteJsonObject(String dataTypeName, String id, JSONObject json) {
287         return doWriteRaw(dataTypeName, id, json.toString().getBytes());
288     }
289
290     /**
291      * Write one object into Database
292      *
293      * @param esId Database index or null
294      * @param dataTypeName Name of datatype
295      * @param json String in JSON format.
296      * @return esId of the object
297      */
298
299     public String doWriteRaw(String dataTypeName, String esId, byte[] json) {
300
301         if (esIndexAlias == null) {
302             throw new IllegalArgumentException("Missing Index");
303         }
304
305         IndexRequestBuilder request = esId == null || esId.isEmpty() ? client.prepareIndex(esIndexAlias, dataTypeName)
306                 : client.prepareIndex(esIndexAlias, dataTypeName, esId);
307
308         IndexResponse response = null;
309         try {
310             response = request.setSource(json).execute().actionGet();
311         } catch (ElasticsearchException e) {
312             log.warn("ES Exception {} Json: {}", e.getMessage(), new String(json));
313         }
314
315         if (response == null) {
316             String jsonString = new String(json);
317             log.warn("Response null during write: {} {}", esId, jsonString);
318             return null;
319         } else {
320             return response.getId();
321         }
322     }
323
324     /**
325      * Write JSON Data. First level contains datatype, next level id Example "datatype" : { "id" : { } }
326      * @param json Object
327      */
328     public void doWriteJSONObject(JSONObject json) {
329
330         Set<String> docTypes = getStringKeySet(json);
331         log.debug("Found number of keys: {} keys: {}", json.length(), docTypes.size());
332         for (String docType : docTypes) {
333             JSONObject objects = json.optJSONObject(docType);
334             if (objects == null) {
335                 log.debug("Skip json {} with class {}", docType, json.get(docType).getClass());
336             } else {
337                 doWriteJsonObjectsWithIds(docType, objects);
338             }
339         }
340     }
341
342     /**
343      * Write object and Id of object for a doctype
344      * @param docType of the objects
345      * @param objects a bunch of objects with ids as object name
346      */
347     private void doWriteJsonObjectsWithIds(String docType, JSONObject objects) {
348         Set<String> ids = getStringKeySet(objects);
349         log.debug("write doctype {} with elements {}", docType, ids.size());
350         for (String id : ids) {
351             JSONObject jsonIdObject = objects.optJSONObject(id);
352             if (jsonIdObject == null) {
353                 log.debug("Skip jsonsub {} with class {}", id, objects.get(id).getClass());
354             } else {
355                 if (log.isTraceEnabled()) {
356                     log.trace("Jsonsub object of id {} '{}'", id, jsonIdObject);
357                 }
358                 this.doWriteRaw(docType, id, jsonIdObject.toString().getBytes());
359             }
360         }
361     }
362
363     /**
364      * Remove Object from database
365      */
366     @Override
367     public boolean doRemove(String dataTypeName, IsEsObject esId) {
368
369         if (esIndexAlias == null) {
370             throw new IllegalArgumentException("Missing Index");
371         }
372
373         DeleteResponse response =
374                 client.prepareDelete(esIndexAlias, dataTypeName, esId.getEsId()).execute().actionGet();
375
376         return response.isFound();
377     }
378
379     /**
380      * Read Json Object from database
381      */
382     @Override
383     public @Nullable BytesReference doReadJsonData(String dataTypeName, IsEsObject esId) {
384
385         if (esId.getEsId() == null) {
386             throw new IllegalArgumentException("Read access to object without database Id");
387         }
388
389         return doReadJsonData(dataTypeName, esId.getEsId());
390     }
391
392     /**
393      * Read Json Object from database
394      */
395     @Override
396     public @Nullable BytesReference doReadJsonData(String dataTypeName, String esId) {
397
398         log.debug("NetworkIndex read: {}", esIndexAlias);
399
400         GetResponse response = client.prepareGet(esIndexAlias, dataTypeName, esId)
401                 // .setOperationThreaded(false)
402                 .execute().actionGet();
403
404         return response.isExists() ? response.getSourceAsBytesRef() : null;
405     }
406
407
408     @Override
409     public SearchHit[] doReadByQueryJsonData(int start, int length, String dataTypeName, QueryBuilder qb) {
410
411         log.debug("NetworkIndex query and read: {}", esIndexAlias);
412
413         SearchResponse response1 = client.prepareSearch(esIndexAlias).setTypes(dataTypeName).setQuery(qb).setFrom(start)
414                 .setSize(length).execute().actionGet();
415
416         return response1.getHits().hits();
417     }
418
419
420     @Override
421     public SearchHit[] doReadAllJsonData(int start, int length, String dataTypeName) {
422         // Use query
423         QueryBuilder qb = QueryBuilders.matchAllQuery();
424         return doReadByQueryJsonData(start, length, dataTypeName, qb);
425     }
426
427     /**
428      * Write Json datetype that is specified by file to ES
429      *
430      * @param dataType ES Datatype name
431      * @param fileName file name
432      */
433     public void writeJsonObjectsFromFile(String dataType, String fileName) {
434
435         log.debug("Start: Index: '{}' ' datatype: '{}'  File: '{}'", esIndexAlias, dataType, fileName);
436
437         String content = null;
438
439         try {
440             content = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8);
441         } catch (IOException e1) {
442             log.warn("Can not read file: {}", e1.getMessage());
443         }
444
445         if (content != null && content.charAt(0) == 0xfeff) {
446             content = content.substring(1);
447             log.debug("Delete first char {} {}", dataType, fileName);
448         }
449
450         if (content != null) {
451             IndexResponse response = null;
452             try {
453                 response = client.prepareIndex(esIndexAlias, dataType).setSource(content).execute().actionGet();
454             } catch (ElasticsearchException e) {
455                 log.error("ElasticsearchException during write:  for {} from {} from {}", e.getMessage(), dataType, fileName);
456             } catch (Exception e) {
457                 log.error("Exception during write:  for {} from {} from {}", e.getMessage(), dataType, fileName);
458             }
459
460             if (response != null) {
461                 if (!response.isCreated()) {
462                     String responseAsString = response.toString();
463                     log.warn("Jackson Response not created: {} {} {}", dataType, fileName, responseAsString);
464                 } else {
465                     log.debug("Created: {}", response.getId());
466                 }
467             } else {
468                 log.warn("Jackson Response null after write {} {}", dataType, fileName);
469             }
470         }
471
472     }
473
474     @Override
475     public void closeDb() {
476         if (client != null) {
477             client.close();
478         }
479     }
480
481
482     // For any reason the function json.keySet() was not working in Oxygen => replaced by iterator
483     public static @Nonnull Set<String> getStringKeySet(JSONObject json) {
484         Set<String> keys = new HashSet<>();
485         Iterator<?> iterator = json.keys();
486         while (iterator.hasNext()) {
487             keys.add((String) iterator.next());
488         }
489         return keys;
490     }
491
492
493     private static class HtDatabaseClientException extends RuntimeException {
494
495         private static final long serialVersionUID = 1L;
496
497         public HtDatabaseClientException(String string, String esIndexAlias) {
498             super(string + " Effected index: " + "esIndexAlias");
499         }
500     }
501
502 }