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