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