Fix sonar issues :
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / util / NodeUtils.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
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 package org.onap.aai.sparky.util;
22
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.lang.Thread.UncaughtExceptionHandler;
28 import java.net.URI;
29 import java.nio.ByteBuffer;
30 import java.security.SecureRandom;
31 import java.sql.Timestamp;
32 import java.text.ParseException;
33 import java.text.SimpleDateFormat;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Date;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.TimeZone;
43 import java.util.concurrent.ExecutorService;
44 import java.util.concurrent.Executors;
45 import java.util.concurrent.ThreadFactory;
46 import java.util.concurrent.TimeUnit;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50 import javax.servlet.http.HttpServletRequest;
51 import javax.xml.stream.XMLStreamConstants;
52
53 import org.onap.aai.cl.api.Logger;
54 import org.onap.aai.sparky.logging.AaiUiMsgs;
55 import org.onap.aai.sparky.viewandinspect.config.SparkyConstants;
56 import org.restlet.Request;
57
58 import com.fasterxml.jackson.core.JsonProcessingException;
59 import com.fasterxml.jackson.databind.JsonNode;
60 import com.fasterxml.jackson.databind.ObjectMapper;
61 import com.fasterxml.jackson.databind.ObjectWriter;
62 import com.fasterxml.jackson.databind.SerializationFeature;
63 import com.fasterxml.jackson.databind.ser.FilterProvider;
64 import com.google.common.util.concurrent.ThreadFactoryBuilder;
65
66
67 /**
68  * The Class NodeUtils.
69  */
70 public class NodeUtils {
71   private static SecureRandom sRandom = new SecureRandom();
72   
73   private static final Pattern URL_VERSION_PREFIX = Pattern.compile("/v[0-9]+/(.*)");
74   private static final Pattern OXM_VERSION_PREFIX = Pattern.compile(".{0,256}_v([0-9]{1,5})[^_]*");
75   private static final Pattern GIZMO_VERSION_PREFIX = Pattern.compile("[/]*services/inventory/v[0-9]+/(.*)");
76   private static final Pattern GIZMO_RELATIONSHIP_VERSION_PREFIX = Pattern.compile("services/inventory/relationships/v[0-9]+/(.*)");
77                                                                                     
78   
79   public static synchronized String getRandomTxnId(){
80       byte bytes[] = new byte[6];
81       sRandom.nextBytes(bytes);
82       return Integer.toUnsignedString(ByteBuffer.wrap(bytes).getInt());
83   }
84
85   /**
86    * Builds the depth padding.
87    *
88    * @param depth the depth
89    * @return the string
90    */
91   public static String buildDepthPadding(int depth) {
92     StringBuilder sb = new StringBuilder(32);
93
94     for (int x = 0; x < depth; x++) {
95       sb.append("   ");
96     }
97
98     return sb.toString();
99   }
100   
101   public static String extractOxmVersionFromPath(String filePath) {
102
103     try {
104       
105       Matcher m = OXM_VERSION_PREFIX.matcher(filePath);
106
107       if (m.matches()) {
108
109         if ( m.groupCount() >= 1) {
110           return m.group(1);
111         }
112           
113       }
114     } catch (Exception e) {
115     }
116     
117     return null;
118     
119   }
120
121   public static String extractRawPathWithoutVersion(String selfLinkUri) {
122
123     try {
124
125       String rawPath = new URI(selfLinkUri).getRawPath();
126       
127       Matcher m = URL_VERSION_PREFIX.matcher(rawPath);
128
129       if (m.matches()) {
130
131         if ( m.groupCount() >= 1) {
132           return m.group(1);
133         }
134           
135       }
136     } catch (Exception e) {
137     }
138     
139     return null;
140     
141   }
142   
143   public static String extractRawGizmoPathWithoutVersion(String resourceLink) {
144
145     try {
146
147       String rawPath = new URI(resourceLink).getRawPath();
148       
149       Matcher m = GIZMO_VERSION_PREFIX.matcher(rawPath);
150
151       if (m.matches()) {
152
153         if ( m.groupCount() >= 1) {
154           return m.group(1);
155         }
156           
157       }
158     } catch (Exception e) {
159     }
160     
161     return null;
162     
163   }
164   
165   public static String extractRawGizmoRelationshipPathWithoutVersion(String resourceLink) {
166
167     try {
168
169       String rawPath = new URI(resourceLink).getRawPath();
170       
171       Matcher m = GIZMO_RELATIONSHIP_VERSION_PREFIX.matcher(rawPath);
172
173       if (m.matches()) {
174
175         if ( m.groupCount() >= 1) {
176           return m.group(1);
177         }
178           
179       }
180     } catch (Exception e) {
181     }
182     
183     return null;
184     
185   }  
186   
187   
188
189
190   /**
191    * Checks if is numeric.
192    *
193    * @param numberStr the number str
194    * @return true, if is numeric
195    */
196   public static boolean isNumeric(String numberStr) {
197
198     try {
199       Double.parseDouble(numberStr);
200     } catch (Exception exc) {
201       return false;
202     }
203
204     return true;
205
206   }
207
208   /**
209    * Creates the named executor.
210    *
211    * @param name the name
212    * @param numWorkers the num workers
213    * @param logger the logger
214    * @return the executor service
215    */
216   public static ExecutorService createNamedExecutor(String name, int numWorkers, final Logger logger) {
217     UncaughtExceptionHandler uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
218
219       @Override
220       public void uncaughtException(Thread thread, Throwable exc) {
221
222         logger.error(AaiUiMsgs.ERROR_GENERIC, thread.getName() + ": " + exc);
223
224       }
225     };
226
227     ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(name + "-%d")
228         .setUncaughtExceptionHandler(uncaughtExceptionHandler).build();
229
230     return Executors.newScheduledThreadPool(numWorkers + 1, namedThreadFactory);
231   }
232
233
234   public static String calculateEditAttributeUri(String link) {
235     String uri = null;
236
237     if (link != null) {
238
239       Pattern pattern = Pattern.compile(SparkyConstants.URI_VERSION_REGEX_PATTERN);
240       Matcher matcher = pattern.matcher(link);
241       if (matcher.find()) {
242         uri = link.substring(matcher.end());
243       }
244     }
245     return uri;
246   }
247
248   
249   /**
250    * Generate unique sha digest.
251    *
252    * @param keys the keys
253    * @return the string
254    */
255   public static String generateUniqueShaDigest(String... keys) {
256
257     if ((keys == null) || keys.length == 0) {
258       return null;
259     }
260
261     final String keysStr = Arrays.asList(keys).toString();
262     final String hashedId = org.apache.commons.codec.digest.DigestUtils.sha256Hex(keysStr);
263
264     return hashedId;
265   }
266
267   /**
268    * Gets the node field as text.
269    *
270    * @param node the node
271    * @param fieldName the field name
272    * @return the node field as text
273    */
274   public static String getNodeFieldAsText(JsonNode node, String fieldName) {
275
276     String fieldValue = null;
277
278     JsonNode valueNode = node.get(fieldName);
279
280     if (valueNode != null) {
281       fieldValue = valueNode.asText();
282     }
283
284     return fieldValue;
285   }
286
287   private static final String ENTITY_RESOURCE_KEY_FORMAT = "%s.%s";
288
289   /**
290    * Convert a millisecond duration to a string format
291    * 
292    * @param millis A duration to convert to a string form
293    * @return A string of the form "X Days Y Hours Z Minutes A Seconds".
294    */
295
296   private static final String TIME_BREAK_DOWN_FORMAT =
297       "[ %d days, %d hours, %d minutes, %d seconds ]";
298
299   /**
300    * Gets the duration breakdown.
301    *
302    * @param millis the millis
303    * @return the duration breakdown
304    */
305   public static String getDurationBreakdown(long millis) {
306
307     if (millis < 0) {
308       return String.format(TIME_BREAK_DOWN_FORMAT, 0, 0, 0, 0);
309     }
310
311     long days = TimeUnit.MILLISECONDS.toDays(millis);
312     millis -= TimeUnit.DAYS.toMillis(days);
313     long hours = TimeUnit.MILLISECONDS.toHours(millis);
314     millis -= TimeUnit.HOURS.toMillis(hours);
315     long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
316     millis -= TimeUnit.MINUTES.toMillis(minutes);
317     long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
318
319     return String.format(TIME_BREAK_DOWN_FORMAT, days, hours, minutes, seconds);
320
321   }
322
323   /**
324    * Checks if is equal.
325    *
326    * @param n1 the n 1
327    * @param n2 the n 2
328    * @return true, if is equal
329    */
330   public static boolean isEqual(JsonNode n1, JsonNode n2) {
331
332     /*
333      * due to the inherent nature of json being unordered, comparing object representations of the
334      * same keys and values but different order makes comparison challenging. Let's try an
335      * experiment where we compare the structure of the json, and then simply compare the sorted
336      * order of that structure which should be good enough for what we are trying to accomplish.
337      */
338
339     TreeWalker walker = new TreeWalker();
340     List<String> n1Paths = new ArrayList<String>();
341     List<String> n2Paths = new ArrayList<String>();
342
343     walker.walkTree(n1Paths, n1);
344     walker.walkTree(n2Paths, n2);
345
346     Collections.sort(n1Paths);
347     Collections.sort(n2Paths);
348
349     return n1Paths.equals(n2Paths);
350
351   }
352
353   /**
354    * Concat array.
355    *
356    * @param list the list
357    * @return the string
358    */
359   public static String concatArray(List<String> list) {
360     return concatArray(list, " ");
361   }
362   
363  private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
364   
365   public static String getCurrentTimeStamp() {
366     SimpleDateFormat dateFormat = new SimpleDateFormat(TIMESTAMP_FORMAT);
367     Timestamp timestamp = new Timestamp(System.currentTimeMillis());
368     return dateFormat.format(timestamp);
369   }
370   
371   /**
372    * Concat array.
373    *
374    * @param list the list
375    * @param delimiter the delimiter
376    * @return the string
377    */
378   public static String concatArray(List<String> list, String delimiter) {
379
380     if (list == null || list.size() == 0) {
381       return "";
382     }
383
384     StringBuilder result = new StringBuilder(64);
385
386     boolean firstValue = true;
387
388     for (String item : list) {
389
390       if (firstValue) {
391         result.append(item);
392         firstValue = false;
393       } else {
394         result.append(delimiter).append(item);
395       }
396
397     }
398
399     return result.toString();
400
401   }
402
403   /**
404    * Concat array.
405    *
406    * @param values the values
407    * @return the string
408    */
409   public static String concatArray(String[] values) {
410
411     if (values == null || values.length == 0) {
412       return "";
413     }
414
415     StringBuilder result = new StringBuilder(64);
416
417     boolean firstValue = true;
418
419     for (String item : values) {
420
421       if (firstValue) {
422         result.append(item);
423         firstValue = false;
424       } else {
425         result.append(".").append(item);
426       }
427
428     }
429
430     return result.toString();
431
432   }
433
434   /**
435    * Builds the entity resource key.
436    *
437    * @param entityType the entity type
438    * @param resourceId the resource id
439    * @return the string
440    */
441   public static String buildEntityResourceKey(String entityType, String resourceId) {
442     return String.format(ENTITY_RESOURCE_KEY_FORMAT, entityType, resourceId);
443   }
444
445   /**
446    * Extract resource id from link.
447    *
448    * @param link the link
449    * @return the string
450    */
451   public static String extractResourceIdFromLink(String link) {
452
453     if (link == null) {
454       return null;
455     }
456
457     int linkLength = link.length();
458     if (linkLength == 0) {
459       return null;
460     }
461
462     /*
463      * if the last character != / then we need to change the lastIndex position
464      */
465
466     int startIndex = 0;
467     String resourceId = null;
468     if ("/".equals(link.substring(linkLength - 1))) {
469       // Use-case:
470       // https://ext1.test.onap.com:9292/aai/v7/business/customers/customer/customer-1/service-subscriptions/service-subscription/service-subscription-1/
471       startIndex = link.lastIndexOf("/", linkLength - 2);
472       resourceId = link.substring(startIndex + 1, linkLength - 1);
473     } else {
474       // Use-case:
475       // https://ext1.test.onap.com:9292/aai/v7/business/customers/customer/customer-1/service-subscriptions/service-subscription/service-subscription-1
476       startIndex = link.lastIndexOf("/");
477       resourceId = link.substring(startIndex + 1, linkLength);
478     }
479
480     String result = null;
481
482     if (resourceId != null) {
483       try {
484         result = java.net.URLDecoder.decode(resourceId, "UTF-8");
485       } catch (Exception exc) {
486         /*
487          * if there is a failure decoding the parameter we will just return the original value.
488          */
489         result = resourceId;
490       }
491     }
492
493     return result;
494
495   }
496
497   /**
498    * Gets the xml stream constant as str.
499    *
500    * @param value the value
501    * @return the xml stream constant as str
502    */
503   public static String getXmlStreamConstantAsStr(int value) {
504     switch (value) {
505       case XMLStreamConstants.ATTRIBUTE:
506         return "ATTRIBUTE";
507       case XMLStreamConstants.CDATA:
508         return "CDATA";
509       case XMLStreamConstants.CHARACTERS:
510         return "CHARACTERS";
511       case XMLStreamConstants.COMMENT:
512         return "COMMENT";
513       case XMLStreamConstants.DTD:
514         return "DTD";
515       case XMLStreamConstants.END_DOCUMENT:
516         return "END_DOCUMENT";
517       case XMLStreamConstants.END_ELEMENT:
518         return "END_ELEMENT";
519       case XMLStreamConstants.ENTITY_DECLARATION:
520         return "ENTITY_DECLARATION";
521       case XMLStreamConstants.ENTITY_REFERENCE:
522         return "ENTITY_REFERENCE";
523       case XMLStreamConstants.NAMESPACE:
524         return "NAMESPACE";
525       case XMLStreamConstants.NOTATION_DECLARATION:
526         return "NOTATION_DECLARATION";
527       case XMLStreamConstants.PROCESSING_INSTRUCTION:
528         return "PROCESSING_INSTRUCTION";
529       case XMLStreamConstants.SPACE:
530         return "SPACE";
531       case XMLStreamConstants.START_DOCUMENT:
532         return "START_DOCUMENT";
533       case XMLStreamConstants.START_ELEMENT:
534         return "START_ELEMENT";
535
536       default:
537         return "Unknown(" + value + ")";
538     }
539   }
540
541   /**
542    * Convert object to json.
543    *
544    * @param object the object
545    * @param pretty the pretty
546    * @return the string
547    * @throws JsonProcessingException the json processing exception
548    */
549   public static String convertObjectToJson(Object object, boolean pretty)
550       throws JsonProcessingException {
551     ObjectWriter ow = null;
552
553     ObjectMapper mapper = new ObjectMapper();
554     mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
555     
556     if (pretty) {
557       ow = mapper.writer().withDefaultPrettyPrinter();
558
559     } else {
560       ow = mapper.writer();
561     }
562
563     return ow.writeValueAsString(object);
564   }
565   
566   /**
567    * Convert object to json by selectively choosing certain fields thru filters.
568    * Example use case: 
569    * based on request type we might need to send different serialization of the UiViewFilterEntity
570    *
571    * @param object the object
572    * @param pretty the pretty
573    * @return the string
574    * @throws JsonProcessingException the json processing exception
575    */
576   public static String convertObjectToJson(Object object, boolean pretty, FilterProvider filters)
577       throws JsonProcessingException {
578     ObjectWriter ow = null;
579
580     ObjectMapper mapper = new ObjectMapper();
581     mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
582     
583     if (pretty) {
584       ow = mapper.writer(filters).withDefaultPrettyPrinter();
585
586     } else {
587       ow = mapper.writer(filters);
588     }
589
590     return ow.writeValueAsString(object);
591   }
592   
593
594   /**
595    * Convert json str to json node.
596    *
597    * @param jsonStr the json str
598    * @return the json node
599    * @throws IOException Signals that an I/O exception has occurred.
600    */
601   public static JsonNode convertJsonStrToJsonNode(String jsonStr) throws IOException {
602     ObjectMapper mapper = new ObjectMapper();
603     if (jsonStr == null || jsonStr.length() == 0) {
604       return null;
605     }
606
607     return mapper.readTree(jsonStr);
608   }
609
610   /**
611    * Convert object to xml.
612    *
613    * @param object the object
614    * @return the string
615    * @throws JsonProcessingException the json processing exception
616    */
617   public static String convertObjectToXml(Object object) throws JsonProcessingException {
618     ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
619     String jsonOutput = ow.writeValueAsString(object);
620
621     if (jsonOutput == null) {
622       return null;
623     }
624
625     return JsonXmlConverter.convertJsonToXml(jsonOutput);
626
627   }
628
629   /**
630    * Extract objects by key.
631    *
632    * @param node the node
633    * @param searchKey the search key
634    * @param foundObjects the found objects
635    */
636   public static void extractObjectsByKey(JsonNode node, String searchKey,
637       Collection<JsonNode> foundObjects) {
638
639     if ( node == null ) {
640       return;
641     }
642     
643     if (node.isObject()) {
644       Iterator<Map.Entry<String, JsonNode>> nodeIterator = node.fields();
645
646       while (nodeIterator.hasNext()) {
647         Map.Entry<String, JsonNode> entry = nodeIterator.next();
648         if (!entry.getValue().isValueNode()) {
649           extractObjectsByKey(entry.getValue(), searchKey, foundObjects);
650         }
651
652         String name = entry.getKey();
653         if (name.equalsIgnoreCase(searchKey)) {
654
655           JsonNode entryNode = entry.getValue();
656
657           if (entryNode.isArray()) {
658
659             Iterator<JsonNode> arrayItemsIterator = entryNode.elements();
660             while (arrayItemsIterator.hasNext()) {
661               foundObjects.add(arrayItemsIterator.next());
662             }
663
664           } else {
665             foundObjects.add(entry.getValue());
666           }
667
668
669         }
670       }
671     } else if (node.isArray()) {
672       Iterator<JsonNode> arrayItemsIterator = node.elements();
673       while (arrayItemsIterator.hasNext()) {
674         extractObjectsByKey(arrayItemsIterator.next(), searchKey, foundObjects);
675       }
676
677     }
678   }
679     
680   public static String extractObjectValueByKey(JsonNode node, String searchKey) {
681
682     if (node == null) {
683       return null;
684     }
685
686     if (node.isObject()) {
687       Iterator<Map.Entry<String, JsonNode>> nodeIterator = node.fields();
688
689       while (nodeIterator.hasNext()) {
690         Map.Entry<String, JsonNode> entry = nodeIterator.next();
691         if (!entry.getValue().isValueNode()) {
692           return extractObjectValueByKey(entry.getValue(), searchKey);
693         }
694
695         String name = entry.getKey();
696         if (name.equalsIgnoreCase(searchKey)) {
697
698           JsonNode entryNode = entry.getValue();
699
700           if (entryNode.isArray()) {
701
702             Iterator<JsonNode> arrayItemsIterator = entryNode.elements();
703             while (arrayItemsIterator.hasNext()) {
704               return arrayItemsIterator.next().asText();
705             }
706
707           } else {
708             return entry.getValue().asText();
709           }
710
711
712         }
713       }
714     } else if (node.isArray()) {
715       Iterator<JsonNode> arrayItemsIterator = node.elements();
716       while (arrayItemsIterator.hasNext()) {
717         return extractObjectValueByKey(arrayItemsIterator.next(), searchKey);
718       }
719
720     }
721
722     return null;
723
724   }
725
726   /**
727    * Convert array into list.
728    *
729    * @param node the node
730    * @param instances the instances
731    */
732   public static void convertArrayIntoList(JsonNode node, Collection<JsonNode> instances) {
733
734     if (node.isArray()) {
735       Iterator<JsonNode> arrayItemsIterator = node.elements();
736       while (arrayItemsIterator.hasNext()) {
737         instances.add(arrayItemsIterator.next());
738       }
739
740     } else {
741       instances.add(node);
742     }
743
744   }
745
746   /**
747    * Extract field values from object.
748    *
749    * @param node the node
750    * @param attributesToExtract the attributes to extract
751    * @param fieldValues the field values
752    */
753   public static void extractFieldValuesFromObject(JsonNode node,
754       Collection<String> attributesToExtract, Collection<String> fieldValues) {
755
756     if (node == null) {
757       return;
758     }
759
760     if (node.isObject()) {
761
762       JsonNode valueNode = null;
763
764       for (String attrToExtract : attributesToExtract) {
765
766         valueNode = node.get(attrToExtract);
767
768         if (valueNode != null) {
769
770           if (valueNode.isValueNode()) {
771             fieldValues.add(valueNode.asText());
772           }
773         }
774       }
775     }
776   }
777
778   /**
779    * Extract field value from object.
780    *
781    * @param node the node
782    * @param fieldName the field name
783    * @return the string
784    */
785   public static String extractFieldValueFromObject(JsonNode node, String fieldName) {
786
787     if (node == null) {
788       return null;
789     }
790
791     if (node.isObject()) {
792
793       JsonNode valueNode = node.get(fieldName);
794
795       if (valueNode != null) {
796
797         if (valueNode.isValueNode()) {
798           return valueNode.asText();
799         }
800       }
801
802     }
803     return null;
804
805   }
806
807   /**
808    * Format timestamp.
809    *
810    * @param timestamp the timestamp
811    * @return the string
812    */
813   public static String formatTimestamp(String timestamp) {
814     try {
815       SimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
816       originalFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
817       Date toDate = originalFormat.parse(timestamp);
818       SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
819       newFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
820       return newFormat.format(toDate);
821
822     } catch (ParseException pe) {
823       return timestamp;
824     }
825   }
826  
827   /**
828    * Gets the HttpRequest payload.
829    *
830    * @param request the request
831    * @return the body
832    * @throws IOException Signals that an I/O exception has occurred.
833    */
834   public static String getBody(HttpServletRequest request) throws IOException {
835     InputStream inputStream = request.getInputStream();
836     return getBodyFromStream(inputStream);
837   }
838   
839   
840
841   /**
842    * Gets the Restlet Request payload.
843    *
844    * @param request the request
845    * @return the body
846    * @throws IOException Signals that an I/O exception has occurred.
847    */
848   public static String getBody(Request request) throws IOException {
849     InputStream inputStream = request.getEntity().getStream();
850     return getBodyFromStream(inputStream);
851   }
852   
853
854   /**
855    * Gets the payload from the input stream of a request.
856    *
857    * @param request the request
858    * @return the body
859    * @throws IOException Signals that an I/O exception has occurred.
860    */
861   public static String getBodyFromStream(InputStream inputStream) throws IOException {
862
863     String body = null;
864     StringBuilder stringBuilder = new StringBuilder();
865     BufferedReader bufferedReader = null;
866
867     try {
868       if (inputStream != null) {
869         bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
870         char[] charBuffer = new char[128];
871         int bytesRead = -1;
872         while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
873           stringBuilder.append(charBuffer, 0, bytesRead);
874         }
875       } else {
876         stringBuilder.append("");
877       }
878     } catch (IOException ex) {
879       throw ex;
880     } finally {
881       if (bufferedReader != null) {
882           bufferedReader.close();
883       }
884     }
885
886     body = stringBuilder.toString();
887     return body;
888   }
889
890   
891   /**
892    * The main method.
893    *
894    * @param args the arguments
895    * @throws ParseException the parse exception
896    */
897   public static void main(String[] args) throws ParseException {
898     String date = "20170110T112312Z";
899     SimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd'T'hhmmss'Z'");
900     Date toDate = originalFormat.parse(date);
901     SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss'Z'");
902     System.out.println(newFormat.format(toDate));
903
904   }
905
906
907
908 }