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