Updating aai adapter to v16 model
[ccsdk/sli/adaptors.git] / aai-service / provider / src / main / java / org / onap / ccsdk / sli / adaptors / aai / AAIDeclarations.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *             reserved.
7  * ================================================================================
8  * Modifications Copyright (C) 2018 IBM.
9  * ================================================================================
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ============LICENSE_END=========================================================
22  */
23
24 /**
25  * @author Rich Tabedzki
26  *
27  */
28 package org.onap.ccsdk.sli.adaptors.aai;
29
30 import java.io.IOException;
31 import java.io.UnsupportedEncodingException;
32 import java.lang.annotation.Annotation;
33 import java.lang.reflect.Field;
34 import java.lang.reflect.InvocationTargetException;
35 import java.lang.reflect.Method;
36 import java.net.MalformedURLException;
37 import java.net.URISyntaxException;
38 import java.net.URL;
39 import java.net.URLDecoder;
40 import java.net.URLEncoder;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.LinkedList;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.SortedSet;
49 import java.util.TreeSet;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52
53 import javax.xml.bind.annotation.XmlType;
54
55 import org.apache.commons.lang.StringUtils;
56 import org.onap.ccsdk.sli.adaptors.aai.data.AAIDatum;
57 import org.onap.ccsdk.sli.adaptors.aai.query.FormattedQueryResultList;
58 import org.onap.ccsdk.sli.adaptors.aai.query.Result;
59 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
60 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
61 import org.onap.aai.inventory.v16.GenericVnf;
62 import org.onap.aai.inventory.v16.Image;
63 import org.onap.aai.inventory.v16.InventoryResponseItem;
64 import org.onap.aai.inventory.v16.InventoryResponseItems;
65 import org.onap.aai.inventory.v16.L3Network;
66 import org.onap.aai.inventory.v16.LogicalLink;
67 import org.onap.aai.inventory.v16.Metadata;
68 import org.onap.aai.inventory.v16.Metadatum;
69 import org.onap.aai.inventory.v16.Pnf;
70 import org.onap.aai.inventory.v16.RelatedToProperty;
71 import org.onap.aai.inventory.v16.Relationship;
72 import org.onap.aai.inventory.v16.RelationshipData;
73 import org.onap.aai.inventory.v16.RelationshipList;
74 import org.onap.aai.inventory.v16.ResultData;
75 import org.onap.aai.inventory.v16.SearchResults;
76 import org.onap.aai.inventory.v16.ServiceInstance;
77 import org.onap.aai.inventory.v16.Vlan;
78 import org.onap.aai.inventory.v16.Vlans;
79 import org.onap.aai.inventory.v16.Vserver;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82
83 import com.fasterxml.jackson.core.JsonParseException;
84 import com.fasterxml.jackson.databind.JsonMappingException;
85 import com.fasterxml.jackson.databind.ObjectMapper;
86
87
88 public abstract class AAIDeclarations implements AAIClient {
89
90     public static final String TRUSTSTORE_PATH    = "org.onap.ccsdk.sli.adaptors.aai.ssl.trust";
91     public static final String TRUSTSTORE_PSSWD   = "org.onap.ccsdk.sli.adaptors.aai.ssl.trust.psswd";
92     public static final String KEYSTORE_PATH      = "org.onap.ccsdk.sli.adaptors.aai.ssl.key";
93     public static final String KEYSTORE_PSSWD     = "org.onap.ccsdk.sli.adaptors.aai.ssl.key.psswd";
94
95     public static final String APPLICATION_ID     = "org.onap.ccsdk.sli.adaptors.aai.application";
96
97     public static final String CLIENT_NAME          = "org.onap.ccsdk.sli.adaptors.aai.client.name";
98     public static final String CLIENT_PWWD          = "org.onap.ccsdk.sli.adaptors.aai.client.psswd";
99
100
101     public static final String CONNECTION_TIMEOUT = "connection.timeout";
102     public static final String READ_TIMEOUT       = "read.timeout";
103
104     public static final String TARGET_URI         = "org.onap.ccsdk.sli.adaptors.aai.uri";
105
106     public static final String AAI_VERSION          = "org.onap.ccsdk.sli.adaptors.aai.version";
107
108     // Availability zones query
109     public static final String QUERY_PATH         = "org.onap.ccsdk.sli.adaptors.aai.path.query";
110
111     // Update
112     public static final String UPDATE_PATH          = "org.onap.ccsdk.sli.adaptors.aai.update";
113
114     // Service instance
115     public static final String SVC_INSTANCE_PATH  = "org.onap.ccsdk.sli.adaptors.aai.path.svcinst";
116     public static final String SVC_INST_QRY_PATH  = "org.onap.ccsdk.sli.adaptors.aai.path.svcinst.query";
117
118     // VServer
119     public static final String NETWORK_VSERVER_PATH  = "org.onap.ccsdk.sli.adaptors.aai.path.vserver";
120
121     public static final String VNF_IMAGE_QUERY_PATH      = "org.onap.ccsdk.sli.adaptors.aai.path.vnf.image.query";
122
123     public static final String PARAM_SERVICE_TYPE     = "org.onap.ccsdk.sli.adaptors.aai.param.service.type";
124     public static final String CERTIFICATE_HOST_ERROR = "org.onap.ccsdk.sli.adaptors.aai.host.certificate.ignore";
125
126     // UBB Notify
127     public static final String UBB_NOTIFY_PATH        = "org.onap.ccsdk.sli.adaptors.aai.path.notify";
128     public static final String SELFLINK_AVPN          = "org.onap.ccsdk.sli.adaptors.aai.notify.selflink.avpn";
129     public static final String SELFLINK_FQDN          = "org.onap.ccsdk.sli.adaptors.aai.notify.selflink.fqdn";
130
131     //Service
132     public static final String SERVICE_PATH              = "org.onap.ccsdk.sli.adaptors.aai.path.service";
133
134     // site-pair-sets
135     public static final String SITE_PAIR_SET_PATH     = "org.onap.ccsdk.sli.adaptors.aai.path.site.pair.set";
136
137     // node query (1602)
138     public static final String QUERY_NODES_PATH          = "org.onap.ccsdk.sli.adaptors.aai.query.nodes";
139
140     private static final String VERSION_PATTERN = "/v$/";
141  
142     private static final String AAI_SERVICE_EXCEPTION = "AAI Service Exception";
143
144     protected abstract Logger getLogger();
145     public abstract AAIExecutorInterface getExecutor();
146                                                                                 
147     private static final String RELATIONSHIP_DATA= "Retrofitting relationship data: ";
148
149
150     @Override
151     public QueryStatus query(String resource, boolean localOnly, String select, String key, String prefix, String orderBy, SvcLogicContext ctx)
152             throws SvcLogicException {
153
154         getLogger().debug("AAIService.query \tresource = "+resource);
155
156         String vnfId;
157         String vnfName = null;
158         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
159         getLogger().debug("key = "+ nameValues.toString());
160
161         if(!AAIServiceUtils.isValidFormat(resource, nameValues)) {
162             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported. Key string contains invaid identifiers", resource));
163             return QueryStatus.FAILURE;
164         }
165
166         if(resource == null || resource.isEmpty() || AAIRequest.createRequest(resource, nameValues) == null) {
167             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported", resource));
168             return QueryStatus.FAILURE;
169         }
170
171         // process data using new model
172         boolean useNewModelProcessing = true;
173         // process server query by name the old way
174         if("vserver".equals(resource) || "vserver2".equals(resource)){
175             if(nameValues.containsKey("vserver_name") || nameValues.containsKey("vserver-name") || nameValues.containsKey("vserver.vserver_name") || nameValues.containsKey("vserver.vserver-name"))
176                 useNewModelProcessing = false;
177         }
178         if("generic-vnf".equals(resource)){
179             if(nameValues.containsKey("vnf_name") || nameValues.containsKey("vnf-name") || nameValues.containsKey("generic_vnf.vnf_name") || nameValues.containsKey("generic-vnf.vnf-name"))
180                 useNewModelProcessing = false;
181         }
182
183         // process data using new model
184         if(useNewModelProcessing && AAIRequest.createRequest(resource, nameValues) != null) {
185
186             try {
187                 return newModelQuery(resource, localOnly, select, key, prefix, orderBy, ctx);
188             } catch (Exception exc) {
189                 getLogger().warn("Failed query - returning FAILURE", exc);
190                 return QueryStatus.FAILURE;
191             }
192         }
193
194         ObjectMapper mapper = AAIService.getObjectMapper();
195         Map<String,Object> attributes = new HashMap<>();
196
197         String modifier = null;
198
199         if(resource.contains(":")) {
200             String[] tokens = resource.split(":");
201             resource = tokens[0];
202             if(tokens.length > 1) {
203                 modifier = tokens[1];
204             }
205         }
206
207         resource = resource.toLowerCase().replace("-", "_");
208
209         try {
210
211             switch(resource) {
212                 case "generic_vnf":
213                     vnfId = nameValues.get("vnf_id");
214                     if(nameValues.containsKey("vnf_id"))
215                         vnfId = nameValues.get("vnf_id");
216                     else if(nameValues.containsKey("generic_vnf.vnf_name"))
217                         vnfId = nameValues.get("generic_vnf.vserver_name");
218
219                     if(nameValues.containsKey("vnf_name"))
220                         vnfName = nameValues.get("vnf_name");
221                     else if(nameValues.containsKey("generic_vnf.vnf_name"))
222                         vnfName = nameValues.get("generic_vnf.vnf_name");
223
224                     if(vnfId != null && !vnfId.isEmpty()) {
225                         // at this point of the project this part should not be executed
226                         vnfId = vnfId.trim().replace("'", "").replace("$", "").replace("'", "");
227                         GenericVnf vnf = this.requestGenericVnfData(vnfId);
228                         if(vnf == null) {
229                             return QueryStatus.NOT_FOUND;
230                         }
231
232                         attributes = mapper.convertValue(vnf, attributes.getClass());
233                     } else if(vnfName != null && !vnfName.isEmpty()) {
234                         try {
235                             vnfName = vnfName.trim().replace("'", "").replace("$", "").replace("'", "");
236                             GenericVnf vnf = this.requestGenericVnfeNodeQuery(vnfName);
237                             if(vnf == null) {
238                                 return QueryStatus.NOT_FOUND;
239                             }
240                             vnfId=vnf.getVnfId();
241                             nameValues.put("vnf_id", vnfId);
242                             attributes = mapper.convertValue(vnf, attributes.getClass());
243                         } catch (AAIServiceException exc) {
244                             int errorCode = exc.getReturnCode();
245                             switch(errorCode) {
246                                 case 400:
247                                 case 404:
248                                 case 412:
249                                     break;
250                                 default:
251                                     getLogger().warn("Caught exception trying to refresh generic VNF", exc);
252                             }
253                             ctx.setAttribute(prefix + ".error.message", exc.getMessage());
254                             if(errorCode >= 300) {
255                                 ctx.setAttribute(prefix + ".error.http.response-code",
256                                         Integer.toString(exc.getReturnCode()));
257                             }
258                             return QueryStatus.FAILURE;
259                         }
260                     } else {
261                         getLogger().warn("No arguments are available to process generic VNF");
262                         return QueryStatus.FAILURE;
263                     }
264                     break;
265                 case "vserver":
266                 case "vserver2":
267                     String vserverName = null;
268                     if(nameValues.containsKey("vserver_name"))
269                         vserverName = nameValues.get("vserver_name");
270                     else if(nameValues.containsKey("vserver.vserver_name"))
271                         vserverName = nameValues.get("vserver.vserver_name");
272
273                     String vserverId = null;
274                     if(nameValues.containsKey("vserver_id"))
275                         vserverId = nameValues.get("vserver_id");
276                     if(nameValues.containsKey("vserver.vserver_id"))
277                         vserverId = nameValues.get("vserver.vserver_id");
278                     String tenantId = nameValues.get("teannt_id");
279
280                     if(vserverName != null) vserverName = vserverName.trim().replace("'", "").replace("$", "").replace("'", "");
281                     if(vserverId != null) vserverId = vserverId.trim().replace("'", "").replace("$", "").replace("'", "");
282                     if(tenantId != null) tenantId = tenantId.trim().replace("'", "").replace("$", "").replace("'", "");
283
284                     if (vserverName != null) {
285                         URL vserverUrl = null;
286                         try {
287                             vserverUrl = this.requestVserverURLNodeQuery(vserverName);
288                         } catch (AAIServiceException aaiexc) {
289                             getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
290                             ctx.setAttribute(prefix + ".error.message", aaiexc.getMessage());
291                             if (aaiexc.getReturnCode() >= 300) {
292                                 ctx.setAttribute(prefix + ".error.http" + "" + ".response-code", Integer.toString(aaiexc.getReturnCode()));
293                             }
294
295                             if (aaiexc.getReturnCode() == 404)
296                                 return QueryStatus.NOT_FOUND;
297                             else
298                                 return QueryStatus.FAILURE;
299                         }
300                         if (vserverUrl == null) {
301                             return QueryStatus.NOT_FOUND;
302                         }
303
304                         tenantId = getTenantIdFromVserverUrl(vserverUrl);
305                         String cloudOwner = getCloudOwnerFromVserverUrl(vserverUrl);
306                         String cloudRegionId = getCloudRegionFromVserverUrl(vserverUrl);
307
308                         Vserver vserver = null;
309                         try {
310                             vserver = this.requestVServerDataByURL(vserverUrl);
311                         } catch (AAIServiceException aaiexc) {
312                             getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
313                             ctx.setAttribute(prefix + ".error.message", aaiexc.getMessage());
314                             if (aaiexc.getReturnCode() >= 300) {
315                                 ctx.setAttribute(prefix + ".error.http" + ".response-code", Integer.toString(aaiexc.getReturnCode()));
316                             }
317
318                             if (aaiexc.getReturnCode() == 404)
319                                 return QueryStatus.NOT_FOUND;
320                             else
321                                 return QueryStatus.FAILURE;
322                         }
323                         if (vserver == null) {
324                             return QueryStatus.NOT_FOUND;
325                         }
326                         attributes = mapper.convertValue(vserver, attributes.getClass());
327                         if (!attributes.containsKey("tenant-id") && tenantId != null) {
328                             attributes.put("tenant-id", tenantId);
329                         }
330                         if (!attributes.containsKey("cloud-owner") && cloudOwner != null) {
331                             attributes.put("cloud-owner", cloudOwner);
332                         }
333                         if (!attributes.containsKey("cloud-region-id") && cloudRegionId != null) {
334                             attributes.put("cloud-region-id", cloudRegionId);
335                         }
336                     } else if (vserverId != null && tenantId != null) {
337                         Vserver vserver = this.requestVServerData(tenantId, vserverId, "att-aic", "AAIAIC25");
338                         if(vserver == null) {
339                             return QueryStatus.NOT_FOUND;
340                         }
341                         attributes = mapper.convertValue(vserver, attributes.getClass());
342                         if(!attributes.containsKey("tenant-id") && tenantId != null){
343                             attributes.put("tenant-id", tenantId);
344                         }
345                     } else {
346                         return QueryStatus.FAILURE;
347                     }
348                     break;
349
350                 default:
351                     return QueryStatus.FAILURE;
352             }
353
354             QueryStatus retval = QueryStatus.SUCCESS;
355
356             if (attributes == null || attributes.isEmpty()) {
357                 retval = QueryStatus.NOT_FOUND;
358                 getLogger().debug("No data found");
359             } else {
360                 if (ctx != null) {
361                     if (prefix != null) {
362                         ArrayList<String> keys = new ArrayList<>(attributes.keySet());
363
364                         int numCols = keys.size();
365
366                         for (int i = 0; i < numCols; i++) {
367                             String colValue;
368                             String colName = keys.get(i);
369                             Object object = attributes.get(colName);
370
371                             if(object != null && object instanceof String) {
372                                 colValue = (String)object;
373
374                                 if (prefix != null) {
375                                     getLogger().debug("Setting "+prefix    + "." + colName.replaceAll("_", "-")+" = "+ colValue);
376                                     ctx.setAttribute(prefix    + "." + colName.replaceAll("_", "-"), colValue);
377                                 } else {
378                                     getLogger().debug("Setting " + colValue.replaceAll("_", "-")+" = "+colValue);
379                                     ctx.setAttribute(colValue.replaceAll("_", "-"), colValue);
380                                 }
381                             } else if(object != null && object instanceof Map) {
382                                 if(colName.equals(modifier) || "relationship-list".equals(colName)){
383                                     String localNodifier = modifier;
384                                     if(localNodifier == null)
385                                         localNodifier = "relationship-list";
386                                     Map<String, Object> properties = (Map<String, Object>)object;
387                                     writeMap(properties, prefix+"."+localNodifier,  ctx);
388                                 }
389                             }
390                         }
391                     }
392                 }
393             }
394             getLogger().debug("Query - returning " + retval);
395             return retval;
396
397         } catch (Exception exc) {
398             getLogger().warn("Failed query - returning FAILURE", exc);
399             return QueryStatus.FAILURE;
400         }
401     }
402
403
404     public void writeMap(Map<String, Object> properties, String prefix, SvcLogicContext ctx) {
405         Set<String> mapKeys = properties.keySet();
406
407         for(String mapKey : mapKeys) {
408             Object entity = properties.get(mapKey);
409             if(entity instanceof ArrayList) {
410                 writeList((ArrayList<?>)entity, prefix + "." + mapKey, ctx);
411             } else
412             if(entity instanceof String ||  entity instanceof Long || entity instanceof Integer || entity instanceof Boolean) {
413                 ctx.setAttribute(prefix + "." + mapKey, entity.toString());
414                 getLogger().debug(prefix + "." + mapKey + " : " + entity.toString());
415             } else if(entity instanceof Map) {
416                 String localPrefix = prefix;
417                 if(mapKey != null) {
418                     localPrefix = String.format("%s.%s", prefix, mapKey);
419                 }
420                 writeMap( (Map<String, Object>)entity,  localPrefix,  ctx);
421             }
422         }
423     }
424
425     private void writeList(ArrayList<?> list, String prefix, SvcLogicContext ctx) {
426         for(int i = 0; i < list.size(); i++ ) {
427             Object entity = list.get(i);
428             if(entity instanceof Map) {
429                 writeMap( (Map<String, Object>)entity,  prefix + "[" + i + "]",  ctx);
430             } else
431             if(entity instanceof String ||  entity instanceof Long || entity instanceof Integer || entity instanceof Boolean) {
432                 ctx.setAttribute(prefix, entity.toString());
433                 getLogger().debug(prefix  + " : " + entity.toString());
434             }
435         }
436
437         if(!list.isEmpty()) {
438             ctx.setAttribute(prefix + "_length", Integer.toString(list.size()));
439             getLogger().debug(prefix + "_length"  + " : " + Integer.toString(list.size()));
440         }
441     }
442
443     @Override
444     public QueryStatus save(String resource, boolean force, boolean localOnly, String key, Map<String, String> params, String prefix, SvcLogicContext ctx)
445             throws SvcLogicException {
446
447         getLogger().debug("AAIService.save\tresource="+resource);
448         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
449
450         if(!AAIServiceUtils.isValidFormat(resource, nameValues)) {
451             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported. Key string contains invaid identifiers", resource));
452             return QueryStatus.FAILURE;
453         }
454
455         if(resource == null || resource.isEmpty() || AAIRequest.createRequest(resource, nameValues) == null) {
456             getLogger().warn("AAIService.save has unspecified resource");
457             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported", resource));
458             return QueryStatus.FAILURE;
459         }
460         // keys passed
461         getLogger().debug("key = "+ Arrays.toString(nameValues.entrySet().toArray()));
462
463         // process params
464         if(params.containsKey("prefix")) {
465             Map<String, String> tmpParams = ctxGetBeginsWith(ctx, params.get("prefix"));
466             if(!tmpParams.isEmpty()) {
467                 params.putAll(tmpParams);
468 //                params.remove("prefix");
469             }
470         }
471         // params passed
472         getLogger().debug("parms = "+ Arrays.toString(params.entrySet().toArray()));
473
474         boolean useNewModelProcessing = true;
475         // process server query by name the old way
476         if("vserver".equals(resource) || "vserver2".equals(resource)){
477             if(nameValues.containsKey("vserver-name")) {
478                 useNewModelProcessing = false;
479             }
480
481             if(!params.containsKey("vserver-selflink")) {
482
483                 AAIRequest request = AAIRequest.createRequest(resource, nameValues);
484                 URL path = null;
485                 try {
486                     request.processRequestPathValues(nameValues);
487                     path = request.getRequestUrl("GET", null);
488                     params.put("vserver-selflink", path.toString());
489                 } catch (UnsupportedEncodingException | MalformedURLException | URISyntaxException e) {
490                     getLogger().warn("URL error Exception", e);
491                     params.put("vserver-selflink", "/vserver");
492                 }
493             }
494         }
495
496         // process data using new model
497         if(useNewModelProcessing && AAIRequest.createRequest(resource, nameValues) != null) {
498
499             try {
500                 if(!resource.contains(":")){
501                     return newModelSave(resource, force, key, params, prefix, ctx);
502                 } else {
503                     String[] tokens = resource.split(":");
504                     String localResource = tokens[0];
505                     String dependency = tokens[1];
506
507                     AAIDatum instance = newModelObjectRequest( localResource, nameValues, prefix, ctx);
508                     if(instance == null) {
509                         return QueryStatus.NOT_FOUND;
510                     }
511
512                     switch(dependency){
513                         case "relationship-list":
514                             newModelProcessRelationshipList(instance, params, prefix, ctx);
515                             break;
516                         case "metadata":
517                             newModelProcessMetadata(instance, params, prefix, ctx);
518                             break;
519                     }
520                     // create a method to update relationship-list
521                     AAIRequest request = AAIRequest.createRequest(localResource, nameValues);
522                     request.setRequestObject(instance);
523                     request.processRequestPathValues(nameValues);
524
525                     getExecutor().post(request);
526                     getLogger().debug("Save relationship list - returning SUCCESS");
527                     return QueryStatus.SUCCESS;
528                 }
529             } catch (Exception exc) {
530                 ctx.setAttribute(prefix + ".error.message", exc.getMessage());
531                 if(exc instanceof AAIServiceException) {
532                     AAIServiceException aaiexc = (AAIServiceException)exc;
533                     if(aaiexc.getReturnCode() >= 300) {
534                         ctx.setAttribute(prefix + ".error.http" + ".response-code", Integer.toString(aaiexc.getReturnCode()));
535                     }
536
537                     if(aaiexc.getReturnCode() == 404) {
538                         return QueryStatus.NOT_FOUND;
539                     }
540                 }
541                 getLogger().warn("Failed save() - returning FAILURE", exc);
542                 return QueryStatus.FAILURE;
543             }
544         } else {
545             getLogger().debug("Save() request for {} is not supported- returning FAILURE", resource);
546             return QueryStatus.FAILURE;
547         }
548     }
549
550     @Override
551     public QueryStatus update(String resource, String key, Map<String, String> params, String prefix, SvcLogicContext ctx) throws SvcLogicException {
552
553         resource = resource.toLowerCase();
554         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
555         getLogger().debug("key = "+ Arrays.toString(nameValues.entrySet().toArray()));
556         if(!AAIServiceUtils.isValidFormat(resource, nameValues)) {
557             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported. Key string contains invaid identifiers", resource));
558             return QueryStatus.FAILURE;
559         }
560
561         if(resource == null || resource.isEmpty() || AAIRequest.createRequest(resource, nameValues) == null) {
562             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not supported", resource));
563             return QueryStatus.FAILURE;
564         }
565
566         // check if request is for groups
567         if(!AAIServiceUtils.containsResource(resource, nameValues)) {
568             ctx.setAttribute(String.format("%s.error.message", prefix), String.format("Resource %s is not permitted in 'update' operation", resource));
569             return QueryStatus.FAILURE;
570         }
571
572         getLogger().debug("parms = "+ Arrays.toString(params.entrySet().toArray()));
573
574         AAIRequest request = AAIRequest.createRequest(resource, nameValues);
575         request = new UpdateRequest(request, params);
576
577         String[] arguments = request.getArgsList();
578         for(String name : arguments) {
579             String modifiedKey = name.replaceAll("-", "_");
580             if(nameValues.containsKey(modifiedKey)) {
581                 String argValue = nameValues.get(modifiedKey);
582                 if(argValue != null) argValue = argValue.trim().replace("'", "").replace("$", "").replace("'", "");
583                 request.addRequestProperty(name, argValue);
584             }
585         }
586
587         try {
588             QueryStatus retval = QueryStatus.SUCCESS;
589
590             retval = newModelQuery(resource, false, null, key, "tmpDelete", null,  ctx);
591
592             if(retval == null || retval != QueryStatus.SUCCESS) {
593                 return retval;
594             }
595
596             String resourceVersion = ctx.getAttribute("tmpDelete.resource-version");
597             if(resourceVersion == null) {
598                 return QueryStatus.NOT_FOUND;
599             }
600             params.put("resource-version", resourceVersion);
601
602             request.processRequestPathValues(nameValues);
603             getExecutor().patch(request, resourceVersion);
604         } catch(AAIServiceException aaiexc) {
605             getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
606             if(aaiexc.getReturnCode() == 404)
607                 return QueryStatus.NOT_FOUND;
608             else
609                 return QueryStatus.FAILURE;
610         } catch (Exception exc) {
611             getLogger().warn("Failed update - returning FAILURE", exc);
612             return QueryStatus.FAILURE;
613         }
614
615         getLogger().debug("Update - returning SUCCESS");
616         return QueryStatus.SUCCESS;
617     }
618
619     @Override
620     public QueryStatus delete(String resource, String key, SvcLogicContext ctx) throws SvcLogicException {
621         getLogger().debug("AAIService.delete\tresource="+resource);
622         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
623         getLogger().debug("key = "+ Arrays.toString(nameValues.entrySet().toArray()));
624
625         if(!AAIServiceUtils.isValidFormat(resource, nameValues)) {
626             ctx.setAttribute(String.format("%s.error.message", "aaiData"), String.format("Resource %s is not supported. Key string contains invaid identifiers", resource));
627             return QueryStatus.FAILURE;
628         }
629
630         if(resource == null || resource.isEmpty() || AAIRequest.createRequest(resource, nameValues) == null) {
631             ctx.setAttribute(String.format("%s.error.message", "tmpDelete"), String.format("Resource %s is not supported", resource));
632             return QueryStatus.FAILURE;
633         }
634
635         // check if request is for groups
636         if(!AAIServiceUtils.containsResource(resource, nameValues)) {
637             ctx.setAttribute(String.format("%s.error.message", "tmpDelete"), String.format("Resource %s is not permitted in 'delete' operation", resource));
638             return QueryStatus.FAILURE;
639         }
640
641         if(AAIRequest.createRequest(resource, nameValues) != null) {
642             if(resource.contains(":")) {
643                 switch (resource.split(":")[1]){
644                     case "relationship-list":
645                         return processDeleteRelationshipList(resource, key, ctx, nameValues);
646                     case "metadata":
647                         return processDeleteMetadata(resource, key, ctx, nameValues);
648                 }
649             }
650
651
652             try {
653                 QueryStatus retval = QueryStatus.SUCCESS;
654
655                 retval = newModelQuery(resource, false, null, key, "tmpDelete", null,  ctx);
656
657                 if(retval == null || retval != QueryStatus.SUCCESS) {
658                     return retval;
659                 }
660
661                 String resourceVersion = ctx.getAttribute("tmpDelete.resource-version");
662                 if(resourceVersion == null) {
663                     return QueryStatus.NOT_FOUND;
664                 }
665
666                 try {
667                     AAIRequest request = AAIRequest.createRequest(resource, nameValues);
668                     if(request == null) {
669                         return QueryStatus.FAILURE;
670                     }
671
672                     request.processRequestPathValues(nameValues);
673
674                     if(getExecutor().delete(request, resourceVersion)) {
675                         return QueryStatus.SUCCESS;
676                     }
677                 } catch(AAIServiceException aaiexc) {
678                     getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
679                     if(aaiexc.getReturnCode() == 404)
680                         return QueryStatus.NOT_FOUND;
681                     else
682                         return QueryStatus.FAILURE;
683
684                 } catch (Exception exc) {
685                     getLogger().warn("requestGenericVnfData", exc);
686                     return QueryStatus.FAILURE;
687                 }
688
689             } catch (Exception exc) {
690                 getLogger().warn("Failed delete - returning FAILURE", exc);
691                 return QueryStatus.FAILURE;
692             }
693         } else {
694             String resourceName = resource;
695             String identifier = null;
696
697             if(resourceName.contains(":")) {
698                 String[] tokens = resourceName.split(":");
699                 if(tokens != null && tokens.length > 0) {
700                     resourceName = tokens[0];
701                     identifier = tokens[1];
702                 }
703             }
704             if("relationship-list".equals(identifier) || "relationshipList".equals(identifier)) {
705 //                RelationshipRequest relationshipRequest = new RelationshipRequest();
706                 if("generic-vnf".equals(resourceName)){
707                     String vnfId = nameValues.get("vnf_id");
708                     String relatedTo  = nameValues.get("related_to");
709                     vnfId = vnfId.trim().replace("'", "").replace("$", "").replace("'", "");
710                     relatedTo = relatedTo.trim().replace("'", "").replace("$", "").replace("'", "");
711
712                     GenericVnf vnf;
713                     try {
714                         vnf = this.requestGenericVnfData(vnfId);
715                         if(vnf == null)
716                             return QueryStatus.NOT_FOUND;
717                     } catch (AAIServiceException exc) {
718                         getLogger().warn("Failed delete - returning NOT_FOUND", exc);
719                         return QueryStatus.NOT_FOUND;
720                     }
721                     boolean itemRemoved = false;
722                     RelationshipList relationshipList = vnf.getRelationshipList();
723                     List<Relationship> relationships = relationshipList.getRelationship();
724                     List<Relationship> iterableList = new LinkedList<>(relationships);
725                     for(Relationship relationship : iterableList) {
726                         if(relationship.getRelatedTo().equals(relatedTo)) {
727                             relationships.remove(relationship);
728                             itemRemoved = true;
729                         }
730                     }
731
732                     if(!itemRemoved)
733                         return QueryStatus.NOT_FOUND;
734                     try {
735                         this.postGenericVnfData(vnf.getVnfId(), vnf);
736                     } catch (AAIServiceException exc) {
737                         if(exc.getReturnCode() == 404){
738                             return QueryStatus.NOT_FOUND;
739                         } else {
740                             getLogger().warn("Failed delete - returning FAILURE", exc);
741                             return QueryStatus.FAILURE;
742                         }
743                     }
744                     return QueryStatus.SUCCESS;
745                 }
746             }
747         }
748         return QueryStatus.FAILURE;
749     }
750
751     @Override
752     public QueryStatus exists(String resource, String key, String prefix, SvcLogicContext ctx) throws SvcLogicException {
753         return query(resource, false, null, key, prefix, null, ctx);
754     }
755
756     @Override
757     public QueryStatus isAvailable(String arg0, String arg1, String arg2, SvcLogicContext arg3)
758             throws SvcLogicException {
759         throw new SvcLogicException("Method AAIService.isAvailable() has not been implemented yet");
760     }
761
762     @Override
763     public QueryStatus notify(String resource, String action, String key, SvcLogicContext ctx) throws SvcLogicException {
764         throw new SvcLogicException("Method AAIService.notify() has not been implemented yet");
765     }
766
767     //    @Override
768     public QueryStatus newModelQuery(String resource, boolean localOnly, String select, String key, String prefix, String orderBy, SvcLogicContext ctx) {
769
770         QueryStatus retval = QueryStatus.SUCCESS;
771         String modifier = null;
772
773         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
774         if(resource.contains(":")) {
775             modifier = resource.split(":")[1];
776         }
777
778         try {
779             AAIRequest request = AAIRequest.createRequest(resource, nameValues);
780             if(request == null) {
781                 return QueryStatus.FAILURE;
782             }
783
784             Map<String, String> params = new HashMap<>();
785
786             request.processRequestPathValues(nameValues);
787             if(nameValues.containsKey("prefix")){
788                 Map<String, String> tmpParams = ctxGetBeginsWith(ctx, nameValues.get("prefix"));
789                 if(!tmpParams.isEmpty()) {
790                     params.putAll(tmpParams);
791                 }
792             }
793             String rv = getExecutor().get(request);
794
795             retval = processResponseData(rv, resource, request, prefix,  ctx, nameValues, modifier);
796
797         } catch(AAIServiceException aaiexc) {
798             getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
799             int errorCode = aaiexc.getReturnCode();
800             ctx.setAttribute(prefix + ".error.message", aaiexc.getMessage());
801             if(errorCode >= 300) {
802                 ctx.setAttribute(prefix + ".error.http.response-code",
803                         Integer.toString(aaiexc.getReturnCode()));
804             }
805
806             if(aaiexc.getReturnCode() == 404)
807                 return QueryStatus.NOT_FOUND;
808
809             return QueryStatus.FAILURE;
810         } catch (Exception exc) {
811             getLogger().warn("requestGenericVnfData", exc);
812             ctx.setAttribute(prefix + ".error.message", exc.getMessage());
813             return QueryStatus.FAILURE;
814         }
815
816         return retval;
817     }
818
819     public QueryStatus processResponseData(String rv, String resource, AAIRequest request, String prefix,  SvcLogicContext ctx, Map<String, String> nameValues, String modifier) throws JsonParseException, JsonMappingException, IOException, AAIServiceException
820     {
821         Object response;
822
823         if(rv == null) {
824             return QueryStatus.NOT_FOUND;
825         }
826
827         response = request.jsonStringToObject(rv);
828         if(response == null) {
829             return QueryStatus.NOT_FOUND;
830         }
831
832         if("generic-query".equals(resource)) {
833             SearchResults rd = SearchResults.class.cast(response);
834             List<ResultData> rdList = rd.getResultData();
835             if(rdList == null || rdList.isEmpty()) {
836                 return QueryStatus.NOT_FOUND;
837             }
838             ResultData rDatum = rdList.get(0);
839             nameValues.put("selflink", rDatum.getResourceLink());
840             AAIRequest req2 = AAIRequest.createRequest(rDatum.getResourceType(), nameValues);
841             req2.processRequestPathValues(nameValues);
842             rv = getExecutor().get(req2);
843             if(rv == null) {
844                 return QueryStatus.NOT_FOUND;
845             }
846
847             response = req2.jsonStringToObject(rv);
848             if(response == null) {
849                 return QueryStatus.NOT_FOUND;
850             }
851         }
852
853         if("nodes-query".equals(resource)) {
854             SearchResults rd = SearchResults.class.cast(response);
855             List<ResultData> rdList = rd.getResultData();
856             if(rdList == null || rdList.isEmpty()) {
857                 return QueryStatus.NOT_FOUND;
858             }
859             ResultData rDatum = rdList.get(0);
860             response = rDatum;
861         }
862
863         if("formatted-query".equals(resource) || "custom-query".equals(resource)) {
864             FormattedQueryResultList rd = FormattedQueryResultList.class.cast(response);
865             List<Result> iRIlist = rd.getResults();
866             if(iRIlist == null || iRIlist.isEmpty()) {
867                 return QueryStatus.NOT_FOUND;
868             }
869         }
870
871         // process relationship list
872         // this is a temporary soluton to address the realationship handling changes added in Release 17.07
873         try {
874             Class<?> clazz = response.getClass();
875             Method getter = clazz.getMethod("getRelationshipList");
876             Object obj = getter.invoke(response);
877             if(obj != null && obj instanceof RelationshipList) {
878                 RelationshipList list = RelationshipList.class.cast(obj);
879                 AAIServiceUtils.populateRelationshipDataFromPath(list);
880             }
881         } catch(Exception exc) {
882             getLogger().debug(RELATIONSHIP_DATA + exc.getMessage());
883         }
884
885         String preFix;
886         if(prefix == null || prefix.isEmpty()) {
887             preFix = "";
888         } else {
889             preFix = prefix + ".";
890         }
891
892         Map<String,Object> props = objectToProperties(response);
893         Set<String> keys = props.keySet();
894         for(String theKey: keys) {
895             if(getLogger().isTraceEnabled())
896                 getLogger().trace(theKey);
897
898             Object value = props.get(theKey);
899             if(value == null)
900                 continue;
901             Object type = value.getClass();
902             if(value instanceof String) {
903                 ctx.setAttribute(preFix + theKey, value.toString());
904                 continue;
905             }
906             if(value instanceof Boolean) {
907                 ctx.setAttribute(preFix + theKey, value.toString());
908                 continue;
909             }
910             if(value instanceof Integer) {
911                 ctx.setAttribute(preFix + theKey, value.toString());
912                 continue;
913             }
914             if(value instanceof Long) {
915                 ctx.setAttribute(preFix + theKey, value.toString());
916                 continue;
917             }
918
919             if(value instanceof ArrayList) {
920                 ArrayList<?> array = ArrayList.class.cast(value);
921                 for(int i = 0; i < array.size(); i++) {
922                     writeList(array, String.format("%s.%s", prefix, theKey), ctx);
923                 }
924                 continue;
925             }
926
927             if("relationship-list".equals(theKey)){
928                 Map<String, Object> relationshipList = (Map<String, Object>)value;
929                 // we are interested in seeing just the selected relationship
930                 if(theKey.equals(modifier)) {
931                     List<?> relationships = (List<?>)relationshipList.get("relationship");
932                     if(relationships != null && !relationships.isEmpty()) {
933
934                         List newRelationships = new LinkedList();
935                         newRelationships.addAll(relationships);
936
937                         for(Object obj : newRelationships){
938                             if(obj instanceof Map<?, ?>) {
939                                 Map<?, ?> relProperties = (Map<?, ?>)obj;
940                                 if(relProperties.containsKey("related-to")) {
941                                     Object relPropsRelatedTo = relProperties.get("related-to");
942
943                                     String relatedTo = nameValues.get("related_to");
944                                     if(relatedTo != null) {
945                                         relatedTo = relatedTo.trim().replace("'", "").replace("$", "").replace("'", "");
946                                         if(!relatedTo.equals(relPropsRelatedTo)) {
947                                             relationships.remove(relProperties);
948                                         }
949                                         continue;
950                                     } else {
951                                         continue;
952                                     }
953                                 }
954                             }
955                         }
956                     }
957                 }
958                 writeMap(relationshipList, String.format("%s.%s", prefix, theKey), ctx);
959                 continue;
960             }
961
962             if(value instanceof Map) {
963                 Map<String, Object> subnetsList = (Map<String, Object>)value;
964                 writeMap(subnetsList, String.format("%s.%s", prefix, theKey), ctx);
965                 continue;
966             }
967
968         }
969         return QueryStatus.SUCCESS;
970     }
971
972
973     public QueryStatus newModelBackupRequest(String resource,  Map<String, String> params,  String prefix,  SvcLogicContext ctx) {
974
975         QueryStatus retval = QueryStatus.SUCCESS;
976         HashMap<String, String> nameValues = new HashMap<>();
977
978         try {
979             AAIRequest request = AAIRequest.createRequest(resource, nameValues);
980             if(request == null) {
981                 return QueryStatus.FAILURE;
982             }
983
984             boolean argsFound = false;
985             String[] arguments = request.getArgsList();
986             for(String name : arguments) {
987                 String tmpName = name.replaceAll("-", "_");
988                 String value = params.get(tmpName);
989                 if(value != null && !value.isEmpty()) {
990                     value = value.trim().replace("'", "").replace("$", "").replace("'", "");
991                     request.addRequestProperty(name, value);
992                     argsFound = true;
993                 }
994             }
995             if(!argsFound) {
996                 getLogger().warn("No arguments were found. Terminating backup request.");
997                 return QueryStatus.FAILURE;
998             }
999
1000             String rv = getExecutor().get(request);
1001             ctx.setAttribute(prefix, rv);
1002         } catch(AAIServiceException aaiexc) {
1003             getLogger().warn(AAI_SERVICE_EXCEPTION, aaiexc);
1004             if(aaiexc.getReturnCode() == 404)
1005                 return QueryStatus.NOT_FOUND;
1006
1007             return QueryStatus.FAILURE;
1008         } catch (Exception exc) {
1009             getLogger().warn("newModelBackupRequest", exc);
1010             return QueryStatus.FAILURE;
1011         }
1012
1013         return retval;
1014     }
1015
1016     public AAIDatum newModelObjectRequest(String resource,  Map<String, String> params,  String prefix,  SvcLogicContext ctx)
1017             throws AAIServiceException {
1018
1019         AAIDatum response = null;
1020
1021         try {
1022             AAIRequest request = AAIRequest.createRequest(resource, params);
1023             if(request == null) {
1024                 return null;
1025             }
1026
1027             request.processRequestPathValues(params);
1028             String rv = getExecutor().get(request);
1029             response = request.jsonStringToObject(rv);
1030         } catch(AAIServiceException aaiexc) {
1031             throw aaiexc;
1032         } catch (Exception exc) {
1033             getLogger().warn("newModelBackupRequest", exc);
1034             throw new AAIServiceException(exc);
1035         }
1036
1037         return response;
1038     }
1039
1040
1041     @Override
1042     public QueryStatus release(String arg0, String arg1, SvcLogicContext arg2) throws SvcLogicException {
1043         throw new SvcLogicException("Method AAIService.release() has not been implemented yet");
1044     }
1045
1046     @Override
1047     public QueryStatus reserve(String arg0, String arg1, String arg2, String arg3, SvcLogicContext arg4)
1048             throws SvcLogicException {
1049         throw new SvcLogicException("Method AAIService.reserve() has not been implemented yet");
1050     }
1051
1052     private QueryStatus newModelSave(String resource, boolean force, String key, Map<String, String> params, String prefix, SvcLogicContext ctx) {
1053         getLogger().debug("Executing newModelSave for resource : " + resource);
1054         HashMap<String, String> nameValues = AAIServiceUtils.keyToHashMap(key, ctx);
1055
1056         try {
1057             ArrayList<String> subResources = new ArrayList<>();
1058             Set<String> set = params.keySet();
1059             Map<String, Method> setters = new HashMap<>();
1060             Map<String, Method> getters = new HashMap<>();
1061
1062             // 1. find class
1063             AAIRequest request = AAIRequest.createRequest(resource, nameValues);
1064             Class<? extends AAIDatum> resourceClass = request.getModelClass();
1065             getLogger().debug(resourceClass.getName());
1066             AAIDatum instance = resourceClass.newInstance();
1067
1068             {
1069                 Annotation[] annotations = resourceClass.getAnnotations();
1070                 for(Annotation annotation : annotations) {
1071                     Class<? extends Annotation> anotationType = annotation.annotationType();
1072                     String annotationName = anotationType.getName();
1073
1074                     // 2. find string property setters and getters for the lists
1075                     if("javax.xml.bind.annotation.XmlType".equals(annotationName)){
1076                         XmlType order = (XmlType)annotation;
1077                         String[]  values = order.propOrder();
1078                         for(String value : values) {
1079                             String id = AAIServiceUtils.camelCaseToDashedString(value);
1080                             Field field = resourceClass.getDeclaredField(value);
1081                             Class<?> type = field.getType();
1082                             Method setter = null;
1083                             try {
1084                                 setter = resourceClass.getMethod("set"+StringUtils.capitalize(value), type);
1085                                 if(type.getName().startsWith("java.lang") || "boolean".equals(type.getName()) || "long".equals(type.getName()) || "int".equals(type.getName())) {
1086                                     try {
1087                                         Object arglist[] = new Object[1];
1088                                         arglist[0] = params.get(id);
1089
1090                                         if(arglist[0] != null) {
1091                                             if(!type.getName().equals("java.lang.String")) {
1092 //                                            getLogger().debug(String.format("Processing %s with parameter %s", types[0].getName(), value));
1093                                                 if("java.lang.Long".equals(type.getName()) || "java.lang.Integer".equals(type.getName())) {
1094                                                     String fv = params.get(id);
1095                                                     if(fv == null || fv.isEmpty()) {
1096                                                         arglist[0] = null;
1097                                                     } else {
1098                                                         arglist[0] = valueOf(type, params.get(id));
1099                                                     }
1100                                                 } else if("boolean".equals(type.getName())) {
1101                                                     arglist[0] = valueOf(Boolean.class, params.get(id));
1102                                                 } else if("int".equals(type.getName())) {
1103                                                     arglist[0] = valueOf(Integer.class, params.get(id));
1104                                                 } else if("long".equals(type.getName())) {
1105                                                     String fv = params.get(id);
1106                                                     if(fv == null || fv.isEmpty()) {
1107                                                         arglist[0] = null;
1108                                                     } else {
1109                                                         arglist[0] = valueOf(Long.class, params.get(id));
1110                                                     }
1111                                                 } else {
1112                                                     arglist[0] = valueOf(type, params.get(id));
1113                                                 }
1114                                             }
1115                                             Object obj = setter.invoke(instance, arglist);
1116                                         }
1117                                         set.remove(id);
1118
1119                                     } catch (Exception x) {
1120                                         Throwable cause = x.getCause();
1121                                         getLogger().warn("Failed process for " + resourceClass.getName(), x);
1122                                     }
1123                                 } else if("java.util.List".equals(type.getName())) {
1124                                     List<String> newValues = new ArrayList<>();
1125                                     String length = id+"_length";
1126                                     if(!params.isEmpty() && params.containsKey(length)) {
1127                                         String tmp = params.get(length);
1128                                         int count = Integer.parseInt(tmp);
1129                                         for(int i=0; i<count; i++) {
1130                                             String tmpValue = params.get(String.format("%s[%d]", id, i));
1131                                             newValues.add(tmpValue);
1132                                         }
1133                                         if(!newValues.isEmpty()) {
1134                                             Object o = setter.invoke(instance, newValues);
1135                                         }
1136                                     }
1137                                     set.remove(id);
1138                                 } else {
1139                                     setters.put(id, setter);
1140                                 }
1141                             } catch(Exception exc) {
1142                                 getLogger().warn(AAI_SERVICE_EXCEPTION, exc);
1143                             }
1144
1145                             Method getter;
1146                             try {
1147                                 getter = resourceClass.getMethod("get"+StringUtils.capitalize(value));
1148                                 if(!type.getName().equals("java.lang.String")) {
1149                                     getters.put(id, getter);
1150                                 }
1151                             } catch(Exception exc) {
1152                                 getLogger().warn(AAI_SERVICE_EXCEPTION, exc);
1153                             }
1154
1155                         }
1156                         subResources.addAll(Arrays.asList(values));
1157                     }
1158                 }
1159             }
1160
1161             // remove getters that have matching setter
1162             for(String setKey : setters.keySet()) {
1163                 if(getters.containsKey(setKey)) {
1164                     getters.remove(setKey);
1165                 }
1166             }
1167
1168             Set<String> relationshipKeys = new TreeSet<>();
1169             Set<String> vlansKeys = new TreeSet<>();
1170             Set<String> metadataKeys = new TreeSet<>();
1171
1172             for(String attribute : set) {
1173                 String value = params.get(attribute);
1174                 if(attribute.startsWith("relationship-list")) {
1175                     relationshipKeys.add(attribute);
1176                 } else if(attribute.startsWith("vlans")) {
1177                     vlansKeys.add(attribute);
1178                 } else if(attribute.startsWith("metadata")) {
1179                     metadataKeys.add(attribute);
1180                 }
1181             }
1182             // 3. find list property getters
1183             for(String attribute : set) {
1184                 String value = params.get(attribute);
1185                 Method method = getters.get(attribute);
1186                 if(method != null) {
1187                     try {
1188                         Object arglist[] = new Object[0];
1189 //                        arglist[0] = value;
1190                         Class<?>[] types = method.getParameterTypes();
1191                         if(types.length == 0){
1192                             Object o = method.invoke(instance, arglist);
1193                             if(o instanceof ArrayList) {
1194                                 ArrayList<String> values = (ArrayList<String>)o;
1195                                 value = value.replace("[", "").replace("]", "");
1196                                 List<String> items = Arrays.asList(value.split("\\s*,\\s*"));
1197                                 for(String s : items) {
1198                                     values.add(s.trim());
1199                                 }
1200                             }
1201                         }
1202                     } catch (Exception x) {
1203                         Throwable cause = x.getCause();
1204                         getLogger().warn("Failed process for " + resourceClass.getName(), x);
1205                     }
1206                 }
1207             }
1208             // 4. Process Relationships
1209             // add relationship list
1210             if( (subResources.contains("relationship-list") || subResources.contains("relationshipList")) &&  !relationshipKeys.isEmpty()) {
1211                 RelationshipList relationshipList = null;
1212                 Object obj = null;
1213                 Method getRelationshipListMethod = null;
1214                 try {
1215                     getRelationshipListMethod = resourceClass.getMethod("getRelationshipList");
1216                 } catch(Exception exc) {
1217                     getLogger().debug(RELATIONSHIP_DATA + exc.getMessage());
1218                 }
1219
1220                 if(getRelationshipListMethod != null){
1221                     try {
1222                         obj = getRelationshipListMethod.invoke(instance);
1223                     } catch (InvocationTargetException x) {
1224                         Throwable cause = x.getCause();
1225                     }
1226                 }
1227                 if(obj != null && obj instanceof RelationshipList){
1228                     relationshipList = (RelationshipList)obj;
1229                 } else {
1230                     relationshipList = new RelationshipList();
1231                     Method setRelationshipListMethod = resourceClass.getMethod("setRelationshipList", RelationshipList.class);
1232                     if(setRelationshipListMethod != null){
1233                         try {
1234                             Object arglist[] = new Object[1];
1235                             arglist[0] = relationshipList;
1236
1237                             obj = setRelationshipListMethod.invoke(instance, arglist);
1238                         } catch (InvocationTargetException x) {
1239                             Throwable cause = x.getCause();
1240                         }
1241                     }
1242                 }
1243
1244                 List<Relationship> relationships = relationshipList.getRelationship();
1245
1246                 int i = 0;
1247                 while(true){
1248                     String searchKey = "relationship-list.relationship[" + i + "].related-to";
1249                     if(!params.containsKey(searchKey))
1250                         break;
1251                     int j = 0;
1252                     String relatedTo = params.get(searchKey);
1253                     String relatedLinkKey = "relationship-list.relationship[" + i + "].related-link";
1254                     String relatedLink = null;
1255                     if(params.containsKey(relatedLinkKey)) {
1256                         relatedLink = params.get(relatedLinkKey);
1257                     }
1258                     Relationship relationship = new Relationship();
1259                     relationships.add(relationship);
1260                     relationship.setRelatedTo(relatedTo);
1261                     String relationshipLabel = "relationship-list.relationship[" + i + "].relationship-label";
1262                     if(params.containsKey(searchKey)) {
1263                         relationship.setRelationshipLabel(params.get(relationshipLabel));
1264                     }
1265                     getLogger().debug("About to process related link of {}", relatedLink);
1266                     if(relatedLink != null) {
1267                         if(relatedLink.contains("v$"))
1268                             relatedLink = relatedLink.replace(VERSION_PATTERN, "/v16/");
1269                         relationship.setRelatedLink(relatedLink);
1270                     } else {
1271                         Map<String, String> relParams = new HashMap<>();
1272
1273                         while(true) {
1274                             String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-key";
1275                             String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-value";
1276                             if(!params.containsKey(searchRelationshipKey))
1277                                 break;
1278
1279                             relParams.put(params.get(searchRelationshipKey), params.get(searchRelationshipValue));
1280                             j++;
1281                         }
1282                         AAIRequest rlRequest = AAIRequest.createRequest(relatedTo, relParams);
1283                         for(Map.Entry<String,String> entry : relParams.entrySet()) {
1284                             rlRequest.addRequestProperty(entry.getKey(), entry.getValue());
1285                         }
1286                         String path = rlRequest.updatePathDataValues(null);
1287                         relationship.setRelatedLink(path);
1288                     }
1289                     {
1290                         int k = 0;
1291                         // process related to properties
1292                         Map<String, String> relParams = new HashMap<String, String>();
1293
1294                         while(true) {
1295                             String searchRelatedToKey = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-key";
1296                             String searchRelatedToValue = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-value";
1297                             if(!params.containsKey(searchRelatedToKey))
1298                                 break;
1299
1300                             RelatedToProperty relDatum = new RelatedToProperty();
1301                             relDatum.setPropertyKey(params.get(searchRelatedToKey));
1302                             relDatum.setPropertyValue(params.get(searchRelatedToValue));
1303                             relationship.getRelatedToProperty().add(relDatum);
1304
1305                             relParams.put(params.get(searchRelatedToKey), params.get(searchRelatedToValue));
1306                             k++;
1307                         }
1308                     }
1309                     i++;
1310                 }
1311             }
1312
1313             // 4. vlans
1314             if(subResources.contains("vlans") &&  !vlansKeys.isEmpty()) {
1315                 Object obj = null;
1316                 Vlans vlanList = null;
1317                 Method getVLansMethod = resourceClass.getMethod("getVlans");
1318                 if(getVLansMethod != null){
1319                     try {
1320                         obj = getVLansMethod.invoke(instance);
1321                     } catch (InvocationTargetException x) {
1322                         Throwable cause = x.getCause();
1323                     }
1324                 }
1325                 if(obj != null && obj instanceof Vlans){
1326                     vlanList = (Vlans)obj;
1327                 } else {
1328                     vlanList = new Vlans();
1329                     Method setVlansMethod = resourceClass.getMethod("setVlans", Vlans.class);
1330                     if(setVlansMethod != null){
1331                         try {
1332                             Object arglist[] = new Object[1];
1333                             arglist[0] = vlanList;
1334
1335                             obj = setVlansMethod.invoke(instance, arglist);
1336                         } catch (InvocationTargetException x) {
1337                             Throwable cause = x.getCause();
1338                         }
1339                     }
1340                 }
1341
1342                 int i = 0;
1343                 while(true){
1344                     String searchKey = "vlans.vlan[" + i + "].vlan-interface";
1345                     if(!params.containsKey(searchKey))
1346                         break;
1347
1348                     String vlanInterface = params.get("vlans.vlan[" + i + "].vlan-interface");
1349                     String vlanIdInner    = params.get("vlans.vlan[" + i + "].vlan-id-inner");
1350                     String vlanIdOute     = params.get("vlans.vlan[" + i + "].vlan-id-outer");
1351                     String speedValue     = params.get("vlans.vlan[" + i + "].speed-value");
1352                     String speedUnits     = params.get("vlans.vlan[" + i + "].speed-units");
1353
1354                     Vlan vlan = new Vlan();
1355                     vlan.setVlanInterface(vlanInterface);
1356
1357                     if(vlanIdInner != null) {
1358                         Long iVlanIdInner = Long.parseLong(vlanIdInner);
1359                         vlan.setVlanIdInner(iVlanIdInner);
1360                     }
1361
1362                     if(vlanIdOute != null) {
1363                         Long iVlanIdOuter = Long.parseLong(vlanIdOute);
1364                         vlan.setVlanIdOuter(iVlanIdOuter);
1365                     }
1366
1367                     if(speedValue != null) {
1368                         vlan.setSpeedValue(speedValue);
1369                         vlan.setSpeedUnits(speedUnits);
1370                     }
1371
1372                     vlanList.getVlan().add(vlan);
1373                     i++;
1374                 }
1375             }
1376
1377             // 5. metadata
1378             if(subResources.contains("metadata") &&  !metadataKeys.isEmpty()) {
1379                 Object obj = null;
1380                 Metadata metadataList = null;
1381                 Method getMetadataMethod = resourceClass.getMethod("getMetadata");
1382                 if(getMetadataMethod != null){
1383                     try {
1384                         obj = getMetadataMethod.invoke(instance);
1385                     } catch (InvocationTargetException x) {
1386                         Throwable cause = x.getCause();
1387                     }
1388                 }
1389                 if(obj != null && obj instanceof Metadata){
1390                     metadataList = (Metadata)obj;
1391                 } else {
1392                     metadataList = new Metadata();
1393                     Method setMetadataMethod = resourceClass.getMethod("setMetadata", Metadata.class);
1394                     if(setMetadataMethod != null){
1395                         try {
1396                             Object arglist[] = new Object[1];
1397                             arglist[0] = metadataList;
1398
1399                             obj = setMetadataMethod.invoke(instance, arglist);
1400                         } catch (InvocationTargetException x) {
1401                             Throwable cause = x.getCause();
1402                         }
1403                     }
1404                 }
1405
1406                 // process data
1407                 int i = 0;
1408                 while(true){
1409                     String metaKey = "metadata.metadatum[" + i + "].meta-key";
1410                     if(!params.containsKey(metaKey))
1411                         break;
1412
1413                     String metaValue = params.get("metadata.metadatum[" + i + "].meta-value");
1414
1415                     Metadatum vlan = new Metadatum();
1416                     vlan.setMetaname(metaKey);
1417                     vlan.setMetaval(metaValue);
1418
1419                     metadataList.getMetadatum().add(vlan);
1420                     i++;
1421                 }
1422
1423             }
1424
1425
1426             // 6. Prepare AAI request
1427             String[] args = request.getArgsList();
1428             for(String arg : args) {
1429                 String modifiedKey = arg.replaceAll("-", "_");
1430                 if(nameValues.containsKey(modifiedKey)) {
1431                     String argValue = nameValues.get(modifiedKey);
1432                     if(argValue != null) argValue = argValue.trim().replace("'", "").replace("$", "").replace("'", "");
1433                     request.addRequestProperty(arg, argValue);
1434                 }
1435             }
1436
1437             request.processRequestPathValues(nameValues);
1438             request.setRequestObject(instance);
1439             Object response = getExecutor().post(request);
1440             if(request.expectsDataFromPUTRequest()){
1441                 if(response != null && response instanceof String) {
1442                     String rv = response.toString();
1443                     QueryStatus retval = processResponseData(rv, resource, request, prefix,  ctx, nameValues, null);
1444                     getLogger().debug("newModelSave - returning " + retval.toString());
1445                     return retval;
1446                 }
1447             }
1448
1449         } catch(AAIServiceException exc){
1450             ctx.setAttribute(prefix + ".error.message", exc.getMessage());
1451             int returnCode = exc.getReturnCode();
1452             if(returnCode >= 300) {
1453                 ctx.setAttribute(prefix + ".error.http.response-code",
1454                         Integer.toString(exc.getReturnCode()));
1455             }
1456
1457             if(returnCode == 400 || returnCode == 412)
1458                 return QueryStatus.FAILURE;
1459             else if(returnCode == 404)
1460                 return QueryStatus.NOT_FOUND;
1461             else {
1462                 getLogger().warn("Failed newModelSave - returning FAILURE", exc);
1463                 return QueryStatus.FAILURE;
1464             }
1465         } catch(Exception exc){
1466             getLogger().warn("Failed newModelSave - returning FAILURE", exc);
1467             ctx.setAttribute(prefix + ".error.message", exc.getMessage());
1468             return QueryStatus.FAILURE;
1469         }
1470
1471         getLogger().debug("newModelSave - returning SUCCESS");
1472         return QueryStatus.SUCCESS;
1473     }
1474
1475     private QueryStatus newModelProcessRelationshipList(Object instance, Map<String, String> params, String prefix, SvcLogicContext ctx) throws Exception {
1476
1477         Class resourceClass = instance.getClass();
1478
1479         Set<String> relationshipKeys = new TreeSet<>();
1480
1481         Set<String> set = params.keySet();
1482
1483         for(String attribute : set) {
1484             String value = params.get(attribute);
1485
1486             if(attribute.startsWith("relationship-list")) {
1487                 relationshipKeys.add(attribute);
1488             }
1489         }
1490
1491         // 3. Process Relationships
1492         // add relationship list
1493         if(!relationshipKeys.isEmpty()) {
1494             RelationshipList relationshipList;
1495             Object obj = null;
1496             Method getRelationshipListMethod = null;
1497             try {
1498                 getRelationshipListMethod = resourceClass.getMethod("getRelationshipList");
1499             } catch(Exception exc) {
1500                 getLogger().debug(RELATIONSHIP_DATA + exc.getMessage());
1501             }
1502             if(getRelationshipListMethod != null){
1503                 try {
1504                     obj = getRelationshipListMethod.invoke(instance);
1505                 } catch (InvocationTargetException x) {
1506                     Throwable cause = x.getCause();
1507                 }
1508             }
1509             if(obj != null && obj instanceof RelationshipList){
1510                 relationshipList = (RelationshipList)obj;
1511             } else {
1512                 relationshipList = new RelationshipList();
1513                 Method setRelationshipListMethod = resourceClass.getMethod("setRelationshipList", RelationshipList.class);
1514                 if(setRelationshipListMethod != null){
1515                     try {
1516                         Object arglist[] = new Object[1];
1517                         arglist[0] = relationshipList;
1518
1519                         obj = setRelationshipListMethod.invoke(instance, arglist);
1520                     } catch (InvocationTargetException x) {
1521                         Throwable cause = x.getCause();
1522                     }
1523                 }
1524             }
1525
1526             boolean createdNewRelationships = false;
1527             List<Relationship> relationships = relationshipList.getRelationship();
1528             if(relationships == null) {
1529                 relationships = new ArrayList<>();
1530                 createdNewRelationships = true;
1531             }
1532
1533             int i = 0;
1534             while(true){
1535                 String searchKey = "relationship-list.relationship[" + i + "].related-to";
1536                 if(!params.containsKey(searchKey))
1537                     break;
1538
1539                 String relatedTo = params.get(searchKey);
1540                 String relatedLinkKey = "relationship-list.relationship[" + i + "].related-link";
1541                 String relatedLink = null;
1542                 if(params.containsKey(relatedLinkKey)) {
1543                     relatedLink = params.get(relatedLinkKey);
1544                 }
1545
1546                 Relationship relationship = new Relationship();
1547                 relationships.add(relationship);
1548                 relationship.setRelatedTo(relatedTo);
1549
1550                 String relationshipLabel = "relationship-list.relationship[" + i + "].relationship-label";
1551                 if(params.containsKey(searchKey)) {
1552                     relationship.setRelationshipLabel(params.get(relationshipLabel));
1553                 }
1554
1555                 if (relatedLink != null) {
1556                     if(relatedLink.contains("v$"))
1557                         relatedLink = relatedLink.replace(VERSION_PATTERN,  AAIRequest.getSupportedAAIVersion());
1558                     relationship.setRelatedLink(relatedLink);
1559                 } else {
1560                     Map<String, String> relParams = new HashMap<>();
1561                     int j = 0;
1562
1563                     while (true) {
1564                         String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data["
1565                                 + j + "].relationship-key";
1566                         String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data["
1567                                 + j + "].relationship-value";
1568                         if (!params.containsKey(searchRelationshipKey))
1569                             break;
1570
1571                         RelationshipData relDatum = new RelationshipData();
1572                         relDatum.setRelationshipKey(params.get(searchRelationshipKey));
1573                         relDatum.setRelationshipValue(params.get(searchRelationshipValue));
1574                         relationship.getRelationshipData().add(relDatum);
1575
1576                         relParams.put(params.get(searchRelationshipKey), params.get(searchRelationshipValue));
1577                         j++;
1578                     }
1579                     AAIRequest rlRequest = AAIRequest.createRequest(relatedTo, relParams);
1580                     for (Map.Entry<String, String> entry : relParams.entrySet()) {
1581                         rlRequest.addRequestProperty(entry.getKey(), entry.getValue());
1582                     }
1583                     String path = rlRequest.updatePathDataValues(null);
1584                     relationship.setRelatedLink(path);
1585                 }
1586                 {
1587                     int k = 0;
1588                     // process related to properties
1589                     Map<String, String> relParams = new HashMap<String, String>();
1590
1591                     while(true) {
1592                         String searchRelatedToKey = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-key";
1593                         String searchRelatedToValue = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-value";
1594                         if(!params.containsKey(searchRelatedToKey))
1595                             break;
1596
1597                         RelatedToProperty relDatum = new RelatedToProperty();
1598                         relDatum.setPropertyKey(params.get(searchRelatedToKey));
1599                         relDatum.setPropertyValue(params.get(searchRelatedToValue));
1600                         relationship.getRelatedToProperty().add(relDatum);
1601
1602                         relParams.put(params.get(searchRelatedToKey), params.get(searchRelatedToValue));
1603                         k++;
1604                     }
1605                 }
1606
1607                 i++;
1608             }
1609         }
1610
1611         return QueryStatus.SUCCESS;
1612     }
1613
1614     private QueryStatus newModelProcessMetadata(Object instance, Map<String, String> params, String prefix, SvcLogicContext ctx) throws Exception {
1615
1616         if (!(instance instanceof ServiceInstance) && !(instance instanceof Image)) {
1617             throw new IllegalArgumentException("request is not applicable for selected request");
1618         }
1619
1620         Class resourceClass = instance.getClass();
1621         Set<String> metadataKeys = new TreeSet<String>();
1622         Set<String> set = params.keySet();
1623         for(String attribute : set) {
1624             if(attribute.startsWith("metadata")) {
1625                 metadataKeys.add(attribute);
1626             }
1627         }
1628
1629         // 3. Process Metadata
1630         // add metadata
1631         if(!metadataKeys.isEmpty()) {
1632             Metadata metadata = null;
1633             Object obj = null;
1634             Method getMetadataMethod = resourceClass.getMethod("getMetadata");
1635             if(getMetadataMethod != null){
1636                 try {
1637                     obj = getMetadataMethod.invoke(instance);
1638                 } catch (InvocationTargetException x) {
1639                     Throwable cause = x.getCause();
1640                 }
1641             }
1642             if(obj != null && obj instanceof Metadata){
1643                 metadata = (Metadata)obj;
1644             } else {
1645                 metadata = new Metadata();
1646                 Method setMetadataMethod = resourceClass.getMethod("setMetadata", Metadata.class);
1647                 if(setMetadataMethod != null){
1648                     try {
1649                         setMetadataMethod.invoke(instance, metadata);
1650                     } catch (InvocationTargetException x) {
1651                     }
1652                 }
1653             }
1654
1655             List<Metadatum> metadatumList = metadata.getMetadatum();
1656             int i = 0;
1657             while(true){
1658                 String metaNameKey = "metadata.metadatum[" + i + "].metaname";
1659                 String metaValueKey = "metadata.metadatum[" + i + "].metaval";
1660                 if(!params.containsKey(metaNameKey) || !params.containsKey(metaValueKey))
1661                     break;
1662
1663                 Metadatum metadatum = new Metadatum();
1664                 metadatum.setMetaname(params.get(metaNameKey));
1665                 metadatum.setMetaval(params.get(metaValueKey));
1666                 metadatumList.add(metadatum);
1667
1668                 i++;
1669             }
1670         }
1671
1672         return QueryStatus.SUCCESS;
1673     }
1674
1675     private Relationship findRelationship(List<Relationship> relationships, String relatedTo) {
1676         if(relatedTo == null)
1677             return null;
1678
1679         for(Relationship relationship : relationships) {
1680             if(relationship.getRelatedTo().equals(relatedTo)){
1681                 return relationship;
1682             }
1683         }
1684         return null;
1685     }
1686
1687
1688     public QueryStatus backup(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
1689         String resource = params.get("resource").toLowerCase();
1690         String prefix = params.get("data-key");
1691
1692         HashMap<String, String> nameValues = new HashMap<>();
1693         if(AAIRequest.createRequest(resource, nameValues) != null) {
1694
1695             try {
1696                 return newModelBackupRequest(resource, params, prefix, ctx);
1697             } catch (Exception exc) {
1698                 getLogger().warn("Failed backup - returning FAILURE", exc);
1699                 return QueryStatus.FAILURE;
1700             }
1701         }
1702
1703         return QueryStatus.NOT_FOUND;
1704     }
1705
1706     @Override
1707     public QueryStatus restore(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException {
1708
1709         QueryStatus retval = QueryStatus.SUCCESS;
1710         String resource = params.get("resource").toLowerCase();
1711         String prefix = params.get("data-key");
1712
1713         HashMap<String, String> nameValues = new HashMap<>();
1714         if(AAIRequest.createRequest(resource, nameValues) != null) {
1715
1716             try {
1717                 retval = newModelBackupRequest(resource, params, "tmpRestore", ctx);
1718                 if(retval == QueryStatus.SUCCESS) {
1719                     ctx.setAttribute("tmpRestore", null);
1720                 }
1721             } catch (Exception exc) {
1722                 getLogger().warn("Failed restore - returning FAILURE", exc);
1723                 return QueryStatus.FAILURE;
1724             }
1725         }
1726
1727         return QueryStatus.NOT_FOUND;
1728     }
1729
1730     protected Map<String, Object> objectToProperties(Object object) {
1731         ObjectMapper mapper = AAIService.getObjectMapper();
1732         return mapper.convertValue(object, Map.class);
1733     }
1734
1735     static <T> T valueOf(Class<T> klazz, String arg) {
1736         Exception cause = null;
1737         T ret = null;
1738         try {
1739             ret = klazz.cast(klazz.getDeclaredMethod("valueOf", String.class).invoke(null, arg));
1740         } catch (NoSuchMethodException exc) {
1741             LoggerFactory.getLogger(AAIService.class).warn("Wrong data type", exc);
1742             ret = klazz.cast(arg);
1743         } catch (IllegalAccessException e) {
1744             cause = e;
1745         } catch (InvocationTargetException e) {
1746             cause = e;
1747         }
1748         if (cause == null) {
1749             return ret;
1750         } else {
1751             throw new IllegalArgumentException(cause);
1752         }
1753     }
1754
1755     private QueryStatus processDeleteRelationshipList(String resource, String key, SvcLogicContext ctx, HashMap<String, String> nameValues) {
1756         try {
1757             AAIRequest request = AAIRequest.createRequest(resource.split(":")[0], nameValues);
1758             if(request == null) {
1759                 return QueryStatus.FAILURE;
1760             }
1761
1762             request.processRequestPathValues(nameValues);
1763             URL url = request.getRequestUrl("GET", null);
1764
1765             Class resourceClass = request.getModelClass();
1766             Object instance = getResource(url.toString(), resourceClass);
1767             if(instance == null)
1768                 return QueryStatus.NOT_FOUND;
1769
1770             // get resource version
1771             String resourceVersion = null;
1772             Method getResourceVersionMethod = resourceClass.getMethod("getResourceVersion");
1773             if(getResourceVersionMethod != null){
1774                 try {
1775                     Object object = getResourceVersionMethod.invoke(instance);
1776                     if(object != null)
1777                         resourceVersion = object.toString();
1778                 } catch (InvocationTargetException exc) {
1779                     getLogger().warn("Retrieving resource version", exc);
1780                 }
1781             }
1782
1783             RelationshipList relationshipList = null;
1784             Object obj = null;
1785             Method getRelationshipListMethod = null;
1786             try {
1787                 getRelationshipListMethod = resourceClass.getMethod("getRelationshipList");
1788             } catch(Exception exc) {
1789                 getLogger().debug(RELATIONSHIP_DATA + exc.getMessage());
1790             }
1791             if(getRelationshipListMethod != null){
1792                 try {
1793                     obj = getRelationshipListMethod.invoke(instance);
1794                 } catch (InvocationTargetException x) {
1795                     Throwable cause = x.getCause();
1796                 }
1797             }
1798             if(obj != null && obj instanceof RelationshipList){
1799                 relationshipList = (RelationshipList)obj;
1800             } else {
1801                 getLogger().debug("No relationships found to process.");
1802                 return QueryStatus.NOT_FOUND;
1803             }
1804
1805             if(relationshipList.getRelationship() == null || relationshipList.getRelationship().isEmpty()) {
1806                 return QueryStatus.NOT_FOUND;
1807             }
1808             String relatedTo = nameValues.get("related_to");
1809             if(relatedTo == null) {
1810                 return QueryStatus.FAILURE;
1811             }
1812
1813             relatedTo = relatedTo.replaceAll("_", "-");
1814
1815             String relatedLink = nameValues.get("relationship.related_link");
1816             if(relatedLink != null) {
1817                 relatedLink = URLDecoder.decode(relatedLink, "UTF-8");
1818             }
1819
1820             List<Relationship> relationships = relationshipList.getRelationship();
1821             List<Relationship> relationshipsToDelete = new LinkedList<>();
1822
1823             for(Relationship relationship : relationships) {
1824                 if(relatedTo.equals(relationship.getRelatedTo())) {
1825                     if(relatedLink != null) {
1826                         if(relationship.getRelatedLink() != null ) {
1827                             String localRelatedLink = relationship.getRelatedLink();
1828                             localRelatedLink = URLDecoder.decode(localRelatedLink, "UTF-8");
1829                             if(localRelatedLink.endsWith(relatedLink)) {
1830                                 getLogger().debug(String.format("Found relationship of '%s' to keyword '%s'", relationship.getRelatedTo(),  relatedTo));
1831                                 relationshipsToDelete.add(relationship);
1832                             }
1833                         }
1834                     } else {
1835                         getLogger().debug(String.format("Found relationship of '%s' to keyword '%s'", relationship.getRelatedTo(),  relatedTo));
1836                         relationshipsToDelete.add(relationship);
1837                     }
1838                 }
1839             }
1840             if(relationshipsToDelete == null || relationshipsToDelete.isEmpty()) {
1841                 getLogger().info(String.format("Relationship has not been found for %s", key));
1842                 return QueryStatus.NOT_FOUND;
1843             }
1844
1845             String path = url.toString();
1846             path = path + "/relationship-list/relationship";
1847             URL deleteUrl = new URL(path);
1848
1849             ObjectMapper mapper = AAIService.getObjectMapper();
1850
1851             boolean cumulativeResponse = true;
1852
1853             for(Relationship targetRelationship : relationshipsToDelete) {
1854                 String json_text = mapper.writeValueAsString(targetRelationship);
1855                 boolean response = deleteList(deleteUrl, json_text);
1856                 if(!response)
1857                     cumulativeResponse = response;
1858
1859             }
1860
1861             if(!cumulativeResponse)
1862                 return QueryStatus.FAILURE;
1863
1864             return QueryStatus.SUCCESS;
1865
1866         } catch(Exception exc) {
1867             getLogger().warn("processDelete", exc);
1868             return QueryStatus.FAILURE;
1869         }
1870     }
1871
1872     private QueryStatus processDeleteMetadata(String resource, String key, SvcLogicContext ctx, HashMap<String, String> nameValues) {
1873         try {
1874             AAIRequest request = AAIRequest.createRequest(resource, nameValues);
1875             if(request == null) {
1876                 return QueryStatus.FAILURE;
1877             }
1878
1879             request.processRequestPathValues(nameValues);
1880             URL url = request.getRequestUrl("GET", null);
1881
1882             Class<?> resourceClass = request.getModelClass();
1883             Object instance = getResource(url.toString(), resourceClass);
1884
1885             // get resource version
1886             String resourceVersion = null;
1887             Method getResourceVersionMethod = resourceClass.getMethod("getResourceVersion");
1888             if(getResourceVersionMethod != null){
1889                 try {
1890                     resourceVersion = (String) getResourceVersionMethod.invoke(instance);
1891                 } catch (InvocationTargetException x) {
1892                 }
1893             }
1894
1895             Metadata metadata = null;
1896             Object obj = null;
1897             Method getMetadataMethod = resourceClass.getMethod("getMetadata");
1898             if(getMetadataMethod != null){
1899                 try {
1900                     obj = getMetadataMethod.invoke(instance);
1901                 } catch (InvocationTargetException x) {
1902                     Throwable cause = x.getCause();
1903                 }
1904             }
1905             if(obj != null && obj instanceof Metadata){
1906                 metadata = (Metadata)obj;
1907             } else {
1908                 getLogger().debug("No metadata found to process.");
1909                 return QueryStatus.NOT_FOUND;
1910             }
1911
1912             if(metadata.getMetadatum() == null || metadata.getMetadatum().isEmpty()) {
1913                 return QueryStatus.NOT_FOUND;
1914             }
1915
1916             List<Metadatum> metadatumList = metadata.getMetadatum();
1917             Metadatum metadatumToDelete = null;
1918
1919             final String metaname = nameValues.get("metaname");
1920
1921             for(Metadatum metadatum : metadatumList) {
1922                 getLogger().debug(String.format("Comparing existing metadatum of '%s' to keyword '%s'", metadatum.getMetaname(),  metaname));
1923                 if(metaname.equals(metadatum.getMetaname())) {
1924                     metadatumToDelete = metadatum;
1925                     break;
1926                 }
1927             }
1928             if(metadatumToDelete == null) {
1929                 getLogger().info(String.format("Metadatum has not been found for %s", key));
1930                 return QueryStatus.NOT_FOUND;
1931             }
1932
1933             String path = url.toString();
1934             path = path + "/metadata/metadatum/" + encodeQuery( metadatumToDelete.getMetaname() ) +
1935                     "?resource-version=" + metadatumToDelete.getResourceVersion();
1936             URL deleteUrl = new URL(path);
1937             boolean response = deleteList(deleteUrl, null);
1938
1939             if(!response)
1940                 return QueryStatus.FAILURE;
1941
1942             return QueryStatus.SUCCESS;
1943
1944         } catch(Exception exc) {
1945             getLogger().warn("processDelete", exc);
1946             return QueryStatus.FAILURE;
1947         }
1948     }
1949
1950     protected String encodeQuery(String param) throws UnsupportedEncodingException {
1951         return URLEncoder.encode(param, "UTF-8").replace("+", "%20");
1952     }
1953
1954     static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) {
1955         Map<String, String> tmpPrefixMap = new HashMap<>();
1956
1957         if(prefix == null || prefix.isEmpty()){
1958             return tmpPrefixMap;
1959         }
1960
1961         for( String key : ctx.getAttributeKeySet() ) {
1962             if( key.startsWith(prefix) ) {
1963                 String tmpKey = key.substring(prefix.length() + 1);
1964                 tmpPrefixMap.put( tmpKey, ctx.getAttribute(key));
1965             }
1966         }
1967
1968         Map<String, String> prefixMap = new HashMap<>();
1969         Pattern p = Pattern.compile(".*\\[\\d\\]");
1970
1971         SortedSet<String> keys = new TreeSet<String>(tmpPrefixMap.keySet () );
1972         for(String key : keys) {
1973             Matcher m = p.matcher(key);
1974             if(m.matches()) {
1975                 continue;
1976             } else if(key.endsWith("_length")) {
1977                 String listKey = key.substring(0, key.indexOf("_length"));
1978                 int max = Integer.parseInt(tmpPrefixMap.get(key));
1979
1980                 ArrayList<String> data = new ArrayList<>();
1981                 for(int x = 0; x < max; x++){
1982                     String tmpKey = String.format("%s[%d]", listKey, x);
1983                     String tmpValue = tmpPrefixMap.get(tmpKey);
1984                     if(tmpValue != null && !tmpValue.isEmpty()) {
1985                         data.add(tmpValue);
1986                     }
1987                 }
1988                 if(!data.isEmpty()) {
1989                     prefixMap.put(listKey, data.toString());
1990                 } else {
1991                     prefixMap.put(key, tmpPrefixMap.get(key));
1992                 }
1993             } else {
1994                 prefixMap.put(key, tmpPrefixMap.get(key));
1995             }
1996         }
1997
1998         return prefixMap;
1999     }
2000
2001     public abstract <T> T getResource(String key, Class<T> type) throws AAIServiceException ;
2002     protected abstract boolean deleteList(URL url, String caller) throws AAIServiceException;
2003 }