Implement support for inner variables
[ccsdk/sli/adaptors.git] / aai-service / provider / src / main / java / org / onap / ccsdk / sli / adaptors / aai / AAIClientRESTExecutor.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                         reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.ccsdk.sli.adaptors.aai;
23
24 import java.io.BufferedReader;
25 import java.io.ByteArrayInputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.io.OutputStreamWriter;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.InvocationTargetException;
33 import java.lang.reflect.Method;
34 import java.lang.reflect.Modifier;
35 import java.net.HttpURLConnection;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.nio.charset.StandardCharsets;
39 import java.security.KeyManagementException;
40 import java.security.KeyStore;
41 import java.security.NoSuchAlgorithmException;
42 import java.text.SimpleDateFormat;
43 import java.util.Properties;
44
45 import javax.net.ssl.HostnameVerifier;
46 import javax.net.ssl.HttpsURLConnection;
47 import javax.net.ssl.KeyManagerFactory;
48 import javax.net.ssl.SSLContext;
49 import javax.net.ssl.SSLSession;
50 import javax.net.ssl.SSLSocketFactory;
51
52 import org.apache.commons.codec.binary.Base64;
53 import org.onap.ccsdk.sli.adaptors.aai.AAIService.TransactionIdTracker;
54 import org.onap.ccsdk.sli.adaptors.aai.data.AAIDatum;
55 import org.onap.ccsdk.sli.adaptors.aai.data.ErrorResponse;
56 import org.onap.ccsdk.sli.adaptors.aai.data.RequestError;
57 import org.onap.ccsdk.sli.adaptors.aai.data.ResourceVersion;
58 import org.onap.ccsdk.sli.adaptors.aai.data.ServiceException;
59 import org.onap.ccsdk.sli.core.sli.MetricLogger;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 import com.fasterxml.jackson.databind.DeserializationFeature;
64 import com.fasterxml.jackson.databind.ObjectMapper;
65 import org.apache.http.impl.EnglishReasonPhraseCatalog;
66
67 /**
68  * The AAIClientRESTExecutor class provides CRUD API for AAI Client service.
69  * @author  richtabedzki
70  */
71 public class AAIClientRESTExecutor implements AAIExecutorInterface {
72
73     private final String truststorePath;
74     private final String truststorePassword;
75     private final String keystorePath;
76     private final String keystorePassword;
77     private final Boolean ignoreCertificateHostError;
78     // authentication credentials
79     private String userName;
80     private String userPassword;
81     private final String applicationId;
82
83     /**
84      * class Constructor
85      * @param props - properties to initialize an instance.
86      */
87     public AAIClientRESTExecutor(Properties props) {
88         super();
89
90         userName            = props.getProperty(AAIService.CLIENT_NAME);
91         userPassword        = props.getProperty(AAIService.CLIENT_PWWD);
92
93         if(userName == null || userName.isEmpty()){
94             LOG.debug("Basic user name is not set");
95         }
96         if(userPassword == null || userPassword.isEmpty()) {
97             LOG.debug("Basic password is not set");
98         }
99
100         truststorePath     = props.getProperty(AAIService.TRUSTSTORE_PATH);
101         truststorePassword = props.getProperty(AAIService.TRUSTSTORE_PSSWD);
102         keystorePath         = props.getProperty(AAIService.KEYSTORE_PATH);
103         keystorePassword     = props.getProperty(AAIService.KEYSTORE_PSSWD);
104
105         String tmpApplicationId =props.getProperty(AAIService.APPLICATION_ID);
106         if(tmpApplicationId == null || tmpApplicationId.isEmpty()) {
107             tmpApplicationId = "SDNC";
108         }
109         applicationId = tmpApplicationId;
110
111         String iche = props.getProperty(AAIService.CERTIFICATE_HOST_ERROR);
112         boolean host_error = false;
113         if(iche != null && !iche.isEmpty()) {
114             host_error = Boolean.valueOf(iche);
115         }
116
117         ignoreCertificateHostError = host_error;
118
119         HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){
120             public boolean verify(String string,SSLSession ssls) {
121                 return ignoreCertificateHostError;
122             }
123         });
124
125         if(truststorePath != null && truststorePassword != null && (new File(truststorePath)).exists()) {
126             System.setProperty("javax.net.ssl.trustStore", truststorePath);
127             System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
128         }
129
130         if(keystorePath != null && keystorePassword != null && (new File(keystorePath)).exists())
131         {
132             //both jersey and HttpURLConnection can use this
133             SSLContext ctx = null;
134             try {
135                 ctx = SSLContext.getInstance("TLS");
136
137                 KeyManagerFactory kmf = null;
138                 try (FileInputStream fin = new FileInputStream(keystorePath)){
139                     String storeType = "PKCS12";
140                     String def = KeyStore.getDefaultType();
141                     kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
142
143                     String extension = keystorePath.substring(keystorePath.lastIndexOf(".") + 1);
144
145                     if(extension != null && !extension.isEmpty() && extension.equalsIgnoreCase("JKS")) {
146                         storeType = "JKS";
147                     }
148                     KeyStore ks = KeyStore.getInstance(storeType);
149
150                     char[] pwd = keystorePassword.toCharArray();
151                     ks.load(fin, pwd);
152                     kmf.init(ks, pwd);
153                 } catch (Exception ex) {
154                     LOG.error("AAIResource", ex);
155                 }
156
157                 ctx.init(kmf.getKeyManagers(), null, null);
158
159                 CTX = ctx;
160                 LOG.debug("SSLContext created");
161
162             } catch (KeyManagementException | NoSuchAlgorithmException exc) {
163                 LOG.error("AAIResource", exc);
164             }
165         }
166
167         try {
168             Field methodsField = HttpURLConnection.class.getDeclaredField("methods");
169             methodsField.setAccessible(true);
170             // get the methods field modifiers
171             Field modifiersField = Field.class.getDeclaredField("modifiers");
172             // bypass the "private" modifier
173             modifiersField.setAccessible(true);
174
175             // remove the "final" modifier
176             modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);
177
178             /* valid HTTP methods */
179             String[] methods = {
180                        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
181             };
182             // set the new methods - including patch
183             methodsField.set(null, methods);
184
185         } catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
186             LOG.warn("Adding PATCH method", e);
187         }
188         LOG.info("AAIResource.ctor initialized.");
189
190     }
191
192     private static final Logger LOG = LoggerFactory.getLogger(AAIService.class);
193     private static final String NOT_PROVIDED = "NOT PROVIDED";
194     private final MetricLogger ml = new MetricLogger();
195
196     private SSLContext CTX;
197
198
199     private int connection_timeout = 300000;
200
201     private int read_timeout = 300000;
202
203     /**
204      * Returns an String that contains JSON data returned from the AAI Server.
205      * <p>
206      * This method always returns immediately, whether or not the
207      * data exists.
208      *
209      * @param  request  an instance of AAIRequiest representing
210      *                 the request made by DirectedGraph node.
211      * @return      the JSON based representation of data instance requested.
212      * @see         String
213      */
214     @Override
215     public String get(AAIRequest request) throws AAIServiceException {
216         String response = null;
217         InputStream inputStream = null;
218         HttpURLConnection con = null;
219         URL requestUrl = null;
220
221         StringBuilder errorStringBuilder = new StringBuilder();
222
223         try {
224
225             if(request.getRequestObject() != null) {
226                 requestUrl = request.getRequestUrl(HttpMethod.POST, null);
227                 requestUrl = appendDepth(requestUrl, request);
228                 con = getConfiguredConnection(requestUrl, HttpMethod.POST);
229                 String json_text = request.toJSONString();
230                 LOGwriteDateTrace("data", json_text);
231                 logMetricRequest("POST "+requestUrl.getPath(), json_text, requestUrl.getPath());
232                 OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
233                 osw.write(json_text);
234                 osw.flush();
235             } else {
236                 requestUrl = request.getRequestUrl(HttpMethod.GET, null);
237                 requestUrl = appendDepth(requestUrl, request);
238                 con = getConfiguredConnection(requestUrl, HttpMethod.GET);
239                 logMetricRequest("GET "+requestUrl.getPath(), "", requestUrl.getPath());
240             }
241
242             // Check for errors
243             int responseCode = con.getResponseCode();
244             if (responseCode == HttpURLConnection.HTTP_OK) {
245                 inputStream = con.getInputStream();
246             } else {
247                 inputStream = con.getErrorStream();
248             }
249             String responseMessage = null;
250             try {
251                 responseMessage = con.getResponseMessage();
252             } catch(Exception exc) {
253                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
254             } finally {
255                 if(responseMessage == null)
256                         responseMessage = NOT_PROVIDED;
257             }
258
259             // Process the response
260             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
261             logMetricResponse(responseCode, responseMessage);
262
263             if(inputStream == null) inputStream = new ByteArrayInputStream("".getBytes(StandardCharsets.UTF_8));
264             BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
265
266             ObjectMapper mapper = AAIService.getObjectMapper();
267
268             if (responseCode == HttpURLConnection.HTTP_OK) {
269                 StringBuilder stringBuilder = new StringBuilder();
270                 String line = null;
271                 while( ( line = reader.readLine() ) != null ) {
272                     stringBuilder.append( line );
273                 }
274                 response = stringBuilder.toString();
275                 try {
276                     Object object = mapper.readValue(response, Object.class);
277                     LOGwriteEndingTrace(HttpURLConnection.HTTP_OK, responseMessage, mapper.writeValueAsString(object));
278                 } catch(Exception exc) {
279                     LOGwriteEndingTrace(HttpURLConnection.HTTP_OK, responseMessage, mapper.writeValueAsString(response));
280                 }
281             } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
282                 LOGwriteEndingTrace(responseCode, responseMessage, "Entry does not exist.");
283                 ErrorResponse errorresponse = null;
284                 try {
285                     errorresponse = mapper.readValue(reader, ErrorResponse.class);
286                 } catch(Exception exc) {
287                     errorresponse = new ErrorResponse();
288                     RequestError requestError = new RequestError();
289                     ServiceException serviceException = new ServiceException();
290                     serviceException.setText("Entry does not exist.");
291                     requestError.setServiceException(serviceException);
292                     errorresponse.setRequestError(requestError );
293                 }
294                 throw new AAIServiceException(responseCode, errorresponse);
295             } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
296                 StringBuilder stringBuilder = new StringBuilder();
297                 String line = null;
298                 while( ( line = reader.readLine() ) != null ) {
299                     stringBuilder.append( line );
300                 }
301                 LOGwriteEndingTrace(responseCode, responseMessage, stringBuilder.toString());
302                 ServiceException serviceException = new ServiceException();
303                 serviceException.setMessageId("HTTP_UNAUTHORIZED");
304                 serviceException.setText(stringBuilder.toString());
305                 RequestError requestError = new RequestError();
306                 requestError.setServiceException(serviceException);
307                 ErrorResponse errorresponse = new ErrorResponse();
308                 errorresponse.setRequestError(requestError);
309                 throw new AAIServiceException(responseCode, errorresponse);
310             } else {
311                 String line = null;
312                 while( ( line = reader.readLine() ) != null ) {
313                     errorStringBuilder.append("\n").append( line );
314                 }
315
316                 ErrorResponse errorresponse = mapper.readValue(errorStringBuilder.toString(), ErrorResponse.class);
317                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
318                 throw new AAIServiceException(responseCode, errorresponse);
319             }
320
321         } catch(AAIServiceException aaiexc) {
322             throw aaiexc;
323         } catch (Exception exc) {
324             LOG.warn(errorStringBuilder.toString(), exc);
325             throw new AAIServiceException(exc);
326         } finally {
327             if(inputStream != null){
328                 try {
329                     inputStream.close();
330                 } catch(Exception exc) {
331                     LOG.warn("", exc);
332                 }
333             }
334         }
335         return response;
336     }
337
338     /**
339      * Returns an String that contains JSON data returned from the AAI Server.
340      * <p>
341      * This method always returns immediately, whether or not the
342      * data exists.
343      *
344      * @param  request  an instance of AAIRequiest representing
345      *                 the request made by DirectedGraph node.
346      * @return      the JSON based representation of data instance requested.
347      * @see         String
348      */
349     @Override
350     public String post(AAIRequest request) throws AAIServiceException {
351         InputStream inputStream = null;
352
353         try {
354             String resourceVersion = null;
355             AAIDatum instance = request.getRequestObject();
356
357             try {
358                 Method getResourceVersionMethod = instance.getClass().getMethod("getResourceVersion");
359                 if(getResourceVersionMethod != null){
360                     try {
361                         Object object = getResourceVersionMethod.invoke(instance);
362                         if(object != null)
363                             resourceVersion = object.toString();
364                     } catch (InvocationTargetException exc) {
365                         LOG.warn("", exc);
366                     }
367                 }
368             } catch(Exception exc) {
369                 LOG.error("", exc);
370             }
371
372             URL requestUrl = request.getRequestUrl(HttpMethod.PUT, resourceVersion);
373             HttpURLConnection con = getConfiguredConnection(requestUrl, HttpMethod.PUT);
374             ObjectMapper mapper = AAIService.getObjectMapper();
375             String jsonText = request.toJSONString();
376
377             LOGwriteDateTrace("data", jsonText);
378             logMetricRequest("PUT "+requestUrl.getPath(), jsonText, requestUrl.getPath());
379
380             OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
381             osw.write(jsonText);
382             osw.flush();
383
384             // Check for errors
385             int responseCode = con.getResponseCode();
386             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
387                 inputStream = con.getInputStream();
388             } else {
389                 inputStream = con.getErrorStream();
390             }
391             String responseMessage = null;
392             try {
393                 responseMessage = con.getResponseMessage();
394             } catch(Exception exc) {
395                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
396             } finally {
397                 if(responseMessage == null)
398                         responseMessage = NOT_PROVIDED;
399             }
400
401             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
402             logMetricResponse(responseCode, responseMessage);
403
404             // Process the response
405             BufferedReader reader;
406             String line = null;
407             reader = new BufferedReader( new InputStreamReader( inputStream ) );
408             mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
409
410             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
411                 StringBuilder stringBuilder = new StringBuilder();
412
413                 while( ( line = reader.readLine() ) != null ) {
414                     stringBuilder.append( line );
415                 }
416                 LOGwriteEndingTrace(responseCode, responseMessage, (stringBuilder.length() > 0) ? stringBuilder.toString() : "{no-data}");
417                 return stringBuilder.toString();
418             } else {
419                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
420                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
421
422                 throw new AAIServiceException(responseCode, errorresponse);
423             }
424         } catch(AAIServiceException aaiexc) {
425             throw aaiexc;
426         } catch (Exception exc) {
427             LOG.warn("AAIRequestExecutor.post", exc);
428             throw new AAIServiceException(exc);
429         } finally {
430             try {
431                 if(inputStream != null)
432                 inputStream.close();
433             } catch (Exception exc) {
434                 LOG.warn("AAIRequestExecutor.post", exc);
435             }
436         }
437     }
438
439     /**
440      * Returns Boolean that contains completion state of the command executed.
441      * <p>
442      * This method always returns immediately, whether or not the
443      * data exists.
444      *
445      * @param  request  an instance of AAIRequiest representing
446      * @param  resourceVersion  a resource version of the data instacne to be deleted.
447      *                 the request made by DirectedGraph node.
448      * @return      completion state of the command.
449      * @see         String
450      */
451     @Override
452     public Boolean delete(AAIRequest request, String resourceVersion) throws AAIServiceException {
453         Boolean response = null;
454         InputStream inputStream = null;
455
456         if(resourceVersion == null) {
457             throw new AAIServiceException("resource-version is required for DELETE request");
458         }
459
460         try {
461             URL requestUrl = request.getRequestUrl(HttpMethod.DELETE, resourceVersion);
462             HttpURLConnection conn = getConfiguredConnection(requestUrl, HttpMethod.DELETE);
463             logMetricRequest("DELETE "+requestUrl.getPath(), "", requestUrl.getPath());
464             conn.setDoOutput(true);
465
466             // Check for errors
467             int responseCode = conn.getResponseCode();
468             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
469                 inputStream = conn.getInputStream();
470             } else {
471                 inputStream = conn.getErrorStream();
472             }
473             String responseMessage = null;
474             try {
475                 responseMessage = conn.getResponseMessage();
476             } catch(Exception exc) {
477                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
478             } finally {
479                 if(responseMessage == null)
480                         responseMessage = NOT_PROVIDED;
481             }
482
483             // Process the response
484             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
485             logMetricResponse(responseCode, responseMessage);
486
487             if(inputStream == null) inputStream = new ByteArrayInputStream("".getBytes(StandardCharsets.UTF_8));
488             BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
489             String line = null;
490
491             ObjectMapper mapper = AAIService.getObjectMapper();
492
493             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
494                 StringBuilder stringBuilder = new StringBuilder();
495
496                 while( ( line = reader.readLine() ) != null ) {
497                     stringBuilder.append( line );
498                 }
499                 LOGwriteEndingTrace(responseCode, responseMessage, stringBuilder.toString());
500                 response = true;
501             } else if(responseCode == HttpURLConnection.HTTP_NOT_FOUND ) {
502                 LOGwriteEndingTrace(responseCode, responseMessage, "Entry does not exist.");
503                 response = false;
504             } else {
505                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
506                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
507                 throw new AAIServiceException(responseCode, errorresponse);
508             }
509         } catch(AAIServiceException aaiexc) {
510             throw aaiexc;
511         } catch (Exception exc) {
512             LOG.warn("delete", exc);
513             throw new AAIServiceException(exc);
514         } finally {
515             if(inputStream != null){
516                 try {
517                     inputStream.close();
518                 } catch(Exception exc) {
519                     LOG.warn("delete", exc);
520                 }
521             }
522         }
523         return response;
524     }
525
526     /**
527      * Returns an String that contains JSON data returned from the AAI Server.
528      * <p>
529      * This method always returns immediately, whether or not the
530      * data exists.
531      *
532      * @param  request  an instance of AAIRequiest representing
533      *                 the request made by DirectedGraph node.
534      * @param clas   an definition of the class for which data will be returned
535      * @return      the instance of the class with data.
536      * @see         String
537      */
538     @Override
539     public Object query(AAIRequest request, Class clas) throws AAIServiceException {
540         Object response = null;
541         InputStream inputStream = null;
542
543         try {
544             URL requestUrl = request.getRequestQueryUrl(HttpMethod.GET);
545             HttpURLConnection con = getConfiguredConnection(requestUrl, HttpMethod.GET);
546             logMetricRequest("GET "+requestUrl.getPath(), "", requestUrl.getPath());
547
548             // Check for errors
549             int responseCode = con.getResponseCode();
550             if (responseCode == HttpURLConnection.HTTP_OK) {
551                 inputStream = con.getInputStream();
552             } else {
553                 inputStream = con.getErrorStream();
554             }
555             String responseMessage = null;
556             try {
557                 responseMessage = con.getResponseMessage();
558             } catch(Exception exc) {
559                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
560             } finally {
561                 if(responseMessage == null)
562                         responseMessage = NOT_PROVIDED;
563             }
564
565             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
566             logMetricResponse(responseCode, responseMessage);
567             ObjectMapper mapper = AAIService.getObjectMapper();
568
569             if (responseCode == HttpURLConnection.HTTP_OK) {
570                 // Process the response
571                 BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
572                 response = mapper.readValue(reader, clas);
573                 LOGwriteEndingTrace(HttpURLConnection.HTTP_OK, "SUCCESS", mapper.writeValueAsString(response));
574             } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
575                 LOGwriteEndingTrace(responseCode, "HTTP_NOT_FOUND", "Entry does not exist.");
576                 return response;
577             } else {
578                 BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
579                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
580                 LOGwriteEndingTrace(responseCode, "FAILURE", mapper.writeValueAsString(errorresponse));
581                 throw new AAIServiceException(responseCode, errorresponse);
582             }
583
584         } catch(AAIServiceException aaiexc) {
585             throw aaiexc;
586         } catch (Exception exc) {
587             LOG.warn("GET", exc);
588             throw new AAIServiceException(exc);
589         } finally {
590             if(inputStream != null){
591                 try {
592                     inputStream.close();
593                 } catch(Exception exc) {
594                     LOG.warn("GET", exc);
595                 }
596             }
597         }
598         return response;
599     }
600
601     @Override
602     public Boolean patch(AAIRequest request, String resourceVersion) throws AAIServiceException {
603         InputStream inputStream = null;
604
605         try {
606             AAIDatum instance = request.getRequestObject();
607             if(instance instanceof ResourceVersion) {
608                 resourceVersion = ((ResourceVersion)instance).getResourceVersion();
609             }
610
611             URL requestUrl = null;
612             HttpURLConnection con = getConfiguredConnection(requestUrl = request.getRequestUrl("PATCH", resourceVersion), "PATCH");
613             ObjectMapper mapper = AAIService.getObjectMapper();
614             String jsonText = request.toJSONString();
615
616             LOGwriteDateTrace("data", jsonText);
617             logMetricRequest("PATCH "+requestUrl.getPath(), jsonText, requestUrl.getPath());
618
619             OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
620             osw.write(jsonText);
621             osw.flush();
622
623             // Check for errors
624             int responseCode = con.getResponseCode();
625             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
626                 inputStream = con.getInputStream();
627             } else {
628                 inputStream = con.getErrorStream();
629             }
630             String responseMessage = null;
631             try {
632                 responseMessage = con.getResponseMessage();
633             } catch(Exception exc) {
634                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
635             } finally {
636                 if(responseMessage == null)
637                         responseMessage = NOT_PROVIDED;
638             }
639
640             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
641             logMetricResponse(responseCode, responseMessage);
642
643             // Process the response
644             BufferedReader reader;
645             String line = null;
646             reader = new BufferedReader( new InputStreamReader( inputStream ) );
647             mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
648
649             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
650                 StringBuilder stringBuilder = new StringBuilder();
651
652                 while( ( line = reader.readLine() ) != null ) {
653                     stringBuilder.append( line );
654                 }
655                 LOGwriteEndingTrace(responseCode, responseMessage, (stringBuilder.length() > 0) ? stringBuilder.toString() : "{no-data}");
656                 return true;
657             } else {
658                 StringBuilder stringBuilder = new StringBuilder();
659
660                 while( ( line = reader.readLine() ) != null ) {
661                     stringBuilder.append("\n").append( line );
662                 }
663                 LOG.info(stringBuilder.toString());
664
665
666                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
667                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
668
669                 throw new AAIServiceException(responseCode, errorresponse);
670             }
671         } catch(AAIServiceException aaiexc) {
672             throw aaiexc;
673         } catch (Exception exc) {
674             LOG.warn("AAIRequestExecutor.patch", exc);
675             throw new AAIServiceException(exc);
676         } finally {
677             try {
678                 if(inputStream != null)
679                 inputStream.close();
680             } catch (Exception exc) {
681                 LOG.warn("AAIRequestExecutor.patch", exc);
682             }
683         }
684     }
685
686     /**
687      *
688      * @param httpReqUrl
689      * @param method
690      * @return
691      * @throws Exception
692      */
693     protected HttpURLConnection getConfiguredConnection(URL httpReqUrl, String method) throws Exception {
694         HttpURLConnection con = (HttpURLConnection) httpReqUrl.openConnection();
695
696         // Set up the connection properties
697         con.setRequestProperty("Connection", "close");
698         con.setDoInput(true);
699         con.setDoOutput(true);
700         con.setUseCaches(false);
701         con.setConnectTimeout(connection_timeout);
702         con.setReadTimeout(read_timeout);
703         con.setRequestMethod(method);
704         con.setRequestProperty("Accept", "application/json");
705         con.setRequestProperty("Transfer-Encoding","chunked");
706         con.setRequestProperty("Content-Type",
707                 "PATCH".equalsIgnoreCase(method) ? "application/merge-patch+json" : "application/json");
708         con.setRequestProperty("X-FromAppId", applicationId);
709         con.setRequestProperty("X-TransactionId", TransactionIdTracker.getNextTransactionId());
710         String mlId = ml.getRequestID();
711         if (mlId != null && !mlId.isEmpty()) {
712             LOG.debug(String.format("MetricLogger requestId = %s", mlId));
713             con.setRequestProperty(MetricLogger.REQUEST_ID, mlId);
714         } else {
715             LOG.debug("MetricLogger requestId is null");
716         }
717
718         if (userName != null && !userName.isEmpty() && userPassword != null && !userPassword.isEmpty()) {
719             String basicAuth = "Basic " + new String(Base64.encodeBase64((userName + ":" + userPassword).getBytes()));
720             con.setRequestProperty("Authorization", basicAuth);
721         }
722
723         if (con instanceof HttpsURLConnection && CTX != null) {
724             SSLSocketFactory sockFact = CTX.getSocketFactory();
725             HttpsURLConnection.class.cast(con).setSSLSocketFactory(sockFact);
726         }
727         return con;
728     }
729
730     private URL appendDepth(URL requestUrl, AAIRequest request) throws MalformedURLException {
731
732         String depth = request.requestProperties.getProperty("depth", "1");
733         String path = requestUrl.toString();
734         if(path.contains("?depth=") || path.contains("&depth=")) {
735             return requestUrl;
736         } else {
737             if(path.contains("?")) {
738                 path = String.format("%s&depth=%s", path, depth);
739             } else {
740                 path = String.format("%s?depth=%s", path, depth);
741             }
742             return new URL(path);
743         }
744     }
745
746     public void logMetricRequest(String targetServiceName, String msg, String path){
747         String svcInstanceId = "";
748         String svcName = null;
749         String partnerName = null;
750         String targetEntity = "A&AI";
751         String targetVirtualEntity = null;
752
753         ml.logRequest(svcInstanceId, svcName, partnerName, targetEntity, targetServiceName, targetVirtualEntity, msg);
754     }
755
756     public void logMetricResponse(int responseCode, String responseDescription){
757         ml.logResponse(responseCode < 400 ? "COMPLETE" : "ERROR", Integer.toString(responseCode), responseDescription);
758     }
759
760     protected void LOGwriteFirstTrace(String method, String url) {
761         String time = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(System.currentTimeMillis());
762         LOG.info("A&AI transaction :");
763         LOG.info("Request Time : " + time + ", Method : " + method);
764         LOG.info("Request URL : "+ url);
765     }
766
767     protected void LOGwriteDateTrace(String name, String data) {
768         LOG.info("Input - " + name  + " : " + data);
769     }
770
771     protected void LOGwriteEndingTrace(int response_code, String comment, String data) {
772         LOG.info("Response code : " + response_code +", " + comment);
773         LOG.info(String.format("Response data : %s", data));
774     }
775
776 }