Fixed major sonar issues in AAIClientRESTExecutor
[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() && "JKS".equalsIgnoreCase(extension)) {
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             requestUrl = request.getRequestUrl("PATCH", resourceVersion);
613             HttpURLConnection con = getConfiguredConnection(requestUrl, "PATCH");
614             ObjectMapper mapper = AAIService.getObjectMapper();
615             String jsonText = request.toJSONString();
616
617             LOGwriteDateTrace("data", jsonText);
618             logMetricRequest("PATCH "+requestUrl.getPath(), jsonText, requestUrl.getPath());
619
620             OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
621             osw.write(jsonText);
622             osw.flush();
623
624             // Check for errors
625             int responseCode = con.getResponseCode();
626             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
627                 inputStream = con.getInputStream();
628             } else {
629                 inputStream = con.getErrorStream();
630             }
631             String responseMessage = null;
632             try {
633                 responseMessage = con.getResponseMessage();
634             } catch(Exception exc) {
635                 LOG.info("Exception occured", exc.getMessage());
636                 responseMessage = EnglishReasonPhraseCatalog.INSTANCE.getReason(responseCode,null);
637             } finally {
638                 if(responseMessage == null)
639                         responseMessage = NOT_PROVIDED;
640             }
641
642             LOG.info("HttpURLConnection result: {} : {}", responseCode, responseMessage);
643             logMetricResponse(responseCode, responseMessage);
644
645             // Process the response
646             BufferedReader reader;
647             String line = null;
648             reader = new BufferedReader( new InputStreamReader( inputStream ) );
649             mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
650
651             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
652                 StringBuilder stringBuilder = new StringBuilder();
653
654                 while( ( line = reader.readLine() ) != null ) {
655                     stringBuilder.append( line );
656                 }
657                 LOGwriteEndingTrace(responseCode, responseMessage, (stringBuilder.length() > 0) ? stringBuilder.toString() : "{no-data}");
658                 return true;
659             } else {
660                 StringBuilder stringBuilder = new StringBuilder();
661
662                 while( ( line = reader.readLine() ) != null ) {
663                     stringBuilder.append("\n").append( line );
664                 }
665                 LOG.info(stringBuilder.toString());
666
667
668                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
669                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
670
671                 throw new AAIServiceException(responseCode, errorresponse);
672             }
673         } catch(AAIServiceException aaiexc) {
674             throw aaiexc;
675         } catch (Exception exc) {
676             LOG.warn("AAIRequestExecutor.patch", exc);
677             throw new AAIServiceException(exc);
678         } finally {
679             try {
680                 if(inputStream != null)
681                 inputStream.close();
682             } catch (Exception exc) {
683                 LOG.warn("AAIRequestExecutor.patch", exc);
684             }
685         }
686     }
687
688     /**
689      *
690      * @param httpReqUrl
691      * @param method
692      * @return
693      * @throws Exception
694      */
695     protected HttpURLConnection getConfiguredConnection(URL httpReqUrl, String method) throws Exception {
696         HttpURLConnection con = (HttpURLConnection) httpReqUrl.openConnection();
697
698         // Set up the connection properties
699         con.setRequestProperty("Connection", "close");
700         con.setDoInput(true);
701         con.setDoOutput(true);
702         con.setUseCaches(false);
703         con.setConnectTimeout(connection_timeout);
704         con.setReadTimeout(read_timeout);
705         con.setRequestMethod(method);
706         con.setRequestProperty("Accept", "application/json");
707         con.setRequestProperty("Transfer-Encoding","chunked");
708         con.setRequestProperty("Content-Type",
709                 "PATCH".equalsIgnoreCase(method) ? "application/merge-patch+json" : "application/json");
710         con.setRequestProperty("X-FromAppId", applicationId);
711         con.setRequestProperty("X-TransactionId", TransactionIdTracker.getNextTransactionId());
712         String mlId = ml.getRequestID();
713         if (mlId != null && !mlId.isEmpty()) {
714             LOG.debug(String.format("MetricLogger requestId = %s", mlId));
715             con.setRequestProperty(MetricLogger.REQUEST_ID, mlId);
716         } else {
717             LOG.debug("MetricLogger requestId is null");
718         }
719
720         if (userName != null && !userName.isEmpty() && userPassword != null && !userPassword.isEmpty()) {
721             String basicAuth = "Basic " + new String(Base64.encodeBase64((userName + ":" + userPassword).getBytes()));
722             con.setRequestProperty("Authorization", basicAuth);
723         }
724
725         if (con instanceof HttpsURLConnection && CTX != null) {
726             SSLSocketFactory sockFact = CTX.getSocketFactory();
727             HttpsURLConnection.class.cast(con).setSSLSocketFactory(sockFact);
728         }
729         return con;
730     }
731
732     private URL appendDepth(URL requestUrl, AAIRequest request) throws MalformedURLException {
733
734         String depth = request.requestProperties.getProperty("depth", "1");
735         String path = requestUrl.toString();
736         if(path.contains("?depth=") || path.contains("&depth=")) {
737             return requestUrl;
738         } else {
739             if(path.contains("?")) {
740                 path = String.format("%s&depth=%s", path, depth);
741             } else {
742                 path = String.format("%s?depth=%s", path, depth);
743             }
744             return new URL(path);
745         }
746     }
747
748     public void logMetricRequest(String targetServiceName, String msg, String path){
749         String svcInstanceId = "";
750         String svcName = null;
751         String partnerName = null;
752         String targetEntity = "A&AI";
753         String targetVirtualEntity = null;
754
755         ml.logRequest(svcInstanceId, svcName, partnerName, targetEntity, targetServiceName, targetVirtualEntity, msg);
756     }
757
758     public void logMetricResponse(int responseCode, String responseDescription){
759         ml.logResponse(responseCode < 400 ? "COMPLETE" : "ERROR", Integer.toString(responseCode), responseDescription);
760     }
761
762     protected void LOGwriteFirstTrace(String method, String url) {
763         String time = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(System.currentTimeMillis());
764         LOG.info("A&AI transaction :");
765         LOG.info("Request Time : " + time + ", Method : " + method);
766         LOG.info("Request URL : "+ url);
767     }
768
769     protected void LOGwriteDateTrace(String name, String data) {
770         LOG.info("Input - " + name  + " : " + data);
771     }
772
773     protected void LOGwriteEndingTrace(int response_code, String comment, String data) {
774         LOG.info("Response code : " + response_code +", " + comment);
775         LOG.info(String.format("Response data : %s", data));
776     }
777
778 }