Implement support for inner variables
[ccsdk/sli/adaptors.git] / aai-service / provider / src / main / java / org / onap / ccsdk / sli / adaptors / aai / AAIDeclarations.java
index f1663d2..aa16ef3 100755 (executable)
@@ -28,8 +28,10 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -45,27 +47,6 @@ import java.util.regex.Pattern;
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.commons.lang.StringUtils;
-import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
-import org.onap.ccsdk.sli.core.sli.SvcLogicException;
-import org.onap.ccsdk.sli.core.sli.SvcLogicResource.QueryStatus;
-import org.openecomp.aai.inventory.v11.GenericVnf;
-import org.openecomp.aai.inventory.v11.InventoryResponseItem;
-import org.openecomp.aai.inventory.v11.InventoryResponseItems;
-import org.openecomp.aai.inventory.v11.L3Network;
-import org.openecomp.aai.inventory.v11.LogicalLink;
-import org.openecomp.aai.inventory.v11.Metadata;
-import org.openecomp.aai.inventory.v11.Metadatum;
-import org.openecomp.aai.inventory.v11.Pnf;
-import org.openecomp.aai.inventory.v11.Relationship;
-import org.openecomp.aai.inventory.v11.RelationshipData;
-import org.openecomp.aai.inventory.v11.RelationshipList;
-import org.openecomp.aai.inventory.v11.ResultData;
-import org.openecomp.aai.inventory.v11.SearchResults;
-import org.openecomp.aai.inventory.v11.ServiceInstance;
-import org.openecomp.aai.inventory.v11.Vlan;
-import org.openecomp.aai.inventory.v11.Vlans;
-import org.openecomp.aai.inventory.v11.Vserver;
-import org.onap.ccsdk.sli.adaptors.aai.AAIService.AAIRequestExecutor;
 import org.onap.ccsdk.sli.adaptors.aai.data.AAIDatum;
 import org.onap.ccsdk.sli.adaptors.aai.query.FormattedQueryResultList;
 import org.onap.ccsdk.sli.adaptors.aai.query.InstanceFilter;
@@ -74,6 +55,27 @@ import org.onap.ccsdk.sli.adaptors.aai.query.NamedQuery;
 import org.onap.ccsdk.sli.adaptors.aai.query.NamedQueryData;
 import org.onap.ccsdk.sli.adaptors.aai.query.QueryParameters;
 import org.onap.ccsdk.sli.adaptors.aai.query.Result;
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
+import org.onap.ccsdk.sli.core.sli.SvcLogicException;
+import org.onap.aai.inventory.v14.GenericVnf;
+import org.onap.aai.inventory.v14.Image;
+import org.onap.aai.inventory.v14.InventoryResponseItem;
+import org.onap.aai.inventory.v14.InventoryResponseItems;
+import org.onap.aai.inventory.v14.L3Network;
+import org.onap.aai.inventory.v14.LogicalLink;
+import org.onap.aai.inventory.v14.Metadata;
+import org.onap.aai.inventory.v14.Metadatum;
+import org.onap.aai.inventory.v14.Pnf;
+import org.onap.aai.inventory.v14.RelatedToProperty;
+import org.onap.aai.inventory.v14.Relationship;
+import org.onap.aai.inventory.v14.RelationshipData;
+import org.onap.aai.inventory.v14.RelationshipList;
+import org.onap.aai.inventory.v14.ResultData;
+import org.onap.aai.inventory.v14.SearchResults;
+import org.onap.aai.inventory.v14.ServiceInstance;
+import org.onap.aai.inventory.v14.Vlan;
+import org.onap.aai.inventory.v14.Vlans;
+import org.onap.aai.inventory.v14.Vserver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,6 +102,8 @@ public abstract class AAIDeclarations implements AAIClient {
 
     public static final String TARGET_URI         = "org.onap.ccsdk.sli.adaptors.aai.uri";
 
+    public static final String AAI_VERSION          = "org.onap.ccsdk.sli.adaptors.aai.version";
+
     // Availability zones query
     public static final String QUERY_PATH         = "org.onap.ccsdk.sli.adaptors.aai.path.query";
 
@@ -135,9 +139,11 @@ public abstract class AAIDeclarations implements AAIClient {
     // node query (1602)
     public static final String QUERY_NODES_PATH          = "org.onap.ccsdk.sli.adaptors.aai.query.nodes";
 
+    private static final String VERSION_PATTERN = "/v$/";
+
 
     protected abstract Logger getLogger();
-    public abstract AAIRequestExecutor getExecutor();
+    public abstract AAIExecutorInterface getExecutor();
 
 
     @Override
@@ -389,8 +395,6 @@ public abstract class AAIDeclarations implements AAIClient {
             getLogger().warn("Failed query - returning FAILURE", exc);
             return QueryStatus.FAILURE;
         }
-
-//        return QueryStatus.SUCCESS;
     }
 
 
@@ -479,8 +483,7 @@ public abstract class AAIDeclarations implements AAIClient {
                     request.processRequestPathValues(nameValues);
                     path = request.getRequestUrl("GET", null);
                     params.put("vserver-selflink", path.toString());
-                } catch (UnsupportedEncodingException | MalformedURLException e) {
-                    // TODO : Fix this
+                } catch (UnsupportedEncodingException | MalformedURLException | URISyntaxException e) {
                     params.put("vserver-selflink", "/vserver");
                 }
             }
@@ -503,9 +506,12 @@ public abstract class AAIDeclarations implements AAIClient {
                     }
 
                     switch(dependency){
-                    case "relationship-list":
-                        newModelProcessRelationshipList(instance, params, prefix, ctx);
-                        break;
+                        case "relationship-list":
+                            newModelProcessRelationshipList(instance, params, prefix, ctx);
+                            break;
+                        case "metadata":
+                            newModelProcessMetadata(instance, params, prefix, ctx);
+                            break;
                     }
                     // create a method to update relationship-list
                     AAIRequest request = AAIRequest.createRequest(localResource, nameValues);
@@ -513,8 +519,8 @@ public abstract class AAIDeclarations implements AAIClient {
                     request.processRequestPathValues(nameValues);
 
                     getExecutor().post(request);
-                        getLogger().debug("Save relationship list - returning SUCCESS");
-                        return QueryStatus.SUCCESS;
+                    getLogger().debug("Save relationship list - returning SUCCESS");
+                    return QueryStatus.SUCCESS;
                 }
             } catch (Exception exc) {
                 ctx.setAttribute(prefix + ".error.message", exc.getMessage());
@@ -532,45 +538,9 @@ public abstract class AAIDeclarations implements AAIClient {
                 return QueryStatus.FAILURE;
             }
         } else {
-            String reSource = resource.toLowerCase().replace("-", "_");
-                String vnfId;
-
-            try {
-                switch(reSource) {
-                    case "generic_vnf":
-                    case "generic-vnf":
-                        vnfId = nameValues.get("vnf_id");
-                        if(vnfId == null) {
-                            getLogger().debug("Save(generic-vnf) with no vnf-id specified. Returning FAILURE");
-                            return QueryStatus.FAILURE;
-                        }
-                        vnfId = vnfId.trim().replace("'", "").replace("$", "").replace("'", "");
-                        GenericVnf vnf = this.requestGenericVnfData(vnfId);
-                        String status = params.get("prov-status");
-                        boolean updated = false;
-                        if(status != null && !status.isEmpty()) {
-                            vnf.setProvStatus(status);
-                        }
-                        if(updated) {
-                            this.postGenericVnfData(vnfId, vnf);
-                        }
-                        break;
-                    case "vpe":
-                        return update( resource,  key, params, prefix, ctx) ;
-
-                    default:
-                        getLogger().debug("Save() executing default path - returning FAILURE");
-                        return QueryStatus.FAILURE;
-                }
-            } catch (Exception exc) {
-                getLogger().warn("Failed save - returning FAILURE", exc);
-                ctx.setAttribute(prefix + ".error.message", exc.getMessage());
-                return QueryStatus.FAILURE;
-            }
+            getLogger().debug("Save() request for {} is not supported- returning FAILURE", resource);
+            return QueryStatus.FAILURE;
         }
-
-        getLogger().debug("Save - returning SUCCESS");
-        return QueryStatus.SUCCESS;
     }
 
     @Override
@@ -665,7 +635,12 @@ public abstract class AAIDeclarations implements AAIClient {
 
         if(AAIRequest.createRequest(resource, nameValues) != null) {
             if(resource.contains(":")) {
-                return processDeleteRelationshipList(resource, key, ctx, nameValues);
+                switch (resource.split(":")[1]){
+                    case "relationship-list":
+                        return processDeleteRelationshipList(resource, key, ctx, nameValues);
+                    case "metadata":
+                        return processDeleteMetadata(resource, key, ctx, nameValues);
+                }
             }
 
 
@@ -740,7 +715,7 @@ public abstract class AAIDeclarations implements AAIClient {
                     boolean itemRemoved = false;
                     RelationshipList relationshipList = vnf.getRelationshipList();
                     List<Relationship> relationships = relationshipList.getRelationship();
-                    List<Relationship> iterableList = new LinkedList<Relationship>(relationships);
+                    List<Relationship> iterableList = new LinkedList<>(relationships);
                     for(Relationship relationship : iterableList) {
                         if(relationship.getRelatedTo().equals(relatedTo)) {
                             relationships.remove(relationship);
@@ -750,17 +725,6 @@ public abstract class AAIDeclarations implements AAIClient {
 
                     if(!itemRemoved)
                         return QueryStatus.NOT_FOUND;
-
-//                    AAIRequest masterRequest = new GenericVnfRequest();
-//                    masterRequest.addRequestProperty(GenericVnfRequest.VNF_ID, vnfId);
-//                    relationshipRequest.addMasterRequest(masterRequest);
-//                    Map<String, String> attributes = objectToProperties(vnf);
-//                    try {
-//                        Boolean result = getExecutor().delete(relationshipRequest, attributes.get(AAIRequest.RESOURCE_VERSION));
-//                    } catch (AAIServiceException e) {
-//                        return QueryStatus.FAILURE;
-//                    }
-
                     try {
                         this.postGenericVnfData(vnf.getVnfId(), vnf);
                     } catch (AAIServiceException exc) {
@@ -786,20 +750,17 @@ public abstract class AAIDeclarations implements AAIClient {
     @Override
     public QueryStatus isAvailable(String arg0, String arg1, String arg2, SvcLogicContext arg3)
             throws SvcLogicException {
-        // TODO Auto-generated method stub
         throw new SvcLogicException("Method AAIService.isAvailable() has not been implemented yet");
     }
 
     @Override
     public QueryStatus notify(String resource, String action, String key, SvcLogicContext ctx) throws SvcLogicException {
-        // TODO Auto-generated method stub
         throw new SvcLogicException("Method AAIService.notify() has not been implemented yet");
     }
 
 //    @Override
     public QueryStatus newModelQuery(String resource, boolean localOnly, String select, String key, String prefix, String orderBy, SvcLogicContext ctx) {
 
-        Object response = null;
         QueryStatus retval = QueryStatus.SUCCESS;
         String modifier = null;
 
@@ -850,7 +811,7 @@ public abstract class AAIDeclarations implements AAIClient {
         return retval;
     }
 
-    public QueryStatus processResponseData(String rv, String resource, AAIRequest request, String prefix,  SvcLogicContext ctx, HashMap<String, String> nameValues, String modifier) throws JsonParseException, JsonMappingException, IOException, AAIServiceException
+    public QueryStatus processResponseData(String rv, String resource, AAIRequest request, String prefix,  SvcLogicContext ctx, Map<String, String> nameValues, String modifier) throws JsonParseException, JsonMappingException, IOException, AAIServiceException
     {
         Object response;
 
@@ -1081,14 +1042,12 @@ public abstract class AAIDeclarations implements AAIClient {
 
     @Override
     public QueryStatus release(String arg0, String arg1, SvcLogicContext arg2) throws SvcLogicException {
-        // TODO Auto-generated method stub
         throw new SvcLogicException("Method AAIService.release() has not been implemented yet");
     }
 
     @Override
     public QueryStatus reserve(String arg0, String arg1, String arg2, String arg3, SvcLogicContext arg4)
             throws SvcLogicException {
-        // TODO Auto-generated method stub
         throw new SvcLogicException("Method AAIService.reserve() has not been implemented yet");
     }
 
@@ -1125,24 +1084,37 @@ public abstract class AAIDeclarations implements AAIClient {
                             Method setter = null;
                             try {
                                 setter = resourceClass.getMethod("set"+StringUtils.capitalize(value), type);
-                                if(type.getName().startsWith("java.lang") || "boolean".equals(type.getName()) || "long".equals(type.getName())) {
+                                if(type.getName().startsWith("java.lang") || "boolean".equals(type.getName()) || "long".equals(type.getName()) || "int".equals(type.getName())) {
                                     try {
-                                        setter.setAccessible(true);
                                         Object arglist[] = new Object[1];
                                         arglist[0] = params.get(id);
 
                                         if(arglist[0] != null) {
                                             if(!type.getName().equals("java.lang.String")) {
 //                                            getLogger().debug(String.format("Processing %s with parameter %s", types[0].getName(), value));
-                                                if("boolean".equals(type.getName())) {
+                                                if("java.lang.Long".equals(type.getName()) || "java.lang.Integer".equals(type.getName())) {
+                                                    String fv = params.get(id);
+                                                    if(fv == null || fv.isEmpty()) {
+                                                        arglist[0] = null;
+                                                    } else {
+                                                        arglist[0] = valueOf(type, params.get(id));
+                                                    }
+                                                } else if("boolean".equals(type.getName())) {
                                                     arglist[0] = valueOf(Boolean.class, params.get(id));
+                                                } else if("int".equals(type.getName())) {
+                                                    arglist[0] = valueOf(Integer.class, params.get(id));
                                                 } else if("long".equals(type.getName())) {
+                                                    String fv = params.get(id);
+                                                    if(fv == null || fv.isEmpty()) {
+                                                        arglist[0] = null;
+                                                    } else {
                                                         arglist[0] = valueOf(Long.class, params.get(id));
+                                                    }
                                                 } else {
                                                     arglist[0] = valueOf(type, params.get(id));
                                                 }
                                             }
-                                            Object o = setter.invoke(instance, arglist);
+                                            Object obj = setter.invoke(instance, arglist);
                                         }
                                         set.remove(id);
 
@@ -1150,11 +1122,11 @@ public abstract class AAIDeclarations implements AAIClient {
                                         Throwable cause = x.getCause();
                                         getLogger().warn("Failed process for " + resourceClass.getName(), x);
                                     }
-                                } else if(type.getName().equals("java.util.List")) {
+                                } else if("java.util.List".equals(type.getName())) {
                                     List<String> newValues = new ArrayList<>();
                                     String length = id+"_length";
                                     if(!params.isEmpty() && params.containsKey(length)) {
-                                        String tmp = params.get(length).toString();
+                                        String tmp = params.get(length);
                                         int count = Integer.parseInt(tmp);
                                         for(int i=0; i<count; i++) {
                                             String tmpValue = params.get(String.format("%s[%d]", id, i));
@@ -1215,7 +1187,6 @@ public abstract class AAIDeclarations implements AAIClient {
                 Method method = getters.get(attribute);
                 if(method != null) {
                     try {
-                        method.setAccessible(true);
                         Object arglist[] = new Object[0];
 //                        arglist[0] = value;
                         Class<?>[] types = method.getParameterTypes();
@@ -1251,7 +1222,6 @@ public abstract class AAIDeclarations implements AAIClient {
 
                 if(getRelationshipListMethod != null){
                     try {
-                        getRelationshipListMethod.setAccessible(true);
                         obj = getRelationshipListMethod.invoke(instance);
                     } catch (InvocationTargetException x) {
                         Throwable cause = x.getCause();
@@ -1264,7 +1234,6 @@ public abstract class AAIDeclarations implements AAIClient {
                     Method setRelationshipListMethod = resourceClass.getMethod("setRelationshipList", RelationshipList.class);
                     if(setRelationshipListMethod != null){
                         try {
-                            setRelationshipListMethod.setAccessible(true);
                             Object arglist[] = new Object[1];
                             arglist[0] = relationshipList;
 
@@ -1292,21 +1261,27 @@ public abstract class AAIDeclarations implements AAIClient {
                     Relationship relationship = new Relationship();
                     relationships.add(relationship);
                     relationship.setRelatedTo(relatedTo);
+                    String relationshipLabel = "relationship-list.relationship[" + i + "].relationship-label";
+                    if(params.containsKey(searchKey)) {
+                        relationship.setRelationshipLabel(params.get(relationshipLabel));
+                    }
+                    getLogger().debug("About to process related link of {}", relatedLink);
                     if(relatedLink != null) {
+                        if(relatedLink.contains("v$"))
+                            relatedLink = relatedLink.replace(VERSION_PATTERN, "/v14/");
                         relationship.setRelatedLink(relatedLink);
                     } else {
-//                        List<RelationshipData> relData = relationship.getRelationshipData();
-                        Map<String, String> relParams = new HashMap<String, String>();
+                        Map<String, String> relParams = new HashMap<>();
 
-                    while(true) {
-                        String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-key";
-                        String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-value";
-                            if(!params.containsKey(searchRelationshipKey))
-                            break;
+                        while(true) {
+                            String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-key";
+                            String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-value";
+                                if(!params.containsKey(searchRelationshipKey))
+                                break;
 
-                            relParams.put(params.get(searchRelationshipKey), params.get(searchRelationshipValue));
-                        j++;
-                    }
+                                relParams.put(params.get(searchRelationshipKey), params.get(searchRelationshipValue));
+                            j++;
+                        }
                         AAIRequest rlRequest = AAIRequest.createRequest(relatedTo, relParams);
                         for(Map.Entry<String,String> entry : relParams.entrySet()) {
                             rlRequest.addRequestProperty(entry.getKey(), entry.getValue());
@@ -1314,6 +1289,26 @@ public abstract class AAIDeclarations implements AAIClient {
                         String path = rlRequest.updatePathDataValues(null);
                         relationship.setRelatedLink(path);
                     }
+                    {
+                        int k = 0;
+                        // process related to properties
+                        Map<String, String> relParams = new HashMap<String, String>();
+
+                        while(true) {
+                            String searchRelatedToKey = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-key";
+                            String searchRelatedToValue = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-value";
+                            if(!params.containsKey(searchRelatedToKey))
+                                break;
+
+                            RelatedToProperty relDatum = new RelatedToProperty();
+                            relDatum.setPropertyKey(params.get(searchRelatedToKey));
+                            relDatum.setPropertyValue(params.get(searchRelatedToValue));
+                            relationship.getRelatedToProperty().add(relDatum);
+
+                            relParams.put(params.get(searchRelatedToKey), params.get(searchRelatedToValue));
+                            k++;
+                        }
+                    }
                     i++;
                 }
             }
@@ -1325,7 +1320,6 @@ public abstract class AAIDeclarations implements AAIClient {
                 Method getVLansMethod = resourceClass.getMethod("getVlans");
                 if(getVLansMethod != null){
                     try {
-                        getVLansMethod.setAccessible(true);
                         obj = getVLansMethod.invoke(instance);
                     } catch (InvocationTargetException x) {
                         Throwable cause = x.getCause();
@@ -1338,7 +1332,6 @@ public abstract class AAIDeclarations implements AAIClient {
                     Method setVlansMethod = resourceClass.getMethod("setVlans", Vlans.class);
                     if(setVlansMethod != null){
                         try {
-                            setVlansMethod.setAccessible(true);
                             Object arglist[] = new Object[1];
                             arglist[0] = vlanList;
 
@@ -1391,7 +1384,6 @@ public abstract class AAIDeclarations implements AAIClient {
                 Method getMetadataMethod = resourceClass.getMethod("getMetadata");
                 if(getMetadataMethod != null){
                     try {
-                        getMetadataMethod.setAccessible(true);
                         obj = getMetadataMethod.invoke(instance);
                     } catch (InvocationTargetException x) {
                         Throwable cause = x.getCause();
@@ -1404,7 +1396,6 @@ public abstract class AAIDeclarations implements AAIClient {
                     Method setMetadataMethod = resourceClass.getMethod("setMetadata", Metadata.class);
                     if(setMetadataMethod != null){
                         try {
-                            setMetadataMethod.setAccessible(true);
                             Object arglist[] = new Object[1];
                             arglist[0] = metadataList;
 
@@ -1415,10 +1406,6 @@ public abstract class AAIDeclarations implements AAIClient {
                     }
                 }
 
-                if(metadataList.getMetadatum() == null) {
-//                    metadataList.setMetadatum(new ArrayList<Metadatum>());
-                }
-
                 // process data
                 int i = 0;
                 while(true){
@@ -1517,7 +1504,6 @@ public abstract class AAIDeclarations implements AAIClient {
             }
             if(getRelationshipListMethod != null){
                 try {
-                    getRelationshipListMethod.setAccessible(true);
                     obj = getRelationshipListMethod.invoke(instance);
                 } catch (InvocationTargetException x) {
                     Throwable cause = x.getCause();
@@ -1530,7 +1516,6 @@ public abstract class AAIDeclarations implements AAIClient {
                 Method setRelationshipListMethod = resourceClass.getMethod("setRelationshipList", RelationshipList.class);
                 if(setRelationshipListMethod != null){
                     try {
-                        setRelationshipListMethod.setAccessible(true);
                         Object arglist[] = new Object[1];
                         arglist[0] = relationshipList;
 
@@ -1544,7 +1529,7 @@ public abstract class AAIDeclarations implements AAIClient {
             boolean createdNewRelationships = false;
             List<Relationship> relationships = relationshipList.getRelationship();
             if(relationships == null) {
-                relationships = new ArrayList<Relationship>();
+                relationships = new ArrayList<>();
                 createdNewRelationships = true;
             }
 
@@ -1554,7 +1539,6 @@ public abstract class AAIDeclarations implements AAIClient {
                 if(!params.containsKey(searchKey))
                     break;
 
-                int j = 0;
                 String relatedTo = params.get(searchKey);
                 String relatedLinkKey = "relationship-list.relationship[" + i + "].related-link";
                 String relatedLink = null;
@@ -1563,29 +1547,126 @@ public abstract class AAIDeclarations implements AAIClient {
                 }
 
                 Relationship relationship = new Relationship();
-                    relationships.add(relationship);
-                    relationship.setRelatedTo(relatedTo);
-                    if(relatedLink != null) {
-                        relationship.setRelatedLink(relatedLink);
-                } else  {
+                relationships.add(relationship);
+                relationship.setRelatedTo(relatedTo);
+
+                String relationshipLabel = "relationship-list.relationship[" + i + "].relationship-label";
+                if(params.containsKey(searchKey)) {
+                    relationship.setRelationshipLabel(params.get(relationshipLabel));
+                }
+
+                if (relatedLink != null) {
+                    if(relatedLink.contains("v$"))
+                        relatedLink = relatedLink.replace(VERSION_PATTERN,  AAIRequest.getSupportedAAIVersion());
+                    relationship.setRelatedLink(relatedLink);
+                } else {
                     Map<String, String> relParams = new HashMap<>();
+                    int j = 0;
 
-                while(true) {
-                    String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-key";
-                    String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data[" + j + "].relationship-value";
-                    if(!params.containsKey(searchRelationshipKey))
-                        break;
+                    while (true) {
+                        String searchRelationshipKey = "relationship-list.relationship[" + i + "].relationship-data["
+                                + j + "].relationship-key";
+                        String searchRelationshipValue = "relationship-list.relationship[" + i + "].relationship-data["
+                                + j + "].relationship-value";
+                        if (!params.containsKey(searchRelationshipKey))
+                            break;
+
+                        RelationshipData relDatum = new RelationshipData();
+                        relDatum.setRelationshipKey(params.get(searchRelationshipKey));
+                        relDatum.setRelationshipValue(params.get(searchRelationshipValue));
+                        relationship.getRelationshipData().add(relDatum);
 
                         relParams.put(params.get(searchRelationshipKey), params.get(searchRelationshipValue));
-                    j++;
-                }
+                        j++;
+                    }
                     AAIRequest rlRequest = AAIRequest.createRequest(relatedTo, relParams);
-                    for(Map.Entry<String,String> entry : relParams.entrySet()) {
+                    for (Map.Entry<String, String> entry : relParams.entrySet()) {
                         rlRequest.addRequestProperty(entry.getKey(), entry.getValue());
                     }
                     String path = rlRequest.updatePathDataValues(null);
                     relationship.setRelatedLink(path);
                 }
+                {
+                    int k = 0;
+                    // process related to properties
+                    Map<String, String> relParams = new HashMap<String, String>();
+
+                    while(true) {
+                        String searchRelatedToKey = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-key";
+                        String searchRelatedToValue = "relationship-list.relationship[" + i + "].related-to-property[" + k + "].property-value";
+                        if(!params.containsKey(searchRelatedToKey))
+                            break;
+
+                        RelatedToProperty relDatum = new RelatedToProperty();
+                        relDatum.setPropertyKey(params.get(searchRelatedToKey));
+                        relDatum.setPropertyValue(params.get(searchRelatedToValue));
+                        relationship.getRelatedToProperty().add(relDatum);
+
+                        relParams.put(params.get(searchRelatedToKey), params.get(searchRelatedToValue));
+                        k++;
+                    }
+                }
+
+                i++;
+            }
+        }
+
+        return QueryStatus.SUCCESS;
+    }
+
+    private QueryStatus newModelProcessMetadata(Object instance, Map<String, String> params, String prefix, SvcLogicContext ctx) throws Exception {
+
+        if (!(instance instanceof ServiceInstance) && !(instance instanceof Image)) {
+            throw new IllegalArgumentException("request is not applicable for selected request");
+        }
+
+        Class resourceClass = instance.getClass();
+        Set<String> metadataKeys = new TreeSet<String>();
+        Set<String> set = params.keySet();
+        for(String attribute : set) {
+            if(attribute.startsWith("metadata")) {
+                metadataKeys.add(attribute);
+            }
+        }
+
+        // 3. Process Metadata
+        // add metadata
+        if(!metadataKeys.isEmpty()) {
+            Metadata metadata = null;
+            Object obj = null;
+            Method getMetadataMethod = resourceClass.getMethod("getMetadata");
+            if(getMetadataMethod != null){
+                try {
+                    obj = getMetadataMethod.invoke(instance);
+                } catch (InvocationTargetException x) {
+                    Throwable cause = x.getCause();
+                }
+            }
+            if(obj != null && obj instanceof Metadata){
+                metadata = (Metadata)obj;
+            } else {
+                metadata = new Metadata();
+                Method setMetadataMethod = resourceClass.getMethod("setMetadata", Metadata.class);
+                if(setMetadataMethod != null){
+                    try {
+                        setMetadataMethod.invoke(instance, metadata);
+                    } catch (InvocationTargetException x) {
+                    }
+                }
+            }
+
+            List<Metadatum> metadatumList = metadata.getMetadatum();
+            int i = 0;
+            while(true){
+                String metaNameKey = "metadata.metadatum[" + i + "].metaname";
+                String metaValueKey = "metadata.metadatum[" + i + "].metaval";
+                if(!params.containsKey(metaNameKey) || !params.containsKey(metaValueKey))
+                    break;
+
+                Metadatum metadatum = new Metadatum();
+                metadatum.setMetaname(params.get(metaNameKey));
+                metadatum.setMetaval(params.get(metaValueKey));
+                metadatumList.add(metadatum);
 
                 i++;
             }
@@ -1638,10 +1719,7 @@ public abstract class AAIDeclarations implements AAIClient {
             try {
                 retval = newModelBackupRequest(resource, params, "tmpRestore", ctx);
                 if(retval == QueryStatus.SUCCESS) {
-                    String current_json = ctx.getAttribute("tmpRestore");
-                    ctx.  setAttribute("tmpRestore", null);
-
-                    String snapshot_json = ctx.getAttribute(prefix);
+                    ctx.setAttribute("tmpRestore", null);
                 }
             } catch (Exception exc) {
                 getLogger().warn("Failed restore - returning FAILURE", exc);
@@ -1679,7 +1757,7 @@ public abstract class AAIDeclarations implements AAIClient {
 
     private QueryStatus processDeleteRelationshipList(String resource, String key, SvcLogicContext ctx, HashMap<String, String> nameValues) {
         try {
-            AAIRequest request = AAIRequest.createRequest(resource, nameValues);
+            AAIRequest request = AAIRequest.createRequest(resource.split(":")[0], nameValues);
             if(request == null) {
                 return QueryStatus.FAILURE;
             }
@@ -1697,12 +1775,11 @@ public abstract class AAIDeclarations implements AAIClient {
             Method getResourceVersionMethod = resourceClass.getMethod("getResourceVersion");
             if(getResourceVersionMethod != null){
                 try {
-                    getResourceVersionMethod.setAccessible(true);
                     Object object = getResourceVersionMethod.invoke(instance);
                     if(object != null)
                         resourceVersion = object.toString();
-                } catch (InvocationTargetException x) {
-                    Throwable cause = x.getCause();
+                } catch (InvocationTargetException exc) {
+                    getLogger().warn("Retrieving resource version", exc);
                 }
             }
 
@@ -1716,7 +1793,6 @@ public abstract class AAIDeclarations implements AAIClient {
             }
             if(getRelationshipListMethod != null){
                 try {
-                    getRelationshipListMethod.setAccessible(true);
                     obj = getRelationshipListMethod.invoke(instance);
                 } catch (InvocationTargetException x) {
                     Throwable cause = x.getCause();
@@ -1745,7 +1821,7 @@ public abstract class AAIDeclarations implements AAIClient {
             }
 
             List<Relationship> relationships = relationshipList.getRelationship();
-            List<Relationship> relationshipsToDelete = new LinkedList<Relationship>();
+            List<Relationship> relationshipsToDelete = new LinkedList<>();
 
             for(Relationship relationship : relationships) {
                 if(relatedTo.equals(relationship.getRelatedTo())) {
@@ -1779,7 +1855,7 @@ public abstract class AAIDeclarations implements AAIClient {
 
             for(Relationship targetRelationship : relationshipsToDelete) {
                 String json_text = mapper.writeValueAsString(targetRelationship);
-                boolean response = deleteRelationshipList(deleteUrl, json_text);
+                boolean response = deleteList(deleteUrl, json_text);
                 if(!response)
                     cumulativeResponse = response;
 
@@ -1796,6 +1872,88 @@ public abstract class AAIDeclarations implements AAIClient {
         }
     }
 
+    private QueryStatus processDeleteMetadata(String resource, String key, SvcLogicContext ctx, HashMap<String, String> nameValues) {
+        try {
+            AAIRequest request = AAIRequest.createRequest(resource, nameValues);
+            if(request == null) {
+                return QueryStatus.FAILURE;
+            }
+
+            request.processRequestPathValues(nameValues);
+            URL url = request.getRequestUrl("GET", null);
+
+            Class<?> resourceClass = request.getModelClass();
+            Object instance = getResource(url.toString(), resourceClass);
+
+            // get resource version
+            String resourceVersion = null;
+            Method getResourceVersionMethod = resourceClass.getMethod("getResourceVersion");
+            if(getResourceVersionMethod != null){
+                try {
+                    resourceVersion = (String) getResourceVersionMethod.invoke(instance);
+                } catch (InvocationTargetException x) {
+                }
+            }
+
+            Metadata metadata = null;
+            Object obj = null;
+            Method getMetadataMethod = resourceClass.getMethod("getMetadata");
+            if(getMetadataMethod != null){
+                try {
+                    obj = getMetadataMethod.invoke(instance);
+                } catch (InvocationTargetException x) {
+                    Throwable cause = x.getCause();
+                }
+            }
+            if(obj != null && obj instanceof Metadata){
+                metadata = (Metadata)obj;
+            } else {
+                getLogger().debug("No metadata found to process.");
+                return QueryStatus.NOT_FOUND;
+            }
+
+            if(metadata.getMetadatum() == null || metadata.getMetadatum().isEmpty()) {
+                return QueryStatus.NOT_FOUND;
+            }
+
+            List<Metadatum> metadatumList = metadata.getMetadatum();
+            Metadatum metadatumToDelete = null;
+
+            final String metaname = nameValues.get("metaname");
+
+            for(Metadatum metadatum : metadatumList) {
+                getLogger().debug(String.format("Comparing existing metadatum of '%s' to keyword '%s'", metadatum.getMetaname(),  metaname));
+                if(metaname.equals(metadatum.getMetaname())) {
+                    metadatumToDelete = metadatum;
+                    break;
+                }
+            }
+            if(metadatumToDelete == null) {
+                getLogger().info(String.format("Metadatum has not been found for %s", key));
+                return QueryStatus.NOT_FOUND;
+            }
+
+            String path = url.toString();
+            path = path + "/metadata/metadatum/" + encodeQuery( metadatumToDelete.getMetaname() ) +
+                    "?resource-version=" + metadatumToDelete.getResourceVersion();
+            URL deleteUrl = new URL(path);
+            boolean response = deleteList(deleteUrl, null);
+
+            if(!response)
+                return QueryStatus.FAILURE;
+
+            return QueryStatus.SUCCESS;
+
+        } catch(Exception exc) {
+            getLogger().warn("processDelete", exc);
+            return QueryStatus.FAILURE;
+        }
+    }
+
+    protected String encodeQuery(String param) throws UnsupportedEncodingException {
+        return URLEncoder.encode(param, "UTF-8").replace("+", "%20");
+    }
+
     static final Map<String, String> ctxGetBeginsWith( SvcLogicContext ctx, String prefix ) {
         Map<String, String> tmpPrefixMap = new HashMap<>();
 
@@ -1813,7 +1971,7 @@ public abstract class AAIDeclarations implements AAIClient {
         Map<String, String> prefixMap = new HashMap<>();
         Pattern p = Pattern.compile(".*\\[\\d\\]");
 
-        SortedSet<String> keys = new TreeSet(tmpPrefixMap.keySet () );
+        SortedSet<String> keys = new TreeSet<String>(tmpPrefixMap.keySet () );
         for(String key : keys) {
             Matcher m = p.matcher(key);
             if(m.matches()) {
@@ -1952,5 +2110,5 @@ public abstract class AAIDeclarations implements AAIClient {
     }
 
     public abstract <T> T getResource(String key, Class<T> type) throws AAIServiceException ;
-    protected abstract boolean deleteRelationshipList(URL url, String caller) throws AAIServiceException;
+    protected abstract boolean deleteList(URL url, String caller) throws AAIServiceException;
 }