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