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