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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
16 * ============LICENSE_END==========================================================================
17 ******************************************************************************/
18 package org.onap.ccsdk.features.sdnr.wt.devicemanager.base.database;
20 import java.io.IOException;
21 import java.net.InetAddress;
22 import java.net.UnknownHostException;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.Files;
25 import java.nio.file.Paths;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
30 import javax.annotation.Nonnull;
31 import javax.annotation.Nullable;
32 import org.elasticsearch.ElasticsearchException;
33 import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
34 import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
35 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
36 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
37 import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
38 import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder;
39 import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
40 import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
41 import org.elasticsearch.action.delete.DeleteResponse;
42 import org.elasticsearch.action.get.GetResponse;
43 import org.elasticsearch.action.index.IndexRequestBuilder;
44 import org.elasticsearch.action.index.IndexResponse;
45 import org.elasticsearch.action.search.SearchResponse;
46 import org.elasticsearch.client.Client;
47 import org.elasticsearch.client.transport.TransportClient;
48 import org.elasticsearch.cluster.node.DiscoveryNode;
49 import org.elasticsearch.common.bytes.BytesReference;
50 import org.elasticsearch.common.settings.Settings;
51 import org.elasticsearch.common.transport.InetSocketTransportAddress;
52 import org.elasticsearch.common.unit.TimeValue;
53 import org.elasticsearch.index.query.QueryBuilder;
54 import org.elasticsearch.index.query.QueryBuilders;
55 import org.elasticsearch.search.SearchHit;
56 import org.json.JSONObject;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
64 public class HtDatabaseClientAbstract implements HtDataBase, AutoCloseable {
66 private final Logger log = LoggerFactory.getLogger(HtDatabaseClientAbstract.class);
68 private static int DELAYSECONDS = 10;
69 private final Client client;
70 private String esIndexAlias;
73 * Full database initialization.
75 * @param esIndex Database index
76 * @param esNodeserverName Servername or Server-IP that hosts the node.
77 * @param esClusterName Name of the cluster
78 * @param esNodeName Name of the node within the cluster to connect to.
79 * @throws UnknownHostException Servername not known.
81 public HtDatabaseClientAbstract(String esIndex, String esNodeserverName, String esClusterName, String esNodeName)
82 throws UnknownHostException {
84 this.esIndexAlias = esIndex;
87 Settings.settingsBuilder().put("cluster.name", esClusterName).put("node.name", esNodeName).build();
88 this.client = getClient(esNodeserverName, settings);
93 * Do not use the hostname, but use the IP for getting the client
95 * @param esIndex index to be used by the client
96 * @param esClusterName name of the ES cluster
97 * @param esNodeName name of the node
98 * @throws UnknownHostException if hostname not known
100 public HtDatabaseClientAbstract(String esIndex, String esClusterName, String esNodeName)
101 throws UnknownHostException {
103 this.esIndexAlias = esIndex;
105 Settings.settingsBuilder().put("cluster.name", esClusterName).put("node.name", esNodeName).build();
106 this.client = getClient(null, settings);
111 * Simple database initialization. Query all ES configuration information from cluster node.
113 * @param esIndex Database index
114 * @param esNodeserverHostName Servername or Server-IP that hosts the node.
115 * @throws UnknownHostException Servername not known.
118 public HtDatabaseClientAbstract(String esIndex, String esNodeserverHostName) throws UnknownHostException {
120 this.esIndexAlias = esIndex;
122 Settings settings = Settings.settingsBuilder().put("client.transport.ignore_cluster_name", true)
123 .put("client.transport.sniff", true).build();
124 this.client = getClient(esNodeserverHostName, settings);
128 * Simple database initialization. Query all ES configuration information from cluster node.
130 * @param esIndex Database index
131 * @param database database node descriptor
133 public HtDatabaseClientAbstract(String esIndex, @Nonnull HtDatabaseNode database) {
135 this.esIndexAlias = esIndex;
136 this.client = database.getClient();
140 /*----------------------------------
141 * some constructing functions, used by public constructors
145 * @param esNodeserverName
148 * @throws UnknownHostException
150 private final TransportClient getClient(@Nullable String esNodeserverName, Settings settings)
151 throws UnknownHostException {
153 TransportClient newClient = TransportClient.builder().settings(settings).build();
155 if (esNodeserverName != null) {
156 InetAddress nodeIp = InetAddress.getByName(esNodeserverName);
157 newClient.addTransportAddress(new InetSocketTransportAddress(nodeIp, 9300));
164 private void setup(TransportClient newClient) {
165 NodesInfoResponse nodeInfos = newClient.admin().cluster().prepareNodesInfo().get();
166 String clusterName = nodeInfos.getClusterName().value();
168 // ------ Debug/ Info
169 StringBuffer logInfo = new StringBuffer();
170 logInfo.append("Create ES Client an localhost for Cluster '");
171 logInfo.append(clusterName);
172 logInfo.append("' for index '");
173 logInfo.append(esIndexAlias);
174 logInfo.append("' Nodelist: ");
175 for (DiscoveryNode node : newClient.connectedNodes()) {
177 logInfo.append(node.toString());
178 logInfo.append(") ");
180 log.info(logInfo.toString());
181 // ------ Debug/ Info
183 log.info("Starting Database service. Short wait.");
185 ClusterHealthResponse nodeStatus = newClient.admin().cluster().prepareHealth().setWaitForGreenStatus()
186 // .setWaitForYellowStatus()
187 .setTimeout(TimeValue.timeValueSeconds(DELAYSECONDS)).get();
188 log.debug("Elasticsearch client started with status {}", nodeStatus.toString());
191 List<DiscoveryNode> nodeList = newClient.connectedNodes();
193 if (nodeList.isEmpty()) {
194 log.info("ES Client created for nodes: <empty node list>");
197 for (DiscoveryNode dn : nodeList) {
198 log.info("ES Client created for node#{}: {}", t, dn.getName());
202 Runtime.getRuntime().addShutdownHook(new Thread() {
205 log.info("Shutdown node " + HtDatabaseClientAbstract.class.getSimpleName());
209 log.info("Database service started.");
214 /*----------------------------------
219 public String getNetworkIndex() {
224 public void setNetworkIndex(String es_index) {
225 this.esIndexAlias = es_index;
229 public Client getClient() {
233 /*----------------------------------
241 public void close() {
246 * Create an ES index. Delete an existing index.
248 public void doDeleteIndex() {
249 log.info("Remove index {}", esIndexAlias);
251 if (esIndexAlias == null) {
252 throw new IllegalArgumentException("Missing Index");
258 IndicesExistsResponse res = client.admin().indices().prepareExists(esIndexAlias).execute().actionGet();
260 if (res.isExists()) {
261 log.info("Delete Index start: {}", esIndexAlias);
262 DeleteIndexRequestBuilder delIdx = client.admin().indices().prepareDelete(esIndexAlias);
263 delIdx.execute().actionGet();
264 log.info("Delete Index done.");
267 } catch (ElasticsearchException e) {
268 log.warn(e.getDetailedMessage());
273 * Verify if index already created
275 * @return boolean accordingly
277 public boolean isExistsIndex() {
279 if (esIndexAlias == null) {
280 throw new IllegalArgumentException("Missing Index");
283 log.debug("Check status of ES index: {}", esIndexAlias);
285 final IndicesExistsResponse indexStatus =
286 client.admin().indices().prepareExists(esIndexAlias).execute().actionGet();
288 return indexStatus.isExists();
294 * Create and write the mapping and setting of the index
296 * @param jsonIndexMappingSetting with mapping and setting definition Object or null for no
299 public void doCreateIndexWithMapping(JSONObject jsonIndexMappingSetting) {
301 if (esIndexAlias == null) {
302 throw new IllegalArgumentException("Missing Index");
306 // Create index with mapping and setting
307 String esIndexName = esIndexAlias + "_v1";
308 log.debug("Create not existing ES index: {} with alias:{}", esIndexName, esIndexAlias);
310 doCreateIndexWithMappingsAndSettings(esIndexName, jsonIndexMappingSetting);
313 log.debug("Set alias {} to index {}", esIndexAlias, esIndexName);
314 IndicesAliasesResponse setAliasResponse =
315 client.admin().indices().prepareAliases().addAlias(esIndexName, esIndexAlias).execute().actionGet();
316 log.debug("CreateIndex response {}", setAliasResponse);
318 } catch (ElasticsearchException e) {
319 log.warn("ElasticsearchException: {}", e.getDetailedMessage());
325 * Assign each mapping in the mappings section as separate mapping entry
327 * @param createIndexRequestBuilder builder for command to ES
328 * @param jsonIndexMappingSetting json with mapping information
330 private void doCreateIndexWithMappingsAndSettings(String esIndexName, JSONObject jsonIndexMappingSetting) {
332 CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(esIndexName);
333 if (createIndexRequestBuilder == null) {
334 throw new HtDatabaseClientException("No client. Can not create index.", esIndexAlias);
337 if (jsonIndexMappingSetting != null) {
339 doAddMappings(createIndexRequestBuilder, jsonIndexMappingSetting);
340 doAddSetting(createIndexRequestBuilder, jsonIndexMappingSetting);
341 log.debug(" doCreateIndexWithMapping");
342 } catch (RuntimeException e) {
343 log.info("Exception during adding mappings or settings to CreateIndexRequestBuilder. ", e);
347 CreateIndexResponse createResponse = createIndexRequestBuilder.execute().actionGet();
348 log.debug("CreateIndex response {}", createResponse);
352 * Add one or more mappings to command
354 * @param createIndexRequestBuilder to add parameters
355 * @param jsonIndexMappingSetting contains mapping and setting information
357 private void doAddMappings(CreateIndexRequestBuilder createIndexRequestBuilder,
358 JSONObject jsonIndexMappingSetting) {
360 // If there are json information .. verify if they contain mappings
361 JSONObject jsonMapping = jsonIndexMappingSetting.optJSONObject("mappings");
363 // Handle optional mappings if requested
364 if (jsonMapping != null) {
365 log.debug("Set mapping for index {} {}", esIndexAlias, jsonMapping);
367 // For any reason the function below was not working without iterator
368 Set<String> keys = getStringKeySet(jsonMapping);
369 if (log.isDebugEnabled()) {
370 log.debug("Found length: {} keys: {}", jsonMapping.length(), keys.size());
373 for (String docType : keys) {
374 JSONObject jsonObject = jsonMapping.getJSONObject(docType);
375 if (jsonObject != null) {
376 String jsonObjectString = jsonObject.toString();
377 log.debug("Doctype:{} mapping:{}", docType, jsonObjectString);
378 createIndexRequestBuilder.addMapping(docType, jsonObjectString);
379 log.debug("Mapping created Doctype:{}", docType);
381 log.debug("No jsonObject for docType {}", docType);
385 log.debug("No mapping requested for index {}", esIndexAlias);
390 * Add one setting to command
392 * @param createIndexRequestBuilder to add parameters
393 * @param jsonIndexMappingSetting contains mapping and setting information
395 private void doAddSetting(CreateIndexRequestBuilder createIndexRequestBuilder, JSONObject jsonIndexMappingSetting) {
396 // Handle optional settings if requested
397 log.debug("Handle settings");
398 JSONObject jsonSettings = jsonIndexMappingSetting.optJSONObject("settings");
399 if (jsonSettings != null) {
400 log.debug("Set setting for index {} {}", esIndexAlias, jsonSettings);
401 createIndexRequestBuilder.setSettings(Settings.settingsBuilder().loadFromSource(jsonSettings.toString()));
403 log.debug("No settings requested for index {}", esIndexAlias);
408 * Create Index with alias according to definition, but no mapping
410 public void doCreateIndex() {
411 doCreateIndexWithMapping(null);
415 * Write a JSON mapping definition for a document from a file to ES Hint: A later change of the
416 * mapping is not possible.
418 * @param jsonString String with mapping definition in JSON Format
421 public void doWriteMappingJson(String jsonString) {
423 if (esIndexAlias == null) {
424 throw new IllegalArgumentException("Missing Index");
426 if (jsonString == null) {
427 String s = "Mapping string parameter is null";
429 throw new IllegalArgumentException(s);
434 log.debug("Check status of ES index: {}", esIndexAlias);
436 final IndicesExistsResponse indexStatus =
437 client.admin().indices().prepareExists(esIndexAlias).execute().actionGet();
439 if (indexStatus.isExists()) {
440 log.debug("ES index exists: {}", esIndexAlias);
441 // A change of mapping is not working. This here works only for new datatypes
443 PutMappingResponse res = client.admin().indices().preparePutMapping(esIndexAlias).setSource(jsonString)
444 .execute().actionGet();
445 if (log.isDebugEnabled()) {
446 log.debug("Result: {}", res);
450 log.debug("Create not existing ES index: {}", esIndexAlias);
452 CreateIndexRequestBuilder createIndexRequestBuilder =
453 client.admin().indices().prepareCreate(esIndexAlias);
454 createIndexRequestBuilder.addMapping(jsonString).execute().actionGet();
457 } catch (ElasticsearchException e) {
458 log.warn(e.getDetailedMessage());
463 * Write a Json mapping definition for a document from a file to ES
465 * @param fileName Filename with json definition.
467 public void doWriteMappingFromFile(String fileName) {
470 log.info("Write mapping from File: {}", fileName);
472 if (esIndexAlias == null) {
473 throw new IllegalArgumentException("Missing Index");
476 if (fileName == null) {
477 log.warn("No mapping for {} specified in parameter file.", esIndexAlias);
481 String content = null;
484 content = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8);
485 } catch (IOException e1) {
486 log.warn("Problem with file {}: {}", fileName, e1.getMessage());
489 doWriteMappingJson(content);
494 * Write list with json objects from json files
496 * @param docTypeAndFileName List with 2 String Array. String[0] Contains the dataType name
497 * String[1] Contains the filename
499 public void doWriteJsonFiles(List<String[]> docTypeAndFileName) {
501 if (docTypeAndFileName != null) {
502 log.debug("Write number of JSONFiles: {}", docTypeAndFileName.size());
504 for (String[] s : docTypeAndFileName) {
506 writeJsonObjectsFromFile(s[0], s[1]);
508 log.warn("Wrong parameters number. Entry: {}", t);
516 * Write one object into Database
518 * @param esId Database index
519 * @param dataTypeName Name of datatype
520 * @param json String in JSON format.
521 * @return esId of the object
524 public String doWriteJsonString(String dataTypeName, IsEsObject esId, String json) {
525 return doWriteByteArray(dataTypeName, esId, json.getBytes());
529 * Write one object into Database
531 * @param esId Database index
532 * @param dataTypeName Name of datatype
533 * @param json String in JSON format.
534 * @return esId of the object
538 public String doWriteByteArray(String dataTypeName, IsEsObject esId, byte[] json) {
539 return doWriteRaw(dataTypeName, esId.getEsId(), json);
543 * Write one object into Database
545 * @param dataTypeName Name of datatype
546 * @param id id of the object or null
547 * @param json Object as json
548 * @return esId of the Object
550 public String doWriteJsonObject(String dataTypeName, String id, JSONObject json) {
551 return doWriteRaw(dataTypeName, id, json.toString().getBytes());
555 * Write one object into Database
557 * @param esId Database index or null
558 * @param dataTypeName Name of datatype
559 * @param json String in JSON format.
560 * @return esId of the object
563 public String doWriteRaw(String dataTypeName, String esId, byte[] json) {
565 if (esIndexAlias == null) {
566 throw new IllegalArgumentException("Missing Index");
569 IndexRequestBuilder request = esId == null || esId.isEmpty() ? client.prepareIndex(esIndexAlias, dataTypeName)
570 : client.prepareIndex(esIndexAlias, dataTypeName, esId);
572 IndexResponse response = null;
574 response = request.setSource(json).execute().actionGet();
575 } catch (ElasticsearchException e) {
576 log.warn("ES Exception {} Json: {}", e.getMessage(), new String(json));
579 if (response == null) {
580 String jsonString = new String(json);
581 log.warn("Response null during write: {} {}", esId, jsonString);
584 return response.getId();
589 * Write JSON Data. First level contains datatype, next level id Example "datatype" : { "id" : { } }
592 public void doWriteJSONObject(JSONObject json) {
594 Set<String> docTypes = getStringKeySet(json);
595 log.debug("Found number of keys: {} keys: {}", json.length(), docTypes.size());
596 for (String docType : docTypes) {
597 JSONObject objects = json.optJSONObject(docType);
598 if (objects == null) {
599 log.debug("Skip json {} with class {}", docType, json.get(docType).getClass());
601 doWriteJsonObjectsWithIds(docType, objects);
607 * Write object and Id of object for a doctype
608 * @param docType of the objects
609 * @param objects a bunch of objects with ids as object name
611 private void doWriteJsonObjectsWithIds(String docType, JSONObject objects) {
612 Set<String> ids = getStringKeySet(objects);
613 log.debug("write doctype {} with elements {}", docType, ids.size());
614 for (String id : ids) {
615 JSONObject jsonIdObject = objects.optJSONObject(id);
616 if (jsonIdObject == null) {
617 log.debug("Skip jsonsub {} with class {}", id, objects.get(id).getClass());
619 if (log.isTraceEnabled()) {
620 log.trace("Jsonsub object of id {} '{}'", id, jsonIdObject);
622 this.doWriteRaw(docType, id, jsonIdObject.toString().getBytes());
628 * Remove Object from database
631 public boolean doRemove(String dataTypeName, IsEsObject esId) {
633 if (esIndexAlias == null) {
634 throw new IllegalArgumentException("Missing Index");
637 DeleteResponse response =
638 client.prepareDelete(esIndexAlias, dataTypeName, esId.getEsId()).execute().actionGet();
640 return response.isFound();
644 * Read Json Object from database
647 public @Nullable BytesReference doReadJsonData(String dataTypeName, IsEsObject esId) {
649 if (esId.getEsId() == null) {
650 throw new IllegalArgumentException("Read access to object without database Id");
653 return doReadJsonData(dataTypeName, esId.getEsId());
657 * Read Json Object from database
660 public @Nullable BytesReference doReadJsonData(String dataTypeName, String esId) {
662 log.debug("NetworkIndex read: {}", esIndexAlias);
664 GetResponse response = client.prepareGet(esIndexAlias, dataTypeName, esId)
665 // .setOperationThreaded(false)
666 .execute().actionGet();
668 return response.isExists() ? response.getSourceAsBytesRef() : null;
673 public SearchHit[] doReadByQueryJsonData(int start, int length, String dataTypeName, QueryBuilder qb) {
675 log.debug("NetworkIndex query and read: {}", esIndexAlias);
677 SearchResponse response1 = client.prepareSearch(esIndexAlias).setTypes(dataTypeName).setQuery(qb).setFrom(start)
678 .setSize(length).execute().actionGet();
680 return response1.getHits().hits();
685 public SearchHit[] doReadAllJsonData(int start, int length, String dataTypeName) {
687 QueryBuilder qb = QueryBuilders.matchAllQuery();
688 return doReadByQueryJsonData(start, length, dataTypeName, qb);
692 * Write Json datetype that is specified by file to ES
694 * @param dataType ES Datatype name
695 * @param fileName file name
697 public void writeJsonObjectsFromFile(String dataType, String fileName) {
699 log.debug("Start: Index: '{}' ' datatype: '{}' File: '{}'", esIndexAlias, dataType, fileName);
701 String content = null;
704 content = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8);
705 } catch (IOException e1) {
706 log.warn("Can not read file: {}", e1.getMessage());
709 if (content != null && content.charAt(0) == 0xfeff) {
710 content = content.substring(1);
711 log.debug("Delete first char {} {}", dataType, fileName);
714 if (content != null) {
715 IndexResponse response = null;
717 response = client.prepareIndex(esIndexAlias, dataType).setSource(content).execute().actionGet();
718 } catch (ElasticsearchException e) {
719 log.error("ElasticsearchException during write: for {} from {} from {}", e.getMessage(), dataType, fileName);
720 } catch (Exception e) {
721 log.error("Exception during write: for {} from {} from {}", e.getMessage(), dataType, fileName);
724 if (response != null) {
725 if (!response.isCreated()) {
726 String responseAsString = response.toString();
727 log.warn("Jackson Response not created: {} {} {}", dataType, fileName, responseAsString);
729 log.debug("Created: {}", response.getId());
732 log.warn("Jackson Response null after write {} {}", dataType, fileName);
739 public void closeDb() {
740 if (client != null) {
746 // For any reason the function json.keySet() was not working in Oxygen => replaced by iterator
747 public static @Nonnull Set<String> getStringKeySet(JSONObject json) {
748 Set<String> keys = new HashSet<>();
749 Iterator<?> iterator = json.keys();
750 while (iterator.hasNext()) {
751 keys.add((String) iterator.next());
757 private static class HtDatabaseClientException extends RuntimeException {
759 private static final long serialVersionUID = 1L;
761 public HtDatabaseClientException(String string, String esIndexAlias) {
762 super(string + " Effected index: " + "esIndexAlias");