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