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