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