aa134cb4af2de6d08fbe90caf493ed96fd6e9345
[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 (FileInputStream fin = new FileInputStream(keystorePath)){
142                     String storeType = "PKCS12";
143                     String def = KeyStore.getDefaultType();
144                     kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
145
146                     String extension = keystorePath.substring(keystorePath.lastIndexOf(".") + 1);
147
148                     if(extension != null && !extension.isEmpty() && extension.equalsIgnoreCase("JKS")) {
149                         storeType = "JKS";
150                     }
151                     KeyStore ks = KeyStore.getInstance(storeType);
152
153                     char[] pwd = keystorePassword.toCharArray();
154                     ks.load(fin, pwd);
155                     kmf.init(ks, pwd);
156                 } catch (Exception ex) {
157                     LOG.error("AAIResource", ex);
158                 }
159
160                 ctx.init(kmf.getKeyManagers(), null, null);
161                 config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties( new HostnameVerifier() {
162                         @Override
163                         public boolean verify( String s, SSLSession sslSession ) {
164                             return ignoreCertificateHostError;
165                         }
166                 }, ctx));
167
168                 CTX = ctx;
169                 LOG.debug("SSLContext created");
170
171             } catch (KeyManagementException | NoSuchAlgorithmException exc) {
172                 LOG.error("AAIResource", exc);
173             }
174         }
175
176         try {
177             Field methodsField = HttpURLConnection.class.getDeclaredField("methods");
178             methodsField.setAccessible(true);
179             // get the methods field modifiers
180             Field modifiersField = Field.class.getDeclaredField("modifiers");
181             // bypass the "private" modifier
182             modifiersField.setAccessible(true);
183
184             // remove the "final" modifier
185             modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);
186
187             /* valid HTTP methods */
188             String[] methods = {
189                        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
190             };
191             // set the new methods - including patch
192             methodsField.set(null, methods);
193
194         } catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
195             LOG.warn("Adding PATCH method", e);
196         }
197         LOG.info("AAIResource.ctor initialized.");
198
199     }
200
201     private static final Logger LOG = LoggerFactory.getLogger(AAIService.class);
202     private final MetricLogger ml = new MetricLogger();
203
204     private SSLContext CTX;
205
206
207     private int connection_timeout = 300000;
208
209     private int read_timeout = 300000;
210
211     /**
212      * Returns an String that contains JSON data returned from the AAI Server.
213      * <p>
214      * This method always returns immediately, whether or not the
215      * data exists.
216      *
217      * @param  request  an instance of AAIRequiest representing
218      *                 the request made by DirectedGraph node.
219      * @return      the JSON based representation of data instance requested.
220      * @see         String
221      */
222     @Override
223     public String get(AAIRequest request) throws AAIServiceException {
224         String response = null;
225         InputStream inputStream = null;
226         HttpURLConnection con = null;
227         URL requestUrl = null;
228
229         StringBuilder errorStringBuilder = new StringBuilder();
230
231         try {
232
233             if(request.getRequestObject() != null) {
234                 requestUrl = request.getRequestUrl(HttpMethod.POST, null);
235                 requestUrl = appendDepth(requestUrl, request);
236                 con = getConfiguredConnection(requestUrl, HttpMethod.POST);
237                 String json_text = request.toJSONString();
238                 LOGwriteDateTrace("data", json_text);
239                 logMetricRequest("POST "+requestUrl.getPath(), json_text, requestUrl.getPath());
240                 OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
241                 osw.write(json_text);
242                 osw.flush();
243             } else {
244                 requestUrl = request.getRequestUrl(HttpMethod.GET, null);
245                 requestUrl = appendDepth(requestUrl, request);
246                 con = getConfiguredConnection(requestUrl, HttpMethod.GET);
247                 logMetricRequest("GET "+requestUrl.getPath(), "", requestUrl.getPath());
248             }
249
250             // Check for errors
251             String responseMessage = con.getResponseMessage();
252             int responseCode = con.getResponseCode();
253             if (responseCode == HttpURLConnection.HTTP_OK) {
254                 inputStream = con.getInputStream();
255             } else {
256                 inputStream = con.getErrorStream();
257             }
258
259             // Process the response
260             LOG.debug("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             String responseMessage = con.getResponseMessage();
386             int responseCode = con.getResponseCode();
387             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
388                 inputStream = con.getInputStream();
389             } else {
390                 inputStream = con.getErrorStream();
391             }
392
393             LOG.debug("HttpURLConnection result:" + responseCode + " : " + responseMessage);
394             logMetricResponse(responseCode, responseMessage);
395
396             // Process the response
397             BufferedReader reader;
398             String line = null;
399             reader = new BufferedReader( new InputStreamReader( inputStream ) );
400             mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
401
402             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
403                 StringBuilder stringBuilder = new StringBuilder();
404
405                 while( ( line = reader.readLine() ) != null ) {
406                     stringBuilder.append( line );
407                 }
408                 LOGwriteEndingTrace(responseCode, responseMessage, (stringBuilder.length() > 0) ? stringBuilder.toString() : "{no-data}");
409                 return stringBuilder.toString();
410             } else {
411                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
412                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
413
414                 throw new AAIServiceException(responseCode, errorresponse);
415             }
416         } catch(AAIServiceException aaiexc) {
417             throw aaiexc;
418         } catch (Exception exc) {
419             LOG.warn("AAIRequestExecutor.post", exc);
420             throw new AAIServiceException(exc);
421         } finally {
422             try {
423                 if(inputStream != null)
424                 inputStream.close();
425             } catch (Exception exc) {
426                 LOG.warn("AAIRequestExecutor.post", exc);
427             }
428         }
429     }
430
431     /**
432      * Returns Boolean that contains completion state of the command executed.
433      * <p>
434      * This method always returns immediately, whether or not the
435      * data exists.
436      *
437      * @param  request  an instance of AAIRequiest representing
438      * @param  resourceVersion  a resource version of the data instacne to be deleted.
439      *                 the request made by DirectedGraph node.
440      * @return      completion state of the command.
441      * @see         String
442      */
443     @Override
444     public Boolean delete(AAIRequest request, String resourceVersion) throws AAIServiceException {
445         Boolean response = null;
446         InputStream inputStream = null;
447
448         if(resourceVersion == null) {
449             throw new AAIServiceException("resource-version is required for DELETE request");
450         }
451
452         try {
453             URL requestUrl = request.getRequestUrl(HttpMethod.DELETE, resourceVersion);
454             HttpURLConnection conn = getConfiguredConnection(requestUrl, HttpMethod.DELETE);
455             logMetricRequest("DELETE "+requestUrl.getPath(), "", requestUrl.getPath());
456             conn.setDoOutput(true);
457
458             // Check for errors
459             String responseMessage = conn.getResponseMessage();
460             int responseCode = conn.getResponseCode();
461             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
462                 inputStream = conn.getInputStream();
463             } else {
464                 inputStream = conn.getErrorStream();
465             }
466
467             // Process the response
468             LOG.debug("HttpURLConnection result:" + responseCode + " : " + responseMessage);
469             logMetricResponse(responseCode, responseMessage);
470
471             if(inputStream == null) inputStream = new ByteArrayInputStream("".getBytes(StandardCharsets.UTF_8));
472             BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
473             String line = null;
474
475             ObjectMapper mapper = AAIService.getObjectMapper();
476
477             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
478                 StringBuilder stringBuilder = new StringBuilder();
479
480                 while( ( line = reader.readLine() ) != null ) {
481                     stringBuilder.append( line );
482                 }
483                 LOGwriteEndingTrace(responseCode, responseMessage, stringBuilder.toString());
484                 response = true;
485             } else if(responseCode == HttpURLConnection.HTTP_NOT_FOUND ) {
486                 LOGwriteEndingTrace(responseCode, responseMessage, "Entry does not exist.");
487                 response = false;
488             } else {
489                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
490                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
491                 throw new AAIServiceException(responseCode, errorresponse);
492             }
493         } catch(AAIServiceException aaiexc) {
494             throw aaiexc;
495         } catch (Exception exc) {
496             LOG.warn("delete", exc);
497             throw new AAIServiceException(exc);
498         } finally {
499             if(inputStream != null){
500                 try {
501                     inputStream.close();
502                 } catch(Exception exc) {
503                     LOG.warn("delete", exc);
504                 }
505             }
506         }
507         return response;
508     }
509
510     /**
511      * Returns an String that contains JSON data returned from the AAI Server.
512      * <p>
513      * This method always returns immediately, whether or not the
514      * data exists.
515      *
516      * @param  request  an instance of AAIRequiest representing
517      *                 the request made by DirectedGraph node.
518      * @param clas   an definition of the class for which data will be returned
519      * @return      the instance of the class with data.
520      * @see         String
521      */
522     @Override
523     public Object query(AAIRequest request, Class clas) throws AAIServiceException {
524         Object response = null;
525         InputStream inputStream = null;
526
527         try {
528             URL requestUrl = request.getRequestQueryUrl(HttpMethod.GET);
529             HttpURLConnection con = getConfiguredConnection(requestUrl, HttpMethod.GET);
530             logMetricRequest("GET "+requestUrl.getPath(), "", requestUrl.getPath());
531
532             // Check for errors
533             String responseMessage = con.getResponseMessage();
534             int responseCode = con.getResponseCode();
535             if (responseCode == HttpURLConnection.HTTP_OK) {
536                 inputStream = con.getInputStream();
537             } else {
538                 inputStream = con.getErrorStream();
539             }
540
541             logMetricResponse(responseCode, responseMessage);
542             ObjectMapper mapper = AAIService.getObjectMapper();
543
544             if (responseCode == HttpURLConnection.HTTP_OK) {
545                 // Process the response
546                 BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
547                 response = mapper.readValue(reader, clas);
548                 LOGwriteEndingTrace(HttpURLConnection.HTTP_OK, "SUCCESS", mapper.writeValueAsString(response));
549             } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
550                 LOGwriteEndingTrace(responseCode, "HTTP_NOT_FOUND", "Entry does not exist.");
551                 return response;
552             } else {
553                 BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) );
554                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
555                 LOGwriteEndingTrace(responseCode, "FAILURE", mapper.writeValueAsString(errorresponse));
556                 throw new AAIServiceException(responseCode, errorresponse);
557             }
558
559         } catch(AAIServiceException aaiexc) {
560             throw aaiexc;
561         } catch (Exception exc) {
562             LOG.warn("GET", exc);
563             throw new AAIServiceException(exc);
564         } finally {
565             if(inputStream != null){
566                 try {
567                     inputStream.close();
568                 } catch(Exception exc) {
569                     LOG.warn("GET", exc);
570                 }
571             }
572         }
573         return response;
574     }
575
576     @Override
577     public Boolean patch(AAIRequest request, String resourceVersion) throws AAIServiceException {
578         InputStream inputStream = null;
579
580         try {
581             AAIDatum instance = request.getRequestObject();
582             if(instance instanceof ResourceVersion) {
583                 resourceVersion = ((ResourceVersion)instance).getResourceVersion();
584             }
585
586             URL requestUrl = null;
587             HttpURLConnection con = getConfiguredConnection(requestUrl = request.getRequestUrl("PATCH", resourceVersion), "PATCH");
588             ObjectMapper mapper = AAIService.getObjectMapper();
589             String jsonText = request.toJSONString();
590
591             LOGwriteDateTrace("data", jsonText);
592             logMetricRequest("PATCH "+requestUrl.getPath(), jsonText, requestUrl.getPath());
593
594             OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
595             osw.write(jsonText);
596             osw.flush();
597
598             // Check for errors
599             String responseMessage = con.getResponseMessage();
600             int responseCode = con.getResponseCode();
601             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
602                 inputStream = con.getInputStream();
603             } else {
604                 inputStream = con.getErrorStream();
605             }
606
607             LOG.info("HttpURLConnection result: " + responseCode + " : " + responseMessage);
608             logMetricResponse(responseCode, responseMessage);
609
610             // Process the response
611             BufferedReader reader;
612             String line = null;
613             reader = new BufferedReader( new InputStreamReader( inputStream ) );
614             mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
615
616             if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_ACCEPTED || responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
617                 StringBuilder stringBuilder = new StringBuilder();
618
619                 while( ( line = reader.readLine() ) != null ) {
620                     stringBuilder.append( line );
621                 }
622                 LOGwriteEndingTrace(responseCode, responseMessage, (stringBuilder.length() > 0) ? stringBuilder.toString() : "{no-data}");
623                 return true;
624             } else {
625                 StringBuilder stringBuilder = new StringBuilder();
626
627                 while( ( line = reader.readLine() ) != null ) {
628                     stringBuilder.append("\n").append( line );
629                 }
630                 LOG.info(stringBuilder.toString());
631
632
633                 ErrorResponse errorresponse = mapper.readValue(reader, ErrorResponse.class);
634                 LOGwriteEndingTrace(responseCode, responseMessage, mapper.writeValueAsString(errorresponse));
635
636                 throw new AAIServiceException(responseCode, errorresponse);
637             }
638         } catch(AAIServiceException aaiexc) {
639             throw aaiexc;
640         } catch (Exception exc) {
641             LOG.warn("AAIRequestExecutor.patch", exc);
642             throw new AAIServiceException(exc);
643         } finally {
644             try {
645                 if(inputStream != null)
646                 inputStream.close();
647             } catch (Exception exc) {
648                 LOG.warn("AAIRequestExecutor.patch", exc);
649             }
650         }
651     }
652
653     /**
654      *
655      * @param httpReqUrl
656      * @param method
657      * @return
658      * @throws Exception
659      */
660     protected HttpURLConnection getConfiguredConnection(URL httpReqUrl, String method) throws Exception {
661         HttpURLConnection con = (HttpURLConnection) httpReqUrl.openConnection();
662
663         // Set up the connection properties
664         con.setRequestProperty("Connection", "close");
665         con.setDoInput(true);
666         con.setDoOutput(true);
667         con.setUseCaches(false);
668         con.setConnectTimeout(connection_timeout);
669         con.setReadTimeout(read_timeout);
670         con.setRequestMethod(method);
671         con.setRequestProperty("Accept", "application/json");
672         con.setRequestProperty("Transfer-Encoding","chunked");
673         con.setRequestProperty("Content-Type",
674                 "PATCH".equalsIgnoreCase(method) ? "application/merge-patch+json" : "application/json");
675         con.setRequestProperty("X-FromAppId", applicationId);
676         con.setRequestProperty("X-TransactionId", TransactionIdTracker.getNextTransactionId());
677         String mlId = ml.getRequestID();
678         if (mlId != null && !mlId.isEmpty()) {
679             LOG.debug(String.format("MetricLogger requestId = %s", mlId));
680             con.setRequestProperty(MetricLogger.REQUEST_ID, mlId);
681         } else {
682             LOG.debug("MetricLogger requestId is null");
683         }
684
685         if (userName != null && !userName.isEmpty() && userPassword != null && !userPassword.isEmpty()) {
686             String basicAuth = "Basic " + new String(Base64.encodeBase64((userName + ":" + userPassword).getBytes()));
687             con.setRequestProperty("Authorization", basicAuth);
688         }
689
690         if (con instanceof HttpsURLConnection && CTX != null) {
691             SSLSocketFactory sockFact = CTX.getSocketFactory();
692             HttpsURLConnection.class.cast(con).setSSLSocketFactory(sockFact);
693         }
694         return con;
695     }
696
697     private URL appendDepth(URL requestUrl, AAIRequest request) throws MalformedURLException {
698
699         String depth = request.requestProperties.getProperty("depth", "1");
700         String path = requestUrl.toString();
701         if(path.contains("?depth=") || path.contains("&depth=")) {
702             return requestUrl;
703         } else {
704             if(path.contains("?")) {
705                 path = String.format("%s&depth=%s", path, depth);
706             } else {
707                 path = String.format("%s?depth=%s", path, depth);
708             }
709             return new URL(path);
710         }
711     }
712
713     public void logMetricRequest(String targetServiceName, String msg, String path){
714         String svcInstanceId = "";
715         String svcName = null;
716         String partnerName = null;
717         String targetEntity = "A&AI";
718         String targetVirtualEntity = null;
719
720         ml.logRequest(svcInstanceId, svcName, partnerName, targetEntity, targetServiceName, targetVirtualEntity, msg);
721     }
722
723     public void logMetricResponse(int responseCode, String responseDescription){
724         ml.logResponse(responseCode < 400 ? "COMPLETE" : "ERROR", Integer.toString(responseCode), responseDescription);
725     }
726
727     protected void LOGwriteFirstTrace(String method, String url) {
728         String time = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(System.currentTimeMillis());
729         LOG.info("A&AI transaction :");
730         LOG.info("Request Time : " + time + ", Method : " + method);
731         LOG.info("Request URL : "+ url);
732     }
733
734     protected void LOGwriteDateTrace(String name, String data) {
735         LOG.info("Input - " + name  + " : " + data);
736     }
737
738     protected void LOGwriteEndingTrace(int response_code, String comment, String data) {
739         LOG.info("Response code : " + response_code +", " + comment);
740         LOG.info(String.format("Response data : %s", data));
741     }
742
743 }