Improve the performance of resoures microservice
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / Introspector.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aai.introspection;
21
22 import com.att.eelf.configuration.EELFLogger;
23 import com.att.eelf.configuration.EELFManager;
24 import com.google.common.base.CaseFormat;
25 import org.apache.commons.lang.ClassUtils;
26 import org.eclipse.persistence.exceptions.DynamicException;
27 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
28 import org.onap.aai.logging.ErrorLogHelper;
29 import org.onap.aai.logging.LogFormatTools;
30 import org.onap.aai.restcore.MediaType;
31 import org.onap.aai.schema.enums.ObjectMetadata;
32 import org.onap.aai.schema.enums.PropertyMetadata;
33 import org.onap.aai.setup.SchemaVersion;
34 import org.onap.aai.workarounds.NamingExceptions;
35
36 import java.io.UnsupportedEncodingException;
37 import java.lang.reflect.InvocationTargetException;
38 import java.util.*;
39 import java.util.stream.Collectors;
40
41
42 public abstract class Introspector implements Cloneable {
43
44         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
45
46         protected String className;
47         protected String uriChain = "";
48         protected Loader loader;
49         protected final NamingExceptions namingException = NamingExceptions.getInstance();
50         private Set<String> uniqueProperties = null;
51         private Set<String> indexedProperties = null;
52         private Set<String> allKeys = null;
53         protected Introspector(Object obj) {
54         }
55
56         public abstract boolean hasProperty(String name);
57
58         protected String convertPropertyName (String name) {
59                 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
60         }
61
62         protected abstract Object get(String name);
63         protected abstract void set(String name, Object value);
64         /**
65          *
66          * @param name the property name you'd like to retrieve the value for
67          * @return the value of the property
68          */
69         public <T> T getValue(String name) {
70                 String convertedName = convertPropertyName(name);
71                 Object result = null;
72
73                 if (this.hasProperty(name)) {
74                         result = this.get(convertedName);
75                 } else {
76                         /* property not found - slightly ambiguous */
77                         return null;
78                 }
79
80                 Class<?> clazz = this.getClass(name);
81                 if (this.isListType(name) && result == null) {
82                         try {
83                                 this.set(convertedName, clazz.newInstance());
84                                 result = this.get(convertedName);
85                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
86                                 LOGGER.warn(e.getMessage(),e);
87                         }
88                 }
89
90                 return (T)result;
91         }
92
93         public Introspector getWrappedValue(String name) {
94                 String convertedName = convertPropertyName(name);
95                 Object value = null;
96
97                 if (this.hasProperty(name)) {
98                         value = this.get(convertedName);
99                 } else {
100                         /* property not found - slightly ambiguous */
101                         return null;
102                 }
103
104                 Class<?> clazz = this.getClass(name);
105                 if (this.isListType(name) && value == null) {
106                         try {
107                                 this.set(convertedName, clazz.newInstance());
108                                 value = this.get(convertedName);
109                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
110                                 LOGGER.warn(e.getMessage(),e);
111                         }
112                 }
113                 if (value != null) {
114                         return IntrospectorFactory.newInstance(this.getModelType(), value);
115                 } else {
116                         //no value
117                         return null;
118                 }
119
120         }
121
122         public List<Introspector> getWrappedListValue(String name) {
123                 String convertedName = convertPropertyName(name);
124                 Object value = null;
125                 List<Introspector> resultList = new ArrayList<>();
126                 if (this.hasProperty(name)) {
127                         value = this.get(convertedName);
128                 } else {
129                         /* property not found - slightly ambiguous */
130                         return null;
131                 }
132                 boolean isListType = this.isListType(name);
133                 if (!this.isListType(name)) {
134                         return null;
135                 }
136                 Class<?> clazz = this.getClass(name);
137                 if (isListType && value == null) {
138                         try {
139                                 this.set(convertedName, clazz.newInstance());
140                                 value = this.get(convertedName);
141                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
142                                 LOGGER.warn(e.getMessage(),e);
143                         }
144                 }
145
146                 List<Object> valueList = (List<Object>)value;
147
148                 for (Object item : valueList) {
149                         resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
150                 }
151
152                 return resultList;
153
154         }
155
156         public Object castValueAccordingToSchema(String name, Object obj) {
157                 Object result = obj;
158                 Class<?> nameClass = this.getClass(name);
159                 if (nameClass == null) {
160                         throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
161                 }
162                 if (obj != null) {
163
164                         try {
165                                 if (!obj.getClass().getName().equals(nameClass.getName())) {
166                                         if (nameClass.isPrimitive()) {
167                                                 nameClass = ClassUtils.primitiveToWrapper(nameClass);
168                                                 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
169                                         }
170                                         if (obj instanceof String) {
171                                                 result = nameClass.getConstructor(String.class).newInstance(obj);
172                                         } else if (!this.isListType(name) && !this.isComplexType(name)){
173                                                 //box = obj.toString();
174                                                 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
175                                         }
176                                 }
177                         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
178                                         | InvocationTargetException | NoSuchMethodException | SecurityException e) {
179                                 ErrorLogHelper.logError("AAI_4017", e.getMessage());
180                         }
181                 }
182                 return result;
183         }
184
185         public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
186                 List<Object> result = new ArrayList<>();
187
188                 for (Object item : objs) {
189                         result.add(this.castValueAccordingToSchema(name, item));
190                 }
191
192                 return result;
193
194         }
195         /**
196          *
197          * @param name the property name you'd like to set the value of
198          * @param obj the value to be set
199          * @return
200          */
201         public void setValue(String name, Object obj) throws IllegalArgumentException {
202                 Object box = this.castValueAccordingToSchema(name, obj);
203
204                 name = convertPropertyName(name);
205                 this.set(name, box);
206         }
207         /**
208          *
209          * @return a list of all the properties available on the object
210          */
211         public abstract Set<String> getProperties();
212
213         public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
214                 final Set<String> temp = new LinkedHashSet<>();
215                 this.getProperties().stream().filter(item -> {
216                         return p.test(this, item);
217                 }).forEach(item -> {
218                         temp.add(item);
219                 });
220                 final Set<String> result = Collections.unmodifiableSet(temp);
221
222                 return result;
223
224         }
225
226         public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p){
227         return this.getProperties()
228             .stream()
229             .filter(item -> p.test(this, item))
230             .filter(this::isSimpleType)
231             .collect(Collectors.toSet());
232     }
233         /**
234          *
235          * @return a list of the required properties on the object
236          */
237         public abstract Set<String> getRequiredProperties();
238         /**
239          *
240          * @return a list of the properties that can be used to query the object in the db
241          */
242         public abstract Set<String> getKeys();
243         /**
244          *
245          * @return a list of the all key properties for this object
246          */
247         public Set<String> getAllKeys() {
248                 Set<String> result = null;
249                 if (this.allKeys == null) {
250                         Set<String> keys = this.getKeys();
251                         result = new LinkedHashSet<>();
252                         result.addAll(keys);
253                         String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
254                         if (altKeys != null) {
255                                 String[] altKeysArray = altKeys.split(",");
256                                 for (String altKey : altKeysArray) {
257                                         result.add(altKey);
258                                 }
259                         }
260                         result = Collections.unmodifiableSet(result);
261                         this.allKeys = result;
262                 }
263                 result = this.allKeys;
264                 return result;
265         }
266
267         public Set<String> getIndexedProperties() {
268                 Set<String> result = null;
269
270                 if (this.indexedProperties == null) {
271                         result = new LinkedHashSet<>();
272                         Set<String> keys = this.getKeys();
273                         result.addAll(keys);
274                         String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
275                         if (altKeys != null) {
276                                 String[] altKeysArray = altKeys.split(",");
277                                 for (String altKey : altKeysArray) {
278                                         result.add(altKey);
279                                 }
280                         }
281                         this.indexedProperties = Collections.unmodifiableSet(result);
282                 }
283                 result = this.indexedProperties;
284                 return result;
285         }
286
287         public Set<String> getUniqueProperties() {
288                 Set<String> result = null;
289                 if (this.uniqueProperties == null) {
290                         String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
291                         result = new LinkedHashSet<>();
292                         if (altKeys != null) {
293                                 String[] altKeysArray = altKeys.split(",");
294                                 for (String altKey : altKeysArray) {
295                                         result.add(altKey);
296                                 }
297                         }
298                         this.uniqueProperties = Collections.unmodifiableSet(result);
299
300                 }
301                 result = this.uniqueProperties;
302                 return result;
303         }
304
305         public Set<String> getDependentOn() {
306                 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
307                 if (dependentOn == null) {
308                         return  new LinkedHashSet<>();
309                 }
310                 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
311         }
312         /**
313          *
314          * @param name
315          * @return the string name of the java class of the named property
316          */
317         public String getType(String name) {
318                 Class<?> resultClass = this.getClass(name);
319                 String result = "";
320
321                 if (resultClass != null) {
322                         result = resultClass.getName();
323                         if (result.equals("java.util.ArrayList")) {
324                                 result = "java.util.List";
325                         }
326                 }
327
328                 return result;
329         }
330         /**
331          * This will returned the generic parameterized type of the underlying
332          * object if it exists
333          * @param name
334          * @return the generic type of the java class of the underlying object
335          */
336         public String getGenericType(String name) {
337                 Class<?> resultClass = this.getGenericTypeClass(name);
338                 String result = "";
339
340                 if (resultClass != null) {
341                         result = resultClass.getName();
342                 }
343
344                 return result;
345         }
346         /**
347          *
348          * @return the string name of the java class of the underlying object
349          */
350         public abstract String getJavaClassName();
351
352         /**
353          *
354          * @param name the property name
355          * @return the Class object
356          */
357         public abstract Class<?> getClass(String name);
358
359         public abstract Class<?> getGenericTypeClass(String name);
360
361         /**
362          *
363          * @param name the property name
364          * @return a new instance of the underlying type of this property
365          * @throws AAIUnknownObjectException
366          */
367         public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
368                 String type = this.getType(name);
369                 return loader.objectFromName(type);
370         }
371
372         public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
373                 String type = this.getGenericType(name);
374                 return loader.objectFromName(type);
375         }
376
377
378         public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
379
380                 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
381
382                 return result;
383
384         }
385
386         public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
387
388                 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
389
390                 return result;
391
392         }
393         /**
394          * Is this type not a Java String or primitive
395          * @param name
396          * @return
397          */
398         public boolean isComplexType(String name) {
399                 String result = this.getType(name);
400
401                 if (result.contains("aai") || result.equals("java.lang.Object")) {
402                         return true;
403                 } else {
404                         return false;
405                 }
406         }
407
408         public boolean isComplexGenericType(String name) {
409                 String result = this.getGenericType(name);
410
411                 if (result.contains("aai")) {
412                         return true;
413                 } else {
414                         return false;
415                 }
416         }
417
418         public boolean isSimpleType(String name) {
419                 return !(this.isComplexType(name) || this.isListType(name));
420         }
421
422         public boolean isSimpleGenericType(String name) {
423                 return !this.isComplexGenericType(name);
424         }
425
426         public boolean isListType(String name) {
427                 String result = this.getType(name);
428
429                 if (result.contains("java.util.List")) {
430                         return true;
431                 } else {
432                         return false;
433                 }
434         }
435
436         public boolean isContainer() {
437                 Set<String> props = this.getProperties();
438                 boolean result = false;
439                 if (props.size() == 1 && this.isListType(props.iterator().next())) {
440                         result = true;
441                 }
442
443                 return result;
444         }
445
446         public abstract String getChildName();
447         public String getChildDBName() {
448                 String result = this.getChildName();
449
450                 result = namingException.getDBName(result);
451                 return result;
452         }
453         public abstract String getName();
454
455         public String getDbName() {
456                 String lowerHyphen = this.getName();
457
458                 lowerHyphen = namingException.getDBName(lowerHyphen);
459
460                 return lowerHyphen;
461         }
462
463         public abstract ModelType getModelType();
464
465         public boolean hasChild(Introspector child) {
466                 boolean result = false;
467                 //check all inheriting types for this child
468                 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
469                         String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
470                         for (String inheritor : inheritors) {
471                                 try {
472                                         Introspector temp = this.loader.introspectorFromName(inheritor);
473                                         result = temp.hasProperty(child.getName());
474                                         if (result) {
475                                                 break;
476                                         }
477                                 } catch (AAIUnknownObjectException e) {
478                                         LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
479                                 }
480                         }
481                 } else {
482                         result = this.hasProperty(child.getName());
483                 }
484                 return result;
485         }
486
487         public void setURIChain(String uri) {
488                 this.uriChain = uri;
489         }
490         public abstract String getObjectId() throws UnsupportedEncodingException;
491
492         public String getURI() throws UnsupportedEncodingException {
493                 //String result = this.uriChain;
494                 String result = "";
495                 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
496                 String container = this.getMetadata(ObjectMetadata.CONTAINER);
497                 if (this.isContainer()) {
498                          result += "/" + this.getName();
499                 } else {
500
501                         if (container != null) {
502                                 result += "/" + container;
503                         }
504                         result += "/" + this.getDbName() + "/" + this.findKey();
505
506                         if (namespace != null && !namespace.equals("")) {
507                                 result = "/" + namespace + result;
508                         }
509                 }
510
511
512                 return result;
513         }
514
515         public String getGenericURI() {
516                 String result = "";
517                 if (this.isContainer()) {
518                          result += "/" + this.getName();
519                 } else {
520                         result += "/" + this.getDbName();
521                         for (String key : this.getKeys()) {
522                                 result += "/{" + this.getDbName() + "-" + key + "}";
523                         }
524                 }
525
526                 return result;
527         }
528
529         public String getFullGenericURI() {
530                 String result = "";
531                 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
532                 String container = this.getMetadata(ObjectMetadata.CONTAINER);
533                 if (this.isContainer()) {
534                          result += "/" + this.getName();
535                 } else {
536
537
538                         if (container != null) {
539                                 result += "/" + container;
540                         }
541                         result += "/" + this.getDbName();
542
543                         for (String key : this.getKeys()) {
544                                 result += "/{" + this.getDbName() + "-" + key + "}";
545                         }
546                         if (namespace != null && !namespace.equals("")) {
547                                 result = "/" + namespace + result;
548                         }
549
550                 }
551
552                 return result;
553         }
554
555         public abstract String preProcessKey(String key);
556
557         protected abstract String findKey() throws UnsupportedEncodingException;
558
559         public abstract String marshal(MarshallerProperties properties);
560
561         public abstract Object clone();
562
563         public abstract Object getUnderlyingObject();
564
565         public String marshal(boolean formatted) {
566                 MarshallerProperties properties =
567                                 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
568
569                 return marshal(properties);
570         }
571         public String makeSingular(String word) {
572
573                 String result = word;
574                 result = result.replaceAll("(?:([ho])es|s)$", "");
575
576                 if (result.equals("ClassesOfService")) {
577                         result = "ClassOfService";
578                 } else if (result.equals("CvlanTag")) {
579                         result = "CvlanTagEntry";
580                 } else if (result.equals("Metadata")) {
581                         result = "Metadatum";
582                 }
583                 return result;
584         }
585
586         protected String makePlural(String word) {
587                 String result = word;
588
589                 if (result.equals("cvlan-tag-entry")) {
590                         return "cvlan-tags";
591                 } else if (result.equals("class-of-service")) {
592                         return "classes-of-service";
593                 } else if (result.equals("metadatum")) {
594                         return "metadata";
595                 }
596                 result = result.replaceAll("([a-z])$", "$1s");
597                 result = result.replaceAll("([hox])s$", "$1es");
598                 /*
599                 if (result.equals("classes-of-services")) {
600                         result = "classes-of-service";
601                 }*/
602
603                 return result;
604         }
605
606         public abstract String getMetadata(ObjectMetadata metadataName);
607         public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
608         public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
609                 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
610                 Optional<String> result = Optional.empty();
611
612                 if (!resultValue.isEmpty()) {
613                         result = Optional.of(resultValue);
614                 }
615                 return result;
616
617         }
618
619         public abstract SchemaVersion getVersion();
620         public Loader getLoader() {
621                 return this.loader;
622         }
623
624         public boolean isTopLevel() {
625
626                 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;
627         }
628
629 }