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.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.eclipse.jdt.annotation.NonNull;
28 import org.onap.ccsdk.features.sdnr.wt.common.database.DatabaseClient;
29 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
30 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
31 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
32 import org.onap.ccsdk.features.sdnr.wt.yangtools.YangToolsMapper;
33 import org.onap.ccsdk.features.sdnr.wt.yangtools.YangToolsMapper2;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.Entity;
35 import org.opendaylight.yangtools.concepts.Builder;
36 import org.opendaylight.yangtools.yang.binding.DataObject;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.fasterxml.jackson.core.JsonProcessingException;
43 * Class to rw yang-tool generated objects into elasticsearch database. For "ES _id" exchange the esIdAddAtributteName is used.
44 * This attribute mast be of type String and contains for read and write operations the object id.
45 * The function can be used without id handling.
46 * If id handling is required the parameter needs to be specified by class definition in yang and setting the name by using setAttributeName()
48 * Due to using Jackson base interfaces the org.eclipse.jdt.annotation.NonNull needs to be used here to get rid of warnings
50 * @param <T> Yang tools generated class object.
52 public class EsDataObjectReaderWriter2<T extends DataObject> {
54 private final Logger LOG = LoggerFactory.getLogger(EsDataObjectReaderWriter2.class);
56 /** Typename for elastic search data schema **/
57 private String dataTypeName;
59 /** Elasticsearch Database client to be used **/
60 private DatabaseClient db;
62 /** Mapper with configuration to use opendaylight yang-tools builder pattern for object creation **/
63 private YangToolsMapper2<T> yangtoolsMapper;
65 /** Class of T as attribute to allow JSON to Class object mapping **/
66 private Class<T> clazz;
68 /** Field is used to write id. If null no id handling **/
69 private @Nullable Field field;
71 /** Attribute that is used as id field for the database object **/
72 private @Nullable String esIdAddAtributteName;
74 /** Interface to be used for write operations. Rule for write: T extends S and **/
75 private Class<? extends DataObject> writeInterfaceClazz; // == "S"
78 * Elasticsearch database read and write for specific class, defined by opendaylight yang-tools.
80 * @param db Database access client
81 * @param dataTypeName typename in database schema
82 * @param clazz class of type to be handled
83 * @throws ClassNotFoundException
85 public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, Entity dataTypeName, @Nonnull Class<T> clazz, @Nullable Class<B> builderClazz) throws ClassNotFoundException {
86 this(db, dataTypeName.getName(), clazz, builderClazz);
88 public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, Entity dataTypeName, @Nonnull Class<T> clazz) throws ClassNotFoundException {
89 this(db, dataTypeName.getName(), clazz, null);
91 public <X extends T, @NonNull B extends Builder<X>> EsDataObjectReaderWriter2(DatabaseClient db, String dataTypeName, @Nonnull Class<T> clazz, @Nullable Class<B> builderClazz) throws ClassNotFoundException {
92 LOG.info("Create {} for datatype {} class {}", this.getClass().getName(), dataTypeName, clazz.getName());
94 this.esIdAddAtributteName = null;
96 this.writeInterfaceClazz = clazz;
98 this.dataTypeName = dataTypeName;
99 this.yangtoolsMapper = new YangToolsMapper2<>(clazz, builderClazz);
104 * Simlar to {@link #setEsIdAttributeName()}, but adapts the parameter to yangtools attribute naming schema
105 * @param esIdAttributeName is converted to UnderscoreCamelCase
106 * @return this for further operations.
108 public EsDataObjectReaderWriter2<T> setEsIdAttributeNameCamelized(String esIdAttributeName) {
109 return setEsIdAttributeName(YangToolsMapper.toCamelCaseAttributeName(esIdAttributeName));
113 * Attribute name of class that is containing the object id
114 * @param esIdAttributeName of the implementation class for the yangtools interface.
115 * Expected attribute name format is CamelCase with leading underline. @
116 * @return this for further operations.
117 * @throws SecurityException if no access or IllegalArgumentException if wrong type or no attribute with this name.
119 public EsDataObjectReaderWriter2<T> setEsIdAttributeName(String esIdAttributeName) {
120 LOG.debug("Set attribute '{}'", esIdAttributeName);
121 this.esIdAddAtributteName = null; // Reset status
124 Field attributeField;
126 Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
127 if (builder == null) {
128 String msg = "No builder for " + clazz;
130 throw new IllegalArgumentException(msg);
132 T object = builder.build();
133 attributeField = object.getClass().getDeclaredField(esIdAttributeName);
134 if (attributeField.getType().equals(String.class)) {
135 attributeField.setAccessible(true);
136 this.esIdAddAtributteName = esIdAttributeName; // Set new status if everything OK
137 this.field = attributeField;
139 String msg = "Wrong field type " + attributeField.getType().getName() + " of " + esIdAttributeName;
141 throw new IllegalArgumentException(msg);
144 } catch (NoSuchFieldException e) {
145 // Convert to run-time exception
146 String msg = "NoSuchFieldException for '" + esIdAttributeName + "' in class " + clazz.getName();
148 throw new IllegalArgumentException(msg);
149 } catch (SecurityException e) {
150 LOG.debug("Access problem "+esIdAttributeName,e);
157 * Specify subclass of T for write operations.
158 * @param writeInterfaceClazz
160 public EsDataObjectReaderWriter2<T> setWriteInterface( @Nonnull Class<? extends DataObject> writeInterfaceClazz ) {
161 LOG.debug("Set write interface to {}", writeInterfaceClazz);
162 if (writeInterfaceClazz == null) {
163 throw new IllegalArgumentException("Null not allowed here.");
166 this.writeInterfaceClazz = writeInterfaceClazz;
170 public interface IdGetter<S extends DataObject> {
171 String getId(S object);
174 public <S extends DataObject> void write(List<S> objectList, IdGetter<S> idGetter) {
175 for (S object : objectList) {
176 write(object, idGetter.getId(object));
181 * Write child object to database with specific id
183 * @param @Nullable esId use the id or if null generate unique id
184 * @return String with id or null
186 public @Nullable <S extends DataObject> String write(S object, @Nullable String esId) {
187 if (writeInterfaceClazz.isInstance(object)) {
189 String json = yangtoolsMapper.writeValueAsString(object);
190 return db.doWriteRaw(dataTypeName, esId, json);
191 } catch (JsonProcessingException e) {
192 LOG.error("Write problem: ", e);
195 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
196 writeInterfaceClazz.getName());
201 * Update partial child object to database with match/term query
204 * @return String with esId or null
206 public @Nullable <S extends DataObject> String update(S object, QueryBuilder query) {
207 if (writeInterfaceClazz.isInstance(object)) {
209 String json = yangtoolsMapper.writeValueAsString(object);
210 return db.doUpdate(this.dataTypeName,json,query);
211 } catch (JsonProcessingException e) {
212 LOG.error("Update problem: ", e);
215 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
216 writeInterfaceClazz.getName());
221 * Write/ update partial child object to database with specific id Write if not
222 * exists, else update
225 * @return String with esId or null
227 public @Nullable <S extends DataObject> String update(S object, String esId) {
228 return this.updateOrCreate(object, esId,null);
231 * See {@link doUpdateOrCreate(String dataTypeName, String esId, String json, List<String> doNotUpdateField) }
233 public @Nullable <S extends DataObject> String updateOrCreate(S object, String esId,List<String> onlyForInsert) {
234 if (writeInterfaceClazz.isInstance(object)) {
236 String json = yangtoolsMapper.writeValueAsString(object);
237 return db.doUpdateOrCreate(dataTypeName, esId, json,onlyForInsert);
238 } catch (JsonProcessingException e) {
239 LOG.error("Update problem: ", e);
242 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
243 writeInterfaceClazz.getName());
249 * Read object from database, by using the id field
253 public @Nullable T read(String esId) {
257 String json = db.doReadJsonData(dataTypeName, esId);
260 res = yangtoolsMapper.readValue(json.getBytes(), clazz);
261 } catch (IOException e) {
262 LOG.error("Problem: ", e);
265 LOG.debug("Can not read from DB id {} type {}", esId, dataTypeName);
273 * @param esId to identify the object.
276 public boolean remove(String esId) {
277 return db.doRemove(this.dataTypeName, esId);
280 public int remove(QueryBuilder query) {
281 return this.db.doRemove(this.dataTypeName, query);
284 * Get all elements of related type
285 * @return all Elements
287 public SearchResult<T> doReadAll() {
288 return doReadAll(null);
290 public SearchResult<T> doReadAll(QueryBuilder query) {
291 return this.doReadAll(query,false);
294 * Read all existing objects of a type
295 * @param query for the elements
296 * @return the list of all objects
299 public SearchResult<T> doReadAll(QueryBuilder query, boolean ignoreException) {
301 SearchResult<T> res = new SearchResult<>();
302 int idx = 0; //Idx for getAll
303 int iterateLength = 100; //Step width for iterate
305 SearchResult<SearchHit> result;
306 List<SearchHit> hits;
309 LOG.debug("read data in {} with query {}",dataTypeName,query.toJSON());
310 result=db.doReadByQueryJsonData( dataTypeName, query,ignoreException);
313 result = db.doReadAllJsonData(dataTypeName,ignoreException);
315 hits=result.getHits();
316 LOG.debug("Read: {} elements: {} Failures: {}",dataTypeName,hits.size(), yangtoolsMapper.getMappingFailures());
319 idx += result.getHits().size();
320 for (SearchHit hit : hits) {
321 object = getT(hit.getSourceAsString());
322 LOG.debug("Mapp Object: {}\nSource: '{}'\nResult: '{}'\n Failures: {}", hit.getId(),
323 hit.getSourceAsString(), object, yangtoolsMapper.getMappingFailures());
324 if (object != null) {
325 setEsId(object, hit.getId());
328 LOG.warn("Mapp result null Object: {}\n Source: '{}'\n : '", hit.getId(), hit.getSourceAsString());
332 } while (hits.size() == iterateLength); // Do it until end indicated, because less hits than iterateLength
334 res.setTotal(result.getTotal());
338 /* ---------------------------------------------
342 private void setEsId(T object, String esId) {
345 field.set(object, esId);
346 } catch (IllegalArgumentException | IllegalAccessException e) {
347 LOG.debug("Field set problem.", e); }
351 private @Nullable T getT(String jsonString) {
353 return yangtoolsMapper.readValue( jsonString, clazz );
354 } catch (IOException e) {
355 LOG.info("Mapping problem", e);