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