Containerization feature of SO
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / onap / so / bpmn / core / json / JsonUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
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
22 package org.onap.so.bpmn.core.json;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.StringTokenizer;
31
32 import org.camunda.bpm.engine.delegate.DelegateExecution;
33 import org.camunda.bpm.engine.runtime.Execution;
34 import org.json.JSONArray;
35 import org.json.JSONException;
36 import org.json.JSONObject;
37 import org.json.XML;
38 import org.onap.so.bpmn.core.xml.XmlTool;
39 import org.onap.so.exceptions.ValidationException;
40 import org.onap.so.logger.MsoLogger;
41
42 import com.fasterxml.jackson.databind.JsonNode;
43 import com.github.fge.jackson.JsonLoader;
44 import com.github.fge.jsonschema.core.exceptions.ProcessingException;
45 import com.github.fge.jsonschema.core.report.ProcessingReport;
46 import com.github.fge.jsonschema.main.JsonSchemaFactory;
47 import com.github.fge.jsonschema.main.JsonValidator;
48
49 /**
50  * Utility class for JSON processing
51  *
52  * @version 1.0
53  *
54  * Note: It was observed, that depending on the JSON implementation, an org.json.JSONException or a
55  *       java.util.NoSuchElementException will be thrown in the event of the key value being "not found"
56  *       in a JSON document. A general check has been added to the applicable catch blocks for this
57  *       this type of behavior to reduce the amount of logging. As a key value not being found is
58  *       expect behavior, it makes no sense to log the stack trace associated with this type of failure.
59  */
60 public class JsonUtils {
61
62         private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, JsonUtils.class);
63         private static int MSOJsonIndentFactor = 3;
64
65         /**
66          * Uses the JSONObject static method to convert a XML doc to JSON.
67          *
68          * @param  xml          String containing the XML doc
69          * @param  pretty       flag to determine if the output should be formatted
70          * @return String containing the JSON translation
71          */
72         public static String xml2json(String xml, Boolean pretty) {
73                 try {
74                         // name spaces cause problems, so just remove them
75                         JSONObject jsonObj = XML.toJSONObject(XmlTool.removeNamespaces(xml));
76                         if (!pretty) {
77                                 return jsonObj.toString();
78                         } else {
79                                 // add an indent to make it 'pretty'
80                                 return jsonObj.toString(MSOJsonIndentFactor);
81                         }
82                 } catch (Exception e){
83                                 msoLogger.debug("xml2json(): unable to parse xml and convert to json. Exception was: " + e.toString(), e);
84                                 return null;
85                 }
86         }
87
88         /**
89          * Invokes xml2json(String, Boolean) defaulting to 'pretty' output.
90          *
91          * @param  xml  String containing the XML doc
92          * @return String containing the JSON translation
93          */
94         public static String xml2json(String xml) {
95                 return xml2json(xml, true);
96         }
97
98         /**
99          * Uses the JSONObject static method to convert a JSON doc to XML.
100          * Note: this method may not generate valid XML if the JSONObject
101          * contains JSONArrays which are used to represent XML attributes
102          * in the JSON doc.
103          *
104          * @param  jsonStr      String containing the JSON doc
105          * @param  pretty       flag to determine if the output should be formatted
106          * @return String containing the XML translation
107          */
108         public static String json2xml(String jsonStr, Boolean pretty) {
109
110                 try {
111                         JSONObject jsonObj = new JSONObject(jsonStr);
112                         if (pretty) {
113 //                              return XmlTool.normalize(XML.toString(jsonObj));
114 //                              use the local class method which properly handles certain JSONArray content
115                                 return XmlTool.normalize(toXMLString(jsonObj, null));
116                         } else {
117 //                              return XML.toString(jsonObj);
118 //                              use the local class method which properly handles certain JSONArray content
119                                 return toXMLString(jsonObj, null);
120                         }
121                 } catch (Exception e){
122                                 msoLogger.debug("json2xml(): unable to parse json and convert to xml. Exception was: " + e.toString(), e);
123                                 return null;
124                 }
125         }
126
127         /**
128          * Uses a modified version of the org.json.XML toString() algorithm
129          * to convert a JSONObject to an XML Doc. The intent of this is to
130          * correctly generate XML from JSON including TAGs for JSONArrays
131          *
132          * @param  obj  org.json.JSON object to be converted to XML
133          * @param  tagName      optional XML tagname supplied primarily during recursive calls
134          * @return String containing the XML translation
135          */
136         public static String toXMLString(Object obj, String tagName) throws JSONException {
137                 StringBuilder strBuf = new StringBuilder();
138                 int i;
139                 JSONArray jsonArr;
140                 JSONObject jsonObj;
141                 String key;
142                 Iterator<String> keys;
143                 int len;
144                 String str;
145                 Object curObj;
146                 if (obj instanceof JSONObject) {
147                         // msoLogger.debug("toXMLString(): is a JSONObject");
148                         // append "<tagName>" to the XML output
149                         if (tagName != null) {
150 //                              msoLogger.debug("toXMLString(): adding opening tagName: " + tagName);
151                                 strBuf.append("<");
152                                 strBuf.append(tagName);
153                                 strBuf.append(">");
154                         }
155                         // iterate thru the keys.
156                         jsonObj = (JSONObject) obj;
157                         keys = jsonObj.keys();
158                         while (keys.hasNext()) {
159                                 key = keys.next();
160                                 // msoLogger.debug("toXMLString(): key is " + k);
161                                 curObj = jsonObj.opt(key);
162                                 if (curObj == null) {
163                                         curObj = "";
164                                 }
165                                 if (curObj instanceof String) {
166                                         str = (String) curObj;
167                                 } else {
168                                         str = null;
169                                 }
170                                 // append the content to the XML output
171                                 if ("content".equals(key)) {
172                                         if (curObj instanceof JSONArray) {
173                                                 jsonArr = (JSONArray) curObj;
174                                                 len = jsonArr.length();
175                                                 for (i = 0; i < len; i += 1) {
176                                                         if (i > 0) {
177                                                                 strBuf.append('\n');
178                                                         }
179                                                         strBuf.append(XML.escape(jsonArr.get(i).toString()));
180                                                 }
181                                         } else {
182                                                 strBuf.append(XML.escape(curObj.toString()));
183                                         }
184                                 // append an array of similar keys to the XML output
185                                 } else if (curObj instanceof JSONArray) {
186                                         jsonArr = (JSONArray) curObj;
187                                         len = jsonArr.length();
188 //                                      msoLogger.debug("toXMLString(): found JSONArray: " + key + ", size: " + len);
189                                         for (i = 0; i < len; i += 1) {
190                                                 curObj = jsonArr.get(i);
191                                                 if (curObj instanceof JSONArray) {
192 //                                                      The XML tags for the nested array should be generated below when this method
193 //                                                      is called recursively and the JSONArray object is passed
194 //                                                      strBuf.append("<");
195 //                                                      strBuf.append(key);
196 //                                                      strBuf.append(">");
197                                                         strBuf.append(toXMLString(curObj, null));
198 //                                                      strBuf.append("</");
199 //                                                      strBuf.append(key);
200 //                                                      strBuf.append(">");
201                                                 } else {
202 //                                                      msoLogger.debug("toXMLString(): recursive call toXML() with tagName null");
203                                                         // append the opening tag for the array (before 1st element)
204                                                         if (i == 0) {
205                                                                 strBuf.append("<");
206                                                                 strBuf.append(key);
207                                                                 strBuf.append(">");
208                                                         }
209                                                         // append the opening tag for the array
210                                                         strBuf.append(toXMLString(curObj, null));
211                                                         // append the closing tag for the array (after last element)
212                                                         if (i == (len - 1)) {
213                                                                 strBuf.append("</");
214                                                                 strBuf.append(key);
215                                                                 strBuf.append(">");
216                                                         }
217                                                 }
218                                         }
219                                 } else if (curObj.equals("")) {
220                                         // append a closing tag "<key>" to the XML output
221                                         strBuf.append("<");
222                                         strBuf.append(key);
223                                         strBuf.append("/>");
224                                 } else {
225 //                                      msoLogger.debug("toXMLString(): recursive call toXMLString() with tagName: " + key);
226                                         strBuf.append(toXMLString(curObj, key));
227                                 }
228                                 // msoLogger.debug("toXML(): partial XML: " + strBuf.toString());
229                         }
230                         if (tagName != null) {
231                                 // append the closing tag "</tagName>" to the XML output
232 //                              msoLogger.debug("toXMLString(): adding closing tagName: " + tagName);
233                                 strBuf.append("</");
234                                 strBuf.append(tagName);
235                                 strBuf.append(">");
236                         }
237                         return strBuf.toString();
238                 // XML does not have good support for arrays. If an array appears in a place
239                 // where XML is lacking, synthesize an < array > element.
240                 } else if (obj instanceof JSONArray) {
241                         jsonArr = (JSONArray) obj;
242                         len = jsonArr.length();
243                         for (i = 0; i < len; ++i) {
244                                 curObj = jsonArr.opt(i);
245                                 strBuf.append(toXMLString(curObj, (tagName == null) ? "array"
246                                                 : tagName));
247                         }
248                         return strBuf.toString();
249                 } else {
250 //                      msoLogger.debug("toXML(): in else block with tagName: " + tagName);
251                         str = (obj == null) ? "null" : XML.escape(obj.toString());
252                         return (tagName == null) ? "\"" + str + "\""
253                                         : (str.length() == 0) ? "<" + tagName + "/>" : "<"
254                                                         + tagName + ">" + str + "</" + tagName + ">";
255                 }
256         }
257
258         /**
259          * Invokes json2xml(String, Boolean) defaulting to 'pretty' output.
260          *
261          * @param  jsonStr      String containing the XML doc
262          * @return String containing the JSON translation
263          */
264         public static String json2xml(String jsonStr) {
265                 return json2xml(jsonStr, true);
266         }
267
268         /**
269          * Formats the JSON String using the value of MSOJsonIndentFactor.
270          *
271          * @param  jsonStr      String containing the JSON doc
272          * @return String containing the formatted JSON doc
273          */
274         public static String prettyJson(String jsonStr) {
275 //              String isDebugLogEnabled = "true";
276                 try {
277                         JSONObject jsonObj = new JSONObject(jsonStr);
278                         return jsonObj.toString(MSOJsonIndentFactor);
279                 } catch (Exception e){
280                         msoLogger.debug("prettyJson(): unable to parse/format json input. Exception was: " + e.toString(), e);
281                         return null;
282                 }
283         }
284
285         /**
286          * Returns an Iterator over the JSON keys in the specified JSON doc.
287          *
288          * @param  jsonStr      String containing the JSON doc
289          * @return Iterator over the JSON keys
290          * @throws JSONException if the doc cannot be parsed
291          */
292         public static Iterator <String> getJsonIterator(String jsonStr) throws JSONException {
293                 return new JSONObject(jsonStr).keys();
294         }
295
296         /**
297          * Returns the name of the "root" property in the specified JSON doc. The
298          * "root" property is the single top-level property in the JSON doc. An
299          * exception is thrown if the doc is empty or if it contains more than one
300          * top-level property.
301          *
302          * @param  jsonStr      String containing the JSON doc
303          * @return the name of the "root" property
304          * @throws JSONException if the doc cannot be parsed, or if it is empty, or if
305          *         it contains more than one top-level property
306          */
307         public static String getJsonRootProperty(String jsonStr) throws JSONException {
308                 Iterator<String> iter = getJsonIterator(jsonStr);
309
310                 if (!iter.hasNext()) {
311                         throw new JSONException("Empty JSON object");
312                 }
313
314                 String rootPropertyName = iter.next();
315
316                 if (iter.hasNext()) {
317                         throw new JSONException("JSON object has more than one root property");
318                 }
319
320                 return rootPropertyName;
321         }
322
323         /**
324          * Invokes the getJsonRawValue() method and returns the String equivalent of
325          * the object returned.
326          *
327          * TBD: May need separate methods for boolean, float, and integer fields if the
328          * String representation is not sufficient to meet client needs.
329          *
330          * @param  jsonStr      String containing the JSON doc
331          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
332          * @return String field value associated with keys
333          */
334         public static String getJsonValue(String jsonStr, String keys) {
335 //              String isDebugLogEnabled = "true";
336                 try {
337                                 Object rawValue = getJsonRawValue(jsonStr, keys);
338                                 if (rawValue == null) {
339                                         return null;
340                                 } else {
341                                         if (rawValue instanceof String) {
342                                                 msoLogger.debug("getJsonValue(): the raw value is a String Object=" + rawValue);
343                                                 return (String) rawValue;
344                                         } else {
345                                                 msoLogger.debug("getJsonValue(): the raw value is NOT a String Object=" + rawValue.toString());
346                                                 return rawValue.toString();
347                                         }
348                                 }
349                 } catch (Exception e) {
350                                 msoLogger.debug("getJsonValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString(),e);
351                 }
352                 return null;
353         }
354
355         /**
356          * Invokes the getJsonRawValue() method with the wrap flag set to true
357          * and returns the String equivalent of the json node object returned.
358          *
359          * @param  jsonStr      String containing the JSON doc
360          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
361          * @return String field value associated with keys
362          */
363         public static String getJsonNodeValue(String jsonStr, String keys) {
364 //              String isDebugLogEnabled = "true";
365                 try {
366                                 Object rawValue = getJsonRawValue(jsonStr, keys, true);
367                                 if (rawValue == null) {
368                                         return null;
369                                 } else {
370                                         if (rawValue instanceof String) {
371                                                 msoLogger.debug("getJsonNodeValue(): the raw value is a String Object=" + rawValue);
372                                                 return (String) rawValue;
373                                         } else {
374                                                 msoLogger.debug("getJsonNodeValue(): the raw value is NOT a String Object=" + rawValue.toString());
375                                                 return rawValue.toString();
376                                         }
377                                 }
378                 } catch (Exception e) {
379                                 msoLogger.debug("getJsonNodeValue(): unable to parse json to retrieve node for field=" + keys + ". Exception was: " + e.toString(), e);
380                 }
381                 return null;
382         }
383
384         /**
385          * Invokes the getJsonRawValue() method and returns the String equivalent of
386          * the object returned.
387          *
388          * TBD: May need separate methods for boolean, float, and integer fields if the
389          * String representation is not sufficient to meet client needs.
390          *
391          * @param  jsonStr      String containing the JSON doc
392          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
393          * @return String field value associated with keys
394          */
395         public static int getJsonIntValue(String jsonStr, String keys) {
396 //              String isDebugLogEnabled = "true";
397                 try {
398                                 Object rawValue = getJsonRawValue(jsonStr, keys);
399                                 if (rawValue == null) {
400                                         return 0;
401                                 } else {
402                                         if (rawValue instanceof Integer) {
403                                                 msoLogger.debug("getJsonIntValue(): the raw value is an Integer Object=" + ((String) rawValue).toString());
404                                                 return (Integer) rawValue;
405                                         } else {
406                                                 msoLogger.debug("getJsonIntValue(): the raw value is NOT an Integer Object=" + rawValue.toString());
407                                                 return 0;
408                                         }
409                                 }
410                 } catch (Exception e) {
411                                 msoLogger.debug("getJsonIntValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString(), e);
412                 }
413                 return 0;
414         }
415
416         /**
417          * Invokes the getJsonRawValue() method and returns the boolean equivalent of
418          * the object returned.
419          *
420          * @param  jsonStr      String containing the JSON doc
421          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
422          * @return boolean field value associated with keys - default is false
423          */
424         public static boolean getJsonBooleanValue(String jsonStr, String keys) {
425                 try {
426                                 Object rawValue = getJsonRawValue(jsonStr, keys);
427                                 if (rawValue == null) {
428                                         return false;
429                                 } else {
430                                         if (rawValue instanceof Boolean) {
431                                                 msoLogger.debug("getJsonBooleanValue(): the raw value is a Boolean Object=" + rawValue);
432                                                 return (Boolean) rawValue;
433                                         } else {
434                                                 msoLogger.debug("getJsonBooleanValue(): the raw value is NOT an Boolean Object=" + rawValue.toString());
435                                                 return false;
436                                         }
437                                 }
438                 } catch (Exception e) {
439                                 msoLogger.debug("getJsonBooleanValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString(),e);
440                 }
441                 return false;
442         }
443
444         /**
445          * Invokes the getJsonParamValue() method to obtain the JSONArray associated with
446          * the specified keys. The JSONArray is then walked to retrieve the first array
447          * value associated with the specified field name (index=0).
448          *
449          * @param  jsonStr      String containing the JSON doc
450          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
451          * @param  name         field name for the param to be retrieved
452          * @return String param value associated with field name
453          */
454         public static String getJsonParamValue(String jsonStr, String keys, String name) {
455                 return getJsonParamValue(jsonStr, keys, name, 0);
456         }
457
458         /**
459          * Invokes the getJsonRawValue() method to obtain the JSONArray associated with
460          * the specified keys. The JSONArray is then walked to retrieve the nth array
461          * value associated with the specified field name and index.
462          *
463          * @param  jsonStr      String containing the JSON doc
464          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
465          * @param  name         field name for the param to be retrieved
466          * @param  index    the nth param associated with name starting at 0
467          * @return String param value associated with field name
468          */
469         public static String getJsonParamValue(String jsonStr, String keys, String name, int index) {
470 //              String isDebugLogEnabled = "true";
471                 try {
472                         Object rawValue = getJsonRawValue(jsonStr, keys);
473                         if (rawValue == null) {
474                                 return null;
475                         } else {
476                                 if (rawValue instanceof JSONArray) {
477                                         msoLogger.debug("getJsonParamValue(): keys=" + keys + " points to JSONArray: " + rawValue.toString());
478                                         int arrayLen = ((JSONArray) rawValue).length();
479                                         if (index < 0 || arrayLen < index+1) {
480                                                 msoLogger.debug("getJsonParamValue(): index: " + index + " is out of bounds for array size of " + arrayLen);
481                                                 return null;
482                                         }
483                                         int foundCnt = 0;
484                                         for (int i = 0; i < arrayLen; i++) {
485                                                 msoLogger.debug("getJsonParamValue(): index: " + i + ", value: " + ((JSONArray) rawValue).get(i).toString());
486                                                 if (((JSONArray) rawValue).get(i) instanceof JSONObject) {
487 //                                                      msoLogger.debug("getJsonParamValue(): index: " + i + " is a JSONObject");
488                                                         JSONObject jsonObj = (JSONObject)((JSONArray) rawValue).get(i);
489                                                         String parmValue = jsonObj.get(name).toString();
490                                                         if (parmValue != null) {
491                                                                 msoLogger.debug("getJsonParamValue(): found value: " + parmValue + " for name: " + name + " and index: " + i);
492                                                                 if (foundCnt == index) {
493                                                                         return parmValue;
494                                                                 } else {
495                                                                         foundCnt++;
496                                                                         continue;
497                                                                 }
498                                                         } else {
499                                                                 continue;
500                                                         }
501                                                 } else {
502                                                         msoLogger.debug("getJsonParamValue(): the JSONArray element is NOT a JSONObject=" + rawValue.toString());
503                                                         return null;
504                                                 }
505                                         }
506                                         msoLogger.debug("getJsonParamValue(): content value NOT found for name: " + name);
507                                         return null;
508                                 } else {
509                                         msoLogger.debug("getJsonParamValue(): the raw value is NOT a JSONArray Object=" + rawValue.toString());
510                                         return null;
511                                 }
512                         }
513                 } catch (Exception e) {
514                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
515                         if (e.getMessage().contains("not found")) {
516                                 msoLogger.debug("getJsonParamValue(): failed to retrieve param value for keys:" + keys + ", name=" + name + ": " + e.getMessage());
517                         } else {
518                                 msoLogger.debug("getJsonParamValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString(), e);
519                         }
520                 }
521                 return null;
522         }
523
524         /**
525          * Wrapper to generate the JSONObject to pass to the getJsonValueForKey(JSONObject, String)
526          * method so that recursion over the subobjects can be supported there
527          *
528          * @param  jsonStr      String containing the JSON doc
529          * @param  key          key to the target value
530          * @return String field value associated with key
531          */
532         public static String getJsonValueForKey(String jsonStr, String key) {
533
534                 try {
535                         JSONObject jsonObj = new JSONObject(jsonStr);
536                         return getJsonValueForKey(jsonObj, key);
537                 } catch (Exception e) {
538                                 msoLogger.debug("getJsonValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString(), e);
539                 }
540                 return null;
541         }
542
543         /**
544          * Walks the JSONObject (and sub-objects recursively), searching for the first value associated with the
545          * single key/field name specified. Returns the associated value if found or null if the key is not found
546          *
547          * @param  jsonObj      JSONObject representation of the the JSON doc
548          * @param  key          key to the target value
549          * @return String field value associated with key
550          */
551         public static String getJsonValueForKey(JSONObject jsonObj, String key) {
552
553                 String keyValue = null;
554                 try {
555                         if (jsonObj.has(key)) {
556                                 Object value = jsonObj.get(key);
557                                 msoLogger.debug("getJsonValueForKey(): found value=" + (String) value + ", for key=" + key);
558                                 if (value == null) {
559                                         return null;
560                                 } else {
561                                         return ((String) value);
562                                 }
563                         } else {
564 //                              msoLogger.debug("getJsonValueForKey(): iterating over the keys");
565                                 Iterator <String> itr = jsonObj.keys();
566                                 while (itr.hasNext()) {
567                                         String nextKey = itr.next();
568                                         Object obj = jsonObj.get(nextKey);
569                                         if (obj instanceof JSONObject) {
570 //                                              msoLogger.debug("getJsonValueForKey(): key=" + nextKey + ", points to JSONObject, recursive call on: " +
571 //                                                              ((JSONObject) obj).toString(MSOJsonIndentFactor));
572                                                 keyValue = getJsonValueForKey((JSONObject) obj, key);
573                                                 if (keyValue != null) {
574 //                                                      msoLogger.debug("getJsonValueForKey(): found value=" + keyValue + ", for key=" + key);
575                                                         break;
576                                                 }
577                                         } else {
578                                                 msoLogger.debug("getJsonValueForKey(): key=" + nextKey + ", does not point to a JSONObject, next key");
579                                         }
580                                 }
581                         }
582                 } catch (Exception e) {
583                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
584                         if (e.getMessage().contains("not found")) {
585                                 msoLogger.debug("getJsonValueForKey(): failed to retrieve param value for key=" + key + ": " + e.getMessage());
586                         } else {
587                                 msoLogger.debug("getJsonValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString(), e);
588                         }
589                         keyValue = null;
590                 }
591                 return keyValue;
592         }
593
594         /**
595          * Walks the JSONObject (and sub-objects recursively), searching for the first value associated with the
596          * single key/field name specified. Returns the associated value if found or null if the key is not found
597          *
598          * @param  jsonObj      JSONObject representation of the the JSON doc
599          * @param  key          key to the target value
600          * @return String field value associated with key
601          */
602         public static Integer getJsonIntValueForKey(JSONObject jsonObj, String key) {
603 //              String isDebugLogEnabled = "true";
604                 Integer keyValue = null;
605                 try {
606                         if (jsonObj.has(key)) {
607                                 Integer value = (Integer) jsonObj.get(key);
608                                 msoLogger.debug("getJsonIntValueForKey(): found value=" + value + ", for key=" + key);
609                                 return value;
610                         } else {
611 //                              msoLogger.debug("getJsonIntValueForKey(): iterating over the keys");
612                                 Iterator <String> itr = jsonObj.keys();
613                                 while (itr.hasNext()) {
614                                         String nextKey = itr.next();
615                                         Object obj = jsonObj.get(nextKey);
616                                         if (obj instanceof JSONObject) {
617 //                                              msoLogger.debug("getJsonIntValueForKey(): key=" + nextKey + ", points to JSONObject, recursive call");
618                                                 keyValue = getJsonIntValueForKey((JSONObject) obj, key);
619                                                 if (keyValue != null) {
620 //                                                      msoLogger.debug("getJsonIntValueForKey(): found value=" + keyValue + ", for key=" + key);
621                                                         break;
622                                                 }
623                                         } else {
624                                                 msoLogger.debug("getJsonIntValueForKey(): key=" + nextKey + ", does not point to a JSONObject, next key");
625                                         }
626                                 }
627                         }
628                 } catch (Exception e) {
629                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
630                         if (e.getMessage().contains("not found")) {
631                                 msoLogger.debug("getJsonIntValueForKey(): failed to retrieve param value for key=" + key + ": " + e.getMessage());
632                         } else {
633                                 msoLogger.debug("getJsonIntValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString(),e);
634                         }
635                         keyValue = null;
636                 }
637                 return keyValue;
638         }
639
640         /**
641          * Walks the JSONObject (and sub-objects recursively), searching for the first value associated with the
642          * single key/field name specified. Returns the associated value if found or null if the key is not found
643          *
644          * @param  jsonObj      JSONObject representation of the the JSON doc
645          * @param  key          key to the target value
646          * @return String field value associated with key
647          */
648         public static Boolean getJsonBooleanValueForKey(JSONObject jsonObj, String key) {
649                 Boolean keyValue = null;
650                 try {
651                         if (jsonObj.has(key)) {
652                                 Boolean value = (Boolean) jsonObj.get(key);
653                                 msoLogger.debug("getJsonBooleanValueForKey(): found value=" + value + ", for key=" + key);
654                                 return value;
655                         } else {
656 //                              msoLogger.debug("getJsonBooleanValueForKey(): iterating over the keys");
657                                 Iterator <String> itr = jsonObj.keys();
658                                 while (itr.hasNext()) {
659                                         String nextKey = itr.next();
660                                         Object obj = jsonObj.get(nextKey);
661                                         if (obj instanceof JSONObject) {
662 //                                              msoLogger.debug("getJsonBooleanValueForKey(): key=" + nextKey + ", points to JSONObject, recursive call");
663                                                 keyValue = getJsonBooleanValueForKey((JSONObject) obj, key);
664                                                 if (keyValue != null) {
665 //                                                      msoLogger.debug("getJsonBooleanValueForKey(): found value=" + keyValue + ", for key=" + key);
666                                                         break;
667                                                 }
668                                         } else {
669                                                 msoLogger.debug("getJsonBooleanValueForKey(): key=" + nextKey + ", does not point to a JSONObject, next key");
670                                         }
671                                 }
672                         }
673                 } catch (Exception e) {
674                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
675                         if (e.getMessage().contains("not found")) {
676                                 msoLogger.debug("getJsonBooleanValueForKey(): failed to retrieve param value for key=" + key + ": " + e.getMessage());
677                         } else {
678                                 msoLogger.debug("getJsonBooleanValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString(),e);
679                         }
680                         keyValue = null;
681                 }
682                 return keyValue;
683         }
684
685         /**
686          * Boolean method to determine if a key path is valid for the JSON doc. Invokes
687          * getJsonValue().
688          *
689          * @param  jsonStr      String containing the JSON doc
690          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
691          * @return Boolean true if keys points to value in the JSON doc
692          */
693         public static Boolean jsonValueExists(String jsonStr, String keys) {
694                 if (getJsonRawValue(jsonStr, keys) == null) {
695                         return false;
696                 } else {
697                         return true;
698                 }
699         }
700
701         /**
702          * Inserts the new key/value pair at the appropriate location in the JSON
703          * document after first determining if keyed field already exists. If
704          * it does exist, return the JSON unmodified, otherwise return the new JSON
705          * Note: this method currently only supports String value inserts.
706          *
707          * @param  jsonStr      String containing the JSON doc
708          * @param  keys         full key path to the value to be added in the format of "key1.key2.key3..."
709          * @return String containing the updated JSON doc
710          */
711         public static String addJsonValue(String jsonStr, String keys, String value) {
712
713                 // only attempt to insert the key/value pair if it does not exist
714                 if (!jsonValueExists(jsonStr, keys)) {
715                         return putJsonValue(jsonStr, keys, value);
716                 } else {
717                         msoLogger.debug("addJsonValue(): JSON add failed, key=" + keys + "/value=" + value + " already exists");
718                         return jsonStr;
719                 }
720         }
721
722         /**
723          * Updates the value for the specified key in the JSON document
724          * after first determining if keyed field exists. If it does
725          * not exist, return the JSON unmodified, otherwise return the updated JSON.
726          * Note: this method currently only supports String value updates.
727          *
728          * @param  jsonStr      String containing the JSON doc
729          * @param  keys         full key path to the value to be updated in the format of "key1.key2.key3..."
730          * @return String containing the updated JSON doc
731          */
732         public static String updJsonValue(String jsonStr, String keys, String newValue) {
733 //              String isDebugLogEnabled = "true";
734                 // only attempt to modify the key/value pair if it exists
735                 if (jsonValueExists(jsonStr, keys)) {
736                         return putJsonValue(jsonStr, keys, newValue);
737                 } else {
738                         msoLogger.debug("updJsonValue(): JSON update failed, no value exists for key=" + keys);
739                         return jsonStr;
740                 }
741         }
742
743         /**
744          * Deletes the value for the specified key in the JSON document
745          * after first determining if keyed field exists. If it does
746          * not exist, return the JSON unmodified, otherwise return the updated JSON
747          *
748          * @param  jsonStr      String containing the JSON doc
749          * @param  keys         full key path to the value to be deleted in the format of "key1.key2.key3..."
750          * @return String containing the updated JSON doc
751          */
752         public static String delJsonValue(String jsonStr, String keys) {
753
754                 // only attempt to remove the key/value pair if it exists
755                 if (jsonValueExists(jsonStr, keys)) {
756                         // passing a null value results in a delete
757                         return putJsonValue(jsonStr, keys, null);
758                 } else {
759                         msoLogger.debug("delJsonValue(): JSON delete failed, no value exists for key=" + keys);
760                         return jsonStr;
761                 }
762         }
763
764         /**
765          * Walks the JSON doc using the full key path to retrieve the associated
766          * value. All but the last key points to the 'parent' object name(s) in order
767          * in the JSON hierarchy with the last key pointing to the target value.
768          * The value returned is a Java object.
769          *
770          * @param  jsonStr      String containing the JSON doc
771          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
772          * @return Object field value associated with keys
773          */
774         private static Object getJsonRawValue(String jsonStr, String keys) {
775                 return getJsonRawValue(jsonStr, keys, false);
776         }
777
778         /**
779          * Walks the JSON doc using the full key path to retrieve the associated
780          * value. All but the last key points to the 'parent' object name(s) in order
781          * in the JSON hierarchy with the last key pointing to the target value.
782          * The value returned is a Java object.
783          *
784          * @param  jsonStr      String containing the JSON doc
785          * @param  keys         full key path to the target value in the format of "key1.key2.key3..."
786          * @param  wrap         Boolean which determines if returned JSONObjects sould be "wrapped"
787          *                  Note: wrap does not apply to returned scalar values
788          * @return Object field value associated with keys
789          */
790         private static Object getJsonRawValue(String jsonStr, String keys, Boolean wrap) {
791
792                 String keyStr = "";
793                 try {
794                         JSONObject jsonObj = new JSONObject(jsonStr);
795                         StringTokenizer keyTokens = new StringTokenizer(keys, ".");
796                         while (keyTokens.hasMoreElements()) {
797                                 keyStr = keyTokens.nextToken();
798                                 Object keyValue = jsonObj.get(keyStr);
799                                 if (keyValue instanceof JSONObject) {
800 //                                      msoLogger.debug("getJsonRawValue(): key=" + keyStr + " points to json object");
801                                         jsonObj = (JSONObject) keyValue;
802                                 } else {
803                                         if (keyTokens.hasMoreElements()) {
804                                                 msoLogger.debug("getJsonRawValue(): value found prior to last key for key=" + keyStr);
805                                         }
806                                         return keyValue;
807                                 }
808                         }
809                         // return the json 'node' that the key points to
810                         // note: since this is a json object and not a scalar value,
811                         //       use the wrap flag to determine if the object should
812                         //       be wrapped with a root node value
813                         //       (the last key in the keys String)
814                         if (wrap) {
815                                 JSONObject wrappedJsonObj = new JSONObject();
816                                 wrappedJsonObj.put(keyStr, jsonObj);
817                                 return wrappedJsonObj.toString();
818                         } else {
819                                 return jsonObj.toString();
820                         }
821
822                 } catch (Exception e) {
823                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
824                         if (e.getMessage().contains("not found")) {
825                                 msoLogger.debug("getJsonRawValue(): failed to retrieve param value for key=" + keyStr + ": " + e.getMessage());
826                         } else {
827                                 msoLogger.debug("getJsonRawValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString(),e);
828                         }
829                 }
830                 return null;
831         }
832
833         /**
834          * Private method invoked by the public add, update, and delete methods.
835          *
836          * @param  jsonStr      String containing the JSON doc
837          * @param  keys         full key path to the value to be deleted in the format of "key1.key2.key3..."
838          * @return String containing the updated JSON doc
839          */
840         private static String putJsonValue(String jsonStr, String keys, String value) {
841
842                 String keyStr = "";
843                 try {
844                         JSONObject jsonObj = new JSONObject(jsonStr);
845                         JSONObject jsonObjOut = jsonObj;
846                         StringTokenizer keyTokens = new StringTokenizer(keys, ".");
847                         while (keyTokens.hasMoreElements()) {
848                                 keyStr = keyTokens.nextToken();
849                                 if (keyTokens.hasMoreElements()) {
850                                         Object keyValue = jsonObj.get(keyStr);
851                                         if (keyValue instanceof JSONObject) {
852 //                                              msoLogger.debug("putJsonValue(): key=" + keyStr + " points to json object");
853                                                 jsonObj = (JSONObject) keyValue;
854                                         } else {
855                                                 msoLogger.debug("putJsonValue(): key=" + keyStr + " not the last key but points to non-json object: " + keyValue);
856                                                 return null;
857                                         }
858                                 } else { // at the last/new key value
859                                         jsonObj.put(keyStr, value);
860                                         return jsonObjOut.toString(3);
861                                 }
862                         }
863                         // should not hit this point if the key points to a valid key value
864                         return null;
865
866                 } catch (Exception e) {
867                         // JSONObject::get() throws a "not found" exception if one of the specified keys is not found
868                         if (e.getMessage().contains("not found")) {
869                                 msoLogger.debug("putJsonValue(): failed to put param value for key=" + keyStr + ": " + e.getMessage());
870                         } else {
871                                 msoLogger.debug("putJsonValue(): unable to parse json to put value for key=" + keys + ". Exception was: " + e.toString(),e);
872                         }
873                 }
874                 return null;
875         }
876
877         /**
878          * This json util method converts a json array of Key Value
879          * pair objects into a Java Map.
880          *
881          * @param execution
882          * @param entryArray - the getJsonValue of a json Array of key/value pairs
883          *
884          * @return Map - a Map containing the entries
885          */
886         public Map<String, String> jsonStringToMap(DelegateExecution execution, String entry) {
887                 msoLogger.debug("Started Json String To Map Method");
888
889                 Map<String, String> map = new HashMap<>();
890
891                 //Populate Map
892                 JSONObject obj = new JSONObject(entry);
893
894                 /* Wildfly is pushing a version of org.json which does not
895                  * auto cast to string. Leaving it as an object prevents
896                  * a method not found exception at runtime.
897                  */
898                 final Iterator<String> keys = obj.keys();
899                 while (keys.hasNext()) {
900                         final String key = keys.next();
901                         map.put(key, obj.getString(key));
902                 }
903                 msoLogger.debug("Outgoing Map is: " + map);
904                 msoLogger.debug("Completed Json String To Map Method");
905                 return map;
906         }
907
908         /**
909          * This json util method converts a json array of Key Value
910          * pair objects into a Java Map.
911          *
912          * @param execution
913          * @param entryArray - the getJsonValue of a json Array of key/value pairs
914          * @param keyNode - the name of the node that represents the key
915          * @param valueNode - the name of the node that represents the value
916          * @return Map - a Map containing the entries
917          *
918          */
919         public Map<String, String> entryArrayToMap(DelegateExecution execution, String entryArray, String keyNode, String valueNode) {
920                 msoLogger.debug("Started Entry Array To Map Util Method");
921
922                 Map<String, String> map = new HashMap<>();
923                 //Populate Map
924                 String entryListJson = "{ \"wrapper\":" + entryArray + "}";
925                 JSONObject obj = new JSONObject(entryListJson);
926                 JSONArray arr = obj.getJSONArray("wrapper");
927                 for (int i = 0; i < arr.length(); i++){
928                         JSONObject jo = arr.getJSONObject(i);
929                         String key = jo.getString(keyNode);
930                         String value = jo.getString(valueNode);
931                         map.put(key, value);
932                 }
933                 msoLogger.debug("Completed Entry Array To Map Util Method");
934                 return map;
935         }
936
937         /**
938          * This json util method converts a json array of Key Value pair objects into a Java Map.
939          *
940          * @param entryArray - the json Array of key/value pairs objects
941          * @param keyNode - the name of the node that represents the key
942          * @param valueNode - the name of the node that represents the value
943          * @return Map - a Map containing the entries
944          * @author cb645j
945          *
946          */
947         public Map<String, String> entryArrayToMap(String entryArray, String keyNode, String valueNode){
948                 msoLogger.debug("Started Entry Array To Map Util Method");
949
950                 Map<String, String> map = new HashMap<>();
951                 String entryListJson = "{ \"wrapper\":" + entryArray + "}";
952                 JSONObject obj = new JSONObject(entryListJson); // TODO just put in json array
953                 JSONArray arr = obj.getJSONArray("wrapper");
954                 for(int i = 0; i < arr.length(); i++){
955                         JSONObject jo = arr.getJSONObject(i);
956                         String key = jo.getString(keyNode);
957                         String value = jo.getString(valueNode);
958                         map.put(key, value);
959                 }
960                 msoLogger.debug("Completed Entry Array To Map Util Method");
961                 return map;
962         }
963
964         /**
965          * This json util method converts a json Array of Strings to a Java List. It takes each
966          * String in the json Array and puts it in a Java List<String>.
967          *
968          * @param execution
969          * @param jsonArray - string value of a json array
970          * @return List - a java list containing the strings
971          *
972          * @author cb645j
973          */
974         public List<String> StringArrayToList(Execution execution, String jsonArray){
975                 msoLogger.debug("Started  String Array To List Util Method");
976
977                 List<String> list = new ArrayList<>();
978                 // Populate List
979                 // TODO
980                 String stringListJson = "{ \"strings\":" + jsonArray + "}";
981                 JSONObject obj = new JSONObject(stringListJson);
982                 JSONArray arr = obj.getJSONArray("strings");
983                 for(int i = 0; i < arr.length(); i++){
984                         String s = arr.get(i).toString();
985                         list.add(s);
986                 }
987                 msoLogger.debug("Outgoing List is: " + list);
988                 msoLogger.debug("Completed String Array To List Util Method");
989                 return list;
990         }
991
992         /**
993          * This json util method converts a json Array of Strings to a Java List. It takes each
994          * String in the json Array and puts it in a Java List<String>.
995          *
996          * @param jsonArray - string value of a json array
997          * @return List - a java list containing the strings
998          *
999          * @author cb645j
1000          */
1001         public List<String> StringArrayToList(String jsonArray){
1002                 msoLogger.debug("Started Json Util String Array To List");
1003                 List<String> list = new ArrayList<>();
1004
1005                 JSONArray arr = new JSONArray(jsonArray);
1006                 for(int i = 0; i < arr.length(); i++){
1007                         String s = arr.get(i).toString();
1008                         list.add(s);
1009                 }
1010                 msoLogger.debug("Completed Json Util String Array To List");
1011                 return list;
1012         }
1013
1014         /**
1015          * This json util method converts a json Array of Strings to a Java List. It takes each
1016          * String in the json Array and puts it in a Java List<String>.
1017          *
1018          * @param jsonArray - json array
1019          * @return List - a java list containing the strings
1020          *
1021          * @author cb645j
1022          */
1023         public List<String> StringArrayToList(JSONArray jsonArray){
1024                 msoLogger.debug("Started Json Util String Array To List");
1025                 List<String> list = new ArrayList<>();
1026
1027                 for(int i = 0; i < jsonArray.length(); i++){
1028                         String s = jsonArray.get(i).toString();
1029                         list.add(s);
1030                 }
1031                 msoLogger.debug("Completed Json Util String Array To List");
1032                 return list;
1033         }
1034
1035         /**
1036          *
1037          * Invokes the getJsonRawValue() method to determine if the json element/variable exist.
1038          * Returns true if the json element exist
1039          *
1040          * @param jsonStr - String containing the JSON doc
1041          * @param keys - full key path to the target value in the format of "key1.key2.key3..."
1042          * @return boolean field value associated with keys
1043          *
1044          */
1045         public static boolean jsonElementExist(String jsonStr, String keys){
1046
1047                 try{
1048                         Object rawValue = getJsonRawValue(jsonStr, keys);
1049
1050                         return !(rawValue == null);
1051
1052                 } catch(Exception e){
1053                         msoLogger.debug("jsonElementExist(): unable to determine if json element exist. Exception is: " + e.toString(), e);
1054                 }
1055                 return true;
1056         }
1057
1058         /**
1059          *
1060          * Validates the JSON document against a schema file.
1061          *
1062          * @param  jsonStr      String containing the JSON doc
1063          * @param  jsonSchemaPath full path to a valid JSON schema file
1064          *
1065          */
1066     public static String jsonSchemaValidation(String jsonStr, String jsonSchemaPath) throws ValidationException {
1067         try {
1068                 msoLogger.debug("JSON document to be validated: " + jsonStr);
1069                 JsonNode document = JsonLoader.fromString(jsonStr);
1070 //              JsonNode document = JsonLoader.fromFile(jsonDoc);
1071                 JsonNode schema = JsonLoader.fromPath(jsonSchemaPath);
1072
1073                 JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
1074                 JsonValidator validator = factory.getValidator();
1075
1076                 ProcessingReport report = validator.validate(schema, document);
1077                 msoLogger.debug("JSON schema validation report: " + report.toString());
1078                 return report.toString();
1079         } catch (IOException e) {
1080                 msoLogger.debug("IOException performing JSON schema validation on document: " + e.toString());
1081                 throw new ValidationException(e.getMessage());
1082         } catch (ProcessingException e) {
1083                 msoLogger.debug("ProcessingException performing JSON schema validation on document: " + e.toString());
1084                 throw new ValidationException(e.getMessage());
1085         }
1086     }
1087 }