Optimize the areas where its creating extra memory
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / MoxyStrategy.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 com.google.common.base.Joiner;
26 import org.eclipse.persistence.descriptors.ClassDescriptor;
27 import org.eclipse.persistence.dynamic.DynamicEntity;
28 import org.eclipse.persistence.dynamic.DynamicType;
29 import org.eclipse.persistence.exceptions.DynamicException;
30 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
31 import org.eclipse.persistence.mappings.DatabaseMapping;
32 import org.eclipse.persistence.oxm.XMLField;
33 import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
34 import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
35 import org.onap.aai.config.SpringContextAware;
36 import org.onap.aai.logging.LogFormatTools;
37 import org.onap.aai.nodes.CaseFormatStore;
38 import org.onap.aai.nodes.NodeIngestor;
39 import org.onap.aai.restcore.MediaType;
40 import org.onap.aai.schema.enums.ObjectMetadata;
41 import org.onap.aai.schema.enums.PropertyMetadata;
42 import org.onap.aai.setup.SchemaVersion;
43 import org.springframework.web.util.UriUtils;
44
45 import javax.xml.bind.JAXBException;
46 import javax.xml.bind.Marshaller;
47 import java.io.StringWriter;
48 import java.io.UnsupportedEncodingException;
49 import java.util.*;
50 import java.util.Map.Entry;
51
52 public class MoxyStrategy extends Introspector {
53
54         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyStrategy.class);
55         private DynamicEntity internalObject = null;
56         private DynamicType internalType = null;
57         private DynamicJAXBContext jaxbContext = null;
58         private ClassDescriptor cd = null;
59         private SchemaVersion version = null;
60         private Set<String> properties = null;
61         private Set<String> keys = null;
62         private Set<String> requiredProperties = null;
63
64         private boolean isInitialized = false;
65
66         protected MoxyStrategy(Object obj) {
67                 super(obj);
68                 /* must look up the correct jaxbcontext for this object */
69                 className = MoxyStrategy.class.getSimpleName();
70                 internalObject = (DynamicEntity)obj;
71                 version = nodeIngestor.getVersionFromClassName(internalObject.getClass().getName());
72                 super.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(getModelType(), version);
73                 jaxbContext = nodeIngestor.getContextForVersion(version);
74                 String simpleName = internalObject.getClass().getName();
75                 internalType = jaxbContext.getDynamicType(simpleName);
76
77                 cd = internalType.getDescriptor();
78         }
79
80         private void init() {
81                 isInitialized = true;
82
83                 Set<String> props = new LinkedHashSet<>();
84                 for (String s : internalType.getPropertiesNames()) {
85                     String value = caseFormatStore
86                 .fromLowerCamelToLowerHyphen(s)
87                 .orElseGet(
88                     () -> {
89                         LOGGER.debug("Unable to find {} in the store from lower camel to lower hyphen", s);
90                         return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, s);
91                     }
92                 );
93                         props.add(value);
94
95                 }
96                 props = Collections.unmodifiableSet(props);
97                 this.properties = props;
98
99                 Set<String> requiredProps = new LinkedHashSet<>();
100                 for (DatabaseMapping dm : cd.getMappings()) {
101                         if (dm.getField() instanceof XMLField) {
102                                 XMLField x = (XMLField)dm.getField();
103                                 if (x != null && x.isRequired()) {
104                                         requiredProps.add(this.removeXPathDescriptor(x.getName()));
105                                 }
106                         }
107                 }
108                 requiredProps = Collections.unmodifiableSet(requiredProps);
109                 this.requiredProperties = requiredProps;
110
111                 Set<String> keys = new LinkedHashSet<>();
112
113                 for (String name : internalType.getDescriptor().getPrimaryKeyFieldNames()) {
114                         keys.add(this.removeXPathDescriptor(name));
115                 }
116                 keys = Collections.unmodifiableSet(keys);
117                 this.keys = keys;
118
119
120         }
121
122         @Override
123         public boolean hasProperty(String name) {
124                 String convertedName = convertPropertyName(name);
125
126                 return internalType.containsProperty(convertedName);
127         }
128
129         @Override
130         public Object get(String name) {
131                 return internalObject.get(name);
132         }
133
134         @Override
135         public void set(String name, Object obj){
136
137                 internalObject.set(name, obj);
138         }
139
140         @Override
141         public Set<String> getProperties() {
142
143                 if(!isInitialized){
144                         init();
145                 }
146
147                 return this.properties;
148
149         }
150
151         @Override
152         public Set<String> getRequiredProperties() {
153
154                 if(!isInitialized){
155                         init();
156                 }
157
158                 return this.requiredProperties;
159         }
160
161         @Override
162         public Set<String> getKeys() {
163
164                 if(!isInitialized){
165                         init();
166                 }
167
168                 return this.keys;
169         }
170
171         @Override
172         public Map<PropertyMetadata, String> getPropertyMetadata(String prop) {
173                 String propName = this.convertPropertyName(prop);
174                 DatabaseMapping mapping = cd.getMappingForAttributeName(propName);
175                 Map<PropertyMetadata, String> result = new HashMap<>();
176                 if (mapping != null) {
177                         Set<Entry> entrySet = mapping.getProperties().entrySet();
178                         for (Entry<?,?> entry : entrySet) {
179                                 result.put(
180                                                 PropertyMetadata.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, (String)entry.getKey())), (String)entry.getValue());
181                         }
182                 }
183
184                 return result;
185         }
186
187         @Override
188         public String getJavaClassName() {
189                 return internalObject.getClass().getName();
190         }
191
192
193
194         @Override
195         public Class<?> getClass(String name) {
196                 name = convertPropertyName(name);
197                 Class<?> resultClass = null;
198                 try {
199                         if (internalType.getPropertyType(name) == null) {
200                                 if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) {
201                                         resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass();
202
203                                 } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) {
204                                         resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass();
205                                 } else {
206                                         ClassDescriptor referenceDiscriptor = cd.getMappingForAttributeName(name).getReferenceDescriptor();
207                                         if (referenceDiscriptor != null) {
208                                                 resultClass = referenceDiscriptor.getJavaClass();
209                                         } else {
210                                                 resultClass = Object.class;
211                                         }
212                                 }
213                         } else {
214                                 resultClass = internalType.getPropertyType(name);
215                         }
216                 } catch (DynamicException e) {
217                         //property doesn't exist
218                 }
219                 return resultClass;
220         }
221
222         @Override
223         public Class<?> getGenericTypeClass(String name) {
224                 name = convertPropertyName(name);
225                 Class<?> resultClass = null;
226                 if (internalType.getPropertyType(name) == null) {
227                         if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) {
228                                 resultClass = cd.getMappingForAttributeName(name).getFields().get(0).getType();
229
230                         } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) {
231                                 resultClass = cd.getMappingForAttributeName(name).getReferenceDescriptor().getJavaClass();
232                         }
233                 }
234
235                 return resultClass;
236         }
237
238         @Override
239         public Object getUnderlyingObject() {
240                 return this.internalObject;
241         }
242
243         @Override
244         public String getChildName() {
245
246                 String className = internalObject.getClass().getSimpleName();
247                 String lowerHyphen = caseFormatStore
248             .fromUpperCamelToLowerHyphen(className)
249             .orElseGet(
250                 () -> {
251                     LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", className);
252                     return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
253                 }
254             );
255
256                 if (this.isContainer()) {
257                     String upperCamel = this.getGenericTypeClass(this.getProperties().iterator().next()).getSimpleName();
258
259                         lowerHyphen = caseFormatStore
260                 .fromUpperCamelToLowerHyphen(upperCamel)
261                 .orElseGet(
262                     () -> {
263                         LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", upperCamel);
264                         return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, upperCamel);
265                     }
266                 );
267                 }
268
269                 return lowerHyphen;
270         }
271
272         @Override
273         public String getName() {
274                 String className = internalObject.getClass().getSimpleName();
275                 return caseFormatStore
276             .fromUpperCamelToLowerHyphen(className)
277             .orElseGet(() -> {
278                 LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", className);
279                 return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
280             });
281         }
282
283         @Override
284         public String getObjectId() throws UnsupportedEncodingException {
285                 String result = "";
286                 String container = this.getMetadata(ObjectMetadata.CONTAINER);
287                 if (this.isContainer()) {
288                          result += "/" + this.getName();
289                 } else {
290
291                         if (container != null) {
292                                 result += "/" + container;
293                         }
294                         result += "/" + this.getDbName() + "/" + this.findKey();
295
296                 }
297
298                 return result;
299         }
300
301         @Override
302         protected String findKey() throws UnsupportedEncodingException {
303                 Set<String> keys = null;
304                 keys = this.getKeys();
305                 List<String> results = new ArrayList<>();
306                 for (String key : keys) {
307                         String value = UriUtils.encode(this.getValue(key).toString(), "UTF-8");
308                         results.add(value);
309                 }
310
311                 return Joiner.on("/").join(results);
312         }
313
314         @Override
315         public String preProcessKey (String key) {
316                 String result = "";
317                 String[] split = key.split("/");
318                 int i = 0;
319                 for (i = split.length-1; i >= 0; i--) {
320
321                         if (jaxbContext.getDynamicType(split[i]) != null) {
322                                 break;
323
324                         }
325
326                 }
327                 result = Joiner.on("/").join(Arrays.copyOfRange(split, 0, i));
328
329                 return result;
330
331         }
332
333         @Override
334         public String marshal(MarshallerProperties properties) {
335                 StringWriter result = new StringWriter();
336         try {
337             Marshaller marshaller = jaxbContext.createMarshaller();
338                 if (properties.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
339                                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.MEDIA_TYPE, "application/json");
340                         marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_INCLUDE_ROOT, properties.getIncludeRoot());
341                         marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, properties.getWrapperAsArrayName());
342                         marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false);
343                 }
344
345                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, properties.getFormatted());
346                 marshaller.marshal(this.internalObject, result);
347                 } catch (JAXBException e) {
348             LOGGER.warn("Encountered an jaxb exception during marshalling ", LogFormatTools.getStackTop(e));
349                 }
350
351         return result.toString();
352         }
353
354         @Override
355         public ModelType getModelType() {
356                 return ModelType.MOXY;
357         }
358
359         private String removeXPathDescriptor(String name) {
360
361                 return name.replaceAll("/text\\(\\)", "");
362         }
363
364         @Override
365         public String getMetadata(ObjectMetadata name) {
366
367                 return (String)cd.getProperty(name.toString());
368         }
369
370         @Override
371         public SchemaVersion getVersion() {
372
373                 return this.version;
374         }
375 }