Update the license for 2017-2018 license
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / dbgraphmap / SearchGraph.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.dbgraphmap;
21
22 import java.io.UnsupportedEncodingException;
23 import java.net.URI;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Optional;
31 import java.util.stream.Stream;
32
33 import javax.ws.rs.core.HttpHeaders;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.UriBuilderException;
37 import javax.xml.bind.JAXBException;
38
39 import org.apache.tinkerpop.gremlin.process.traversal.P;
40 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
41 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
43 import org.apache.tinkerpop.gremlin.structure.Vertex;
44 import org.eclipse.persistence.dynamic.DynamicEntity;
45 import org.eclipse.persistence.dynamic.DynamicType;
46 import org.eclipse.persistence.exceptions.DynamicException;
47 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
48 import org.onap.aai.db.DbMethHelper;
49 import org.onap.aai.db.props.AAIProperties;
50 import org.onap.aai.dbgen.PropertyLimitDesc;
51 import org.onap.aai.dbgraphgen.ModelBasedProcessing;
52 import org.onap.aai.dbgraphgen.ResultSet;
53 import org.onap.aai.dbmap.DBConnectionType;
54 import org.onap.aai.exceptions.AAIException;
55 import org.onap.aai.extensions.AAIExtensionMap;
56 import org.onap.aai.introspection.Introspector;
57 import org.onap.aai.introspection.Loader;
58 import org.onap.aai.introspection.LoaderFactory;
59 import org.onap.aai.introspection.ModelType;
60 import org.onap.aai.introspection.MoxyLoader;
61 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
62 import org.onap.aai.parsers.relationship.RelationshipToURI;
63 import org.onap.aai.query.builder.QueryBuilder;
64 import org.onap.aai.schema.enums.ObjectMetadata;
65 import org.onap.aai.schema.enums.PropertyMetadata;
66 import org.onap.aai.serialization.db.DBSerializer;
67 import org.onap.aai.serialization.db.EdgeRule;
68 import org.onap.aai.serialization.db.EdgeRules;
69 import org.onap.aai.serialization.engines.QueryStyle;
70 import org.onap.aai.serialization.engines.TitanDBEngine;
71 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
72 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
73 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
74 import org.onap.aai.util.StoreNotificationEvent;
75
76 import com.att.eelf.configuration.EELFLogger;
77 import com.att.eelf.configuration.EELFManager;
78 import com.google.common.base.CaseFormat;
79
80 import edu.emory.mathcs.backport.java.util.Collections;
81
82 /**
83  * Database Mapping class which acts as the middle man between the REST interface objects 
84  * for the Search namespace 
85
86  */
87 public class SearchGraph {
88
89         private final String COMPONENT = "aaidbmap";
90         private AAIExtensionMap aaiExtMap;
91         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SearchGraph.class);
92         /**
93          * Get the search result based on the includeNodeType and depth provided.
94          *
95          * @param fromAppId the from app id
96          * @param transId the trans id
97          * @param startNodeType the start node type
98          * @param startNodeKeyParams the start node key params
99          * @param includeNodeTypes the include node types
100          * @param depth the depth
101          * @param aaiExtMap the aai ext map
102          * @return Response
103          * @throws AAIException the AAI exception
104          */
105         public Response runGenericQuery (
106                         HttpHeaders headers,
107                         String startNodeType,
108                         List <String> startNodeKeyParams,
109                         List <String> includeNodeTypes,
110                         final int depth,
111                         TransactionalGraphEngine dbEngine,
112                         Loader loader,
113                         UrlBuilder urlBuilder) throws AAIException {
114                 Response response = null;
115                 boolean success = true;
116                 String result = "";
117                 try {                   
118                         dbEngine.startTransaction();
119
120                         if( startNodeType == null ){
121                                 throw new AAIException("AAI_6120", "null start-node-type passed to the generic query"); 
122                         }
123
124                         if( startNodeKeyParams == null ){
125                                 throw new AAIException("AAI_6120", "no key param passed to the generic query"); 
126                         }
127
128                         if( includeNodeTypes == null ){
129                                 throw new AAIException("AAI_6120", "no include params passed to the generic query"); 
130                         }
131
132                         if (depth > 6) {
133                                 throw new AAIException("AAI_6120", "The maximum depth supported by the generic query is 6");
134                         }
135                         final QueryBuilder queryBuilder;
136                         
137                         // there is an issue with service-instance - it is a unique node but still dependent
138                         // for now query it directly without attempting to craft a valid URI    
139                         if (startNodeType.equalsIgnoreCase("service-instance") && startNodeKeyParams.size() == 1) {
140                                 Introspector obj = loader.introspectorFromName(startNodeType);
141                                 // Build a hash with keys to uniquely identify the start Node
142                                 String keyName = null;
143                                 String keyValue = null;
144
145                                 QueryBuilder builder = dbEngine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, "service-instance");
146                                 for( String keyData : startNodeKeyParams ){ 
147                                         int colonIndex = keyData.indexOf(":");
148                                         if( colonIndex <= 0 ){
149                                                 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); 
150                                         }
151                                         else {
152                                                 keyName = keyData.substring(0, colonIndex).split("\\.")[1];
153                                                 keyValue = keyData.substring(colonIndex + 1);
154                                                 builder.getVerticesByProperty(keyName, keyValue);
155                                         }
156                                 }
157                                 
158                                 queryBuilder = builder;
159                         } else {
160                                 URI uri = craftUriFromQueryParams(loader, startNodeType, startNodeKeyParams);
161                                 queryBuilder = dbEngine.getQueryBuilder().createQueryFromURI(uri).getQueryBuilder();
162                         }
163                         List<Vertex> results = queryBuilder.toList();
164                         if( results.isEmpty()){
165                                 throw new AAIException("AAI_6114", "No Node of type " + 
166                                                 startNodeType + 
167                                                 " found for properties: " + 
168                                                 startNodeKeyParams.toString()); 
169                         } else if (results.size() > 1) {
170                                 String detail = "More than one Node found by getUniqueNode for params: " + startNodeKeyParams.toString() + "\n";
171                                 throw new AAIException("AAI_6112", detail); 
172                         }
173
174                         Vertex startNode = results.get(0);
175
176                         Collection <Vertex> ver = new HashSet <>();
177                         List<Vertex> queryResults = new ArrayList<>();
178                         GraphTraversalSource traversalSource = dbEngine.asAdmin().getReadOnlyTraversalSource();
179                         GraphTraversal<Vertex, Vertex> traversal;
180                         if (includeNodeTypes.contains(startNodeType) || depth == 0 || includeNodeTypes.contains("all") )
181                                 ver.add(startNode);
182
183                         // Now look for a node of includeNodeType within a given depth
184                         traversal = traversalSource.withSideEffect("x", ver).V(startNode)
185                         .times(depth).repeat(__.both().store("x")).cap("x").unfold();
186                         
187                         if (!includeNodeTypes.contains("all")) {
188                                 traversal.where(__.has(AAIProperties.NODE_TYPE, P.within(includeNodeTypes)));
189                         }
190                         queryResults = traversal.toList();
191                         
192
193                         if( queryResults.isEmpty()){
194                                 LOGGER.warn("No nodes found - apipe was null/empty");
195                         }
196                         else {                                          
197                                 
198                                 Introspector searchResults = createSearchResults(loader, urlBuilder, queryResults);
199
200                                 String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
201                                 org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
202                                                 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
203
204                                 result = searchResults.marshal(properties);
205                                 response = Response.ok().entity(result).build();
206
207                                 LOGGER.debug(ver.size() + " node(s) traversed, " + queryResults.size() + " found");
208                         }
209                         success = true;
210                 } catch (AAIException e) { 
211                         success = false;
212                         throw e;
213                 } catch (Exception e) {
214                         success = false;
215                         throw new AAIException("AAI_5105", e);
216                 } finally {
217                         if (dbEngine != null) {
218                                 if (success) {
219                                         dbEngine.commit();
220                                 } else {
221                                         dbEngine.rollback();
222                                 }
223                         }
224
225                 }
226
227                 return response;        
228         }       
229
230         private URI craftUriFromQueryParams(Loader loader, String startNodeType, List<String> startNodeKeyParams) throws UnsupportedEncodingException, IllegalArgumentException, UriBuilderException, AAIException {
231                 Introspector relationship = loader.introspectorFromName("relationship");
232                 
233                 relationship.setValue("related-to", startNodeType);
234                 List<Object> relationshipDataList = relationship.getValue("relationship-data");
235
236                 for( String keyData : startNodeKeyParams ){ 
237                         int colonIndex = keyData.indexOf(":");
238                         if( colonIndex <= 0 ){
239                                 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); 
240                         }
241                         else {
242                                 Introspector data = loader.introspectorFromName("relationship-data");
243                                 data.setValue("relationship-key", keyData.substring(0, colonIndex));
244                                 data.setValue("relationship-value", keyData.substring(colonIndex + 1));
245                                 relationshipDataList.add(data.getUnderlyingObject());
246                         }
247                 }
248                 
249                 RelationshipToURI parser = new RelationshipToURI(loader, relationship);
250
251                 return parser.getUri();
252         }
253
254         /**
255          * Run nodes query.
256          *
257          * @param fromAppId the from app id
258          * @param transId the trans id
259          * @param targetNodeType the target node type
260          * @param edgeFilterParams the edge filter params
261          * @param filterParams the filter params
262          * @param aaiExtMap the aai ext map
263          * @return Response
264          * @throws AAIException the AAI exception
265          */
266         public Response runNodesQuery (
267                         HttpHeaders headers,
268                         String targetNodeType,
269                         List <String> edgeFilterParams,
270                         List <String> filterParams,
271                         TransactionalGraphEngine dbEngine,
272                         Loader loader,
273                         UrlBuilder urlBuilder) throws AAIException {
274                 
275                 Response response = null;
276                 boolean success = true;
277         String result = "";
278                 final String EQUALS = "EQUALS";
279                 final String DOES_NOT_EQUAL = "DOES-NOT-EQUAL";
280                 final String EXISTS = "EXISTS";
281                 final String DOES_NOT_EXIST = "DOES-NOT-EXIST";
282                 try {
283                         
284                         dbEngine.startTransaction();
285                         
286                         Introspector target;
287                         
288                         if( targetNodeType == null || targetNodeType == "" ){
289                                 throw new AAIException("AAI_6120", "null or empty target-node-type passed to the node query"); 
290                         }
291
292                         try {
293                                 target = loader.introspectorFromName(targetNodeType);
294                         } catch (AAIUnknownObjectException e) {
295                                 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + targetNodeType + "] passed to node query."); 
296                         }
297                         
298                         if( filterParams.isEmpty()  && edgeFilterParams.isEmpty()){
299                                 // For now, it's ok to pass no filter params.  We'll just return ALL the nodes of the requested type.
300                                 LOGGER.warn("No filters passed to the node query");
301                         }
302
303                         StringBuilder queryStringForMsg = new StringBuilder();  
304                         GraphTraversal<Vertex, Vertex> traversal  = dbEngine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE, targetNodeType);
305                         queryStringForMsg.append("has(\"aai-node-type\"," + targetNodeType + ")");
306                         
307                         for( String filter : filterParams ) {
308                                 String [] pieces = filter.split(":");
309                                 if( pieces.length < 2 ){
310                                         throw new AAIException("AAI_6120", "bad filter passed to node query: [" + filter + "]"); 
311                                 }
312                                 else {
313                                         String propName = this.findDbPropName(target, pieces[0]);
314                                         String filterType = pieces[1];
315                                         if( filterType.equals(EQUALS)){
316                                                 if( pieces.length < 3 ){ 
317                                                         throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); 
318                                                 }
319                                                 String value = "?";
320                                                 if( pieces.length == 3 ){
321                                                         value = pieces[2];
322                                                 }
323                                                 else if( pieces.length > 3 ){
324                                                         // When a ipv6 address comes in as a value, it has colons in it which require us to 
325                                                         // pull the "value" off the end of the filter differently
326                                                         int startPos4Value = propName.length() + filterType.length() + 3;
327                                                         value = filter.substring(startPos4Value);
328                                                 }
329                                                 queryStringForMsg.append(".has(" + propName + "," + value + ")");
330                                                 traversal.has(propName,value);
331                                         }
332                                         else if( filterType.equals(DOES_NOT_EQUAL)){
333                                                 if( pieces.length < 3 ){
334                                                         throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); 
335                                                 }
336                                                 String value = "?";
337                                                 if( pieces.length == 3 ){
338                                                         value = pieces[2];
339                                                 }
340                                                 else if( pieces.length > 3 ){
341                                                         // When a ipv6 address comes in as a value, it has colons in it which require us to 
342                                                         // pull the "value" off the end of the filter differently
343                                                         int startPos4Value = propName.length() + filterType.length() + 3;
344                                                         value = filter.substring(startPos4Value);
345                                                 }
346                                                 queryStringForMsg.append(".hasNot(" + propName + "," + value + ")");
347                                                 traversal.not(__.has(propName,value));
348                                         }
349                                         else if( filterType.equals(EXISTS)){
350                                                 queryStringForMsg.append(".has(" + propName + ")");
351                                                 traversal.has(propName);
352                                         }
353                                         else if( filterType.equals(DOES_NOT_EXIST)){
354                                                 queryStringForMsg.append(".hasNot(" + propName + ")");
355                                                 traversal.hasNot(propName);
356                                         }
357                                         else {
358                                                 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); 
359                                         }
360                                 }
361                         }
362
363                         if (!edgeFilterParams.isEmpty()) {
364                                 // edge-filter=pserver:EXISTS: OR pserver:EXISTS:hostname:XXX
365                                 // edge-filter=pserver:DOES-NOT-EXIST: OR pserver:DOES-NOT-EXIST:hostname:XXX
366                                 String filter = edgeFilterParams.get(0); // we process and allow only one edge filter for now
367                                 String [] pieces = filter.split(":");
368                                 if( pieces.length < 2 || pieces.length == 3 || pieces.length > 4){
369                                         throw new AAIException("AAI_6120", "bad edge-filter passed: [" + filter + "]"); 
370                                 } else {
371                                         String nodeType = pieces[0].toLowerCase();
372                                         String filterType = pieces[1].toUpperCase();
373                                         Introspector otherNode;
374                                         if (!filterType.equals(EXISTS) && !filterType.equals(DOES_NOT_EXIST)) {
375                                                 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); 
376                                         }
377                                         try {
378                                                 otherNode = loader.introspectorFromName(nodeType);
379                                         } catch (AAIUnknownObjectException e) {
380                                                 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to node query."); 
381                                         }
382                                         String propName = null;
383                                         String propValue = null;
384                                         if ( pieces.length >= 3) {
385                                                 propName = this.findDbPropName(otherNode, pieces[2].toLowerCase());
386                                                 propValue = pieces[3];
387                                         }
388                                         String[] edgeLabels = getEdgeLabel(targetNodeType, nodeType);
389                                         
390                                         GraphTraversal<Vertex, Vertex> edgeSearch = __.start();
391                                         
392                                         edgeSearch.both(edgeLabels).has(AAIProperties.NODE_TYPE, nodeType);
393                                         if (propName != null) {
394                                                 // check for matching property
395                                                 if (propValue != null) {
396                                                         edgeSearch.has(propName, propValue);
397                                                 } else {
398                                                         edgeSearch.has(propName);
399                                                 }
400                                         }
401                                         
402                                         if( filterType.equals(DOES_NOT_EXIST)){
403                                                 traversal.where(__.not(edgeSearch));
404                                         } else if (filterType.equals(EXISTS)) {
405                                                 traversal.where(edgeSearch);
406                                         }
407                                 }
408                         }
409
410                         List<Vertex> results = traversal.toList();
411                         Introspector searchResults = createSearchResults(loader, urlBuilder, results);
412
413                         String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
414                         org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
415                                         org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
416
417                         result = searchResults.marshal(properties);
418                         response = Response.ok().entity(result).build();
419
420                         success = true;
421                 } catch (AAIException e) { 
422                         success = false;
423                         throw e;
424                 } catch (Exception e) {
425                         success = false;
426                         throw new AAIException("AAI_5105", e);
427                 } finally {
428                         if (dbEngine != null) {
429                                 if (success) {
430                                         dbEngine.commit();
431                                 } else {
432                                         dbEngine.rollback();
433                                 }
434                         }
435                 }
436
437                 return response;        
438         }
439
440         protected Introspector createSearchResults(Loader loader, UrlBuilder urlBuilder, List<Vertex> results)
441                         throws AAIUnknownObjectException {
442                 Introspector searchResults = loader.introspectorFromName("search-results");
443                 List<Object> resultDataList = searchResults.getValue("result-data");
444                 Stream<Vertex> stream;
445                 if (results.size() >= 50) {
446                         stream = results.parallelStream();
447                 } else {
448                         stream = results.stream();
449                 }
450                 boolean isParallel = stream.isParallel();
451                 stream.forEach(v -> {
452                         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
453                         
454                         String thisNodeURL;
455                         try {
456                                 thisNodeURL = urlBuilder.pathed(v);
457                                 Introspector resultData = loader.introspectorFromName("result-data");
458
459                                 resultData.setValue("resource-type", nodeType);
460                                 resultData.setValue("resource-link", thisNodeURL);
461                                 if (isParallel) {
462                                         synchronized (resultDataList) {
463                                                 resultDataList.add(resultData.getUnderlyingObject());
464                                         }
465                                 } else {
466                                         resultDataList.add(resultData.getUnderlyingObject());
467                                 }
468                         } catch (AAIException | AAIFormatVertexException e) {
469                                 throw new RuntimeException(e);
470                         }
471                         
472                 });
473                 return searchResults;
474         }
475
476         private String findDbPropName(Introspector obj, String propName) {
477                 
478                 Optional<String> result = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS);
479                 if (result.isPresent()) {
480                         return result.get();
481                 } else {
482                         return propName;
483                 }
484         }
485
486
487         /**
488          * Gets the edge label.
489          *
490          * @param targetNodeType the target node type
491          * @param nodeType the node type
492          * @return the edge label
493          * @throws AAIException the AAI exception
494          */
495         public static String[] getEdgeLabel(String targetNodeType, String nodeType) throws AAIException{
496                 Map<String, EdgeRule> rules = EdgeRules.getInstance().getEdgeRules(targetNodeType, nodeType);
497                 String[] results = rules.keySet().toArray(new String[0]);
498                 return results;
499         }
500
501
502         /**
503          * Run named query.
504          *
505          * @param fromAppId the from app id
506          * @param transId the trans id
507          * @param queryParameters the query parameters
508          * @param aaiExtMap the aai ext map
509          * @return the response
510          * @throws JAXBException the JAXB exception
511          * @throws AAIException the AAI exception
512          */
513         public Response runNamedQuery(String fromAppId, String transId, String queryParameters,
514                         DBConnectionType connectionType,
515                         AAIExtensionMap aaiExtMap) throws JAXBException, AAIException {
516
517                 Introspector inventoryItems;
518                 boolean success = true;
519                 TransactionalGraphEngine dbEngine = null;
520                 try {
521                         
522                         MoxyLoader loader = (MoxyLoader)LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
523                         DynamicJAXBContext jaxbContext = loader.getJAXBContext();
524                         dbEngine = new TitanDBEngine(
525                                         QueryStyle.TRAVERSAL,
526                                         connectionType,
527                                         loader);
528                         DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
529                         ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
530
531                         dbEngine.startTransaction();
532                         org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
533                         String contentType = aaiExtMap.getHttpServletRequest().getContentType();
534                         if (contentType != null && contentType.contains("application/xml")) {
535                                 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
536                         }
537
538                         if (queryParameters.length() == 0) { 
539                                 queryParameters = "{}";
540                         }
541
542                         DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
543                         if (modelAndNamedQuerySearch == null) { 
544                                 throw new AAIException("AAI_5105");
545                         }
546                         HashMap<String,Object> namedQueryLookupHash = new HashMap<String,Object>();
547
548                         DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
549                         String namedQueryUuid = null;
550                         if ((qp != null) && qp.isSet("namedQuery")) {    
551                                 DynamicEntity namedQuery = (DynamicEntity) qp.get("namedQuery");
552
553                                 if (namedQuery.isSet("namedQueryUuid")) { 
554                                         namedQueryUuid = namedQuery.get("namedQueryUuid");
555                                 }
556                                 if (namedQuery.isSet("namedQueryName")) { 
557                                         namedQueryLookupHash.put("named-query-name",  namedQuery.get("namedQueryName"));
558                                 }
559                                 if (namedQuery.isSet("namedQueryVersion")) { 
560                                         namedQueryLookupHash.put("named-query-version", namedQuery.get("namedQueryVersion"));
561                                 }
562                         }
563
564                         if (namedQueryUuid == null) { 
565
566                                 DbMethHelper dbMethHelper = new DbMethHelper(loader, dbEngine);
567                                 List<Vertex> namedQueryVertices = dbMethHelper.locateUniqueVertices("named-query", namedQueryLookupHash);
568                                 for (Vertex vert : namedQueryVertices) { 
569                                         namedQueryUuid = vert.<String>property("named-query-uuid").orElse(null); 
570                                         // there should only be one, we'll pick the first if not
571                                         break;
572                                 }
573                         }
574                         
575                         String secondaryFilterCutPoint = null;
576                         
577                         if (modelAndNamedQuerySearch.isSet("secondaryFilterCutPoint")) { 
578                                 secondaryFilterCutPoint = modelAndNamedQuerySearch.get("secondaryFilterCutPoint");
579                         }
580                         
581                         List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
582
583                         mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), 
584                                         startNodeFilterHash, jaxbContext);                      
585
586                         Map<String,Object> secondaryFilterHash = new HashMap<>();
587                         
588                         mapSecondaryFilters((DynamicEntity)modelAndNamedQuerySearch.get("secondaryFilts"), 
589                                         secondaryFilterHash, jaxbContext);                      
590                         
591                         List<ResultSet> resultSet = processor.queryByNamedQuery(transId, fromAppId,
592                                         namedQueryUuid, startNodeFilterHash, aaiExtMap.getApiVersion(), secondaryFilterCutPoint, secondaryFilterHash);
593
594                         inventoryItems = loader.introspectorFromName("inventory-response-items");
595
596                         List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
597
598                         inventoryItems.setValue("inventory-response-item", invItemList);
599                         success = true;
600                 } catch (AAIException e) {
601                         success = false;
602                         throw e;
603                 } catch (Exception e) {
604                         success = false;
605                         throw new AAIException("AAI_5105", e);
606                 } finally {
607                         if (dbEngine != null) {
608                                 if (success) {
609                                         dbEngine.commit();
610                                 } else {
611                                         dbEngine.rollback();
612                                 }
613                         }
614                 }
615
616                 return getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
617         }
618
619         /**
620          * Execute model operation.
621          *
622          * @param fromAppId the from app id
623          * @param transId the trans id
624          * @param queryParameters the query parameters
625          * @param isDelete the is delete
626          * @param aaiExtMap the aai ext map
627          * @return the response
628          * @throws JAXBException the JAXB exception
629          * @throws AAIException the AAI exception
630          * @throws DynamicException the dynamic exception
631          * @throws UnsupportedEncodingException the unsupported encoding exception
632          */
633         public Response executeModelOperation(String fromAppId, String transId, String queryParameters,
634                         DBConnectionType connectionType,
635                         boolean isDelete,
636                         AAIExtensionMap aaiExtMap) throws JAXBException, AAIException, DynamicException, UnsupportedEncodingException {
637                 Response response;
638                 boolean success = true;
639                 TransactionalGraphEngine dbEngine = null;
640                 try {
641                         
642                         MoxyLoader loader = (MoxyLoader) LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
643                         DynamicJAXBContext jaxbContext = loader.getJAXBContext();
644                         dbEngine = new TitanDBEngine(
645                                         QueryStyle.TRAVERSAL,
646                                         connectionType,
647                                         loader);
648                         DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
649                         ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
650                         dbEngine.startTransaction();
651
652
653                         org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
654                         String contentType = aaiExtMap.getHttpServletRequest().getContentType();
655                         if (contentType != null && contentType.contains("application/xml")) {
656                                 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
657                         }
658
659                         if (queryParameters.length() == 0) { 
660                                 queryParameters = "{}";
661                         }
662
663                         DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
664                         if (modelAndNamedQuerySearch == null) { 
665                                 throw new AAIException("AAI_5105");
666                         }
667
668                         Map<String,Object> modelQueryLookupHash = new HashMap<>();
669                         
670                         String modelVersionId = null;
671                         String modelName = null;
672                         String modelInvariantId = null;
673                         String modelVersion = null;
674                         String topNodeType = null;
675
676                         if (modelAndNamedQuerySearch.isSet("topNodeType")) { 
677                                 topNodeType = modelAndNamedQuerySearch.get("topNodeType");
678                         }
679                         
680                         // the ways to get a model:
681                         
682                         // 1.  model-version-id (previously model-name-version-id
683                         // 2.  model-invariant-id (previously model-id) + model-version
684                         // 3.  model-name + model-version
685                                         
686                         // we will support both using the OverloadedModel object in the v9 oxm.  This allows us to unmarshal
687                         // either an old-style model or new-style model + model-ver object
688                         if (modelAndNamedQuerySearch.isSet("queryParameters")) { 
689                                 DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
690
691                                 if (qp.isSet("model")) { 
692                                         DynamicEntity model = (DynamicEntity) qp.get("model");
693
694                                         // on an old-style model object, the following 4 attrs were all present
695                                         if (model.isSet("modelNameVersionId")) { 
696                                                 modelVersionId = model.get("modelNameVersionId");
697                                         }
698                                         if (model.isSet("modelId")) { 
699                                                 modelInvariantId =  model.get("modelId");
700                                         }
701                                         if (model.isSet("modelName")) {
702                                                 modelName = model.get("modelName");
703                                         }
704                                         if (model.isSet("modelVersion")) { 
705                                                 modelVersion =  model.get("modelVersion");
706                                         }
707
708                                         // new style splits model-invariant-id from the other 3 attrs.  This is 
709                                         // the only way to directly look up the model object
710                                         if (model.isSet("modelInvariantId")) { 
711                                                 modelInvariantId =  model.get("modelInvariantId");
712                                         }
713                                                                 
714                                         if (model.isSet("modelVers")) {
715                                                 // we know that this is new style, because modelVers was not an option
716                                                 // before v9
717                                                 DynamicEntity modelVers = (DynamicEntity) model.get("modelVers");
718                                                 if (modelVers.isSet("modelVer")) {
719                                                         List<DynamicEntity> modelVerList = modelVers.get("modelVer");
720                                                         // if they send more than one, too bad, they get the first one
721                                                         DynamicEntity modelVer = modelVerList.get(0);
722                                                         if (modelVer.isSet("modelName")) {
723                                                                 modelName = modelVer.get("modelName");
724                                                         }
725                                                         if (modelVer.isSet("modelVersionId")) { 
726                                                                 modelVersionId =  modelVer.get("modelVersionId");
727                                                         }
728                                                         if (modelVer.isSet("modelVersion")) { 
729                                                                 modelVersion =  modelVer.get("modelVersion");
730                                                         }
731                                                 }
732                                         }
733                                 }
734                         }
735                         
736                         List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
737
738                         String resourceVersion = mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), 
739                                         startNodeFilterHash, jaxbContext);      
740
741                         if (isDelete) {
742
743                                 List<ResultSet> resultSet = processor.queryByModel(transId, fromAppId,
744                                                 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
745
746                                 Map<Object,String> objectToVertMap = new HashMap<>();
747                                 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
748
749                                 ResultSet rs = resultSet.get(0);
750
751                                 Vertex firstVert = rs.getVert();
752                                 String restURI = serializer.getURIForVertex(firstVert).toString();
753                                 String notificationVersion = AAIProperties.LATEST.toString();
754                                 if (restURI.startsWith("/")) {
755                                         restURI = "/aai/" + notificationVersion + restURI;
756                                 } else {
757                                         restURI = "/aai/" + notificationVersion + "/" + restURI;
758                                 }
759                                 
760                                 Map<String,String> delResult = processor.runDeleteByModel( transId, fromAppId,
761                                                 modelVersionId, topNodeType, startNodeFilterHash.get(0), aaiExtMap.getApiVersion(), resourceVersion );
762
763                                 String resultStr = "";
764                                 for (Map.Entry<String,String> ent : delResult.entrySet()) { 
765                                         resultStr += "v[" + ent.getKey() + "] " + ent.getValue() + ",\n";
766                                 }
767                                 resultStr.trim();
768
769                                 // Note - notifications are now done down in the individual "remove" calls done in runDeleteByModel() above.
770
771                                 response = Response.ok(resultStr).build();
772
773                         } else {
774                                 List<ResultSet> resultSet = processor.queryByModel( transId, fromAppId,
775                                                 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
776
777                                 Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
778
779                                 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
780
781                                 inventoryItems.setValue("inventory-response-item", invItemList);
782
783                                 response = getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
784                         }
785                         success = true;
786                 } catch (AAIException e) {
787                         success = false;
788                         throw e;
789                 } catch (Exception e) {
790                         success = false;
791                         throw new AAIException("AAI_5105", e);
792                 } finally {
793                         if (dbEngine != null) {
794                                 if (success) {
795                                         dbEngine.commit();
796                                 } else {
797                                         dbEngine.rollback();
798                                 }
799                         }
800                 }
801
802                 return response;
803         }
804         
805         private Response getResponseFromIntrospector(Introspector obj, HttpHeaders headers) {
806                 boolean isJson = false;
807                 for (MediaType mt : headers.getAcceptableMediaTypes()) {
808                         if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
809                                 isJson = true;
810                                 break;
811                         }
812                 }
813                 org.onap.aai.introspection.MarshallerProperties properties;
814                 if (isJson) {
815                         properties = 
816                                         new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE).build();
817                 } else {
818                         properties = 
819                                         new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE).build();
820                 }
821                 
822                 String marshalledObj = obj.marshal(properties);
823                 return Response.ok().entity(marshalledObj).build();
824         }
825
826         /**
827          * Map instance filters.
828          *
829          * @param instanceFilters the instance filters
830          * @param startNodeFilterHash the start node filter hash
831          * @param jaxbContext the jaxb context
832          * @return the string
833          */
834         private String mapInstanceFilters(DynamicEntity instanceFilters, List<Map<String,Object>> startNodeFilterHash, DynamicJAXBContext jaxbContext) {                        
835
836                 if (instanceFilters == null || !instanceFilters.isSet("instanceFilter")) {
837                         return null;
838                 }
839                 @SuppressWarnings("unchecked")
840                 List<DynamicEntity> instanceFilter = (ArrayList<DynamicEntity>)instanceFilters.get("instanceFilter");
841                 String resourceVersion = null;
842
843                 for (DynamicEntity instFilt : instanceFilter) { 
844                         List<DynamicEntity> any = instFilt.get("any");
845                         HashMap<String,Object> thisNodeFilterHash = new HashMap<String,Object>();
846                         for (DynamicEntity anyEnt : any) { 
847                                 String clazz = anyEnt.getClass().getCanonicalName();
848                                 String simpleClazz = anyEnt.getClass().getSimpleName();
849
850                                 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
851
852                                 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
853
854                                 for (String propName : anyEntType.getPropertiesNames()) {
855                                         // hyphencase the prop and throw it on the hash
856                                         if (anyEnt.isSet(propName)) {
857                                                 thisNodeFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
858                                                 if (propName.equals("resourceVersion") && resourceVersion == null) { 
859                                                         resourceVersion = (String)anyEnt.get(propName);
860                                                 }
861                                         }
862                                 }
863                         }
864                         startNodeFilterHash.add(thisNodeFilterHash);
865                 }
866                 return resourceVersion;
867         }
868
869         /**
870          * Map secondary filters.
871          *
872          * @param secondaryFilts the secondary filters
873          * @param secondaryFilterHash the secondary filter hash
874          * @param jaxbContext the jaxb context
875          * @return the string
876          */
877         private void mapSecondaryFilters(DynamicEntity secondaryFilts, Map<String,Object> secondaryFilterHash, DynamicJAXBContext jaxbContext) {                        
878
879                 if (secondaryFilts == null || !secondaryFilts.isSet("secondaryFilt")) {
880                         return;
881                 }
882                 @SuppressWarnings("unchecked")
883                 List<DynamicEntity> secondaryFilter = (ArrayList<DynamicEntity>)secondaryFilts.get("secondaryFilt");
884                 
885                 for (DynamicEntity secondaryFilt : secondaryFilter) { 
886                         List<DynamicEntity> any = secondaryFilt.get("any");
887                         
888                         for (DynamicEntity anyEnt : any) { 
889                                 String clazz = anyEnt.getClass().getCanonicalName();
890                                 String simpleClazz = anyEnt.getClass().getSimpleName();
891
892                                 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
893
894                                 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
895
896                                 for (String propName : anyEntType.getPropertiesNames()) {
897                                         // hyphencase the prop and throw it on the hash
898                                         if (anyEnt.isSet(propName)) {
899                                                 secondaryFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
900                                         }
901                                 }
902                         }
903                 }
904         }
905         
906         /**
907          * Remap inventory items.
908          *
909          * @param invResultItem the inv result item
910          * @param jaxbContext the jaxb context
911          * @param includeTheseVertices the include these vertices
912          * @param objectToVertMap the object to vert map
913          * @param aaiExtMap the aai ext map
914          * @return the dynamic entity
915          */
916         private DynamicEntity remapInventoryItems(DynamicEntity invResultItem, DynamicJAXBContext jaxbContext, 
917                         Map<String,String> includeTheseVertices, Map<Object,String> objectToVertMap, AAIExtensionMap aaiExtMap) { 
918
919
920                 DynamicEntity inventoryItem = jaxbContext.newDynamicEntity("inventory.aai.onap.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItem");
921                 Object item = invResultItem.get("item");
922                 inventoryItem.set("modelName",                  invResultItem.get("modelName"));
923                 inventoryItem.set("item",                               item);
924                 inventoryItem.set("extraProperties",    invResultItem.get("extraProperties"));
925
926                 String vertexId = "";
927
928                 if (objectToVertMap.containsKey(item)) {
929                         vertexId = objectToVertMap.get(item);
930                 }
931
932                 if (includeTheseVertices.containsKey(vertexId)) { 
933                         if (invResultItem.isSet("inventoryResponseItems")) {
934                                 List<DynamicEntity> invItemList = new ArrayList<DynamicEntity>();
935                                 DynamicEntity inventoryItems = jaxbContext.newDynamicEntity("inventory.aai.att.com." + aaiExtMap.getApiVersion() + ".InventoryResponseItems");
936                                 DynamicEntity subInventoryResponseItems = invResultItem.get("inventoryResponseItems");
937                                 List<DynamicEntity> subInventoryResponseItemList = subInventoryResponseItems.get("inventoryResponseItem");
938                                 for (DynamicEntity ent : subInventoryResponseItemList) { 
939                                         DynamicEntity invItem = remapInventoryItems(ent, jaxbContext, includeTheseVertices, objectToVertMap, aaiExtMap);
940                                         if (invItem != null) { 
941                                                 invItemList.add(invItem);
942                                         }
943                                 }
944                                 inventoryItems.set("inventoryResponseItem", invItemList);
945                                 inventoryItem.set("inventoryResponseItems",  inventoryItems);
946                         }
947                 }
948                 return inventoryItem;
949         }
950
951         /**
952          * Unpack result set.
953          *
954          * @param g the g
955          * @param resultSetList the result set list
956          * @param jaxbContext the jaxb context
957          * @param aaiResources the aai resources
958          * @param objectToVertMap the object to vert map
959          * @param aaiExtMap the aai ext map
960          * @return the array list
961          * @throws AAIException the AAI exception
962          */
963         // this should return an inventoryItem
964         private List<Object> unpackResultSet(List<ResultSet> resultSetList,
965                         TransactionalGraphEngine engine,
966                         Loader loader,
967                         DBSerializer serializer) throws AAIException {
968
969                 List<Object> resultList = new ArrayList<>();
970
971                 for (ResultSet resultSet : resultSetList) { 
972                         
973                         if( resultSet.getVert() == null ){
974                                 continue;
975                         }
976
977                         Introspector inventoryItem = loader.introspectorFromName("inventory-response-item");
978                         Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
979                         // add this inventoryItem to the resultList for this level
980                         resultList.add(inventoryItem.getUnderlyingObject());
981
982                         Vertex vert = resultSet.getVert();
983
984                         String aaiNodeType = vert.<String>property("aai-node-type").orElse(null);
985
986                         if (aaiNodeType != null) {
987                                 Introspector thisObj = loader.introspectorFromName(aaiNodeType);
988
989                                 if (resultSet.getExtraPropertyHash() != null) { 
990                                         Map<String,Object> extraProperties = resultSet.getExtraPropertyHash();  
991
992                                         Introspector extraPropertiesEntity = loader.introspectorFromName("extra-properties");
993
994                                         List<Object> extraPropsList = extraPropertiesEntity.getValue("extra-property");
995
996                                         for (Map.Entry<String,Object> ent : extraProperties.entrySet()) {
997                                                 String propName = ent.getKey();
998                                                 Object propVal = ent.getValue();
999
1000                                                 Introspector extraPropEntity = loader.introspectorFromName("extra-property");
1001
1002                                                 extraPropEntity.setValue("property-name",  propName);
1003                                                 extraPropEntity.setValue("property-value", propVal);
1004
1005                                                 extraPropsList.add(extraPropEntity.getUnderlyingObject());
1006
1007                                         }
1008                                         inventoryItem.setValue("extra-properties", extraPropertiesEntity.getUnderlyingObject());
1009                                 }
1010                                 
1011                                 try {
1012                                         serializer.dbToObject(Collections.singletonList(vert), thisObj, 0, true, "false");
1013                                 } catch (UnsupportedEncodingException  e1) {
1014                                         throw new AAIException("AAI_5105");
1015                                 }
1016                                 PropertyLimitDesc propertyLimitDesc = resultSet.getPropertyLimitDesc();
1017
1018                                 if (propertyLimitDesc != null) {
1019
1020                                         if (PropertyLimitDesc.SHOW_NONE.equals(propertyLimitDesc)) {
1021                                                 HashMap<String,Object> emptyPropertyOverRideHash = new HashMap<String,Object>();
1022                                                 for (String key : thisObj.getAllKeys()) {
1023                                                         emptyPropertyOverRideHash.put(key, null);
1024                                                 }
1025                                                 filterProperties(thisObj, emptyPropertyOverRideHash);
1026                                         } else if (PropertyLimitDesc.SHOW_ALL.equals(propertyLimitDesc)) { 
1027                                                 //keep everything
1028                                         } else if (PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY.equals(propertyLimitDesc)) {
1029                                                 HashMap<String,Object> keysAndNamesPropHash = new HashMap<String,Object>();
1030                                                 
1031                                                 for (String key : thisObj.getAllKeys()) {
1032                                                         keysAndNamesPropHash.put(key, null);
1033                                                 }
1034                                                 String namePropMetaData = thisObj.getMetadata(ObjectMetadata.NAME_PROPS);
1035                                                 if (namePropMetaData != null) {
1036                                                         String[] nameProps = namePropMetaData.split(",");
1037                                                         for (String names : nameProps) {
1038                                                                 keysAndNamesPropHash.put(names, null);
1039                                                         }
1040                                                 }
1041                                                 filterProperties(thisObj, keysAndNamesPropHash);
1042                                         }
1043                                 } else { 
1044                                         if (resultSet.getPropertyOverRideHash() != null && resultSet.getPropertyOverRideHash().size() > 0) { 
1045                                                 Map<String,Object> propertyOverRideHash = resultSet.getPropertyOverRideHash();
1046                                                 if (propertyOverRideHash.containsKey("persona-model-id")) {
1047                                                         propertyOverRideHash.remove("persona-model-id");
1048                                                         propertyOverRideHash.put("model-invariant-id", null);
1049                                                 }
1050                                                 for (String key : thisObj.getAllKeys()) {
1051                                                         propertyOverRideHash.put(key, null);
1052                                                 }
1053                                                 filterProperties(thisObj, propertyOverRideHash);
1054                                         } else {
1055                                                 //keep everything
1056                                         }
1057                                 }
1058
1059                                 if (thisObj != null) { 
1060                                         inventoryItem.setValue("item", thisObj.getUnderlyingObject());
1061
1062                                         String modelName = null;
1063                                         try { 
1064                                                 // Try to get the modelName if we can.  Otherwise, do not fail, just return what we have already.
1065                                                 String modelInvariantIdLocal = (String)vert.<String>property("model-invariant-id-local").orElse(null); // this one points at a model
1066                                                 String modelVersionIdLocal = (String)vert.<String>property("model-version-id-local").orElse(null); // this one points at a model-ver
1067                                                 
1068                                                 if ( (modelInvariantIdLocal != null && modelVersionIdLocal != null) 
1069                                                                 && (modelInvariantIdLocal.length() > 0 && modelVersionIdLocal.length() > 0) ) {
1070                                                         Introspector modelVer = loader.introspectorFromName("model-ver");
1071                                                         modelVer.setValue("model-version-id", modelVersionIdLocal);
1072                                                         QueryBuilder builder = engine.getQueryBuilder().createDBQuery(modelVer);
1073                                                         List<Vertex> modelVerVerts = builder.toList();
1074                                                         if( (modelVerVerts != null) && (modelVerVerts.size() == 1) ) {
1075                                                                 Vertex modelVerVert = modelVerVerts.get(0);
1076                                                                 modelName = modelVerVert.<String>property("model-name").orElse(null); 
1077                                                                 if (modelName != null && modelName.length() > 0) { 
1078                                                                         inventoryItem.setValue("model-name", modelName);
1079                                                                 }
1080                                                         }
1081                                                 }
1082                                         } catch (DynamicException e) { 
1083                                                 ; // it's ok, dynamic object might not have these fields
1084                                         } catch (Exception e) { 
1085                                                 ; // it's ok, couldn't find a matching model
1086                                         }
1087                                         
1088                                         if (resultSet.getSubResultSet() != null) { 
1089                                                 List<ResultSet> subResultSet = resultSet.getSubResultSet();
1090                                                 if (subResultSet != null && subResultSet.size() > 0 ) { 
1091                                                         List<Object> res = unpackResultSet(subResultSet, engine, loader, serializer);
1092                                                         if (res.size() > 0) { 
1093                                                                 inventoryItems.setValue("inventory-response-item", res);
1094                                                                 inventoryItem.setValue("inventory-response-items", inventoryItems.getUnderlyingObject());
1095                                                         }
1096                                                 }
1097                                         }
1098                                 }
1099                         }
1100                 }
1101
1102                 return resultList;
1103         }
1104         
1105         private void filterProperties(Introspector thisObj, Map<String, Object> override) {
1106                 
1107                 thisObj.getProperties().stream().filter(x -> {
1108                         return !override.containsKey(x);
1109                 }).forEach(prop -> {
1110                         if (thisObj.isSimpleType(prop)) {
1111                                 thisObj.setValue(prop, null);
1112                         }
1113                 });
1114
1115         }
1116
1117         /**
1118          * Gets the media type.
1119          *
1120          * @param mediaTypeList the media type list
1121          * @return the media type
1122          */
1123         protected String getMediaType(List <MediaType> mediaTypeList) {
1124                 String mediaType = MediaType.APPLICATION_JSON;  // json is the default
1125                 for (MediaType mt : mediaTypeList) {
1126                         if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
1127                                 mediaType = MediaType.APPLICATION_XML;
1128                         }
1129                 }
1130                 return mediaType;
1131         }
1132 }