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.dataprovider.database;
20 import java.io.IOException;
21 import java.lang.reflect.Field;
22 import java.util.List;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
27 import org.onap.ccsdk.features.sdnr.wt.common.database.DatabaseClient;
28 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
29 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
30 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
31 import org.onap.ccsdk.features.sdnr.wt.dataprovider.yangtools.YangToolsMapper;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.Entity;
33 import org.opendaylight.yangtools.concepts.Builder;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import com.fasterxml.jackson.core.JsonProcessingException;
41 * Class to rw yang-tool generated objects into elasticsearch database. For "ES _id" exchange the esIdAddAtributteName is used.
42 * This attribute mast be of type String and contains for read and write operations the object id.
43 * The function can be used without id handling.
44 * If id handling is required the parameter needs to be specified by class definition in yang and setting the name by using setAttributeName()
46 * @param <T> Yang tools generated class object.
48 public class EsDataObjectReaderWriter<T extends DataObject> {
50 private final Logger LOG = LoggerFactory.getLogger(EsDataObjectReaderWriter.class);
52 /** Typename for elastic search data schema **/
53 private String dataTypeName;
55 /** Elasticsearch Database client to be used **/
56 private DatabaseClient db;
58 /** Mapper with configuration to use opendaylight yang-tools builder pattern for object creation **/
59 private YangToolsMapper yangtoolsMapper;
61 /** Class of T as attribute to allow JSON to Class object mapping **/
62 private Class<T> clazz;
64 /** Field is used to write id. If null no id handling **/
65 private @Nullable Field field;
67 /** Attribute that is used as id field for the database object **/
68 private @Nullable String esIdAddAtributteName;
70 /** Interface to be used for write operations. Rule for write: T extends S and **/
71 private Class<? extends DataObject> writeInterfaceClazz; // == "S"
74 * Elasticsearch database read and write for specific class, defined by opendaylight yang-tools.
76 * @param db Database access client
77 * @param dataTypeName typename in database schema
78 * @param clazz class of type to be handled
79 * @throws ClassNotFoundException
81 public EsDataObjectReaderWriter(DatabaseClient db, Entity dataTypeName, Class<T> clazz) throws ClassNotFoundException {
82 this(db, dataTypeName.getName(), clazz);
84 public EsDataObjectReaderWriter(DatabaseClient db, String dataTypeName, Class<T> clazz) throws ClassNotFoundException {
85 LOG.info("Create {} for datatype {} class {}", this.getClass().getName(), dataTypeName, clazz.getName());
87 this.esIdAddAtributteName = null;
89 this.writeInterfaceClazz = clazz;
91 this.dataTypeName = dataTypeName;
92 this.yangtoolsMapper = new YangToolsMapper();
93 //this.yangtoolsMapper.assertBuilderClass(clazz);
96 // if (! db.isExistsIndex(dataTypeName)) {
97 // throw new IllegalArgumentException("Index "+dataTypeName+" not existing.");
101 public String getDataTypeName() {
104 public Class<T> getClazz() {
108 * Simlar to {@link #setEsIdAttributeName()}, but adapts the parameter to yangtools attribute naming schema
109 * @param esIdAttributeName is converted to UnderscoreCamelCase
110 * @return this for further operations.
112 public EsDataObjectReaderWriter<T> setEsIdAttributeNameCamelized(String esIdAttributeName) {
113 return setEsIdAttributeName(YangToolsMapper.toCamelCaseAttributeName(esIdAttributeName));
117 * Attribute name of class that is containing the object id
118 * @param esIdAttributeName of the implementation class for the yangtools interface.
119 * Expected attribute name format is CamelCase with leading underline. @
120 * @return this for further operations.
121 * @throws SecurityException if no access or IllegalArgumentException if wrong type or no attribute with this name.
123 public EsDataObjectReaderWriter<T> setEsIdAttributeName(String esIdAttributeName) {
124 LOG.debug("Set attribute '{}'", esIdAttributeName);
125 this.esIdAddAtributteName = null; // Reset status
128 Field attributeField;
130 Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
131 T object = builder.build();
132 attributeField = object.getClass().getDeclaredField(esIdAttributeName);
133 if (attributeField.getType().equals(String.class)) {
134 attributeField.setAccessible(true);
135 this.esIdAddAtributteName = esIdAttributeName; //Set new status if everything OK
136 this.field = attributeField;
138 String msg = "Wrong field type " + attributeField.getType().getName() + " of " + esIdAttributeName;
140 throw new IllegalArgumentException(msg);
142 } catch (NoSuchFieldException e) {
143 // Convert to run-time exception
144 String msg = "NoSuchFieldException for '" + esIdAttributeName + "' in class " + clazz.getName();
146 throw new IllegalArgumentException(msg);
147 } catch (SecurityException e) {
148 LOG.debug("Access problem "+esIdAttributeName,e);
155 * Specify subclass of T for write operations.
156 * @param writeInterfaceClazz
158 public EsDataObjectReaderWriter<T> setWriteInterface( @Nonnull Class<? extends DataObject> writeInterfaceClazz ) {
159 LOG.debug("Set write interface to {}", writeInterfaceClazz);
160 if (writeInterfaceClazz == null)
161 throw new IllegalArgumentException("Null not allowed here.");
163 this.writeInterfaceClazz = writeInterfaceClazz;
168 * Write child object to database with specific id
170 * @param @Nullable esId use the id or if null generate unique id
171 * @return String with id or null
173 public @Nullable <S extends DataObject> String write(S object, @Nullable String esId) {
174 if (writeInterfaceClazz.isInstance(object)) {
176 String json = yangtoolsMapper.writeValueAsString(object);
177 return db.doWriteRaw(dataTypeName, esId, json);
178 } catch (JsonProcessingException e) {
179 LOG.error("Write problem: ", e);
182 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
183 writeInterfaceClazz.getName());
188 * Update partial child object to database with match/term query
191 * @return String with esId or null
193 public @Nullable <S extends DataObject> String update(S object, QueryBuilder query) {
194 if (writeInterfaceClazz.isInstance(object)) {
196 String json = yangtoolsMapper.writeValueAsString(object);
197 return db.doUpdate(this.dataTypeName,json,query);
198 } catch (JsonProcessingException e) {
199 LOG.error("Update problem: ", e);
202 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
203 writeInterfaceClazz.getName());
208 * Write/ update partial child object to database with specific id Write if not
209 * exists, else update
212 * @return String with esId or null
214 public @Nullable <S extends DataObject> String update(S object, String esId) {
215 return this.update(object, esId,null);
217 public @Nullable <S extends DataObject> String update(S object, String esId,List<String> onylForInsert) {
218 if (writeInterfaceClazz.isInstance(object)) {
220 String json = yangtoolsMapper.writeValueAsString(object);
221 return db.doUpdateOrCreate(dataTypeName, esId, json,onylForInsert);
222 } catch (JsonProcessingException e) {
223 LOG.error("Update problem: ", e);
226 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
227 writeInterfaceClazz.getName());
233 * Read object from database, by using the id field
237 public @Nullable T read(String esId) {
238 @Nullable T res = (T)null;
240 String json = db.doReadJsonData(dataTypeName, esId);
242 res = yangtoolsMapper.readValue(json.getBytes(), clazz);
243 } catch (IOException e) {
244 LOG.error("Problem: ", e);
252 * @param esId to identify the object.
255 public boolean remove(String esId) {
256 return db.doRemove(this.dataTypeName, esId);
259 public int remove(QueryBuilder query) {
260 return this.db.doRemove(this.dataTypeName, query);
263 * Get all elements of related type
264 * @return all Elements
266 public SearchResult<T> doReadAll() {
267 return doReadAll(null);
269 public SearchResult<T> doReadAll(QueryBuilder query) {
270 return this.doReadAll(query,false);
273 * Read all existing objects of a type
274 * @param query for the elements
275 * @return the list of all objects
278 public SearchResult<T> doReadAll(QueryBuilder query, boolean ignoreException) {
280 SearchResult<T> res = new SearchResult<T>();
281 int idx = 0; //Idx for getAll
282 int iterateLength = 100; //Step width for iterate
284 SearchResult<SearchHit> result;
285 List<SearchHit> hits;
288 LOG.debug("read data in {} with query {}",dataTypeName,query.toJSON());
289 result=db.doReadByQueryJsonData( dataTypeName, query,ignoreException);
292 result = db.doReadAllJsonData(dataTypeName,ignoreException);
294 hits=result.getHits();
295 LOG.debug("Read: {} elements: {} Failures: {}",dataTypeName,hits.size(), yangtoolsMapper.getMappingFailures());
298 idx += result.getHits().size();
299 for (SearchHit hit : hits) {
300 object = getT(hit.getSourceAsString());
301 LOG.debug("Mapp Object: {}\nSource: '{}'\nResult: '{}'\n Failures: {}", hit.getId(),
302 hit.getSourceAsString(), object, yangtoolsMapper.getMappingFailures());
303 if (object != null) {
304 setEsId(object, hit.getId());
307 LOG.warn("Mapp result null Object: {}\n Source: '{}'\n : '", hit.getId(), hit.getSourceAsString());
311 } while (hits.size() == iterateLength); // Do it until end indicated, because less hits than iterateLength
313 res.setTotal(result.getTotal());
317 /* ---------------------------------------------
321 private void setEsId(T object, String esId) {
324 field.set(object, esId);
325 } catch (IllegalArgumentException | IllegalAccessException e) {
326 LOG.debug("Field set problem.", e); }
330 private @Nullable T getT(String jsonString) {
332 return yangtoolsMapper.readValue( jsonString, clazz );
333 } catch (IOException e) {
334 LOG.info("Mapping problem", e);