2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk features
4 * ================================================================================
5 * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database;
24 import java.io.IOException;
25 import java.lang.reflect.Field;
26 import java.util.List;
28 import javax.annotation.Nonnull;
29 import javax.annotation.Nullable;
31 import org.onap.ccsdk.features.sdnr.wt.common.database.DatabaseClient;
32 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
33 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
34 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.yangtools.YangToolsMapper;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.Entity;
37 import org.opendaylight.yangtools.concepts.Builder;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import com.fasterxml.jackson.core.JsonProcessingException;
45 * Class to rw yang-tool generated objects into elasticsearch database. For "ES _id" exchange the esIdAddAtributteName is used.
46 * This attribute mast be of type String and contains for read and write operations the object id.
47 * The function can be used without id handling.
48 * If id handling is required the parameter needs to be specified by class definition in yang and setting the name by using setAttributeName()
50 * @param <T> Yang tools generated class object.
52 public class EsDataObjectReaderWriter<T extends DataObject> {
54 private final Logger LOG = LoggerFactory.getLogger(EsDataObjectReaderWriter.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 YangToolsMapper 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 EsDataObjectReaderWriter(DatabaseClient db, Entity dataTypeName, Class<T> clazz) throws ClassNotFoundException {
86 this(db, dataTypeName.getName(), clazz);
88 public EsDataObjectReaderWriter(DatabaseClient db, String dataTypeName, Class<T> clazz) throws ClassNotFoundException {
89 LOG.info("Create {} for datatype {} class {}", this.getClass().getName(), dataTypeName, clazz.getName());
91 this.esIdAddAtributteName = null;
93 this.writeInterfaceClazz = clazz;
95 this.dataTypeName = dataTypeName;
96 this.yangtoolsMapper = new YangToolsMapper();
97 //this.yangtoolsMapper.assertBuilderClass(clazz);
100 // if (! db.isExistsIndex(dataTypeName)) {
101 // throw new IllegalArgumentException("Index "+dataTypeName+" not existing.");
105 public String getDataTypeName() {
108 public Class<T> getClazz() {
112 * Simlar to {@link #setEsIdAttributeName()}, but adapts the parameter to yangtools attribute naming schema
113 * @param esIdAttributeName is converted to UnderscoreCamelCase
114 * @return this for further operations.
116 public EsDataObjectReaderWriter<T> setEsIdAttributeNameCamelized(String esIdAttributeName) {
117 return setEsIdAttributeName(YangToolsMapper.toCamelCaseAttributeName(esIdAttributeName));
121 * Attribute name of class that is containing the object id
122 * @param esIdAttributeName of the implementation class for the yangtools interface.
123 * Expected attribute name format is CamelCase with leading underline. @
124 * @return this for further operations.
125 * @throws SecurityException if no access or IllegalArgumentException if wrong type or no attribute with this name.
127 public EsDataObjectReaderWriter<T> setEsIdAttributeName(String esIdAttributeName) {
128 LOG.debug("Set attribute '{}'", esIdAttributeName);
129 this.esIdAddAtributteName = null; // Reset status
132 Field attributeField;
134 Builder<T> builder = yangtoolsMapper.getBuilder(clazz);
135 T object = builder.build();
136 attributeField = object.getClass().getDeclaredField(esIdAttributeName);
137 if (attributeField.getType().equals(String.class)) {
138 attributeField.setAccessible(true);
139 this.esIdAddAtributteName = esIdAttributeName; //Set new status if everything OK
140 this.field = attributeField;
142 String msg = "Wrong field type " + attributeField.getType().getName() + " of " + esIdAttributeName;
144 throw new IllegalArgumentException(msg);
146 } catch (NoSuchFieldException e) {
147 // Convert to run-time exception
148 String msg = "NoSuchFieldException for '" + esIdAttributeName + "' in class " + clazz.getName();
150 throw new IllegalArgumentException(msg);
151 } catch (SecurityException e) {
152 LOG.debug("Access problem "+esIdAttributeName,e);
159 * Specify subclass of T for write operations.
160 * @param writeInterfaceClazz
162 public EsDataObjectReaderWriter<T> setWriteInterface( @Nonnull Class<? extends DataObject> writeInterfaceClazz ) {
163 LOG.debug("Set write interface to {}", writeInterfaceClazz);
164 if (writeInterfaceClazz == null)
165 throw new IllegalArgumentException("Null not allowed here.");
167 this.writeInterfaceClazz = writeInterfaceClazz;
172 * Write child object to database with specific id
174 * @param @Nullable esId use the id or if null generate unique id
175 * @return String with id or null
177 public @Nullable <S extends DataObject> String write(S object, @Nullable String esId) {
178 if (writeInterfaceClazz.isInstance(object)) {
180 String json = yangtoolsMapper.writeValueAsString(object);
181 return db.doWriteRaw(dataTypeName, esId, json);
182 } catch (JsonProcessingException e) {
183 LOG.error("Write problem: ", e);
186 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
187 writeInterfaceClazz.getName());
192 * Update partial child object to database with match/term query
195 * @return String with esId or null
197 public @Nullable <S extends DataObject> boolean update(S object, QueryBuilder query) {
198 if (writeInterfaceClazz.isInstance(object)) {
200 String json = yangtoolsMapper.writeValueAsString(object);
201 return db.doUpdate(this.dataTypeName,json,query);
202 } catch (JsonProcessingException e) {
203 LOG.error("Update problem: ", e);
206 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
207 writeInterfaceClazz.getName());
212 * Write/ update partial child object to database with specific id Write if not
213 * exists, else update
216 * @return String with esId or null
218 public @Nullable <S extends DataObject> String update(S object, String esId) {
219 return this.update(object, esId,null);
221 public @Nullable <S extends DataObject> String update(S object, String esId,List<String> onylForInsert) {
222 if (writeInterfaceClazz.isInstance(object)) {
224 String json = yangtoolsMapper.writeValueAsString(object);
225 return db.doUpdateOrCreate(dataTypeName, esId, json,onylForInsert);
226 } catch (JsonProcessingException e) {
227 LOG.error("Update problem: ", e);
230 LOG.error("Type {} does not provide interface {}", object!=null?object.getClass().getName():"null",
231 writeInterfaceClazz.getName());
237 * Read object from database, by using the id field
241 public @Nullable T read(String esId) {
242 @Nullable T res = (T)null;
244 String json = db.doReadJsonData(dataTypeName, esId);
246 res = yangtoolsMapper.readValue(json.getBytes(), clazz);
247 } catch (IOException e) {
248 LOG.error("Problem: ", e);
256 * @param esId to identify the object.
259 public boolean remove(String esId) {
260 return db.doRemove(this.dataTypeName, esId);
263 public int remove(QueryBuilder query) {
264 return this.db.doRemove(this.dataTypeName, query);
267 * Get all elements of related type
268 * @return all Elements
270 public SearchResult<T> doReadAll() {
271 return doReadAll(null);
273 public SearchResult<T> doReadAll(QueryBuilder query) {
274 return this.doReadAll(query,false);
277 * Read all existing objects of a type
278 * @param query for the elements
279 * @return the list of all objects
282 public SearchResult<T> doReadAll(QueryBuilder query, boolean ignoreException) {
284 SearchResult<T> res = new SearchResult<T>();
285 SearchResult<SearchHit> result;
286 List<SearchHit> hits;
288 LOG.debug("read data in {} with query {}", dataTypeName, query.toJSON());
289 result = db.doReadByQueryJsonData(dataTypeName, query, ignoreException);
291 result = db.doReadAllJsonData(dataTypeName, ignoreException);
293 hits = result.getHits();
294 LOG.debug("Read: {} elements: {} Failures: {}", dataTypeName, hits.size(),
295 yangtoolsMapper.getMappingFailures());
298 for (SearchHit hit : hits) {
299 object = getT(hit.getSourceAsString());
300 LOG.debug("Mapp Object: {}\nSource: '{}'\nResult: '{}'\n Failures: {}", hit.getId(),
301 hit.getSourceAsString(), object, yangtoolsMapper.getMappingFailures());
302 if (object != null) {
303 setEsId(object, hit.getId());
306 LOG.warn("Mapp result null Object: {}\n Source: '{}'\n : '", hit.getId(), hit.getSourceAsString());
309 res.setTotal(result.getTotal());
313 /* ---------------------------------------------
317 private void setEsId(T object, String esId) {
320 field.set(object, esId);
321 } catch (IllegalArgumentException | IllegalAccessException e) {
322 LOG.debug("Field set problem.", e); }
326 private @Nullable T getT(String jsonString) {
328 return yangtoolsMapper.readValue( jsonString, clazz );
329 } catch (IOException e) {
330 LOG.info("Mapping problem", e);