summaryrefslogtreecommitdiffstats
path: root/sdnr/wt/data-provider/database/src/main/java
diff options
context:
space:
mode:
authorherbert <herbert.eiselt@highstreet-technologies.com>2019-12-11 15:06:19 +0100
committerTimoney, Dan (dt5972) <dtimoney@att.com>2019-12-13 14:56:50 -0500
commit58f412ad18c1daf622e54ed36db6014f4e04f3f7 (patch)
tree91cc5f6630b4bf6a0ef12c1dee5a03fa2ebbb6cc /sdnr/wt/data-provider/database/src/main/java
parent05e4277510b9947e3cdcbab8c7bb3846a4211af6 (diff)
Add data-provider
data-provider and change to avoid heap overflow Issue-ID: SDNC-994 Signed-off-by: herbert <herbert.eiselt@highstreet-technologies.com> Change-Id: Ifcc6ce1198442e4b48fc08ba71108cfd5b5ca8aa
Diffstat (limited to 'sdnr/wt/data-provider/database/src/main/java')
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/InternalConnectionStatus.java37
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/NetconfTimeStamp.java206
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter.java339
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter2.java360
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/config/EsConfig.java164
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner.java217
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner2.java178
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper.java226
-rw-r--r--sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper2.java263
9 files changed, 1990 insertions, 0 deletions
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/InternalConnectionStatus.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/InternalConnectionStatus.java
new file mode 100644
index 000000000..81876b75b
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/InternalConnectionStatus.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+
+package org.onap.ccsdk.features.sdnr.wt.base.netconf.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ConnectionLogStatus;
+
+public class InternalConnectionStatus {
+ public static ConnectionLogStatus statusFromNodeStatus(ConnectionStatus nodeStatus) {
+ switch(nodeStatus) {
+ case Connected:
+ return ConnectionLogStatus.Connected;
+ case Connecting:
+ return ConnectionLogStatus.Connecting;
+ case UnableToConnect:
+ return ConnectionLogStatus.UnableToConnect;
+ default:
+ return ConnectionLogStatus.Undefined;
+ }
+ }
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/NetconfTimeStamp.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/NetconfTimeStamp.java
new file mode 100644
index 000000000..4857da661
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/base/netconf/util/NetconfTimeStamp.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.base.netconf.util;
+
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Date;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 2019/06/17 Redesign to ZonedDateTime because of sync problems.
+ *
+ * Function is handling the NETCONF and the format used by database and restconf communication.
+ *
+ * Input supported for the formats used in NETCONF messages:
+ *
+ * Format1 ISO 8601 2017-01-18T11:44:49.482-05:00
+ *
+ * Format2 NETCONF - pattern from ietf-yang-types "2013-07-15" Pattern:
+ * "\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-](\d{2}):(\d{2}))"
+ *
+ * Format3 NETCONF DateAndTime CoreModel-CoreFoundationModule-TypeDefinitions vom
+ * 2016-07-01 Example1: 20170118114449.1Z Example2: 20170118114449.1-0500 Pattern:
+ * "\d{4}\d{2}\d{2}\d{2}\d{2}\d{2}.\d+?(Z|[\+\-](\d{2})(\d{2}))" typedef DateAndTime { description
+ * "This primitive type defines the date and time according to the following structure:
+ * 'yyyyMMddhhmmss.s[Z|{+|-}HHMm]' where: yyyy '0000'..'9999' year MM '01'..'12' month dd '01'..'31'
+ * day hh '00'..'23' hour mm '00'..'59' minute ss '00'..'59' second s '.0'..'.9' tenth of second
+ * (set to '.0' if EMS or NE cannot support this granularity) Z 'Z' indicates UTC (rather than local
+ * time) {+|-} '+' or '-' delta from UTC HH '00'..'23' time zone difference in hours Mm '00'..'59'
+ * time zone difference in minutes."; type string; } Format4 E/// specific Example1:
+ * 2017-01-23T13:32:38-05:00 Example2: 2017-01-23T13:32-05:00
+ *
+ * Input formats netconfTime as String according the formats given above
+ *
+ * Return format is String in ISO8601 Format for database and presentation.
+ *
+ * Example formats:
+ * 1) ISO8601. Example 2017-01-18T11:44:49.482-05:00
+ * 2) Microwave ONF. Examples 20170118114449.1Z, 20170118114449.1-0500
+ * 3.1) Ericson. Example: 2017-01-23T13:32:38-05:00
+ * 3.2) Ericson. Example: 2017-01-23T13:32-05:00
+ * Always 10 Groups,
+ * 1:Year 2:Month 3:day 4:Hour 5:minute 6:optional sec 7:optional ms 8:optional Z or 9:offset
+ * signedhour 10:min
+ *
+ * Template:
+ * private static final NetconfTimeStamp NETCONFTIME_CONVERTER = NetconfTimeStamp.getConverter();
+ */
+
+public class NetconfTimeStamp {
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfTimeStamp.class);
+
+ private static final NetconfTimeStamp CONVERTER = new NetconfTimeStamp();
+
+ /**
+ * Specify the input format expected from netconf, and from specific devices.
+ */
+ private static DateTimeFormatter formatterInput = DateTimeFormatter.ofPattern(""
+ + "[yyyy-MM-dd'T'HH:mm[:ss][.SSS][.SS][.S][xxx][xx][X][Z]]"
+ + "[yyyyMMddHHmmss[.SSS][.SS][.S][xxx][xx][X][Z]]"
+ ).withZone(ZoneOffset.UTC);
+
+ /**
+ * Specify output format that is used internally
+ */
+ private static DateTimeFormatter formatterOutput = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.S'Z'")
+ .withZone(ZoneOffset.UTC);
+
+ /**
+ * Use static access
+ */
+ private NetconfTimeStamp() {
+ }
+
+ /*
+ * ------------------------------------ Public function
+ */
+
+ /**
+ * Use this function to get the converter
+ * @return global converter
+ */
+ public static NetconfTimeStamp getConverter() {
+ return CONVERTER;
+ }
+
+ /**
+ * Get actual timestamp as NETCONF specific type NETCONF/YANG 1.0 Format
+ *
+ * @return String with Date in NETCONF/YANG Format Version 1.0.
+ */
+ public String getTimeStampAsNetconfString() {
+ return ZonedDateTime.now(ZoneOffset.UTC).format(formatterOutput);
+ }
+
+ /**
+ * Get actual timestamp as NETCONF specific type NETCONF/YANG 1.0 Format
+
+ * @return String with Date in NETCONF/YANG Format Version 1.0.
+ */
+ public String getTimeStampAsNetconfString(Date date) {
+ return ZonedDateTime.ofInstant(date.toInstant(),ZoneOffset.UTC).format(formatterOutput);
+ }
+
+
+
+ /**
+ * Get actual timestamp as NETCONF specific type NETCONF/YANG 1.0 Format in GMT
+ *
+ * @return DateAndTime Type 1.0. Date in NETCONF/YANG Format Version 1.0.
+ */
+ public DateAndTime getTimeStamp() {
+ return DateAndTime.getDefaultInstance(getTimeStampAsNetconfString());
+ }
+
+ /**
+ * Get time from date as NETCONF specific type NETCONF/YANG 1.0 Format in GMT
+ * @param date specifying the date and time
+ * @return DateAndTime Type 1.0. Date in NETCONF/YANG Format Version 1.0.
+ */
+ public DateAndTime getTimeStamp(Date date) {
+ return DateAndTime.getDefaultInstance(getTimeStampAsNetconfString(date));
+ }
+ /**
+ * Get time from date as NETCONF specific type NETCONF/YANG 1.0 Format in GMT
+ * @param date specifying the date and time
+ * @return DateAndTime Type 1.0. Date in NETCONF/YANG Format Version 1.0.
+ */
+ public DateAndTime getTimeStamp(String date) {
+ return DateAndTime.getDefaultInstance(date);
+ }
+
+ /**
+ * Return the String with a NETCONF time converted to long
+ *
+ * @param netconfTime as String according the formats given above
+ * @return Epoch milliseconds
+ * @throws IllegalArgumentException In case of no compliant time format definition for the string
+ */
+ public long getTimeStampFromNetconfAsMilliseconds(String netconfTime) throws IllegalArgumentException {
+ try {
+ long utcMillis = doParse(netconfTime).toInstant().toEpochMilli();
+ return utcMillis;
+ } catch (DateTimeParseException e) {
+ throw new IllegalArgumentException(
+ "No pattern for NETCONF data string: " + netconfTime + " Msg:" + e.getMessage());
+ }
+ }
+
+ /**
+ * Deliver String result.
+ *
+ * @param netconfTime as String according the formats given above
+ * @return If successful: String in ISO8601 Format for database and presentation. If "wrong formed
+ * input" the Input string with the prefix "Maleformed date" is delivered back.
+ */
+ public String getTimeStampFromNetconf(String netconfTime) {
+ try {
+ String inputUTC = doParse(netconfTime).format(formatterOutput);
+ return inputUTC;
+ } catch (Exception e) {
+ LOG.info(e.getMessage());
+ }
+ LOG.debug("No pattern for NETCONF data string: {}", netconfTime);
+ return "Malformed date: " + netconfTime; // Error handling
+ }
+
+ /*----------------------------------------------------
+ * Private functions
+ */
+
+ private OffsetDateTime doParse(String netconfTime) {
+ return OffsetDateTime.parse(netconfTime, formatterInput);
+ }
+
+ public Date getDateFromNetconf(String netconfTime) {
+ return Date.from(LocalDateTime.parse(netconfTime, formatterInput).atZone(ZoneOffset.UTC).toInstant());
+ }
+
+ public String getTimeStampAsNetconfString(LocalDateTime dt) {
+ return formatterOutput.format(dt);
+ }
+
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter.java
new file mode 100644
index 000000000..ac676024f
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.database;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.onap.ccsdk.features.sdnr.wt.common.database.DatabaseClient;
+import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
+import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
+import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
+import org.onap.ccsdk.features.sdnr.wt.yangtools.YangToolsMapper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.Entity;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+/**
+ * Class to rw yang-tool generated objects into elasticsearch database. For "ES _id" exchange the esIdAddAtributteName is used.
+ * This attribute mast be of type String and contains for read and write operations the object id.
+ * The function can be used without id handling.
+ * If id handling is required the parameter needs to be specified by class definition in yang and setting the name by using setAttributeName()
+ *
+ * @param <T> Yang tools generated class object.
+ */
+public class EsDataObjectReaderWriter<T extends DataObject> {
+
+ private final Logger LOG = LoggerFactory.getLogger(EsDataObjectReaderWriter.class);
+
+ /** Typename for elastic search data schema **/
+ private String dataTypeName;
+
+ /** Elasticsearch Database client to be used **/
+ private DatabaseClient db;
+
+ /** Mapper with configuration to use opendaylight yang-tools builder pattern for object creation **/
+ private YangToolsMapper yangtoolsMapper;
+
+ /** Class of T as attribute to allow JSON to Class object mapping **/
+ private Class<T> clazz;
+
+ /** Field is used to write id. If null no id handling **/
+ private @Nullable Field field;
+
+ /** Attribute that is used as id field for the database object **/
+ private @Nullable String esIdAddAtributteName;
+
+ /** Interface to be used for write operations. Rule for write: T extends S and **/
+ private Class<? extends DataObject> writeInterfaceClazz; // == "S"
+
+ /**
+ * Elasticsearch database read and write for specific class, defined by opendaylight yang-tools.
+ *
+ * @param db Database access client
+ * @param dataTypeName typename in database schema
+ * @param clazz class of type to be handled
+ * @throws ClassNotFoundException
+ */
+ public EsDataObjectReaderWriter(DatabaseClient db, Entity dataTypeName, Class<T> clazz) throws ClassNotFoundException {
+ this(db, dataTypeName.getName(), clazz);
+ }
+ public EsDataObjectReaderWriter(DatabaseClient db, String dataTypeName, Class<T> clazz) throws ClassNotFoundException {
+ LOG.info("Create {} for datatype {} class {}", this.getClass().getName(), dataTypeName, clazz.getName());
+
+ this.esIdAddAtributteName = null;
+ this.field = null;
+ this.writeInterfaceClazz = clazz;
+ this.db = db;
+ this.dataTypeName = dataTypeName;
+ this.yangtoolsMapper = new YangToolsMapper();
+ //this.yangtoolsMapper.assertBuilderClass(clazz);
+ this.clazz = clazz;
+//
+// if (! db.isExistsIndex(dataTypeName)) {
+// throw new IllegalArgumentException("Index "+dataTypeName+" not existing.");
+// }
+ }
+
+ public String getDataTypeName() {
+ return dataTypeName;
+ }
+ public Class<T> getClazz() {
+ return clazz;
+ }
+ /**
+ * Simlar to {@link #setEsIdAttributeName()}, but adapts the parameter to yangtools attribute naming schema
+ * @param esIdAttributeName is converted to UnderscoreCamelCase
+ * @return this for further operations.
+ */
+ public EsDataObjectReaderWriter<T> setEsIdAttributeNameCamelized(String esIdAttributeName) {
+ return setEsIdAttributeName(YangToolsMapper.toCamelCaseAttributeName(esIdAttributeName));
+ }
+
+ /**
+ * Attribute name of class that is containing the object id
+ * @param esIdAttributeName of the implementation class for the yangtools interface.
+ * Expected attribute name format is CamelCase with leading underline. @
+ * @return this for further operations.
+ * @throws SecurityException if no access or IllegalArgumentException if wrong type or no attribute with this name.
+ */
+ public EsDataObjectReaderWriter<T> setEsIdAttributeName(String esIdAttributeName) {
+ LOG.debug("Set attribute '{}'", esIdAttributeName);
+ this.esIdAddAtributteName = null; // Reset status
+ this.field = null;
+
+ Field attributeField;
+ try {
+ Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
+ T object = builder.build();
+ attributeField = object.getClass().getDeclaredField(esIdAttributeName);
+ if (attributeField.getType().equals(String.class)) {
+ attributeField.setAccessible(true);
+ this.esIdAddAtributteName = esIdAttributeName; //Set new status if everything OK
+ this.field = attributeField;
+ } else {
+ String msg = "Wrong field type " + attributeField.getType().getName() + " of " + esIdAttributeName;
+ LOG.debug(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ } catch (NoSuchFieldException e) {
+ // Convert to run-time exception
+ String msg = "NoSuchFieldException for '" + esIdAttributeName + "' in class " + clazz.getName();
+ LOG.debug(msg);
+ throw new IllegalArgumentException(msg);
+ } catch (SecurityException e) {
+ LOG.debug("Access problem "+esIdAttributeName,e);
+ throw e;
+ }
+ return this;
+ }
+
+ /**
+ * Specify subclass of T for write operations.
+ * @param writeInterfaceClazz
+ */
+ public EsDataObjectReaderWriter<T> setWriteInterface( @Nonnull Class<? extends DataObject> writeInterfaceClazz ) {
+ LOG.debug("Set write interface to {}", writeInterfaceClazz);
+ if (writeInterfaceClazz == null)
+ throw new IllegalArgumentException("Null not allowed here.");
+
+ this.writeInterfaceClazz = writeInterfaceClazz;
+ return this;
+ }
+
+ /**
+ * Write child object to database with specific id
+ * @param object
+ * @param @Nullable esId use the id or if null generate unique id
+ * @return String with id or null
+ */
+ public @Nullable <S extends DataObject> String write(S object, @Nullable String esId) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doWriteRaw(dataTypeName, esId, json);
+ } catch (JsonProcessingException e) {
+ LOG.error("Write problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+ /**
+ * Update partial child object to database with match/term query
+ * @param object
+ * @param esId
+ * @return String with esId or null
+ */
+ public @Nullable <S extends DataObject> String update(S object, QueryBuilder query) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doUpdate(this.dataTypeName,json,query);
+ } catch (JsonProcessingException e) {
+ LOG.error("Update problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+ /**
+ * Write/ update partial child object to database with specific id Write if not
+ * exists, else update
+ * @param object
+ * @param esId
+ * @return String with esId or null
+ */
+ public @Nullable <S extends DataObject> String update(S object, String esId) {
+ return this.update(object, esId,null);
+ }
+ public @Nullable <S extends DataObject> String update(S object, String esId,List<String> onylForInsert) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doUpdateOrCreate(dataTypeName, esId, json,onylForInsert);
+ } catch (JsonProcessingException e) {
+ LOG.error("Update problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+
+ /**
+ * Read object from database, by using the id field
+ * @param object
+ * @return
+ */
+ public @Nullable T read(String esId) {
+ @Nullable T res = (T)null;
+ if (esId != null) {
+ String json = db.doReadJsonData(dataTypeName, esId);
+ try {
+ res = yangtoolsMapper.readValue(json.getBytes(), clazz);
+ } catch (IOException e) {
+ LOG.error("Problem: ", e);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Remove object
+ * @param esId to identify the object.
+ * @return success
+ */
+ public boolean remove(String esId) {
+ return db.doRemove(this.dataTypeName, esId);
+ }
+
+ public int remove(QueryBuilder query) {
+ return this.db.doRemove(this.dataTypeName, query);
+ }
+ /**
+ * Get all elements of related type
+ * @return all Elements
+ */
+ public SearchResult<T> doReadAll() {
+ return doReadAll(null);
+ }
+ public SearchResult<T> doReadAll(QueryBuilder query) {
+ return this.doReadAll(query,false);
+ }
+ /**
+ * Read all existing objects of a type
+ * @param query for the elements
+ * @return the list of all objects
+ */
+
+ public SearchResult<T> doReadAll(QueryBuilder query, boolean ignoreException) {
+
+ SearchResult<T> res = new SearchResult<T>();
+ int idx = 0; //Idx for getAll
+ int iterateLength = 100; //Step width for iterate
+
+ SearchResult<SearchHit> result;
+ List<SearchHit> hits;
+ do {
+ if(query!=null) {
+ LOG.debug("read data in {} with query {}",dataTypeName,query.toJSON());
+ result=db.doReadByQueryJsonData( dataTypeName, query,ignoreException);
+ }
+ else {
+ result = db.doReadAllJsonData(dataTypeName,ignoreException);
+ }
+ hits=result.getHits();
+ LOG.debug("Read: {} elements: {} Failures: {}",dataTypeName,hits.size(), yangtoolsMapper.getMappingFailures());
+
+ T object;
+ idx += result.getHits().size();
+ for (SearchHit hit : hits) {
+ object = getT(hit.getSourceAsString());
+ LOG.debug("Mapp Object: {}\nSource: '{}'\nResult: '{}'\n Failures: {}", hit.getId(),
+ hit.getSourceAsString(), object, yangtoolsMapper.getMappingFailures());
+ if (object != null) {
+ setEsId(object, hit.getId());
+ res.add(object);
+ } else {
+ LOG.warn("Mapp result null Object: {}\n Source: '{}'\n : '", hit.getId(), hit.getSourceAsString());
+ }
+ }
+
+ } while (hits.size() == iterateLength); // Do it until end indicated, because less hits than iterateLength
+ // allows.
+ res.setTotal(result.getTotal());
+ return res;
+ }
+
+ /* ---------------------------------------------
+ * Private functions
+ */
+
+ private void setEsId(T object, String esId) {
+ if (field != null) {
+ try {
+ field.set(object, esId);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ LOG.debug("Field set problem.", e); }
+ }
+ }
+
+ private @Nullable T getT(String jsonString) {
+ try {
+ return yangtoolsMapper.readValue( jsonString, clazz );
+ } catch (IOException e) {
+ LOG.info("Mapping problem", e);
+ return (T)null;
+ }
+ }
+
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter2.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter2.java
new file mode 100644
index 000000000..b585b0c60
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/EsDataObjectReaderWriter2.java
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.database;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.onap.ccsdk.features.sdnr.wt.common.database.DatabaseClient;
+import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
+import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
+import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
+import org.onap.ccsdk.features.sdnr.wt.yangtools.YangToolsMapper;
+import org.onap.ccsdk.features.sdnr.wt.yangtools.YangToolsMapper2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.Entity;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+/**
+ * Class to rw yang-tool generated objects into elasticsearch database. For "ES _id" exchange the esIdAddAtributteName is used.
+ * This attribute mast be of type String and contains for read and write operations the object id.
+ * The function can be used without id handling.
+ * If id handling is required the parameter needs to be specified by class definition in yang and setting the name by using setAttributeName()
+ *
+ * Due to using Jackson base interfaces the org.eclipse.jdt.annotation.NonNull needs to be used here to get rid of warnings
+ *
+ * @param <T> Yang tools generated class object.
+ */
+public class EsDataObjectReaderWriter2<T extends DataObject> {
+
+ private final Logger LOG = LoggerFactory.getLogger(EsDataObjectReaderWriter2.class);
+
+ /** Typename for elastic search data schema **/
+ private String dataTypeName;
+
+ /** Elasticsearch Database client to be used **/
+ private DatabaseClient db;
+
+ /** Mapper with configuration to use opendaylight yang-tools builder pattern for object creation **/
+ private YangToolsMapper2<T> yangtoolsMapper;
+
+ /** Class of T as attribute to allow JSON to Class object mapping **/
+ private Class<T> clazz;
+
+ /** Field is used to write id. If null no id handling **/
+ private @Nullable Field field;
+
+ /** Attribute that is used as id field for the database object **/
+ private @Nullable String esIdAddAtributteName;
+
+ /** Interface to be used for write operations. Rule for write: T extends S and **/
+ private Class<? extends DataObject> writeInterfaceClazz; // == "S"
+
+ /**
+ * Elasticsearch database read and write for specific class, defined by opendaylight yang-tools.
+ *
+ * @param db Database access client
+ * @param dataTypeName typename in database schema
+ * @param clazz class of type to be handled
+ * @throws ClassNotFoundException
+ */
+ public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, Entity dataTypeName, @Nonnull Class<T> clazz, @Nullable Class<B> builderClazz) throws ClassNotFoundException {
+ this(db, dataTypeName.getName(), clazz, builderClazz);
+ }
+ public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, Entity dataTypeName, @Nonnull Class<T> clazz) throws ClassNotFoundException {
+ this(db, dataTypeName.getName(), clazz, null);
+ }
+ public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, String dataTypeName, @Nonnull Class<T> clazz, @Nullable Class<B> builderClazz) throws ClassNotFoundException {
+ LOG.info("Create {} for datatype {} class {}", this.getClass().getName(), dataTypeName, clazz.getName());
+
+ this.esIdAddAtributteName = null;
+ this.field = null;
+ this.writeInterfaceClazz = clazz;
+ this.db = db;
+ this.dataTypeName = dataTypeName;
+ this.yangtoolsMapper = new YangToolsMapper2<>(clazz, builderClazz);
+ this.clazz = clazz;
+ }
+
+ /**
+ * Simlar to {@link #setEsIdAttributeName()}, but adapts the parameter to yangtools attribute naming schema
+ * @param esIdAttributeName is converted to UnderscoreCamelCase
+ * @return this for further operations.
+ */
+ public EsDataObjectReaderWriter2<T> setEsIdAttributeNameCamelized(String esIdAttributeName) {
+ return setEsIdAttributeName(YangToolsMapper.toCamelCaseAttributeName(esIdAttributeName));
+ }
+
+ /**
+ * Attribute name of class that is containing the object id
+ * @param esIdAttributeName of the implementation class for the yangtools interface.
+ * Expected attribute name format is CamelCase with leading underline. @
+ * @return this for further operations.
+ * @throws SecurityException if no access or IllegalArgumentException if wrong type or no attribute with this name.
+ */
+ public EsDataObjectReaderWriter2<T> setEsIdAttributeName(String esIdAttributeName) {
+ LOG.debug("Set attribute '{}'", esIdAttributeName);
+ this.esIdAddAtributteName = null; // Reset status
+ this.field = null;
+
+ Field attributeField;
+ try {
+ Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
+ if (builder == null) {
+ String msg = "No builder for " + clazz;
+ LOG.debug(msg);
+ throw new IllegalArgumentException(msg);
+ } else {
+ T object = builder.build();
+ attributeField = object.getClass().getDeclaredField(esIdAttributeName);
+ if (attributeField.getType().equals(String.class)) {
+ attributeField.setAccessible(true);
+ this.esIdAddAtributteName = esIdAttributeName; // Set new status if everything OK
+ this.field = attributeField;
+ } else {
+ String msg = "Wrong field type " + attributeField.getType().getName() + " of " + esIdAttributeName;
+ LOG.debug(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ } catch (NoSuchFieldException e) {
+ // Convert to run-time exception
+ String msg = "NoSuchFieldException for '" + esIdAttributeName + "' in class " + clazz.getName();
+ LOG.debug(msg);
+ throw new IllegalArgumentException(msg);
+ } catch (SecurityException e) {
+ LOG.debug("Access problem "+esIdAttributeName,e);
+ throw e;
+ }
+ return this;
+ }
+
+ /**
+ * Specify subclass of T for write operations.
+ * @param writeInterfaceClazz
+ */
+ public EsDataObjectReaderWriter2<T> setWriteInterface( @Nonnull Class<? extends DataObject> writeInterfaceClazz ) {
+ LOG.debug("Set write interface to {}", writeInterfaceClazz);
+ if (writeInterfaceClazz == null) {
+ throw new IllegalArgumentException("Null not allowed here.");
+ }
+
+ this.writeInterfaceClazz = writeInterfaceClazz;
+ return this;
+ }
+
+ public interface IdGetter<S extends DataObject> {
+ String getId(S object);
+ }
+
+ public <S extends DataObject> void write(List<S> objectList, IdGetter<S> idGetter) {
+ for (S object : objectList) {
+ write(object, idGetter.getId(object));
+ }
+ }
+
+ /**
+ * Write child object to database with specific id
+ * @param object
+ * @param @Nullable esId use the id or if null generate unique id
+ * @return String with id or null
+ */
+ public @Nullable <S extends DataObject> String write(S object, @Nullable String esId) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doWriteRaw(dataTypeName, esId, json);
+ } catch (JsonProcessingException e) {
+ LOG.error("Write problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+ /**
+ * Update partial child object to database with match/term query
+ * @param object
+ * @param esId
+ * @return String with esId or null
+ */
+ public @Nullable <S extends DataObject> String update(S object, QueryBuilder query) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doUpdate(this.dataTypeName,json,query);
+ } catch (JsonProcessingException e) {
+ LOG.error("Update problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+ /**
+ * Write/ update partial child object to database with specific id Write if not
+ * exists, else update
+ * @param object
+ * @param esId
+ * @return String with esId or null
+ */
+ public @Nullable <S extends DataObject> String update(S object, String esId) {
+ return this.updateOrCreate(object, esId,null);
+ }
+ /**
+ * See {@link doUpdateOrCreate(String dataTypeName, String esId, String json, List<String> doNotUpdateField) }
+ */
+ public @Nullable <S extends DataObject> String updateOrCreate(S object, String esId,List<String> onlyForInsert) {
+ if (writeInterfaceClazz.isInstance(object)) {
+ try {
+ String json = yangtoolsMapper.writeValueAsString(object);
+ return db.doUpdateOrCreate(dataTypeName, esId, json,onlyForInsert);
+ } catch (JsonProcessingException e) {
+ LOG.error("Update problem: ", e);
+ }
+ } else {
+ LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
+ writeInterfaceClazz.getName());
+ }
+ return null;
+ }
+
+ /**
+ * Read object from database, by using the id field
+ * @param object
+ * @return
+ */
+ public @Nullable T read(String esId) {
+ @Nullable
+ T res = null;
+ if (esId != null) {
+ String json = db.doReadJsonData(dataTypeName, esId);
+ if (json != null) {
+ try {
+ res = yangtoolsMapper.readValue(json.getBytes(), clazz);
+ } catch (IOException e) {
+ LOG.error("Problem: ", e);
+ }
+ } else {
+ LOG.debug("Can not read from DB id {} type {}", esId, dataTypeName);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Remove object
+ * @param esId to identify the object.
+ * @return success
+ */
+ public boolean remove(String esId) {
+ return db.doRemove(this.dataTypeName, esId);
+ }
+
+ public int remove(QueryBuilder query) {
+ return this.db.doRemove(this.dataTypeName, query);
+ }
+ /**
+ * Get all elements of related type
+ * @return all Elements
+ */
+ public SearchResult<T> doReadAll() {
+ return doReadAll(null);
+ }
+ public SearchResult<T> doReadAll(QueryBuilder query) {
+ return this.doReadAll(query,false);
+ }
+ /**
+ * Read all existing objects of a type
+ * @param query for the elements
+ * @return the list of all objects
+ */
+
+ public SearchResult<T> doReadAll(QueryBuilder query, boolean ignoreException) {
+
+ SearchResult<T> res = new SearchResult<>();
+ int idx = 0; //Idx for getAll
+ int iterateLength = 100; //Step width for iterate
+
+ SearchResult<SearchHit> result;
+ List<SearchHit> hits;
+ do {
+ if(query!=null) {
+ LOG.debug("read data in {} with query {}",dataTypeName,query.toJSON());
+ result=db.doReadByQueryJsonData( dataTypeName, query,ignoreException);
+ }
+ else {
+ result = db.doReadAllJsonData(dataTypeName,ignoreException);
+ }
+ hits=result.getHits();
+ LOG.debug("Read: {} elements: {} Failures: {}",dataTypeName,hits.size(), yangtoolsMapper.getMappingFailures());
+
+ T object;
+ idx += result.getHits().size();
+ for (SearchHit hit : hits) {
+ object = getT(hit.getSourceAsString());
+ LOG.debug("Mapp Object: {}\nSource: '{}'\nResult: '{}'\n Failures: {}", hit.getId(),
+ hit.getSourceAsString(), object, yangtoolsMapper.getMappingFailures());
+ if (object != null) {
+ setEsId(object, hit.getId());
+ res.add(object);
+ } else {
+ LOG.warn("Mapp result null Object: {}\n Source: '{}'\n : '", hit.getId(), hit.getSourceAsString());
+ }
+ }
+
+ } while (hits.size() == iterateLength); // Do it until end indicated, because less hits than iterateLength
+ // allows.
+ res.setTotal(result.getTotal());
+ return res;
+ }
+
+ /* ---------------------------------------------
+ * Private functions
+ */
+
+ private void setEsId(T object, String esId) {
+ if (field != null) {
+ try {
+ field.set(object, esId);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ LOG.debug("Field set problem.", e); }
+ }
+ }
+
+ private @Nullable T getT(String jsonString) {
+ try {
+ return yangtoolsMapper.readValue( jsonString, clazz );
+ } catch (IOException e) {
+ LOG.info("Mapping problem", e);
+ return null;
+ }
+ }
+
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/config/EsConfig.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/config/EsConfig.java
new file mode 100644
index 000000000..c828f3302
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/database/config/EsConfig.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.database.config;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.onap.ccsdk.features.sdnr.wt.common.configuration.Configuration;
+import org.onap.ccsdk.features.sdnr.wt.common.configuration.ConfigurationFileRepresentation;
+import org.onap.ccsdk.features.sdnr.wt.common.database.config.HostInfo;
+import org.onap.ccsdk.features.sdnr.wt.common.database.config.HostInfo.Protocol;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EsConfig implements Configuration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EsConfig.class);
+
+ public static final String SECTION_MARKER_ES = "es";
+
+ private static final String PROPERTY_KEY_DBHOSTS = "esHosts";
+ private static final String PROPERTY_KEY_ARCHIVE_LIMIT = "esArchiveLifetimeSeconds";
+ private static final String PROPERTY_KEY_CLUSTER = "esCluster";
+ private static final String PROPERTY_KEY_ARCHIVE_INTERVAL = "esArchiveCheckIntervalSeconds";
+ private static final String PROPERTY_KEY_NODE = "esNode";
+
+ private static String defaultHostinfo = printHosts(new HostInfo[] { new HostInfo("sdnrdb", 9200, Protocol.HTTP) });
+ private static final String DEFAULT_VALUE_CLUSTER = "";
+ /** check db data in this interval [in seconds] 0 deactivated */
+ private static final String DEFAULT_ARCHIVE_INTERVAL_SEC = "0";
+ /** keep data for this time [in seconds] 30 days */
+ private static final String DEFAULT_ARCHIVE_LIMIT_SEC = String.valueOf(60L * 60L * 24L * 30L);
+ private static final String DEFAULT_KEY_NODE = "elasticsearchnode";
+
+ private final ConfigurationFileRepresentation configuration;
+
+ public EsConfig(ConfigurationFileRepresentation configuration) {
+
+ this.configuration = configuration;
+ this.configuration.addSection(SECTION_MARKER_ES);
+ defaults();
+ }
+
+ /*
+ * Setter
+ */
+
+ public void setNode(String nodeName) {
+ configuration.setProperty(SECTION_MARKER_ES, PROPERTY_KEY_NODE, nodeName);
+ }
+
+ /*
+ * Getter
+ */
+
+ public String getNode() {
+ return configuration.getProperty(SECTION_MARKER_ES, PROPERTY_KEY_NODE);
+ }
+
+ public HostInfo[] getHosts() {
+ String dbHosts = configuration.getProperty(SECTION_MARKER_ES, PROPERTY_KEY_DBHOSTS);
+ return parseHosts(dbHosts);
+ }
+ public void setHosts(HostInfo[] hosts) {
+ this.configuration.setProperty(SECTION_MARKER_ES, PROPERTY_KEY_DBHOSTS, printHosts(hosts));
+ }
+ public String getCluster() {
+ return configuration.getProperty(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_INTERVAL);
+ }
+
+ public void setCluster(String cluster) {
+ configuration.setProperty(SECTION_MARKER_ES, PROPERTY_KEY_CLUSTER, cluster);
+ }
+
+ public long getArchiveCheckIntervalSeconds() {
+ return configuration.getPropertyLong(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_INTERVAL).orElse(0L);
+ }
+
+ public void setArchiveCheckIntervalSeconds(long seconds) {
+ configuration.setProperty(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_INTERVAL, seconds);
+ }
+
+ public long getArchiveLifetimeSeconds() {
+ return configuration.getPropertyLong(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_LIMIT).orElse(0L);
+ }
+
+ public void setArchiveLimit(long seconds) {
+ configuration.setProperty(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_LIMIT, seconds);
+ }
+
+ @Override
+ public String getSectionName() {
+ return SECTION_MARKER_ES;
+ }
+
+ @Override
+ public void defaults() {
+ // Add default if not available
+ configuration.setPropertyIfNotAvailable(SECTION_MARKER_ES, PROPERTY_KEY_DBHOSTS, defaultHostinfo);
+ configuration.setPropertyIfNotAvailable(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_LIMIT,
+ DEFAULT_ARCHIVE_LIMIT_SEC);
+ configuration.setPropertyIfNotAvailable(SECTION_MARKER_ES, PROPERTY_KEY_CLUSTER, DEFAULT_VALUE_CLUSTER);
+ configuration.setPropertyIfNotAvailable(SECTION_MARKER_ES, PROPERTY_KEY_ARCHIVE_INTERVAL,
+ DEFAULT_ARCHIVE_INTERVAL_SEC);
+ configuration.setPropertyIfNotAvailable(SECTION_MARKER_ES, PROPERTY_KEY_NODE, DEFAULT_KEY_NODE);
+ }
+
+ /** @TODO Shift to own class **/
+ private static String printHosts(HostInfo[] h) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < h.length; i++) {
+ sb.append(h[i].toUrl());
+ if (i != h.length - 1) {
+ sb.append(",");
+ }
+ }
+ return sb.toString();
+ }
+
+ /** @TODO Shift to own class **/
+ private static HostInfo[] parseHosts(String string) {
+ List<HostInfo> infos = new ArrayList<HostInfo>();
+ String[] list = string.split(",");
+ if (list.length > 0) {
+ for (String item : list) {
+ try {
+ URL url = new URL(item);
+ infos.add(new HostInfo(url.getHost(), url.getPort(), Protocol.getValueOf(url.getProtocol())));
+ } catch (MalformedURLException e) {
+ LOG.warn("problem parsing url {} : {}", item, e.getMessage());
+ }
+ }
+ }
+ HostInfo[] a = new HostInfo[infos.size()];
+ return infos.toArray(a);
+ }
+
+ @Override
+ public String toString() {
+ return "EsConfig [getNode()=" + getNode() + ", getHosts()=" + Arrays.toString(getHosts()) + ", getCluster()="
+ + getCluster() + ", getArchiveCheckIntervalSeconds()=" + getArchiveCheckIntervalSeconds()
+ + ", getArchiveLifetimeSeconds()=" + getArchiveLifetimeSeconds() + ", getSectionName()="
+ + getSectionName() + "]";
+ }
+
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner.java
new file mode 100644
index 000000000..1ac19ff34
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.yangtools;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YangToolsCloner {
+
+ private static YangToolsMapper yangtoolsMapper = new YangToolsMapper();
+ private static final Logger LOG = LoggerFactory.getLogger(YangToolsCloner.class);
+ public static final int ACCESSOR_FIELD = 0;
+ public static final int ACCESSOR_METHOD = 1;
+
+
+ private final int accessor;
+
+ private YangToolsCloner(int ac) {
+ this.accessor = ac;
+ }
+ public static YangToolsCloner instance() {
+ return instance(ACCESSOR_METHOD);
+ }
+ public static YangToolsCloner instance(int ac) {
+ return new YangToolsCloner(ac);
+ }
+ /**
+ *
+ * @param source source object
+ * @param clazz Class of return object
+ * @return list of cloned object
+ * @return
+ */
+ public <S extends DataObject, T extends DataObject> List<T> cloneList(List<S> source, Class<T> clazz) {
+ return cloneList(source, clazz, null);
+ }
+
+ /**
+ *
+ * @param source source object
+ * @param clazz Class of return object
+ * @attrList filter for attribute Names to clone
+ * @return list of cloned object
+ */
+ public <S extends DataObject, T extends DataObject> List<T> cloneList(List<S> source, Class<T> clazz,
+ @Nullable List<String> attrList) {
+ if (source == null) {
+ return null;
+ }
+ List<T> list = new ArrayList<T>();
+ for (S s : source) {
+ list.add(clone(s, clazz, attrList));
+ }
+ return list;
+ }
+
+ /**
+ *
+ * @param source source object
+ * @param clazz Class of return object
+ * @return cloned object
+ */
+ public <S , T extends DataObject> T clone(S source, Class<T> clazz) {
+ return clone(source, clazz, null);
+ }
+ /**
+ *
+ * @param source source object
+ * @param clazz Class of return object
+ * @attrList if empty copy all else list of attribute Names to clone
+ * @return cloned object
+ */
+ public <S, T extends DataObject> T clone(S source, Class<T> clazz,
+ @Nullable List<String> attrList) {
+ if (source == null) {
+ return (T)null;
+ }
+ Field[] attributeFields;
+ Field sourceField;
+ Method m;
+ Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
+ T object = builder.build();
+ attributeFields = object.getClass().getDeclaredFields();
+ for (Field attributeField : attributeFields) {
+ // check if attr is in inclusion list
+ if (attrList != null && !attrList.contains(attributeField.getName())) {
+ continue;
+ }
+ // ignore QNAME
+ if (attributeField.getName().equals("QNAME")) {
+ continue;
+ }
+
+ attributeField.setAccessible(true);
+ try {
+ if(accessor==ACCESSOR_FIELD) {
+ sourceField = source.getClass().getDeclaredField(attributeField.getName());
+ sourceField.setAccessible(true);
+ if (attributeField.getType().equals(String.class) && !sourceField.getType().equals(String.class)) {
+ attributeField.set(object, String.valueOf(sourceField.get(source)));
+ } else {
+ attributeField.set(object, sourceField.get(source));
+ }
+ }
+ else if(accessor==ACCESSOR_METHOD) {
+ String getter = getter(attributeField.getName());
+ System.out.println("getter="+getter);
+ m = source.getClass().getDeclaredMethod(getter);
+ m.setAccessible(true);
+ if (attributeField.getType().equals(String.class) && !m.getReturnType().equals(String.class)) {
+ attributeField.set(object, String.valueOf(m.invoke(source)));
+ } else {
+ attributeField.set(object, m.invoke(source));
+ }
+ }
+
+ } catch (NoSuchMethodException | NoSuchFieldException e) {
+ // Convert to run-time exception
+ String msg = "no such field " + attributeField.getName() + " in class " + source.getClass().getName();
+ LOG.debug(msg);
+ // throw new IllegalArgumentException(msg);
+ } catch (IllegalAccessException|SecurityException e) {
+ LOG.debug("Access problem " + attributeField.getName(), e);
+ } catch (IllegalArgumentException e) {
+ LOG.debug("argument problem " + attributeField.getName(), e);
+ } catch (InvocationTargetException e) {
+ LOG.debug("invocation problem " + attributeField.getName(), e);
+ }
+ }
+
+ return object;
+ }
+
+ private static String getter(String name) {
+ return String.format("%s%s%s","get",name.substring(1, 2).toUpperCase(),name.substring(2));
+ }
+ public <S extends DataObject, T extends DataObject,B extends Builder<T>> B cloneToBuilder(S source, B builder){
+ return cloneToBuilder(source, builder,null);
+ }
+ public <S extends DataObject, T extends DataObject,B extends Builder<T>> B cloneToBuilder(S source, B builder,
+ @Nullable List<String> attrList) {
+ Field[] attributeFields;
+ Field sourceField;
+ Method m;
+ attributeFields = builder.getClass().getDeclaredFields();
+ for (Field attributeField : attributeFields) {
+ // check if attr is in inclusion list
+ if (attrList != null && !attrList.contains(attributeField.getName())) {
+ continue;
+ }
+ // ignore QNAME
+ if (attributeField.getName().equals("QNAME")) {
+ continue;
+ }
+
+ attributeField.setAccessible(true);
+ try {
+ if(accessor==ACCESSOR_FIELD) {
+ sourceField = source.getClass().getDeclaredField(attributeField.getName());
+ sourceField.setAccessible(true);
+ if (attributeField.getType().equals(String.class) && !sourceField.getType().equals(String.class)) {
+ attributeField.set(builder, String.valueOf(sourceField.get(source)));
+ } else {
+ attributeField.set(builder, sourceField.get(source));
+ }
+ }
+ else if(accessor==ACCESSOR_METHOD) {
+ m = source.getClass().getDeclaredMethod(getter(attributeField.getName()));
+ m.setAccessible(true);
+ if (attributeField.getType().equals(String.class) && !m.getReturnType().equals(String.class)) {
+ attributeField.set(builder, String.valueOf(m.invoke(source)));
+ } else {
+ attributeField.set(builder, m.invoke(source));
+ }
+ }
+
+ } catch (NoSuchMethodException | NoSuchFieldException e) {
+ // Convert to run-time exception
+ String msg = "no such field " + attributeField.getName() + " in class " + source.getClass().getName();
+ LOG.debug(msg);
+ // throw new IllegalArgumentException(msg);
+ } catch (IllegalAccessException|SecurityException e) {
+ LOG.debug("Access problem " + attributeField.getName(), e);
+ } catch (IllegalArgumentException e) {
+ LOG.debug("argument problem " + attributeField.getName(), e);
+ } catch (InvocationTargetException e) {
+ LOG.debug("invocation problem " + attributeField.getName(), e);
+ }
+ }
+ return builder;
+ }
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner2.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner2.java
new file mode 100644
index 000000000..37d7aa4ad
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsCloner2.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.yangtools;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YangToolsCloner2 {
+
+ private static final Logger LOG = LoggerFactory.getLogger(YangToolsCloner2.class);
+
+ public enum Accessor {
+ ACCESSOR_FIELD,
+ ACCESSOR_METHOD;
+ }
+
+ private Accessor accessor;
+
+ public YangToolsCloner2() {
+ LOG.info("Provide new {}",this.getClass().getName());
+ this.accessor = Accessor.ACCESSOR_METHOD;
+ }
+
+ YangToolsCloner2 setAcessor(Accessor accessor) {
+ this.accessor = accessor;
+ return this;
+ }
+
+ Accessor getAccessor() {
+ return accessor;
+ }
+
+ public interface Builder<T> {
+ T build();
+ }
+
+ /**
+ *
+ * @param source source object
+ * @param clazz Class of return object
+ * @attrList filter for attribute Names to clone
+ * @return list of cloned object
+ * @throws Exception
+ */
+ public <S, T> List<T> cloneList(List<S> source, Builder<T> builder, String ... attrList) throws Exception {
+ if (source == null) {
+ return null;
+ }
+ List<T> list = new ArrayList<T>();
+ for (S s : source) {
+ list.add(copyAttributes(s, builder.build(), attrList));
+ }
+ return list;
+ }
+
+ /**
+ * Copy attributes from source to destination object.
+ * Copy the references.
+ * @param source source object
+ * @param clazz Class of return object
+ * @attrList attribute Names NOT to clone.
+ * @return cloned object
+ * @throws Exception
+ */
+ @SuppressWarnings("null")
+ public @Nullable <S, T> T copyAttributes(S source, T destination, String ... attributeArray) throws Exception {
+
+ LOG.debug("copyAttributes source.class {} destination.class {} attributes {}", source, destination, attributeArray.length);
+
+ if (destination == null || source == null)
+ return null;
+
+ List<String> attributeList = Arrays.asList(attributeArray);
+ LOG.debug("copyAttributes 2 attributes {}", attributeList);
+
+ Field[] destinationAttributeFields = source.getClass().getDeclaredFields();
+ String destinationName;
+ Class<?> destinationType;
+ for (Field destinationAttributeField : destinationAttributeFields) {
+ destinationName = destinationAttributeField.getName();
+ destinationType = destinationAttributeField.getType();
+ LOG.debug("Field: {}", destinationName);
+ // check if attr is in exclusion list
+ if (attributeList.contains(destinationName)) {
+ continue;
+ }
+ // ignore QNAME
+ if (destinationName.equals("QNAME")) {
+ continue;
+ }
+
+ destinationAttributeField.setAccessible(true);
+ Object sourceData = null;
+ Class<?> sourceType = null;
+ Class<?> sourceListType = null;
+ try {
+ if (accessor == Accessor.ACCESSOR_FIELD) {
+ Field sourceField;
+ sourceField = source.getClass().getDeclaredField(destinationName);
+ sourceField.setAccessible(true);
+ sourceType = sourceField.getType();
+ sourceData = sourceField.get(source);
+ sourceListType = getListClass(sourceType, sourceData);
+
+ } else if (accessor == Accessor.ACCESSOR_METHOD) {
+ Method sourceMethod;
+ sourceMethod = source.getClass().getDeclaredMethod(getter(destinationName));
+ sourceMethod.setAccessible(true);
+ sourceType = sourceMethod.getReturnType();
+ sourceData = sourceMethod.invoke(source);
+ sourceListType = getListClass(sourceType, sourceData);
+ }
+ LOG.info("Handle {} {} {}", destinationName, destinationType, sourceType);
+ if (destinationType == sourceType) {
+ destinationAttributeField.set(destination, sourceData);
+ } else {
+ throw new Exception(
+ "Problem to copy attribute " + destinationName
+ +" Sourceclass:" +sourceType
+ +" Destinationclass:" + destinationType
+ +" Method:"+accessor.name());
+ }
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException
+ | NoSuchMethodException | InvocationTargetException e) {
+ throw e;
+ }
+ }
+ return destination;
+
+ }
+
+ private static String getter(String name) {
+ if (name == null || name.length() == 0) {
+ return null;
+ } else if (name.length() == 1) {
+ return String.format("%s%s", "get", name.substring(1, 2).toUpperCase());
+ } else { // >= 2
+ return String.format("%s%s%s", "get", name.substring(1, 2).toUpperCase(), name.substring(2));
+ }
+ }
+
+ private static Class<?> getListClass(Class<?> sourceType, Object sourceData) {
+ if (sourceData != null && sourceType.equals(List.class)) {
+ List<Object> sourceDataList = (List<Object>)sourceData;
+ if (sourceDataList.size() > 0) {
+ LOG.info("Is list with type"+sourceDataList.get(0).getClass().getName());
+ } else {
+ LOG.info("Is empty list");
+ }
+ }
+ return(sourceType);
+ }
+
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper.java
new file mode 100644
index 000000000..8306cb7d4
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.yangtools;
+
+import java.io.IOException;
+import javax.annotation.Nullable;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder.Value;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+/**
+ * YangToolsMapper is a specific Jackson mapper configuration for opendaylight yangtools serialization or deserialization of DataObject to/from JSON
+ * TODO ChoiceIn and Credentials deserialization only for LoginPasswordBuilder
+ */
+public class YangToolsMapper extends ObjectMapper {
+
+ private final Logger LOG = LoggerFactory.getLogger(YangToolsMapper.class);
+ private static final long serialVersionUID = 1L;
+ private static BundleContext context;
+
+ public YangToolsMapper() {
+ super();
+ configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
+ setSerializationInclusion(Include.NON_NULL);
+ setAnnotationIntrospector(new YangToolsBuilderAnnotationIntrospector());
+ SimpleModule dateAndTimeSerializerModule = new SimpleModule();
+ dateAndTimeSerializerModule.addSerializer(DateAndTime.class,new CustomDateAndTimeSerializer());
+ registerModule(dateAndTimeSerializerModule );
+ Bundle bundle = FrameworkUtil.getBundle(YangToolsMapper.class);
+ context = bundle != null ? bundle.getBundleContext() : null;
+ }
+
+ @Override
+ public String writeValueAsString(Object value) throws JsonProcessingException {
+ // TODO Auto-generated method stub
+ return super.writeValueAsString(value);
+ }
+ /**
+ * Get Builder object for yang tools interface.
+ * @param <T> yang-tools base datatype
+ * @param clazz class with interface.
+ * @return builder for interface or null if not existing
+ */
+ @SuppressWarnings("unchecked")
+ public @Nullable <T extends DataObject> Builder<T> getBuilder(Class<T> clazz) {
+ String builder = clazz.getName() + "Builder";
+ try {
+ Class<?> clazzBuilder = findClass(builder);
+ return (Builder<T>) clazzBuilder.newInstance();
+ } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+ LOG.debug("Problem ", e);
+ return null;
+ }
+ }
+
+ /**
+ * Callback for handling mapping failures.
+ * @return
+ */
+ public int getMappingFailures() {
+ return 0;
+ }
+
+ /**
+ * Provide mapping of string to attribute names, generated by yang-tools.
+ * "netconf-id" converted to "_netconfId"
+ * @param name with attribute name, not null or empty
+ * @return converted string or null if name was empty or null
+ */
+ public @Nullable static String toCamelCaseAttributeName(final String name) {
+ if (name == null || name.isEmpty())
+ return null;
+
+ final StringBuilder ret = new StringBuilder(name.length());
+ if (!name.startsWith("_"))
+ ret.append('_');
+ int start = 0;
+ for (final String word : name.split("-")) {
+ if (!word.isEmpty()) {
+ if (start++ == 0) {
+ ret.append(Character.toLowerCase(word.charAt(0)));
+ } else {
+ ret.append(Character.toUpperCase(word.charAt(0)));
+ }
+ ret.append(word.substring(1));
+ }
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Adapted Builder callbacks
+ */
+ private static class YangToolsBuilderAnnotationIntrospector extends JacksonAnnotationIntrospector {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Class<?> findPOJOBuilder(AnnotatedClass ac) {
+ try {
+ String builder = null;
+ if (ac.getRawType().equals(Credentials.class)) {
+ builder = "org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder";
+ //System.out.println(DataContainer.class.isAssignableFrom(ac.getRawType()));
+ //System.out.println(ChoiceIn.class.isAssignableFrom(ac.getRawType()));
+
+ }
+ else if(ac.getRawType().equals(DateAndTime.class)) {
+ builder = DateAndTimeBuilder.class.getName();
+ }
+
+ else {
+ if (ac.getRawType().isInterface()) {
+ builder = ac.getName()+"Builder";
+ }
+ }
+ if (builder != null) {
+ //System.out.println("XX1: "+ac.getRawType());
+ //System.out.println("XX2: "+builder);
+ //Class<?> innerBuilder = Class.forName(builder);
+ Class<?> innerBuilder = findClass(builder);
+ //System.out.println("Builder found: "+ innerBuilder);
+ return innerBuilder;
+ }
+ } catch( ClassNotFoundException e ) {
+ // No problem .. try next
+ }
+ return super.findPOJOBuilder(ac);
+ }
+
+ @Override
+ public Value findPOJOBuilderConfig(AnnotatedClass ac) {
+ if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
+ return super.findPOJOBuilderConfig(ac);
+ }
+ return new JsonPOJOBuilder.Value("build", "set");
+ }
+ }
+
+ private static Class<?> findClass(String name) throws ClassNotFoundException {
+ // Try to find in other bundles
+ if (context != null) {
+ //OSGi environment
+ for (Bundle b : context.getBundles()) {
+ try {
+ return b.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ // No problem, this bundle doesn't have the class
+ }
+ }
+ throw new ClassNotFoundException("Can not find Class in OSGi context.");
+ } else {
+ return Class.forName(name);
+ }
+ // not found in any bundle
+ }
+ public static class DateAndTimeBuilder{
+
+ private final String _value;
+
+ public DateAndTimeBuilder(String v) {
+ this._value= v;
+ }
+
+ public DateAndTime build() {
+ return new DateAndTime(_value);
+ }
+
+ }
+ public static class CustomDateAndTimeSerializer extends StdSerializer<DateAndTime>{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public CustomDateAndTimeSerializer() {
+ this(null);
+ }
+ protected CustomDateAndTimeSerializer(Class<DateAndTime> t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(DateAndTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(value.getValue());
+ }
+
+ }
+}
diff --git a/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper2.java b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper2.java
new file mode 100644
index 000000000..0ee76b074
--- /dev/null
+++ b/sdnr/wt/data-provider/database/src/main/java/org/onap/ccsdk/features/sdnr/wt/yangtools/YangToolsMapper2.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ ******************************************************************************/
+package org.onap.ccsdk.features.sdnr.wt.yangtools;
+
+import java.io.IOException;
+import javax.annotation.Nullable;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder.Value;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+/**
+ * YangToolsMapper is a specific Jackson mapper configuration for opendaylight yangtools serialization or deserialization of DataObject to/from JSON
+ * TODO ChoiceIn and Credentials deserialization only for LoginPasswordBuilder
+ */
+public class YangToolsMapper2<T extends DataObject> extends ObjectMapper {
+
+ private final Logger LOG = LoggerFactory.getLogger(YangToolsMapper2.class);
+ private static final long serialVersionUID = 1L;
+ private static String ENTITY = "Entity";
+ private static String BUILDER = "Builder";
+
+ private @Nullable Class<T> clazz;
+ private @Nullable Class<? extends Builder<? extends T>> builderClazz;
+
+ private BundleContext context;
+
+ public <X extends T, B extends Builder<X>> YangToolsMapper2(Class<T> clazz, Class<B> builderClazz) throws ClassNotFoundException {
+ super();
+ configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
+ setSerializationInclusion(Include.NON_NULL);
+ setAnnotationIntrospector(new YangToolsBuilderAnnotationIntrospector());
+ SimpleModule dateAndTimeSerializerModule = new SimpleModule();
+ dateAndTimeSerializerModule.addSerializer(DateAndTime.class, new CustomDateAndTimeSerializer());
+ registerModule(dateAndTimeSerializerModule );
+ Bundle bundle = FrameworkUtil.getBundle(YangToolsMapper2.class);
+
+ this.clazz = clazz;
+ this.builderClazz = builderClazz != null ? builderClazz : getBuilderClass(getBuilderClassName(clazz)) ;
+ context = bundle != null ? bundle.getBundleContext() : null;
+ }
+
+ public YangToolsMapper2() throws ClassNotFoundException {
+ this(null, null);
+ }
+
+
+ @Override
+ public String writeValueAsString(Object value) throws JsonProcessingException {
+ return super.writeValueAsString(value);
+ }
+ /**
+ * Get Builder object for yang tools interface.
+ * @param <T> yang-tools base datatype
+ * @param clazz class with interface.
+ * @return builder for interface or null if not existing
+ */
+ @SuppressWarnings("unchecked")
+ public @Nullable <T extends DataObject> Builder<T> getBuilder(Class<T> clazz) {
+ try {
+ //Class<?> clazzBuilder = getBuilderClass(getBuilderClassName(clazz));
+ return (Builder<T>) builderClazz.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ LOG.debug("Problem ", e);
+ return null;
+ }
+ }
+
+ /**
+ * Callback for handling mapping failures.
+ * @return
+ */
+ public int getMappingFailures() {
+ return 0;
+ }
+
+ /**
+ * Provide mapping of string to attribute names, generated by yang-tools.
+ * "netconf-id" converted to "_netconfId"
+ * @param name with attribute name, not null or empty
+ * @return converted string or null if name was empty or null
+ */
+ public @Nullable static String toCamelCaseAttributeName(final String name) {
+ if (name == null || name.isEmpty())
+ return null;
+
+ final StringBuilder ret = new StringBuilder(name.length());
+ if (!name.startsWith("_"))
+ ret.append('_');
+ int start = 0;
+ for (final String word : name.split("-")) {
+ if (!word.isEmpty()) {
+ if (start++ == 0) {
+ ret.append(Character.toLowerCase(word.charAt(0)));
+ } else {
+ ret.append(Character.toUpperCase(word.charAt(0)));
+ }
+ ret.append(word.substring(1));
+ }
+ }
+ return ret.toString();
+ }
+
+ /** Verify if builder is available
+ * @throws ClassNotFoundException **/
+ public Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
+ return getBuilderClass(getBuilderClassName(clazz));
+ }
+
+ // --- Private functions
+
+ /**
+ * Create name of builder class
+ * @param <T>
+ * @param clazz
+ * @return builders class name
+ * @throws ClassNotFoundException
+ */
+ private static String getBuilderClassName(Class<?> clazz) {
+ return clazz.getName() + BUILDER;
+// String clazzName = clazz.getName();
+// if (clazzName.endsWith(ENTITY)) {
+// return clazzName.replace(ENTITY, BUILDER);
+// } else {
+// return clazzName + BUILDER;
+// }
+ }
+
+ /**
+ * Search builder in context
+ * @param name
+ * @return
+ * @throws ClassNotFoundException
+ */
+ @SuppressWarnings("unchecked")
+ private <X extends T, B extends Builder<X>> Class<B> getBuilderClass(String name) throws ClassNotFoundException {
+ // Try to find in other bundles
+ if (context != null) {
+ //OSGi environment
+ for (Bundle b : context.getBundles()) {
+ try {
+ return (Class<B>) b.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ // No problem, this bundle doesn't have the class
+ }
+ }
+ throw new ClassNotFoundException("Can not find Class in OSGi context.");
+ } else {
+ return (Class<B>) Class.forName(name);
+ }
+ // not found in any bundle
+ }
+
+ // --- Classes
+
+ /**
+ * Adapted Builder callbacks
+ */
+ private class YangToolsBuilderAnnotationIntrospector extends JacksonAnnotationIntrospector {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Class<?> findPOJOBuilder(AnnotatedClass ac) {
+
+ if (ac.getRawType().equals(Credentials.class)) {
+ return org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder.class;
+
+ } else if (ac.getRawType().equals(DateAndTime.class)) {
+ return DateAndTimeBuilder.class;
+
+ } else if (ac.getRawType().equals(clazz)) {
+ return builderClazz;
+ }
+
+ if (ac.getRawType().isInterface()) {
+ String builder = getBuilderClassName(ac.getRawType());
+ try {
+ Class<?> innerBuilder = getBuilderClass(builder);
+ return innerBuilder;
+ } catch (ClassNotFoundException e) {
+ // No problem .. try next
+ }
+ }
+ return super.findPOJOBuilder(ac);
+ }
+
+ @Override
+ public Value findPOJOBuilderConfig(AnnotatedClass ac) {
+ if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
+ return super.findPOJOBuilderConfig(ac);
+ }
+ return new JsonPOJOBuilder.Value("build", "set");
+ }
+ }
+
+ public static class DateAndTimeBuilder{
+
+ private final String _value;
+
+ public DateAndTimeBuilder(String v) {
+ this._value= v;
+ }
+
+ public DateAndTime build() {
+ return new DateAndTime(_value);
+ }
+
+ }
+ public static class CustomDateAndTimeSerializer extends StdSerializer<@NonNull DateAndTime>{
+
+ private static final long serialVersionUID = 1L;
+
+ public CustomDateAndTimeSerializer() {
+ this(null);
+ }
+ protected CustomDateAndTimeSerializer(Class<DateAndTime> t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(DateAndTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(value.getValue());
+ }
+
+ }
+}