001package com.ganteater.ae.processor; 002 003import java.awt.Toolkit; 004import java.awt.datatransfer.Clipboard; 005import java.awt.datatransfer.DataFlavor; 006import java.awt.datatransfer.StringSelection; 007import java.io.BufferedReader; 008import java.io.ByteArrayOutputStream; 009import java.io.File; 010import java.io.FileInputStream; 011import java.io.FileNotFoundException; 012import java.io.FileOutputStream; 013import java.io.FileReader; 014import java.io.IOException; 015import java.io.InputStream; 016import java.io.InputStreamReader; 017import java.io.OutputStream; 018import java.io.StringReader; 019import java.io.StringWriter; 020import java.io.UnsupportedEncodingException; 021import java.net.Inet4Address; 022import java.net.InetAddress; 023import java.net.NetworkInterface; 024import java.net.Socket; 025import java.net.SocketException; 026import java.net.SocketTimeoutException; 027import java.net.URL; 028import java.net.URLConnection; 029import java.net.UnknownHostException; 030import java.security.NoSuchAlgorithmException; 031import java.security.SecureRandom; 032import java.text.DateFormat; 033import java.text.SimpleDateFormat; 034import java.util.ArrayList; 035import java.util.Arrays; 036import java.util.Base64; 037import java.util.Calendar; 038import java.util.Collection; 039import java.util.Collections; 040import java.util.Date; 041import java.util.Enumeration; 042import java.util.HashMap; 043import java.util.HashSet; 044import java.util.Iterator; 045import java.util.LinkedHashMap; 046import java.util.LinkedHashSet; 047import java.util.List; 048import java.util.Map; 049import java.util.Map.Entry; 050import java.util.Properties; 051import java.util.Random; 052import java.util.Set; 053import java.util.StringTokenizer; 054import java.util.TimeZone; 055import java.util.UUID; 056import java.util.concurrent.Executors; 057import java.util.concurrent.ThreadPoolExecutor; 058import java.util.concurrent.TimeUnit; 059import java.util.regex.Matcher; 060import java.util.regex.Pattern; 061import java.util.stream.Collectors; 062import java.util.stream.IntStream; 063 064import javax.swing.JOptionPane; 065import javax.xml.transform.Source; 066import javax.xml.transform.Transformer; 067import javax.xml.transform.TransformerFactory; 068import javax.xml.transform.stream.StreamResult; 069import javax.xml.transform.stream.StreamSource; 070 071import org.apache.commons.collections.CollectionUtils; 072import org.apache.commons.collections.MapUtils; 073import org.apache.commons.io.FileUtils; 074import org.apache.commons.io.IOUtils; 075import org.apache.commons.io.filefilter.IOFileFilter; 076import org.apache.commons.io.filefilter.TrueFileFilter; 077import org.apache.commons.jexl3.JexlBuilder; 078import org.apache.commons.jexl3.JexlContext; 079import org.apache.commons.jexl3.JexlEngine; 080import org.apache.commons.jexl3.JexlExpression; 081import org.apache.commons.jexl3.MapContext; 082import org.apache.commons.lang.ArrayUtils; 083import org.apache.commons.lang.BooleanUtils; 084import org.apache.commons.lang.ObjectUtils; 085import org.apache.commons.lang.StringEscapeUtils; 086import org.apache.commons.lang.StringUtils; 087import org.apache.commons.lang.math.NumberUtils; 088import org.apache.commons.lang3.SystemUtils; 089import org.apache.http.auth.AuthScope; 090import org.apache.http.auth.UsernamePasswordCredentials; 091import org.apache.http.client.CredentialsProvider; 092import org.apache.http.client.config.RequestConfig; 093import org.apache.http.client.methods.CloseableHttpResponse; 094import org.apache.http.client.methods.HttpGet; 095import org.apache.http.client.methods.HttpPost; 096import org.apache.http.client.methods.HttpRequestBase; 097import org.apache.http.client.protocol.HttpClientContext; 098import org.apache.http.client.utils.URLEncodedUtils; 099import org.apache.http.entity.ByteArrayEntity; 100import org.apache.http.impl.client.BasicCredentialsProvider; 101import org.apache.http.impl.client.CloseableHttpClient; 102import org.apache.http.impl.client.HttpClients; 103import org.apache.sling.commons.json.JSONArray; 104import org.apache.sling.commons.json.JSONException; 105import org.apache.sling.commons.json.JSONObject; 106 107import com.ganteater.ae.AEManager; 108import com.ganteater.ae.AEWorkspace; 109import com.ganteater.ae.ChoiceTaskRunner; 110import com.ganteater.ae.CommandException; 111import com.ganteater.ae.ILogger; 112import com.ganteater.ae.MultiTaskRunDialog; 113import com.ganteater.ae.RecipeListener; 114import com.ganteater.ae.TaskCancelingException; 115import com.ganteater.ae.processor.annotation.CommandDescription; 116import com.ganteater.ae.processor.annotation.CommandExamples; 117import com.ganteater.ae.util.AEUtils; 118import com.ganteater.ae.util.AssertionFailedError; 119import com.ganteater.ae.util.NameValuePairImplementation; 120import com.ganteater.ae.util.RegexPathFilter; 121import com.ganteater.ae.util.TestCase; 122import com.ganteater.ae.util.xml.easyparser.EasyParser; 123import com.ganteater.ae.util.xml.easyparser.EasyUtils; 124import com.ganteater.ae.util.xml.easyparser.Node; 125import com.opencsv.CSVReader; 126 127import okhttp3.MediaType; 128import okhttp3.OkHttpClient; 129import okhttp3.Request.Builder; 130import okhttp3.Response; 131 132public class BaseProcessor extends Processor { 133 134 private Random random; 135 136 public BaseProcessor() { 137 } 138 139 public BaseProcessor(Processor parent) throws CommandException { 140 init(parent); 141 } 142 143 public BaseProcessor(AEManager manager, ILogger log, File baseDir) { 144 super(manager, log, baseDir); 145 } 146 147 public BaseProcessor(Map<String, Object> hashMap, Node node, File baseDir) { 148 super(hashMap, node, baseDir); 149 } 150 151 public BaseProcessor(Node configNode, Map<String, Object> startVariables, RecipeListener listener, File startDir, 152 ILogger aLog, Processor parent) throws CommandException { 153 super(configNode, startVariables, listener, startDir, aLog, parent); 154 } 155 156 @CommandExamples({ "<Regexp name='type:property' source='type:property' regex='type:string' group='type:integer'/>", 157 "<Regexp name='type:property' source='type:property' regex='type:string' />" }) 158 public void runCommandRegexp(final Node action) throws Throwable { 159 final String theName = attr(action, "name"); 160 String regex = replaceProperties(action.getAttribute("regex")); 161 int group = Integer.parseInt(attr(action, "group", "0")); 162 Object sourceObj = attrValue(action, "source"); 163 if (sourceObj == null) { 164 throw new IllegalArgumentException("The 'source' value must not be null."); 165 } 166 167 if (sourceObj instanceof String) { 168 sourceObj = new String[] { (String) sourceObj }; 169 } 170 171 Pattern pattern = Pattern.compile(regex); 172 List<String> result = new ArrayList<>(); 173 if (sourceObj instanceof String[]) { 174 String[] sources = (String[]) sourceObj; 175 176 for (String source : sources) { 177 Matcher matcher = pattern.matcher(source); 178 if (matcher.find()) { 179 String theText = matcher.group(group); 180 result.add(theText); 181 } 182 } 183 } 184 setVariableValue(theName, result); 185 } 186 187 @CommandExamples({ "<Replace name='type:property' oldChar='type:integer' newChar='type:integer'/>", 188 "<Replace name='type:property' regex='type:regex' replacement='type:string'/>", 189 "<Replace name='type:property' unescape='enum:java|csv|html|javascript|xml' />", 190 "<Replace name='type:property' escape='enum:java|csv|html|javascript|xml|sql' />" }) 191 public void runCommandReplace(final Node command) throws Throwable { 192 String name = replaceProperties(command.getAttribute("name")); 193 194 Object variableValue = getVariableValue(name); 195 String value = null; 196 if (variableValue instanceof String) { 197 value = (String) variableValue; 198 } else if (variableValue instanceof byte[]) { 199 value = new String((byte[]) variableValue); 200 } else { 201 value = ObjectUtils.toString(variableValue); 202 } 203 value = replaceProperties(value); 204 205 final String theOldChar = replaceProperties(command.getAttribute("oldChar")); 206 final String theNewChar = replaceProperties(command.getAttribute("newChar")); 207 if (theOldChar != null) { 208 value = value.replace(theOldChar, theNewChar); 209 } 210 211 final String regex = replaceProperties(command.getAttribute("regex")); 212 final String replacement = replaceProperties(command.getAttribute("replacement")); 213 if (regex != null && value != null) { 214 value = value.replaceAll(regex, replacement); 215 } 216 217 String unescape = attr(command, "unescape"); 218 if (unescape != null) { 219 unescape = unescape.toLowerCase(); 220 switch (unescape) { 221 case "csv": { 222 value = StringEscapeUtils.unescapeCsv(value); 223 break; 224 } 225 case "html": { 226 value = StringEscapeUtils.unescapeHtml(value); 227 break; 228 } 229 case "java": { 230 value = StringEscapeUtils.unescapeJava(value); 231 break; 232 } 233 case "javascript": { 234 value = StringEscapeUtils.unescapeJavaScript(value); 235 break; 236 } 237 case "xml": { 238 value = StringEscapeUtils.unescapeXml(value); 239 break; 240 } 241 } 242 } 243 244 String escape = attr(command, "escape"); 245 if (escape != null) { 246 escape = escape.toLowerCase(); 247 switch (escape) { 248 case "csv": { 249 value = StringEscapeUtils.escapeCsv(value); 250 break; 251 } 252 case "html": { 253 value = StringEscapeUtils.escapeHtml(value); 254 break; 255 } 256 case "java": { 257 value = StringEscapeUtils.escapeJava(value); 258 break; 259 } 260 case "javascript": { 261 value = StringEscapeUtils.escapeJavaScript(value); 262 break; 263 } 264 case "sql": { 265 value = StringEscapeUtils.escapeSql(value); 266 break; 267 } 268 case "xml": { 269 value = StringEscapeUtils.escapeXml(value); 270 break; 271 } 272 } 273 } 274 275 setVariableValue(name, value); 276 } 277 278 @CommandDescription("The Clipboard command swaps the contents of the system clipboard with the value of the variable specified by the name attribute") 279 @CommandExamples({ "<Clipboard name='type:property'/>" }) 280 public void runCommandClipboard(final Node action) throws Exception { 281 String name = action.getAttribute("name"); 282 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 283 String varValue = null; 284 if (name != null) { 285 varValue = String.valueOf(attrValue(action, "name")); 286 287 String clipboardValue = (String) Toolkit.getDefaultToolkit().getSystemClipboard() 288 .getData(DataFlavor.stringFlavor); 289 290 setVariableValue(name, clipboardValue); 291 } 292 StringSelection stringSelection = new StringSelection(StringUtils.defaultIfEmpty(varValue, StringUtils.EMPTY)); 293 clipboard.setContents(stringSelection, null); 294 } 295 296 @CommandExamples({ 297 "<CheckValue actual='type:string' expected='type:string' mode='enum:normal|trimLines' condition='enum:unequal|equal' onErrorMsg='type:string'/>", 298 "<CheckValue value='type:string' regex='type:regex' onErrorMsg='type:string'/>" }) 299 public void runCommandCheckValue(final Node action) throws Throwable { 300 301 String actual = attr(action, "actual"); 302 String expected = attr(action, "expected"); 303 final String errorMsg = replaceProperties(action.getAttribute("onErrorMsg")); 304 305 if (actual != null && expected != null) { 306 307 final String theRegex = replaceProperties(action.getAttribute("regex")); 308 309 if (theRegex == null) { 310 final boolean theTrimLines = "trimLines".equals(replaceProperties(action.getAttribute("mode"))); 311 try { 312 if (theTrimLines) { 313 actual = getTrimmedLines(actual); 314 expected = getTrimmedLines(expected); 315 } 316 317 final boolean unequal = "unequal".equals(replaceProperties(action.getAttribute("condition"))); 318 if (unequal) { 319 boolean equals = StringUtils.equals(expected, actual); 320 TestCase.assertFalse(errorMsg, equals); 321 } else { 322 TestCase.assertEquals(errorMsg, expected, actual); 323 } 324 } catch (final Throwable e) { 325 taskNode(action, false); 326 throw e; 327 } 328 } else { 329 final String theValue = replaceProperties(action.getAttribute("value")); 330 final Pattern p = Pattern.compile(theRegex); 331 final Matcher m = p.matcher(theValue); 332 if (m.find() == false) { 333 taskNode(action, false); 334 TestCase.fail("Regex validation failed. Regex:" + theRegex + ", value:" + theValue); 335 } 336 } 337 } else { 338 throw new AssertionFailedError(errorMsg); 339 } 340 341 } 342 343 @CommandExamples({ "<CheckNotNull name='type:property' onErrorMsg='type:string'/>" }) 344 public void runCommandCheckNotNull(final Node aCurrentAction) throws Throwable { 345 final String theName = replaceProperties(aCurrentAction.getAttribute("name")); 346 final String theErrorMsg = replaceProperties(aCurrentAction.getAttribute("onErrorMsg")); 347 Object theValue = getVariableValue(theName); 348 if (theValue != null && theValue instanceof String[] && ((String[]) theValue).length == 0) { 349 theValue = null; 350 } 351 try { 352 TestCase.assertNotNull(theErrorMsg, theValue); 353 } catch (final Throwable e) { 354 taskNode(aCurrentAction, false); 355 throw e; 356 } 357 } 358 359 @CommandExamples({ "<Choice name='type:property'>" + "<Task name='type:string'>...</Task></Choice>", 360 "<Choice name='type:property' mode='enum:default|table'>" + "<Task name='type:string'>...</Task></Choice>", 361 "<Choice name='type:property' mode='enum:default|table' type='setup'>" 362 + "<Task name='type:string'/></Choice>", 363 "<Choice name='type:property' mode='enum:default|table' type='call-task'>" 364 + "<Task name='type:recipe'/></Choice>", 365 "<Choice name='type:property'><Task name='type:string'>...</Task><Started>...</Started></Choice>", 366 "<Choice name='type:property' runAllTaskName='type:string' mode='enum:default|table' type='enum:default|call-task'>" 367 + "<Task name='type:string'>...</Task><Started>...</Started></Choice>" }) 368 public void runCommandChoice(final Node aCurrentAction) throws Throwable { 369 370 final String nameOfChoice = replaceProperties(aCurrentAction.getAttribute("name")); 371 Object preset = getVariableValue(nameOfChoice); 372 373 String description = replaceProperties(aCurrentAction.getAttribute("description")); 374 375 final String theRunAllTaskName = replaceProperties(aCurrentAction.getAttribute("runAllTaskName")); 376 String type = replaceProperties(aCurrentAction.getAttribute("type")); 377 final boolean callType = "call-task".equals(type); 378 379 final Map<String, Node> tablesNodes = new LinkedHashMap<>(); 380 381 final String theMode = replaceProperties(aCurrentAction.getAttribute("mode")); 382 383 final Node[] taskNodes = aCurrentAction.getNodes("Task"); 384 for (int i = 0; i < taskNodes.length; i++) { 385 final String name = replaceProperties(taskNodes[i].getAttribute("name")); 386 387 if ("table".equals(theMode) && name != null && name.equals(theRunAllTaskName)) { 388 continue; 389 } 390 391 if (!callType || getListener().getManager().getTestPath(name) != null) { 392 tablesNodes.put(name, taskNodes[i]); 393 } 394 } 395 396 final String[] names = tablesNodes.keySet().toArray(new String[tablesNodes.size()]); 397 398 debug("Task list: " + nameOfChoice); 399 400 final List<String> activeNodes = new ArrayList<>(); 401 402 if ("table".equals(theMode) || "hidden".equals(theMode)) { 403 404 final String exceptionIgnore = replaceProperties(aCurrentAction.getAttribute("exception")); 405 boolean startsWith = false; 406 if (exceptionIgnore != null) { 407 startsWith = exceptionIgnore.startsWith("ignore"); 408 } 409 410 ChoiceTaskRunner choiceRunner = new ChoiceTaskRunner(nameOfChoice, preset == null); 411 boolean visible = !"hidden".equals(theMode); 412 final MultiTaskRunDialog inputSelectChoice = this.recipeListener.getManager().tasksChoice(choiceRunner, 413 names, startsWith, preset, this, visible); 414 415 try { 416 final Iterator<String> selectedTasks = inputSelectChoice.getSelectedTasks(); 417 418 List<String> selectedTaskNames = new ArrayList<>(); 419 while (selectedTasks.hasNext()) { 420 String selectedTask = selectedTasks.next(); 421 activeNodes.add(selectedTask); 422 423 selectedTaskNames.add(selectedTask); 424 inputSelectChoice.begin(selectedTask); 425 426 try { 427 Node action = tablesNodes.get(selectedTask); 428 startCommandInformation(action); 429 430 if (callType) { 431 runCommandTask(action); 432 } else { 433 taskNode(action, false); 434 } 435 inputSelectChoice.end(selectedTask, true); 436 437 } catch (final Throwable e) { 438 inputSelectChoice.end(selectedTask, false); 439 if (inputSelectChoice.isExceptionIgnore() == false) { 440 throw e; 441 } 442 } 443 } 444 445 if ("setup".equals(type)) { 446 setVariableValue(nameOfChoice, selectedTaskNames.toArray(new String[selectedTaskNames.size()])); 447 } 448 } finally { 449 inputSelectChoice.done(); 450 if (inputSelectChoice.isStarted()) { 451 Node[] nodes = aCurrentAction.getNodes("Started"); 452 for (Node node : nodes) { 453 taskNode(node); 454 } 455 } 456 } 457 458 } else { 459 if (this.recipeListener.getManager() != null) { 460 boolean notifyMe = this.recipeListener.isNotifyMe(); 461 description = StringUtils.defaultString(description, nameOfChoice); 462 final String inputSelectChoice = this.recipeListener.getManager().inputChoice(nameOfChoice, description, 463 names, ObjectUtils.toString(preset, null), this, notifyMe); 464 if (inputSelectChoice != null) { 465 activeNodes.add(inputSelectChoice); 466 } 467 } 468 469 debug("Select task: " + (activeNodes != null ? activeNodes : "<not chosen>")); 470 471 if (!activeNodes.isEmpty()) { 472 if (activeNodes.get(0).equals(theRunAllTaskName) == false) { 473 474 if (callType) { 475 runCommandTask(tablesNodes.get(activeNodes.get(0))); 476 } else { 477 taskNode(tablesNodes.get(activeNodes.get(0))); 478 } 479 480 } else { 481 for (int i = 0; i < names.length; i++) { 482 final String theActiveNode = names[i]; 483 if (theActiveNode.equals(theRunAllTaskName) == false) { 484 if (callType) { 485 runCommandTask(tablesNodes.get(theActiveNode)); 486 } else { 487 taskNode(tablesNodes.get(theActiveNode)); 488 } 489 } 490 } 491 } 492 } 493 } 494 495 setVariableValue(nameOfChoice, activeNodes); 496 } 497 498 @CommandExamples({ "<Runnable name='type:property' threadLog='type:boolean'>...code...</Runnable>" }) 499 public void runCommandRunnable(final Node aCurrentAction) throws CommandException { 500 final String theName = replaceProperties(aCurrentAction.getAttribute("name")); 501 String theNameThreads = getTestName(aCurrentAction); 502 if (theNameThreads == null) { 503 theNameThreads = "Thread"; 504 } 505 506 boolean threadLog = Boolean.valueOf(StringUtils.defaultIfBlank(attr(aCurrentAction, "threadLog"), "false")); 507 ILogger theLog = this.log; 508 if (threadLog) { 509 theLog = this.recipeListener.createLog(theNameThreads, false); 510 } 511 512 Node task = new Node("Task"); 513 task.setAttribute("description", aCurrentAction.getAttribute("name")); 514 task.addAll(aCurrentAction); 515 final TaskProcessorThread runnable = new TaskProcessorThread(this, task, getBaseDir(), theLog); 516 setVariableValue(theName, runnable); 517 } 518 519 @CommandExamples({ "<Sort name='type:property' type='enum:natural|random' />" }) 520 public void runCommandSort(final Node aCurrentAction) { 521 final String theName = replaceProperties(aCurrentAction.getAttribute("name")); 522 String theType = replaceProperties(aCurrentAction.getAttribute("type")); 523 if (theType == null) { 524 theType = "natural"; 525 } 526 527 final Object theObject = getVariableValue(theName); 528 if (theObject instanceof String[]) { 529 String[] theArray = (String[]) theObject; 530 if ("natural".equals(theType)) { 531 Arrays.sort(theArray); 532 } else if ("random".equals(theType)) { 533 theArray = randomSort(theArray); 534 } else { 535 throw new IllegalArgumentException("Sort type should be following value: natural, random."); 536 } 537 setVariableValue(theName, theArray); 538 } 539 } 540 541 @CommandExamples({ "<CheckInArray value='type:string' array='type:property' onErrorMsg='type:string'/>" }) 542 public void runCommandCheckInArray(final Node aCurrentAction) throws Throwable { 543 final String theValue = replaceProperties(aCurrentAction.getAttribute("value")); 544 final String theArrayName = replaceProperties(aCurrentAction.getAttribute("array")); 545 final String theErrorMsg = replaceProperties(aCurrentAction.getAttribute("onErrorMsg")); 546 final Object theObject = checkForArray(getVariableValue(theArrayName)); 547 548 if (theObject != null && theObject instanceof Object[]) { 549 final Object[] theValues = (Object[]) theObject; 550 Arrays.sort(theValues); 551 try { 552 TestCase.assertTrue(theErrorMsg, Arrays.binarySearch(theValues, theValue) >= 0); 553 } catch (final Throwable e) { 554 taskNode(aCurrentAction, false); 555 throw e; 556 } 557 } 558 } 559 560 @CommandDescription("The `Tokenizer` command is used to split a variable by the name specified in the `name` attribute. " 561 + "The variable's value will be split by the delimiter specified in the `delim` attribute. The default separator is the `;` character." 562 + "The result will be an array of strings.") 563 @CommandExamples({ "<Tokenizer name='type:property' delim='type:string'/>" }) 564 public void runCommandTokenizer(final Node aCurrentVar) { 565 final Object value = attrValue(aCurrentVar, "name"); 566 String theDelimAttribut = aCurrentVar.getAttribute("delim"); 567 if (theDelimAttribut == null) { 568 theDelimAttribut = ";"; 569 } 570 if (value != null) { 571 String theLine = null; 572 if (value instanceof String) { 573 theLine = (String) value; 574 } else if (value instanceof String[] && ((String[]) value).length == 1) { 575 theLine = ((String[]) value)[0]; 576 } 577 final ArrayList<String> theArrayList = new ArrayList<String>(); 578 if (theLine != null) { 579 final StringTokenizer theStringTokenizer = new StringTokenizer(theLine, theDelimAttribut); 580 while (theStringTokenizer.hasMoreTokens()) { 581 theArrayList.add(theStringTokenizer.nextToken()); 582 } 583 final String[] theArray = new String[theArrayList.size()]; 584 for (int i = 0; i < theArrayList.size(); i++) { 585 if (isStopped()) { 586 break; 587 } 588 theArray[i] = theArrayList.get(i); 589 } 590 setVariableValue(attr(aCurrentVar, "name"), theArray); 591 } else { 592 debug("Tokenized empty string is ignored."); 593 } 594 } 595 } 596 597 @CommandExamples({ "<Date name='type:string' format='type:string' />", 598 "<Date name='type:string' format='type:string' source='type:property' sformat='type:string' />", 599 "<Date name='type:string' format='type:string' shift='type:time' />" }) 600 public void runCommandDate(final Node aCurrentAction) throws Throwable { 601 final String name = replaceProperties(aCurrentAction.getAttribute("name")); 602 String value = replaceProperties(aCurrentAction.getAttribute("value")); 603 if (value == null) { 604 String source = aCurrentAction.getAttribute("source"); 605 if (source != null) { 606 value = (String) getVariableValue(replaceProperties(source)); 607 } 608 } 609 610 if (value == null) { 611 Object variableValue = getVariableValue(name); 612 if (variableValue != null) { 613 value = ObjectUtils.toString(variableValue); 614 } 615 } 616 617 String format = replaceProperties(aCurrentAction.getAttribute("format")); 618 String sformat = StringUtils.defaultString(replaceProperties(aCurrentAction.getAttribute("sformat")), format); 619 620 SimpleDateFormat dateFor = null; 621 if (sformat != null && !"ms".equals(sformat)) { 622 dateFor = new SimpleDateFormat(sformat); 623 dateFor.setTimeZone(TimeZone.getTimeZone("UTC")); 624 } 625 String stringDate = null; 626 if (value == null) { 627 if (dateFor != null) { 628 stringDate = dateFor.format(new Date()); 629 } else { 630 stringDate = Long.toString(new Date().getTime()); 631 } 632 value = stringDate; 633 } 634 635 final String shift = replaceProperties(StringUtils.trim(aCurrentAction.getAttribute("shift"))); 636 637 Date date; 638 if (dateFor != null) { 639 date = dateFor.parse(value); 640 } else { 641 date = new Date(Long.parseLong(value)); 642 } 643 644 if (shift != null) { 645 Calendar c = Calendar.getInstance(); 646 c.setTime(date); 647 String substring = StringUtils.substring(shift, 0, shift.length() - 1); 648 int shiftValue = Integer.parseInt(substring); 649 switch (shift.charAt(shift.length() - 1)) { 650 case 's': 651 c.add(Calendar.SECOND, shiftValue); 652 break; 653 case 'm': 654 c.add(Calendar.MINUTE, shiftValue); 655 break; 656 case 'h': 657 c.add(Calendar.HOUR, shiftValue); 658 break; 659 case 'd': 660 c.add(Calendar.DAY_OF_MONTH, shiftValue); 661 break; 662 case 'w': 663 c.add(Calendar.WEEK_OF_MONTH, shiftValue); 664 break; 665 case 'M': 666 c.add(Calendar.MONTH, shiftValue); 667 break; 668 case 'Y': 669 c.add(Calendar.YEAR, shiftValue); 670 break; 671 } 672 673 date = c.getTime(); 674 } 675 676 if (format != null && !"ms".equals(format)) { 677 dateFor = new SimpleDateFormat(format); 678 dateFor.setTimeZone(TimeZone.getTimeZone("UTC")); 679 stringDate = dateFor.format(date); 680 } else { 681 stringDate = Long.toString(date.getTime()); 682 } 683 684 setVariableValue(name, stringDate); 685 } 686 687 @CommandExamples({ "<Wait delay='type:time'/>", "<Wait/>" }) 688 public void runCommandWait(final Node aCurrentAction) { 689 final long theValue = attrTime(aCurrentAction, "delay", "0"); 690 if (theValue > 0) { 691 quietWait(theValue); 692 } else { 693 pause(); 694 } 695 } 696 697 @CommandDescription("The View command defines a view component to precentation output date provided by Out command.") 698 @CommandExamples({ "<View name='type:string' reuse='type:boolean' type='type:view' />" }) 699 public void runCommandView(final Node aCurrentAction) throws Throwable { 700 this.recipeListener.runCommandView(replaceAttributes(aCurrentAction)); 701 } 702 703 @CommandExamples({ "<Formater name='type:property' type='enum:xml|http-get-request'/>" }) 704 public void runCommandFormater(final Node aCurrentAction) throws Throwable { 705 final String theNameAttribut = replaceProperties(aCurrentAction.getAttribute("name")); 706 final String theTypeAttribut = replaceProperties(aCurrentAction.getAttribute("type")); 707 Object theValue = getVariableValue(theNameAttribut); 708 if (theValue instanceof String) { 709 if ("json".equals(theTypeAttribut)) { 710 theValue = AEUtils.format(ObjectUtils.toString(theValue)); 711 } else { 712 theValue = new String[] { (String) theValue }; 713 } 714 715 setVariableValue(theNameAttribut, theValue); 716 } 717 if (theValue instanceof String[]) { 718 final String[] theValueArray = (String[]) theValue; 719 for (int i = 0; i < theValueArray.length; i++) { 720 if (isStopped()) { 721 break; 722 } 723 if ("xml".equals(theTypeAttribut)) { 724 final EasyParser theParser = new EasyParser(); 725 String theCurrentValue = theValueArray[i]; 726 theCurrentValue = prepareXml(theCurrentValue); 727 final Node object = theParser.getObject(theCurrentValue); 728 theValueArray[i] = object.getXMLText(); 729 } 730 } 731 if (theValueArray.length > 2) { 732 setVariableValue(theNameAttribut, theValueArray); 733 } else if (theValueArray.length == 1) { 734 setVariableValue(theNameAttribut, theValueArray[0]); 735 } else { 736 setVariableValue(theNameAttribut, null); 737 } 738 } 739 } 740 741 @CommandExamples({ "<Trim name='type:property'/>" }) 742 public void runCommandTrim(final Node aCurrentAction) throws Throwable { 743 String name = replaceProperties(aCurrentAction.getAttribute("name")); 744 Object value = getVariableValue(name); 745 if (value instanceof String) { 746 value = StringUtils.trimToNull(ObjectUtils.toString(value)); 747 } else if (value instanceof String[]) { 748 String[] array = (String[]) value; 749 if (array.length == 0) { 750 value = null; 751 } else { 752 List<String> list = new ArrayList<>(); 753 for (String object : array) { 754 String val = ObjectUtils.toString(object); 755 if (StringUtils.isNotBlank(val)) { 756 list.add(object); 757 } 758 } 759 value = list.toArray(new String[list.size()]); 760 } 761 } else if (value instanceof List) { 762 @SuppressWarnings("unchecked") 763 List<String> array = (List<String>) value; 764 if (array.size() == 0) { 765 value = null; 766 } else { 767 List<String> list = new ArrayList<>(); 768 for (String object : array) { 769 String val = ObjectUtils.toString(object); 770 if (StringUtils.isNotBlank(val)) { 771 list.add(object); 772 } 773 } 774 value = list.toArray(new String[list.size()]); 775 } 776 } 777 applyResult(aCurrentAction, name, value); 778 } 779 780 @CommandExamples({ "<Xslt name='type:property' xml='type:property' xsl='type:property'/>" }) 781 public void runCommandXslt(final Node aCurrentAction) throws Throwable { 782 final String theNameAttribut = attr(aCurrentAction, "name"); 783 final String theXmlAttribut = ObjectUtils.toString(attrValue(aCurrentAction, "xml")); 784 final String theXslAttribut = ObjectUtils.toString(attrValue(aCurrentAction, "xsl")); 785 final StringWriter theStringWriter = new StringWriter(); 786 787 if (theXmlAttribut == null || theXmlAttribut.trim().length() == 0 || "null".equals(theXmlAttribut)) { 788 debug("Xml document is empty, transform disable."); 789 return; 790 } 791 792 if (theXslAttribut == null || theXslAttribut.trim().length() == 0 || "null".equals(theXslAttribut)) { 793 debug("Xsl document is empty, transform disable."); 794 return; 795 } 796 797 try { 798 final Source xslSource = new StreamSource(new StringReader(theXslAttribut)); 799 final Transformer transformer = TransformerFactory.newInstance().newTransformer(xslSource); 800 final Source xmlSource = new StreamSource(new StringReader(theXmlAttribut)); 801 802 transformer.transform(xmlSource, new StreamResult(theStringWriter)); 803 804 } catch (final Throwable e) { 805 debug("XML Data to transformation:\n" + theXmlAttribut); 806 debug("XSL:\n" + theXslAttribut); 807 this.log.error("Xsl transformation is failed.", e); 808 throw e; 809 } 810 811 setVariableValue(theNameAttribut, theStringWriter.toString()); 812 } 813 814 @CommandDescription("`Out` command use for output a variable value or text.\r\n" 815 + "- to output the value of a variable, you must use the `name` attribute with the name of the variable. " 816 + "- to output text, you should use inner text.\r\n" 817 + "- `level` attribute defines log level, default log level = `info`\r\n" 818 + "- `type` attribute sets type for log view.\r\n" 819 + "- `file` attribute is used for output data in the file.\r\n" 820 + "`Out` command can be used for output information to the special output view, for this need to use an optional `view` " 821 + "attribute with a view name. The view should be defined by the `View` command.") 822 @CommandExamples({ "<Out name='type:property' />", "<Out view='type:string'> ... </Out>", 823 "<Out name='type:property' type='enum:txt|html|xml|json|~json|url|path|uri|csv'/>", 824 "<Out view='type:string'>...</Out>", "<Out name='type:property' level='enum:info|debug|warn|error'/>", 825 "<Out name='type:property' level='enum:info|debug|warn|error'> ... </Out>", 826 "<Out level='enum:info|debug|warn|error'>... $var{...} ... </Out>", 827 "<Out file='type:path' append='enum:true|false' encoding='UTF-8'/>" }) 828 public void runCommandOut(final Node command) throws UnsupportedEncodingException, IOException { 829 final String name = attr(command, "name"); 830 final String description = attr(command, "description"); 831 String theOutFileNameAttribut = attr(command, "file"); 832 833 String logLevel = command.getAttribute("level"); 834 logLevel = replaceProperties(logLevel); 835 836 if (logLevel == null) { 837 logLevel = StringUtils.defaultIfBlank(getVariableString(DEFAULT_LOG_LEVEL_NAME), "info"); 838 } 839 840 String type = attr(command, "type"); 841 842 FileOutputStream theFileOutputStream = null; 843 try { 844 if (theOutFileNameAttribut != null) { 845 String theAppendAttribut = replaceProperties(command.getAttribute("append")); 846 if (theAppendAttribut == null) { 847 theAppendAttribut = "true"; 848 } 849 theOutFileNameAttribut = replaceProperties(theOutFileNameAttribut); 850 final File file = getFile(theOutFileNameAttribut); 851 file.getParentFile().mkdirs(); 852 theFileOutputStream = new FileOutputStream(file, "true".equals(theAppendAttribut)); 853 } 854 String theEncoding = replaceProperties(command.getAttribute("encoding")); 855 if (theEncoding == null) { 856 theEncoding = "UTF-8"; 857 } 858 859 String theTextOut = null; 860 String varName = name; 861 final Node[] nodes = command.getTextNodes(); 862 863 if (nodes.length > 0) { 864 Node node = nodes[0]; 865 theTextOut = replaceProperties(node.getText()); 866 } else if (command.size() > 0) { 867 String innerXMLText = command.getInnerXMLText(); 868 Node node = new EasyParser().getObject(innerXMLText); 869 EasyUtils.removeTagId(node); 870 theTextOut = replaceProperties(node.toString()); 871 } 872 873 if (theTextOut != null) { 874 if (name != null) { 875 varName = theTextOut + ": " + varName; 876 } 877 if (theFileOutputStream != null) { 878 theFileOutputStream.write(theTextOut.getBytes(theEncoding)); 879 } 880 881 Properties attributes = replaceAttributes(command); 882 this.recipeListener.outToView(this, attributes, theTextOut); 883 } 884 885 if (name != null) { 886 Object theValue = getVariableValue(name); 887 888 if (theValue instanceof byte[]) { 889 if (theFileOutputStream != null) { 890 final byte[] value = (byte[]) theValue; 891 theFileOutputStream.write(value); 892 theFileOutputStream.close(); 893 return; 894 } else { 895 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, theValue, type); 896 return; 897 } 898 } 899 900 if (theValue == null) { 901 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, null, type); 902 903 } else if (theValue instanceof String) { 904 if (theFileOutputStream == null) { 905 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, theValue.toString(), 906 type); 907 } else { 908 theFileOutputStream.write(theValue.toString().getBytes(theEncoding)); 909 } 910 911 this.recipeListener.outToView(this, replaceAttributes(command), theValue); 912 913 } else if (theValue instanceof String[]) { 914 final StringBuffer theBuffer = new StringBuffer(); 915 916 final String[] theValueArray = (String[]) theValue; 917 for (int i = 0; i < theValueArray.length; i++) { 918 if (isStopped()) { 919 break; 920 } 921 922 if (theBuffer.length() > 0) { 923 theBuffer.append("\n"); 924 } 925 theBuffer.append(theValueArray[i]); 926 927 Properties params = replaceAttributes(command); 928 String msg = theValueArray[i]; 929 this.recipeListener.outToView(this, params, msg); 930 } 931 if (theFileOutputStream == null) { 932 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, theBuffer.toString(), 933 type); 934 } else { 935 theFileOutputStream.write(theBuffer.toString().getBytes(theEncoding)); 936 } 937 } else { 938 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, theValue, type); 939 this.recipeListener.outToView(this, replaceAttributes(command), theValue); 940 } 941 } else { 942 outputLog(StringUtils.defaultIfEmpty(description, varName), logLevel, theTextOut, type); 943 } 944 } finally { 945 if (theFileOutputStream != null) { 946 theFileOutputStream.flush(); 947 theFileOutputStream.close(); 948 } 949 } 950 } 951 952 @CommandDescription("The <About> information tag (non-executable) is used to display detailed information about a recipe. " 953 + "This tag includes a <description> element, which provides a description of the recipe. " 954 + "The description can be written in plain text or in Markdown format and displayed as additional information on the menu page. " 955 + "In Markdown format, avoid indenting text lines to properly recognize the description type. " 956 + "Each text line must be properly formatted and less than 120 characters long.") 957 @CommandExamples({ "<About><description>...</description></About>", 958 "<About><attach><file name='type:url|path' /></attach></About>", 959 "<About><author name='type:string' email='type:string' messager='type:string' phone='type:string'/>" 960 + "<attach><file name='type:url|path' width='type:number' height='type:number'/></attach></About>" }) 961 public void runCommandAbout(final Node aCurrentAction) throws Throwable { 962 } 963 964 @CommandExamples({ "<Exist name='type:property' text='type:string' value='type:string'/>", 965 "<Exist name='type:property' array='type:property' value='type:string'/>" }) 966 public void runCommandExist(final Node aCurrentAction) throws Throwable { 967 final String theName = replaceProperties(aCurrentAction.getAttribute("name")); 968 final String theText = replaceProperties(aCurrentAction.getAttribute("text")); 969 final String theArrayName = replaceProperties(aCurrentAction.getAttribute("array")); 970 final String theValue = replaceProperties(aCurrentAction.getAttribute("value")); 971 972 if (theArrayName != null) { 973 final Object variableValue = getVariableValue(theArrayName); 974 if (variableValue instanceof String[]) { 975 final String[] variableValueArray = (String[]) variableValue; 976 for (int i = 0; i < variableValueArray.length; i++) { 977 if (theValue.equals(variableValueArray[i])) { 978 setVariableValue(theName, "true"); 979 return; 980 } 981 } 982 } 983 984 if (variableValue instanceof String) { 985 final String value = (String) variableValue; 986 if (theValue.equals(value)) { 987 setVariableValue(theName, "true"); 988 return; 989 } 990 } 991 setVariableValue(theName, "false"); 992 } else { 993 if (theText == null || theValue == null || theName == null) { 994 return; 995 } 996 final int indexOf = theText.indexOf(theValue); 997 setVariableValue(theName, indexOf >= 0 ? "true" : "false"); 998 } 999 } 1000 1001 @CommandExamples({ "<Load name='type:property' init='console'/>", 1002 "<Load name='type:property' init='console'/>...</Load>", "<Load name='type:property' file='type:path'/>", 1003 "<Load name='type:property' file='type:path' timeout='type:time' mode='enum:default|noreplace|escapeJS|bytes'/>", 1004 "<Load name='type:property' url='type:url' timeout='type:time' mode='enum:default|noreplace|escapeJS|bytes'/>" }) 1005 public void runCommandLoad(final Node action) throws Throwable { 1006 final boolean noreplace = "noreplace".equals(action.getAttribute("mode")); 1007 final boolean isArray = "array".equals(action.getAttribute("mode")); 1008 final boolean defaultMode = "default".equals(action.getAttribute("mode")); 1009 final boolean escapeJSValue = "escapeJS".equals(action.getAttribute("mode")); 1010 final boolean bytes = "bytes".equals(action.getAttribute("mode")); 1011 final boolean base64 = "base64".equals(action.getAttribute("mode")); 1012 1013 String theEncoding = replaceProperties(action.getAttribute("encoding")); 1014 if (theEncoding == null) { 1015 theEncoding = "UTF-8"; 1016 } 1017 1018 final String name = replaceProperties(action.getAttribute("name")); 1019 int timeout = (int) attrTime(action, "timeout", "0"); 1020 1021 final String theUrl = replaceProperties(action.getAttribute("url")); 1022 if (theUrl != null) { 1023 URL url = new URL(theUrl); 1024 long startTime = System.currentTimeMillis(); 1025 while (timeout == 0 || System.currentTimeMillis() < startTime + timeout) { 1026 if (isStopped()) { 1027 break; 1028 } 1029 try { 1030 URLConnection openConnection = url.openConnection(); 1031 openConnection.setReadTimeout(timeout); 1032 byte[] byteArray = IOUtils.toByteArray(openConnection.getInputStream()); 1033 if (escapeJSValue) { 1034 String data = StringEscapeUtils.escapeJavaScript(new String(byteArray)); 1035 setVariableValue(name, data); 1036 } else { 1037 setVariableValue(name, new String(byteArray, theEncoding)); 1038 } 1039 return; 1040 1041 } catch (IOException e) { 1042 if (timeout == 0) { 1043 throw e; 1044 } 1045 quietWait(200); 1046 } 1047 } 1048 } 1049 1050 String filePath = (String) attr(action, "file"); 1051 final boolean theDialog = isActiveInitFor(action, "console"); 1052 1053 if (theDialog) { 1054 File theFile = null; 1055 if (filePath != null) { 1056 theFile = getFile(filePath); 1057 } 1058 filePath = this.recipeListener.getManager().inputFile(name, null, theFile, log, this); 1059 } 1060 1061 if (filePath != null) { 1062 if (base64 || bytes) { 1063 File file = getFile(filePath); 1064 debug("Loading file: " + file); 1065 if (file.exists()) { 1066 try (FileInputStream input = new FileInputStream(file)) { 1067 Object byteArray = IOUtils.toByteArray(input); 1068 if (base64) { 1069 byteArray = Base64.getEncoder().encodeToString((byte[]) byteArray); 1070 } 1071 setVariableValue(name, byteArray); 1072 } 1073 } else { 1074 throw new FileNotFoundException(file.getAbsolutePath()); 1075 } 1076 return; 1077 } 1078 1079 int iterations = timeout / 1000; 1080 if (iterations == 0) { 1081 iterations = 1; 1082 } 1083 for (int i = 0; i < iterations; i++) { 1084 if (isStopped()) { 1085 break; 1086 } 1087 try { 1088 1089 if (!action.isEmpty()) { 1090 File file = getFile(filePath); 1091 try (BufferedReader reader = new BufferedReader(new FileReader(file))) { 1092 String line; 1093 while ((line = reader.readLine()) != null) { 1094 if (this.breakFlag > 0) { 1095 break; 1096 } 1097 setVariableValue(name, line); 1098 taskNode(action, false); 1099 } 1100 } finally { 1101 if (this.breakFlag > 0) { 1102 breakFlag--; 1103 } 1104 } 1105 1106 } else { 1107 1108 String text = AEUtils.loadResource(filePath, getBaseDir(), theEncoding); 1109 if (!noreplace) { 1110 text = replaceProperties(text, defaultMode); 1111 } 1112 1113 if (escapeJSValue) { 1114 String data = StringEscapeUtils.escapeJavaScript(text); 1115 setVariableValue(name, data); 1116 } else { 1117 Object val = text; 1118 if (isArray) { 1119 val = StringUtils.split(text, "\n\r"); 1120 } 1121 1122 setVariableValue(name, val); 1123 } 1124 } 1125 1126 break; 1127 1128 } catch (final FileNotFoundException e) { 1129 if (timeout == 0) { 1130 throw e; 1131 } 1132 quietWait(1000); 1133 } catch (final Exception e) { 1134 throw e; 1135 } 1136 } 1137 } else { 1138 if (isActiveInitFor(action, "mandatory")) { 1139 stop(); 1140 } 1141 } 1142 1143 } 1144 1145 @CommandExamples({ "<Remove name='type:property'/>", "<Remove file='type:path'/>", 1146 "<Remove name='type:property' history='type:boolean'/>" }) 1147 public void runCommandRemove(final Node aCurrentAction) throws Throwable { 1148 String theFileName = aCurrentAction.getAttribute("file"); 1149 if (theFileName != null) { 1150 theFileName = replaceProperties(theFileName); 1151 final File theFile = getFile(theFileName); 1152 if (theFile.isDirectory()) { 1153 FileUtils.deleteDirectory(theFile); 1154 } else { 1155 if (theFile.delete() == false) { 1156 new Exception("File '" + theFile.getAbsolutePath() + "' is not deleted."); 1157 } 1158 } 1159 } 1160 String theVarName = aCurrentAction.getAttribute("name"); 1161 if (theVarName != null) { 1162 theVarName = replaceProperties(theVarName); 1163 setVariableValue(theVarName, null); 1164 if (Boolean.parseBoolean((attr(aCurrentAction, "history")))) { 1165 AEWorkspace.getInstance().setDefaultUserConfiguration(".inputValue." + theVarName, null); 1166 } 1167 } 1168 } 1169 1170 @CommandDescription("Load the web page by `url`. The response is stored in the variable defined by the `name` attribute.\r\n" 1171 + "The optional `auth` attribute set the 'Authorization' header of request.") 1172 @CommandExamples({ "<Get name='type:property' url='type:url' /> ", 1173 "<Get name='type:property' url='type:url' timeout='type:time'/> ", 1174 "<Get name='type:property' url='type:url' auth='type:string' mediaType='type:string' retries='1' timeout='type:time'/> " }) 1175 public void runCommandGet(final Node action) throws Throwable { 1176 1177 String mediaTypeStr = attr(action, "mediaType"); 1178 mediaTypeStr = StringUtils.defaultIfEmpty(mediaTypeStr, "application/json"); 1179 1180 String url = attr(action, "url"); 1181 String auth = attr(action, "auth"); 1182 int retries = Integer.parseInt(attr(action, "retries", "1")); 1183 1184 Builder builder = new okhttp3.Request.Builder().url(url).get(); 1185 1186 if (mediaTypeStr != null) { 1187 builder.addHeader("Content-Type", mediaTypeStr); 1188 } 1189 if (auth != null) { 1190 builder.addHeader("Authorization", auth); 1191 } 1192 1193 okhttp3.Request request = builder.build(); 1194 1195 for (int i = 0; i < retries; i++) { 1196 if (isStopped()) { 1197 break; 1198 } 1199 1200 try { 1201 okhttp3.OkHttpClient.Builder newBuilder = new OkHttpClient().newBuilder(); 1202 long timeout = attrTime(action, "timeout", "0"); 1203 if (timeout > 0) { 1204 newBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS); 1205 newBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS); 1206 newBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS); 1207 } 1208 OkHttpClient client = newBuilder.build(); 1209 1210 try (Response response = client.newCall(request).execute()) { 1211 String value = new String(response.body().bytes(), "UTF-8"); 1212 String name = attr(action, "name"); 1213 if (name != null) { 1214 setVariableValue(name, value); 1215 } 1216 } 1217 1218 break; 1219 } catch (SocketTimeoutException e) { 1220 if (i == 2) { 1221 throw e; 1222 } 1223 } 1224 } 1225 } 1226 1227 @CommandDescription("Post the body defined by the `body` attribute to `url`. " 1228 + "The response is stored in the variable defined by the `name` attribute. " 1229 + "If the body is not simple, you should create a special variable and pass its value via $var{}.\r\n" 1230 + "The optional `auth` attribute set the 'Authorization' header of request.") 1231 @CommandExamples({ "<Post name='type:property' url='type:url' body='type:string'/> ", 1232 "<Post name='type:property' url='type:url' body='type:string' timeout='type:time'/> ", 1233 "<Post name='type:property' url='type:url' auth='type:string' body='type:string' mediaType='type:string' timeout='type:time'/> " }) 1234 public void runCommandPost(final Node aCurrentAction) throws Throwable { 1235 String mediaTypeStr = attr(aCurrentAction, "mediaType"); 1236 mediaTypeStr = StringUtils.defaultIfEmpty(mediaTypeStr, "application/json"); 1237 1238 String url = attr(aCurrentAction, "url"); 1239 String auth = attr(aCurrentAction, "auth"); 1240 String bodyStr = StringUtils.defaultIfEmpty(replaceProperties(aCurrentAction.getAttribute("body")), ""); 1241 1242 MediaType mediaType = MediaType.parse(mediaTypeStr); 1243 okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType, bodyStr); 1244 1245 Builder addHeader = new okhttp3.Request.Builder().url(url).method("POST", body).addHeader("Content-Type", 1246 mediaTypeStr); 1247 if (auth != null) { 1248 addHeader = addHeader.addHeader("Authorization", auth); 1249 } 1250 okhttp3.Request request = addHeader.addHeader("Accept", "*/*").build(); 1251 okhttp3.OkHttpClient.Builder newBuilder = new OkHttpClient().newBuilder(); 1252 1253 long timeout = attrTime(aCurrentAction, "timeout", "0"); 1254 if (timeout > 0) { 1255 newBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS); 1256 newBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS); 1257 newBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS); 1258 } 1259 OkHttpClient client = newBuilder.build(); 1260 try (Response response = client.newCall(request).execute()) { 1261 String value = new String(response.body().bytes(), "UTF-8"); 1262 1263// if(response.code() == 404) { 1264// 1265// } 1266 1267 String name = attr(aCurrentAction, "name"); 1268 if (name != null) { 1269 setVariableValue(name, value); 1270 } 1271 } 1272 } 1273 1274 @CommandDescription("This method allows you to send either HTTP(S) requests or raw socket messages, " 1275 + "with support for parameters, headers, authentication, and response handling." 1276 + "The `Request` command executes a network request based on the configuration attributes:\r\n" 1277 + "- `method`: Specifies the type of network request to perform. Common values are `get` for HTTP GET requests, " 1278 + "`post` for HTTP POST requests, or `socket` for raw socket communication.\r\n" 1279 + "- `request`: is the name of the request map variable which contains the data to be sent with the request. " 1280 + "For HTTP requests, this may include the body and headers. For socket communication, " 1281 + "it can be a string or byte array representing the message to send. \r\n" 1282 + "- `response` is the name of the variable where the response data will be stored after the request is executed. " 1283 + "This allows later access to the result of the network operation.\r\n" 1284 + "- `host` is the target URL or host address for the network request. " 1285 + "For HTTP(S), this is the full URL. For socket communication, " 1286 + "it is typically in the format `hostname:port`\r\n" 1287 + "- `timeout` is the maximum time to wait for the network request to complete before aborting.\r\n" 1288 + "- `userName` is the username used for HTTP Basic Authentication, if required by the target server.\r\n" 1289 + "- `password` is the password used for HTTP Basic Authentication, paired with `userName`.\r\n") 1290 @CommandExamples({ 1291 "<Request method='enum:socket|get|post' request='type:property[Map{header;body}]' " 1292 + "response='type:property[Map{status;body}]' host='type:url' timeout='type:time'/>", 1293 "<Request method='get' response='type:property[Map{status;body}]' host='type:url' " 1294 + "userName='type:string' password='type:string'><param name='type:string' value='type:string'/></Request>" }) 1295 public void runCommandRequest(final Node aCurrentAction) throws Throwable { 1296 Object request = attrValue(aCurrentAction, "request"); 1297 final String responseName = attr(aCurrentAction, "response"); 1298 final String method = StringUtils.defaultIfEmpty(attr(aCurrentAction, "method"), "socket"); 1299 String theUrl = attr(aCurrentAction, "host"); 1300 1301 String userName = attr(aCurrentAction, "userName"); 1302 String password = attr(aCurrentAction, "password"); 1303 HttpClientContext localContext = null; 1304 if (userName != null && password != null) { 1305 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 1306 credentialsProvider.setCredentials(AuthScope.ANY, 1307 new UsernamePasswordCredentials(userName + ":" + password)); 1308 localContext = HttpClientContext.create(); 1309 localContext.setCredentialsProvider(credentialsProvider); 1310 } 1311 1312 HttpRequestBase httpRequest = null; 1313 1314 final Node[] paramNodes = aCurrentAction.getNodes("param"); 1315 if (paramNodes != null && paramNodes.length > 0) { 1316 List<NameValuePairImplementation> parameters = new ArrayList<>(); 1317 for (Node node : paramNodes) { 1318 String name = attr(node, "name"); 1319 String value = attr(node, "value"); 1320 1321 NameValuePairImplementation nameValuePair = new NameValuePairImplementation(name, value); 1322 parameters.add(nameValuePair); 1323 } 1324 1325 String query = URLEncodedUtils.format(parameters, "UTF-8"); 1326 theUrl = theUrl + "?" + query; 1327 } 1328 1329 if (theUrl.toLowerCase().startsWith("http")) { 1330 if ("get".equalsIgnoreCase(method)) { 1331 httpRequest = new HttpGet(theUrl); 1332 1333 } else if ("post".equalsIgnoreCase(method)) { 1334 HttpPost httpPost = new HttpPost(theUrl); 1335 if (request != null) { 1336 @SuppressWarnings("unchecked") 1337 final Map<String, Object> requestMap = (Map<String, Object>) request; 1338 Object bodyObject = requestMap.get("body"); 1339 byte[] body = null; 1340 if (bodyObject instanceof byte[]) { 1341 body = (byte[]) bodyObject; 1342 } else if (bodyObject instanceof String) { 1343 body = ((String) bodyObject).getBytes(); 1344 } 1345 httpPost.setEntity(new ByteArrayEntity(body)); 1346 } 1347 httpRequest = httpPost; 1348 1349 } 1350 int timeout = (int) attrTime(aCurrentAction, "timeout", "5000"); 1351 1352 RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout) 1353 .setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build(); 1354 httpRequest.setConfig(config); 1355 if (request != null) { 1356 @SuppressWarnings("unchecked") 1357 Object headerObj = ((Map<String, Object>) request).get("header"); 1358 if (headerObj instanceof Map) { 1359 @SuppressWarnings("unchecked") 1360 Map<String, String> header = (Map<String, String>) headerObj; 1361 if (header != null) { 1362 Set<Entry<String, String>> entrySet = header.entrySet(); 1363 for (Entry<String, String> entry : entrySet) { 1364 httpRequest.setHeader(entry.getKey(), entry.getValue()); 1365 } 1366 } 1367 } else { 1368 String header = String.valueOf(headerObj); 1369 httpRequest.setHeader(StringUtils.substringBefore(header, ":").trim(), 1370 StringUtils.substringAfter(header, ":").trim()); 1371 } 1372 } 1373 1374 CloseableHttpResponse response; 1375 CloseableHttpClient httpclient = HttpClients.createDefault(); 1376 1377 long duration = System.currentTimeMillis(); 1378 if (localContext == null) { 1379 response = httpclient.execute(httpRequest); 1380 } else { 1381 response = httpclient.execute(httpRequest, localContext); 1382 } 1383 ByteArrayOutputStream body = new ByteArrayOutputStream(); 1384 response.getEntity().writeTo(body); 1385 1386 HashMap<String, Object> aValue = new HashMap<String, Object>(); 1387 aValue.put("body", body.toByteArray()); 1388 aValue.put("status", response.getStatusLine().getStatusCode()); 1389 aValue.put("header", response.getAllHeaders()); 1390 aValue.put("duration", Long.toString(System.currentTimeMillis() - duration)); 1391 setVariableValue(responseName, aValue); 1392 1393 } else { 1394 1395 Socket socket = null; 1396 try { 1397 String host = StringUtils.substringBefore(theUrl, ":"); 1398 int port = Integer.parseInt(StringUtils.defaultIfBlank(StringUtils.substringAfter(theUrl, ":"), "80")); 1399 socket = new Socket(host, port); 1400 OutputStream outputStream = socket.getOutputStream(); 1401 if (request instanceof byte[]) { 1402 IOUtils.write((byte[]) request, outputStream); 1403 } else if (request instanceof String) { 1404 IOUtils.write(((String) request).getBytes(), outputStream); 1405 } 1406 InputStream inputStream = socket.getInputStream(); 1407 byte[] response = IOUtils.toByteArray(inputStream); 1408 setVariableValue(responseName, response); 1409 } finally { 1410 if (socket != null) { 1411 socket.close(); 1412 } 1413 } 1414 1415 } 1416 1417 } 1418 1419 @CommandExamples({ "<Table name='The table of variables'><var name='type:property' value='type:string'/></Table>", 1420 "<Table name='The table of variables' file='type:path'><var name='type:property' value='type:string'/></Table>" }) 1421 public void runCommandTable(final Node aCurrentVar) throws Throwable { 1422 AEManager manager = this.recipeListener.getManager(); 1423 boolean notifyMe = this.recipeListener.isNotifyMe(); 1424 1425 manager.inputDataTable(this, aCurrentVar, notifyMe); 1426 } 1427 1428 @SuppressWarnings({ "unchecked" }) 1429 @CommandExamples({ "<Loop name='type:property' source='type:property' numbers='type:integer'> ... </Loop>", 1430 "<Loop numbers='type:integer'> ... </Loop>", 1431 "<Loop name='type:property' query='select * from ...' maxCount='type:integer'> ... </Loop>" }) 1432 public void runCommandLoop(final Node aCurrentVar) throws Throwable { 1433 String theDescript = replaceProperties(aCurrentVar.getAttribute("description")); 1434 final String theNameAttribut = attr(aCurrentVar, "name"); 1435 boolean mainLoopCommand = false; 1436 1437 final String theNumbers = StringUtils.trimToNull(replaceProperties(aCurrentVar.getAttribute("numbers"))); 1438 if (theDescript == null) { 1439 theDescript = "Loop"; 1440 } 1441 String sourceAttribute = aCurrentVar.getAttribute("source"); 1442 final String theAttribut = replaceProperties(sourceAttribute); 1443 Object loopArrayVar = null; 1444 if (theAttribut != null) { 1445 loopArrayVar = getVariableValue(theAttribut); 1446 } 1447 List<Object> theLoopArray = new ArrayList<>(); 1448 if (loopArrayVar != null) { 1449 if (loopArrayVar instanceof String) { 1450 theLoopArray.add((String) loopArrayVar); 1451 } else if (loopArrayVar instanceof String[]) { 1452 List<String> asList = Arrays.asList((String[]) loopArrayVar); 1453 theLoopArray.addAll(asList); 1454 } else if (loopArrayVar instanceof List) { 1455 theLoopArray = (List<Object>) loopArrayVar; 1456 } else if (loopArrayVar instanceof Map) { 1457 Map map = (Map) loopArrayVar; 1458 theLoopArray.addAll(map.keySet()); 1459 } else if (loopArrayVar instanceof JSONArray) { 1460 JSONArray array = (JSONArray) loopArrayVar; 1461 theLoopArray = array.toList(); 1462 } else { 1463 return; 1464 } 1465 } 1466 int theCount = 0; 1467 boolean infinityLoop = false; 1468 if (StringUtils.isNotBlank(theNumbers)) { 1469 theCount = (int) Double.parseDouble(theNumbers); 1470 } else { 1471 if (!theLoopArray.isEmpty()) { 1472 theCount = theLoopArray.size(); 1473 } else { 1474 infinityLoop = sourceAttribute == null; 1475 } 1476 } 1477 if (isLoopActivated() == false) { 1478 this.mainLoopCommand = true; 1479 mainLoopCommand = true; 1480 } 1481 1482 if (infinityLoop) { 1483 this.mainLoopCommand = false; 1484 } 1485 try { 1486 for (int i = 0; (i < theCount || infinityLoop) && isStopped() == false; i++) { 1487 if (isStopped()) { 1488 break; 1489 } 1490 if (this.breakFlag > 0) { 1491 break; 1492 } 1493 if ((mainLoopCommand && !infinityLoop) || theCount > 5) { 1494 this.recipeListener.setProgress(theDescript, theCount, i, false); 1495 } 1496 startCommandInformation(aCurrentVar); 1497 if (theLoopArray != null && theLoopArray.size() > 0) { 1498 if (i < theLoopArray.size()) { 1499 setVariableValue(theNameAttribut, theLoopArray.get(i)); 1500 } 1501 } else { 1502 setVariableValue(theNameAttribut, ObjectUtils.toString(i)); 1503 } 1504 taskNode(aCurrentVar, false); 1505 } 1506 } finally { 1507 if (this.breakFlag > 0) { 1508 breakFlag--; 1509 } 1510 if (mainLoopCommand) { 1511 this.mainLoopCommand = false; 1512 } 1513 } 1514 } 1515 1516 @CommandExamples({ "<Append name='type:property' value='type:string' type='enum:all|unique'/>", 1517 "<Append name='type:property'>...</Append>" }) 1518 public synchronized void runCommandAppend(final Node aCurrentVar) throws Throwable { 1519 final String name = aCurrentVar.getAttribute("name"); 1520 Object value = attr(aCurrentVar, "value"); 1521 if (value == null) { 1522 value = replaceProperties(aCurrentVar.getInnerText()); 1523 } 1524 1525 String source = aCurrentVar.getAttribute("source"); 1526 if (source != null) { 1527 value = getVariableValue(replaceProperties(source)); 1528 } 1529 1530 Object theOldValue = getVariableValue(name, false); 1531 if (theOldValue == null) { 1532 return; 1533 } 1534 1535 theOldValue = checkForArray(theOldValue); 1536 1537 if (theOldValue instanceof String) { 1538 final String theValue1 = (String) theOldValue; 1539 value = theValue1 + value; 1540 setVariableValue(name, value); 1541 1542 } else if (theOldValue instanceof Object[]) { 1543 final boolean theUniqueTypeAttribut = "unique".equals(aCurrentVar.getAttribute("type")); 1544 1545 final Object[] theValue1 = (Object[]) theOldValue; 1546 Object[] theValue2 = null; 1547 1548 if (theUniqueTypeAttribut) { 1549 Set<Object> targetSet = new LinkedHashSet<>(Arrays.asList(theValue1)); 1550 if (value instanceof String) { 1551 targetSet.add((String) value); 1552 } else if (value instanceof String[]) { 1553 for (String val : (String[]) value) { 1554 targetSet.add(val); 1555 } 1556 } 1557 1558 String[] array = new String[targetSet.size()]; 1559 Iterator<Object> iterator = targetSet.iterator(); 1560 int i = 0; 1561 while (iterator.hasNext()) { 1562 String object = (String) iterator.next(); 1563 array[i++] = object; 1564 } 1565 1566 theValue2 = array; 1567 1568 } else { 1569 List<Object> targetList = new ArrayList<>(Arrays.asList(theValue1)); 1570 if (value instanceof String[]) { 1571 for (String val : (String[]) value) { 1572 targetList.add(val); 1573 } 1574 } else { 1575 targetList.add((String) value); 1576 } 1577 1578 String[] array = new String[targetList.size()]; 1579 int i = 0; 1580 for (Object val : targetList) { 1581 if (val instanceof String) { 1582 array[i++] = (String) val; 1583 } else { 1584 array[i++] = ObjectUtils.toString(val); 1585 } 1586 } 1587 1588 theValue2 = array; 1589 } 1590 1591 applyResult(aCurrentVar, name, theValue2); 1592 } 1593 } 1594 1595 private Object checkForArray(Object theOldValue) { 1596 if (theOldValue instanceof List) { 1597 theOldValue = ((List) theOldValue).toArray(); 1598 } 1599 return theOldValue; 1600 } 1601 1602 @CommandExamples({ "<Rnd name='type:property' symbols='type:integer' type='enum:number|default'/>", 1603 "<Rnd name='type:property' type='uuid'/>", 1604 "<Rnd name='type:property' symbols='type:integer' startCode='type:integer' endCode='type:integer'/>", 1605 "<Rnd name='type:property' source='type:property'/>" }) 1606 public void runCommandRnd(final Node aCurrentVar) throws Throwable { 1607 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 1608 String result = null; 1609 String type = replaceProperties(aCurrentVar.getAttribute("type")); 1610 1611 if ("uuid".equalsIgnoreCase(type)) { 1612 result = UUID.randomUUID().toString(); 1613 1614 } else { 1615 final String source = replaceProperties(aCurrentVar.getAttribute("source")); 1616 if (this.random == null) { 1617 this.random = SecureRandom.getInstance("SHA1PRNG"); 1618 } 1619 if (source != null) { 1620 Object value = getVariableValue(source); 1621 if (value instanceof String[]) { 1622 String[] strings = (String[]) value; 1623 int index = this.random.nextInt(strings.length); 1624 value = strings[index]; 1625 } 1626 setVariableValue(theNameAttribut, value); 1627 return; 1628 } 1629 1630 final String theSymbolsAttribut = replaceProperties(aCurrentVar.getAttribute("symbols")); 1631 1632 final long theBegNum = Long.parseLong(theSymbolsAttribut); 1633 int startCode = 0x41; 1634 final String startCodeStr = replaceProperties(aCurrentVar.getAttribute("startCode")); 1635 if (startCodeStr != null) { 1636 if (startCodeStr.indexOf('x') > 0) { 1637 startCode = Integer.parseInt(startCodeStr.substring(2), 16); 1638 } else { 1639 startCode = Integer.parseInt(startCodeStr); 1640 } 1641 type = "-number"; 1642 } 1643 1644 int endCode = 0x05A; 1645 final String endCodeStr = replaceProperties(aCurrentVar.getAttribute("endCode")); 1646 if (endCodeStr != null) { 1647 if (endCodeStr.indexOf('x') > 0) { 1648 endCode = Integer.parseInt(endCodeStr.substring(2), 16); 1649 } else { 1650 endCode = Integer.parseInt(endCodeStr); 1651 } 1652 type = "-number"; 1653 } 1654 1655 if ("number".equals(type) == false) { 1656 final StringBuffer theBuffer = new StringBuffer(); 1657 for (int i = 0; i < theBegNum; i++) { 1658 theBuffer.append((char) (this.random.nextInt(endCode - startCode) + startCode)); 1659 } 1660 result = theBuffer.toString(); 1661 } else { 1662 final StringBuffer theBuffer = new StringBuffer(); 1663 for (int i = 0; i < theBegNum; i++) { 1664 theBuffer.append(this.random.nextInt(9)); 1665 } 1666 result = theBuffer.toString(); 1667 } 1668 } 1669 1670 setVariableValue(theNameAttribut, result); 1671 } 1672 1673 @CommandExamples({ "<Inc name='type:property' increase='type:number'/>" }) 1674 public void runCommandInc(final Node aCurrentVar) throws Throwable { 1675 String theValueAttribut = aCurrentVar.getAttribute("increase"); 1676 theValueAttribut = replaceProperties(theValueAttribut); 1677 long theIncLong = 1; 1678 if (theValueAttribut != null) { 1679 theIncLong = Long.parseLong(theValueAttribut); 1680 } 1681 final String theAttribut = aCurrentVar.getAttribute("name"); 1682 Object theOldValue = getVariableValue(theAttribut); 1683 1684 if (!(theOldValue instanceof Object[])) { 1685 theOldValue = ObjectUtils.toString(theOldValue, null); 1686 } 1687 1688 if (theOldValue instanceof String) { 1689 final long theLongValue = Long.parseLong((String) theOldValue) + theIncLong; 1690 setVariableValue(theAttribut, Long.toString(theLongValue)); 1691 } 1692 if (theOldValue instanceof String[] && ((String[]) theOldValue).length > 0) { 1693 final long theLongValue = Long.parseLong(((String[]) theOldValue)[0]) + theIncLong; 1694 setVariableValue(theAttribut, Long.toString(theLongValue)); 1695 } else { 1696 new ClassCastException("Tag <Inc> enabled only one number argument."); 1697 } 1698 } 1699 1700 @CommandExamples({ 1701 "<If name='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... </If>", 1702 "<If value='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... <Else> ... </Else></If>", 1703 "<If expression='type:string'> ... </If>", "<If isNull='type:property'> ... </If>", 1704 "<If isNotNull='type:property'> ... </If>" }) 1705 public void runCommandIf(final Node action) throws Throwable { 1706 runIf(action); 1707 } 1708 1709 private boolean runIf(final Node action) throws CommandException, Throwable, ClassNotFoundException { 1710 String theExpressionAttribut = action.getAttribute("expression"); 1711 String isNull = action.getAttribute("isNull"); 1712 String isNotNull = action.getAttribute("isNotNull"); 1713 String nameAttr = attr(action, "name"); 1714 1715 String valueAttr = action.getAttribute("value"); 1716 1717 String theValue1Attribut = action.getAttribute("value1"); 1718 String theValue2Attribut = action.getAttribute("value2"); 1719 String theConditionAttribut = action.getAttribute("condition"); 1720 1721 boolean result = false; 1722 if (nameAttr != null || valueAttr != null) { 1723 Object variableValue = getVariableValue(nameAttr); 1724 String value; 1725 1726 if (variableValue == null) { 1727 value = replaceProperties(valueAttr); 1728 } else { 1729 if (variableValue instanceof String[]) { 1730 value = StringUtils.join((String[]) variableValue, "\n"); 1731 } else { 1732 value = ObjectUtils.toString(variableValue); 1733 } 1734 } 1735 1736 String startsWith = attr(action, "startsWith"); 1737 String endsWith = attr(action, "endsWith"); 1738 String contains = attr(action, "contains"); 1739 String equals = attr(action, "equals"); 1740 String regex = attr(action, "regex"); 1741 String notEqual = attr(action, "notEqual"); 1742 1743 boolean ignoreCase = Boolean.valueOf(attr(action, "ignoreCase", "false")); 1744 1745 boolean condition1 = startsWith == null || (ignoreCase ? StringUtils.startsWithIgnoreCase(value, startsWith) 1746 : StringUtils.startsWith(value, startsWith)); 1747 boolean condition2 = endsWith == null || (ignoreCase ? StringUtils.endsWithIgnoreCase(value, endsWith) 1748 : StringUtils.endsWith(value, endsWith)); 1749 boolean condition3 = contains == null || (ignoreCase ? StringUtils.containsIgnoreCase(value, contains) 1750 : StringUtils.contains(value, contains)); 1751 boolean condition4 = equals == null 1752 || (ignoreCase ? StringUtils.equalsIgnoreCase(value, equals) : StringUtils.equals(value, equals)); 1753 1754 boolean condition5 = true; 1755 if (regex != null) { 1756 final Pattern p = Pattern.compile(regex); 1757 final Matcher m = p.matcher(value); 1758 condition5 = m.find(); 1759 } 1760 1761 boolean condition6 = notEqual == null || (ignoreCase ? !StringUtils.equalsIgnoreCase(value, notEqual) 1762 : !StringUtils.equals(value, notEqual)); 1763 1764 result = condition1 && condition2 && condition3 && condition4 && condition5 && condition6; 1765 execIf(action, result); 1766 return result; 1767 } else if (isNull != null) { 1768 result = getVariableValue(replaceProperties(isNull)) == null; 1769 execIf(action, result); 1770 return result; 1771 } else if (isNotNull != null) { 1772 Object variableValue = getVariableValue(replaceProperties(isNotNull)); 1773 result = variableValue != null; 1774 execIf(action, result); 1775 return result; 1776 } else if (theExpressionAttribut != null) { 1777 theExpressionAttribut = replaceProperties(theExpressionAttribut); 1778 JexlEngine jexl = new JexlBuilder().create(); 1779 1780 JexlExpression expr_c = jexl.createExpression(theExpressionAttribut); 1781 JexlContext context = new MapContext(); 1782 1783 if (expr_c != null) { 1784 Object val = expr_c.evaluate(context); 1785 result = "true".equals(val.toString()); 1786 } 1787 1788 execIf(action, result); 1789 return result; 1790 } else if (theValue1Attribut != null || theValue2Attribut != null) { 1791 if (theConditionAttribut == null) { 1792 theConditionAttribut = "=="; 1793 } 1794 theValue1Attribut = replaceProperties(theValue1Attribut); 1795 theValue2Attribut = replaceProperties(theValue2Attribut); 1796 1797 if ("==".equals(theConditionAttribut) && theValue1Attribut.equals(theValue2Attribut)) { 1798 taskNode(action, false); 1799 result = true; 1800 } else if (("!=".equals(theConditionAttribut) || "unequal".equals(theConditionAttribut)) 1801 && (theValue1Attribut.equals(theValue2Attribut) == false)) { 1802 taskNode(action, false); 1803 result = true; 1804 } else if ("less".equals(theConditionAttribut) 1805 && (Long.parseLong(theValue1Attribut) < Long.parseLong(theValue2Attribut))) { 1806 taskNode(action, false); 1807 result = true; 1808 } else if ("bigger".equals(theConditionAttribut) 1809 && (Long.parseLong(theValue1Attribut) > Long.parseLong(theValue2Attribut))) { 1810 taskNode(action, false); 1811 result = true; 1812 } else { 1813 for (Node command : action.getNodes("Else")) { 1814 taskNode(command, false); 1815 } 1816 } 1817 } 1818 1819 return result; 1820 } 1821 1822 private void execIf(final Node action, boolean result) throws CommandException, Throwable { 1823 if (result) { 1824 taskNode(action, false); 1825 } else { 1826 for (Node command : action.getNodes("Else")) { 1827 if (command.getAttribute("name") == null && action.getAttribute("name") != null) { 1828 command.setAttribute("name", action.getAttribute("name")); 1829 } 1830 if (command.getAttribute("value") == null && action.getAttribute("value") != null) { 1831 command.setAttribute("value", action.getAttribute("value")); 1832 } 1833 if (command.getAttribute("name") != null || command.getAttribute("value") != null) { 1834 if (runIf(command)) { 1835 break; 1836 } 1837 } else { 1838 taskNode(command); 1839 } 1840 } 1841 } 1842 } 1843 1844 @CommandDescription("") 1845 @CommandExamples({ 1846 "<While name='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... </While>", 1847 "<While value1='type:string' value2='type:string' condition='unequal'> ... <Else value1='type:string' value2='type:string' condition='enum:unequal|equal'> ... </Else></While>", 1848 "<While expression='type:string'> ... </While>", "<While isNull='type:property'> ... </While>", 1849 "<While isNotNull='type:property'> ... </While>" }) 1850 public void runCommandWhile(final Node aCurrentVar) throws Throwable { 1851 try { 1852 for (; runIf(aCurrentVar);) { 1853 if (this.breakFlag > 0) { 1854 break; 1855 } 1856 } 1857 } finally { 1858 if (this.breakFlag > 0) { 1859 breakFlag--; 1860 } 1861 } 1862 } 1863 1864 @CommandExamples({ "<Pragma event='console-input' action='default'/>", 1865 "<Pragma event='random-select' action='on'/>", "<Pragma event='log-window' action='single'/>" }) 1866 public void runCommandPragma(final Node aCurrentVar) throws Throwable { 1867 final String theEventAttribut = aCurrentVar.getAttribute("event"); 1868 final String theActionAttribut = aCurrentVar.getAttribute("action"); 1869 1870 if ("console-input".equals(theEventAttribut)) { 1871 boolean consoleDefaultInput = "default".equals(theActionAttribut); 1872 getListener().getManager().setConsoleDefaultInput(consoleDefaultInput); 1873 } else if ("random-select".equals(theEventAttribut)) { 1874 randomSelect = "on".equals(theActionAttribut); 1875 } else { 1876 this.log.error("Pragma ignored. Event: " + theEventAttribut); 1877 } 1878 } 1879 1880 @SuppressWarnings("unchecked") 1881 @CommandDescription("The `name` attribute is a name of variable which provides Runnable or List<Runnable> value. " 1882 + "To define this value, you should use the Runnable command tag.") 1883 @CommandExamples({ 1884 "<Threads name='type:property' threadLog='type:boolean' numbers='type:integer' multi='enum:true|false' />", 1885 "<Threads numbers='type:integer' multi='enum:true|false' mode='enum:wait|nowait'><Out name='" 1886 + TaskProcessorThread.THREAD_ID + "'/></Threads>", 1887 "<Threads multi='enum:true|false'><Out name='" + TaskProcessorThread.THREAD_ID + "'/></Threads>" }) 1888 public void runCommandThreads(final Node aCurrentAction) throws InterruptedException, CommandException { 1889 1890 boolean threadLog = Boolean 1891 .parseBoolean(StringUtils.defaultIfBlank(attr(aCurrentAction, "threadLog"), "false")); 1892 int numbers = Integer.parseInt(StringUtils.defaultIfBlank(attr(aCurrentAction, "numbers"), "1")); 1893 boolean multi = Boolean.parseBoolean(StringUtils.defaultIfBlank(attr(aCurrentAction, "multi"), "true")); 1894 1895 ThreadPoolExecutor threadPool; 1896 if (multi) { 1897 if (numbers > 0) { 1898 threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(numbers); 1899 } else { 1900 threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); 1901 } 1902 } else { 1903 threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); 1904 } 1905 1906 String callableListName = attr(aCurrentAction, "name"); 1907 if (callableListName == null) { 1908 final Iterator<?> theIterator = aCurrentAction.iterator(); 1909 1910 if (numbers == 0) { 1911 while (theIterator.hasNext()) { 1912 final Node theNode = (Node) theIterator.next(); 1913 1914 String theNameThreads = getTestName(theNode); 1915 if (theNameThreads == null) { 1916 theNameThreads = "Thread"; 1917 } 1918 1919 ILogger theLog = this.log; 1920 if (threadLog) { 1921 theLog = this.recipeListener.createLog(theNameThreads, false); 1922 } 1923 1924 final TaskProcessorThread runnable = new TaskProcessorThread(this, theNode, getBaseDir(), theLog); 1925 1926 threadPool.execute(runnable); 1927 1928 } 1929 } else { 1930 final Node theNode = (Node) aCurrentAction.clone(); 1931 theNode.setTag("Task"); 1932 theNode.removeAttribute(callableListName); 1933 1934 for (int i = 0; i < numbers; i++) { 1935 String theNameThreads = getTestName(theNode); 1936 String logName = "Thread #" + i; 1937 if (theNameThreads != null) { 1938 logName = theNameThreads + " #" + i; 1939 } 1940 1941 ILogger theLog = this.log; 1942 if (threadLog) { 1943 theLog = this.recipeListener.createLog(logName, false); 1944 } 1945 1946 final TaskProcessorThread runnable = new TaskProcessorThread(this, theNode, getBaseDir(), theLog); 1947 runnable.getVaribleValue(TaskProcessorThread.THREAD_ID, String.valueOf(i)); 1948 1949 threadPool.execute(runnable); 1950 } 1951 } 1952 1953 } else { 1954 Object callableList = getVariableValue(callableListName); 1955 if (callableList instanceof List) { 1956 for (Runnable runnable : (List<Runnable>) callableList) { 1957 threadPool.execute(runnable); 1958 } 1959 } else if (callableList instanceof Runnable) { 1960 for (int i = 0; i < numbers; i++) { 1961 if (callableList instanceof TaskProcessorThread) { 1962 threadPool.execute(((TaskProcessorThread) callableList).clone(i)); 1963 } else { 1964 threadPool.execute((Runnable) callableList); 1965 } 1966 } 1967 } else { 1968 throw new IllegalArgumentException( 1969 "Variable of type must be Runnable or List<Runnable>, actually: " + callableList); 1970 } 1971 } 1972 1973 String description = replaceProperties(aCurrentAction.getAttribute("description")); 1974 1975 long completedTaskCount = 0; 1976 long size = threadPool.getTaskCount(); 1977 1978 threadPoolList.add(threadPool); 1979 do { 1980 completedTaskCount = threadPool.getCompletedTaskCount(); 1981 progress(size, completedTaskCount, description, false); 1982 Thread.sleep(200); 1983 1984 } while (completedTaskCount < size); 1985 threadPoolList.remove(threadPool); 1986 } 1987 1988 @CommandExamples({ "Run recipe by name: <Task name='type:recipe' />", 1989 "Run recipe by name in async mode: <Task name='type:recipe' mode='enum:sync|async' optional='enum:false|true'/>", 1990 "Recipe command grouping: <Task> ... </Task>", "Run recipe by file path: <Task file='type:path' />" }) 1991 public void runCommandTask(final Node action) throws Throwable { 1992 1993 final boolean optional = Boolean.valueOf(attr(action, "optional", "false")); 1994 1995 final String taskName = replaceProperties(action.getAttribute("name")); 1996 String taskFile = replaceProperties(action.getAttribute("file")); 1997 1998 if (taskFile == null) { 1999 if (taskName != null) { 2000 taskFile = getListener().getManager().getTestPath(taskName); 2001 2002 if (taskFile == null) { 2003 if (optional) { 2004 return; 2005 } else { 2006 throw new Exception("Task with name: '" + taskName + "' not found."); 2007 } 2008 } 2009 } 2010 } else { 2011 if (!(new File(taskFile)).exists()) { 2012 if (optional) { 2013 return; 2014 } else { 2015 throw new Exception("Task with name: '" + taskFile + "' not found."); 2016 } 2017 } 2018 } 2019 2020 final File theBaseDir = getBaseDir(); 2021 try { 2022 if (taskName != null) { 2023 String mode = replaceProperties(action.getAttribute("mode")); 2024 if ("async".equals(mode)) { 2025 getListener().getManager().runTask(taskName, true); 2026 2027 } else { 2028 Properties taskParameters = MapUtils.toProperties(action.getAttributes()); 2029 Set<Entry<Object, Object>> entrySet = taskParameters.entrySet(); 2030 2031 String currentLogLevel = (String) this.variables.get(DEFAULT_LOG_LEVEL_NAME); 2032 String level = (String) taskParameters.get("level"); 2033 2034 for (Entry<Object, Object> entry : entrySet) { 2035 String name = (String) entry.getKey(); 2036 if (!"$ID".equals(name) && !"name".equals(name) && !"file".equals(name) 2037 && !"level".equals(name)) { 2038 String value = replaceProperties((String) entry.getValue()); 2039 this.variables.put(toUpperCaseName(name), value); 2040 } 2041 } 2042 2043 this.variables.put(DEFAULT_LOG_LEVEL_NAME, level); 2044 this.variables = processTesting(taskFile, this.variables, getBaseDir()); 2045 this.variables.put(DEFAULT_LOG_LEVEL_NAME, currentLogLevel); 2046 } 2047 } else { 2048 taskNode(action, false); 2049 } 2050 } finally { 2051 setBaseDir(theBaseDir); 2052 } 2053 } 2054 2055 @CommandExamples({ "<IterationRules name='type:property'><Out name='Iteration'/></IterationRules>", 2056 "<IterationRules name='type:property' timeLimit='type:time' start='type:integer'> ... </IterationRules>", 2057 "<Var name='rules'>#\n[multiple]\na=1\nb $enum=1;2\nc $inc=0;10\nd $file=file_name\n[single]\n...\n[concurrently]\n...\n[independent]\n...\n#</Var>" 2058 + "<IterationRules name='rules'><Out name='Iteration'/></IterationRules>", }) 2059 public void runCommandIterationRules(final Node action) throws IOException, CommandException { 2060 final String name = (String) attrValue(action, "name"); 2061 String theStartAttribut = action.getAttribute("start"); 2062 long theLimit = attrTime(action, "timeLimit", "180000"); 2063 2064 if (theStartAttribut == null) { 2065 theStartAttribut = "0"; 2066 } 2067 2068 final int theStart = Integer.parseInt(theStartAttribut); 2069 if (name == null) { 2070 throw new CommandException("In tag <IterationRules> variable rules is not defined.", this); 2071 } 2072 try { 2073 this.iteratorMode = true; 2074 final TestIterator theTestIterator = new TestIterator(this, name); 2075 final int theMax = theTestIterator.count(); 2076 progress(theMax, 0, "Iteration process", false); 2077 debug("Iteration block. Total count: " + theMax); 2078 final long theStertTime = System.currentTimeMillis(); 2079 for (int i = theStart; i < theMax; i++) { 2080 if (isStopped()) { 2081 break; 2082 } 2083 setVariableValue("Iteration", Integer.toString(i + 1)); 2084 theTestIterator.nextIteration(this); 2085 2086 taskNode(action, false); 2087 2088 progress(theMax, i, "Iteration process", false); 2089 if (i == 0) { 2090 long theTotalTime = ((System.currentTimeMillis() - theStertTime) * theMax) / 60000; 2091 debug("Total Iteration time: " + Long.toString(theTotalTime) + " min."); 2092 if (theTotalTime > theLimit) { 2093 int theConfirm = 0; 2094 if (this.recipeListener.getManager() != null) { 2095 if (this.recipeListener.getManager().isConsoleDefaultInput(attr(action, "name"), 2096 attr(action, "description")) == false) { 2097 if (theTotalTime < 60) { 2098 theConfirm = JOptionPane.showConfirmDialog( 2099 JOptionPane.getRootFrame(), "Total Iteration time: " 2100 + Long.toString(theTotalTime) + " min. \nContinue?", 2101 "Warning", JOptionPane.YES_NO_OPTION); 2102 } else { 2103 theTotalTime /= 60; 2104 theConfirm = JOptionPane.showConfirmDialog(JOptionPane.getRootFrame(), 2105 "Total Iteration time: " + Long.toString(theTotalTime) + " h. \nContinue?", 2106 "Warning", JOptionPane.YES_NO_OPTION); 2107 } 2108 } 2109 if (theConfirm != JOptionPane.YES_OPTION) { 2110 break; 2111 } 2112 } 2113 } 2114 } 2115 } 2116 } finally { 2117 this.iteratorMode = false; 2118 } 2119 } 2120 2121 @CommandExamples({ "<Listdir path='type:path' name='type:property'/>", 2122 "<Listdir name='type:property' path='type:path' filter='type:regex'/>", 2123 "<Listdir name='type:property' path='type:path' filter='type:regex' dirFilter='type:regex' />", 2124 "<Listdir path='type:path' name='type:property' />" }) 2125 public void runCommandListdir(final Node aCurrentVar) throws Throwable { 2126 final String pathAttribut = replaceProperties(aCurrentVar.getAttribute("path")); 2127 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2128 final String fileFilterRegex = attr(aCurrentVar, "filter"); 2129 final String dirFilterRegex = attr(aCurrentVar, "dirFilter"); 2130 File path = getFile(pathAttribut); 2131 2132 if (isActiveInitFor(aCurrentVar, "console")) { 2133 String pathStr = this.recipeListener.getManager().inputFile(theNameAttribut, null, path, log, this); 2134 if (pathStr != null) { 2135 path = new File(pathStr); 2136 } 2137 } 2138 2139 if (path != null) { 2140 debug("Listing of directory: " + path.getAbsolutePath()); 2141 2142 IOFileFilter fileFilter = TrueFileFilter.TRUE; 2143 2144 if (fileFilterRegex != null) { 2145 fileFilter = new RegexPathFilter(fileFilterRegex, dirFilterRegex); 2146 } 2147 2148 Collection<File> files = FileUtils.listFiles(path, fileFilter, 2149 dirFilterRegex != null ? TrueFileFilter.TRUE : null); 2150 List<String> result = files.stream().map(f -> f.getAbsolutePath()).collect(Collectors.toList()); 2151 2152 if (!result.isEmpty()) { 2153 Collections.sort(result); 2154 setVariableValue(theNameAttribut, result); 2155 } 2156 } else { 2157 throw new TaskCancelingException(); 2158 } 2159 } 2160 2161 @CommandExamples({ "<NetworkInterfaces name='type:property' />", 2162 "<NetworkInterfaces name='type:property' host='type:string' filterFor='type:string'/>" }) 2163 public void runCommandNetworkInterfaces(final Node aCurrentAction) throws SocketException, UnknownHostException { 2164 final String name = replaceProperties(aCurrentAction.getAttribute("name")); 2165 String host = replaceProperties(aCurrentAction.getAttribute("host")); 2166 2167 if (host == null) { 2168 Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); 2169 Set<String> hostIps = new HashSet<>(); 2170 while (networkInterfaces.hasMoreElements()) { 2171 NetworkInterface nextElement = networkInterfaces.nextElement(); 2172 Enumeration<InetAddress> inetAddresses = nextElement.getInetAddresses(); 2173 while (inetAddresses.hasMoreElements()) { 2174 InetAddress nextElement2 = inetAddresses.nextElement(); 2175 if (nextElement2 instanceof Inet4Address) { 2176 Inet4Address address = (Inet4Address) nextElement2; 2177 String hostAddress = address.getHostAddress(); 2178 hostIps.add(hostAddress); 2179 } 2180 } 2181 } 2182 setVariableValue(name, new ArrayList<>(hostIps)); 2183 } else { 2184 try { 2185 URL url = new URL(host); 2186 host = url.getHost(); 2187 } catch (Exception e) { 2188 2189 } 2190 2191 InetAddress address1 = InetAddress.getByName(host); 2192 setVariableValue(name, address1.getHostAddress()); 2193 } 2194 } 2195 2196 @CommandDescription("Executes a command on the local computer. The command will be executed if the regular expression specified in `os` matches the OS_NAME system property. " 2197 + "The `dir` attribute specifies the current directory for the command being executed. This attribute can use `~` as the directory for the current recipe.") 2198 @CommandExamples({ "<Command os='type:regex'>...</Command>", 2199 "<Command os='type:regex' env='type:property'>...</Command>", 2200 "<Command name='type:property' os='type:regex' exitValue='type:property' cmd='type:string' noCommandLog='type:boolean' dir='type:path'/>", 2201 "<Command name='type:property' os='type:regex' exitValue='type:property' cmd='type:string' noCommandLog='type:boolean' dir='type:path'/>...</Command>" }) 2202 public void runCommandCommand(final Node action) throws Throwable { 2203 2204 String command = attr(action, "cmd"); 2205 final String name = attr(action, "name"); 2206 final String dir = attr(action, "dir"); 2207 final String os = attr(action, "os"); 2208 2209 String osName = SystemUtils.OS_NAME; 2210 2211 if (os == null || Pattern.compile(os).matcher(osName).matches()) { 2212 if (command == null) { 2213 final Node[] theNodes = action.getTextNodes(); 2214 if (theNodes.length > 0) { 2215 command = replaceProperties(theNodes[0].getText()); 2216 } 2217 } 2218 2219 Object env = attrValue(action, "env"); 2220 if (env != null) { 2221 if (!(env instanceof Map)) { 2222 throw new CommandException("'env' attribute shuld be Map", this); 2223 } 2224 } 2225 @SuppressWarnings("unchecked") 2226 Map<String, String> environment = (Map<String, String>) env; 2227 runSystemCommand(command, name, action, dir, environment); 2228 } 2229 } 2230 2231 private void runSystemCommand(final String command, final String theNameAttribut, final Node aCurrentNode, 2232 String dir, Map<? extends String, ? extends String> env) throws Throwable { 2233 final String prefix = "start "; 2234 if (command.startsWith(prefix) == false) { 2235 2236 String regExp = "\"(\\\"|[^\"])*?\"|[^ ]+"; 2237 Pattern pattern = Pattern.compile(regExp, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); 2238 Matcher matcher = pattern.matcher(command); 2239 List<String> commandTokens = new ArrayList<String>(); 2240 2241 if (SystemUtils.IS_OS_WINDOWS) { 2242 commandTokens.add("cmd.exe"); 2243 commandTokens.add("/c"); 2244 } 2245 2246 while (matcher.find()) { 2247 String trim = matcher.group().trim(); 2248 if (StringUtils.isNotBlank(trim)) { 2249 commandTokens.add(trim); 2250 } 2251 } 2252 String[] parsedCommand = commandTokens.toArray(new String[] {}); 2253 2254 ProcessBuilder builder = new ProcessBuilder(parsedCommand); 2255 2256 if (env != null) { 2257 Map<String, String> environment = builder.environment(); 2258 environment.putAll(env); 2259 } 2260 2261 if (dir != null) { 2262 File directory; 2263 if (dir.startsWith("~/")) { 2264 dir = dir.substring(1); 2265 String recipeFile = this.recipeListener.getManager().getTestPath(testName); 2266 if (recipeFile != null) { 2267 directory = new File(new File(recipeFile).getParent(), dir); 2268 builder.directory(directory); 2269 } 2270 } else { 2271 directory = new File(dir); 2272 builder.directory(directory); 2273 } 2274 } 2275 2276 builder.redirectErrorStream(true); 2277 Process process = builder.start(); 2278 2279 final String stdin = replaceProperties(aCurrentNode.getAttribute("stdin")); 2280 if (stdin != null) { 2281 process.getOutputStream().write(stdin.getBytes()); 2282 process.getOutputStream().close(); 2283 } 2284 2285 processes.add(process); 2286 2287 final BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getInputStream())); 2288 boolean noCommandLog = Boolean.valueOf(attr(aCurrentNode, "noCommandLog")); 2289 if (!noCommandLog) { 2290 debug("Command: " + command); 2291 } else { 2292 debug("Command: ****** **** *****"); 2293 } 2294 2295 try { 2296 final BufferedReader theOutputStream = new BufferedReader( 2297 new InputStreamReader(process.getInputStream())); 2298 String theLine; 2299 2300 boolean line_output = aCurrentNode.size() == 0; 2301 final StringBuffer theBuffer = new StringBuffer(); 2302 while ((theLine = theOutputStream.readLine()) != null && !isStoppedTest()) { 2303 if (breakFlag > 0) { 2304 break; 2305 } 2306 2307 if (line_output) { 2308 theBuffer.append(theLine); 2309 theBuffer.append('\n'); 2310 } else { 2311 setVariableValue(theNameAttribut, theLine); 2312 taskNode(aCurrentNode, false); 2313 } 2314 } 2315 2316 if (this.breakFlag > 0) { 2317 breakFlag--; 2318 } 2319 2320 String exitValueName = attr(aCurrentNode, "exitValue"); 2321 if (exitValueName != null) { 2322 int exitValue = 0; 2323 try { 2324 exitValue = process.exitValue(); 2325 if (exitValue > 0) { 2326 String error = IOUtils.toString(errorStream); 2327 if (StringUtils.isNotBlank(error)) { 2328 throw new RuntimeException(error); 2329 } 2330 } 2331 } catch (IllegalThreadStateException e) { 2332 // 2333 } 2334 setVariableValue(exitValueName, Integer.toString(exitValue)); 2335 } 2336 2337 if (line_output) { 2338 if (theNameAttribut == null) { 2339 debug("System output:" + theBuffer.toString()); 2340 } else { 2341 setVariableValue(theNameAttribut, theBuffer.toString()); 2342 } 2343 } 2344 } finally { 2345 processes.remove(process); 2346 process.destroyForcibly(); 2347 try { 2348 if (!process.waitFor(5, TimeUnit.SECONDS)) { 2349 process.destroyForcibly(); 2350 } 2351 } catch (InterruptedException e) { 2352 Thread.currentThread().interrupt(); 2353 process.destroyForcibly(); 2354 } 2355 } 2356 2357 } else { 2358 final Thread thread = new SystemCommandStart(command.substring(prefix.length()), this.log); 2359 thread.start(); 2360 } 2361 } 2362 2363 @CommandExamples({ "<ArraySize name = 'type:property' array = 'type:property'/>" }) 2364 public void runCommandArraySize(final Node aCurrentVar) throws Throwable { 2365 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2366 final String theArrayNameAttribut = replaceProperties(aCurrentVar.getAttribute("array")); 2367 final Object theValue = getVariableValue(theArrayNameAttribut); 2368 2369 int theResult = 0; 2370 if (theValue instanceof String[]) { 2371 theResult = ((String[]) theValue).length; 2372 } else if (theValue instanceof List) { 2373 theResult = ((List<String>) theValue).size(); 2374 } else if (theValue instanceof byte[]) { 2375 theResult = ((byte[]) theValue).length; 2376 } else if (theValue instanceof String && StringUtils.isNotBlank((String) theValue)) { 2377 theResult = 1; 2378 } else { 2379 theResult = 0; 2380 } 2381 2382 setVariableValue(theNameAttribut, String.valueOf(theResult)); 2383 } 2384 2385 @CommandExamples({ "Get string or array length: <Size name = 'type:property' source = 'type:property'/>" }) 2386 public void runCommandSize(final Node aCurrentVar) throws Throwable { 2387 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2388 final String theArrayNameAttribut = replaceProperties(aCurrentVar.getAttribute("source")); 2389 final Object theValue = getVariableValue(theArrayNameAttribut); 2390 2391 int theResult = 0; 2392 if (theValue instanceof String[]) { 2393 String[] theValue2 = (String[]) theValue; 2394 for (int i = 0; i < theValue2.length; i++) { 2395 theResult += theValue2[i].length(); 2396 } 2397 } else if (theValue instanceof byte[]) { 2398 theResult = ((byte[]) theValue).length; 2399 } else if (theValue instanceof Collection) { 2400 theResult = ((Collection) theValue).size(); 2401 } else if (theValue instanceof String && StringUtils.isNotBlank((String) theValue)) { 2402 theResult = ((String) theValue).length(); 2403 } else { 2404 theResult = 0; 2405 } 2406 2407 setVariableValue(theNameAttribut, String.valueOf(theResult)); 2408 } 2409 2410 @CommandExamples({ "<Time name = 'type:property' action = 'enum:start|continue|pause|stop'/>", 2411 "<Time name = 'type:property' action = 'format' format='mm:ss:SSS'/>", 2412 "<Time name = 'type:property' action = 'duration' >...code whose execution time is to be measured...</Time>" }) 2413 public void runCommandTime(final Node command) throws Throwable { 2414 final String theNameAttribut = replaceProperties(command.getAttribute("name")); 2415 final String format = replaceProperties(command.getAttribute("format")); 2416 final String theTimeCaption = "Time"; 2417 String action = replaceProperties(command.getAttribute("action")); 2418 2419 if ("duration".equals(action)) { 2420 long start = System.currentTimeMillis(); 2421 2422 taskNode(command, false); 2423 2424 long stop = System.currentTimeMillis(); 2425 final String[] variableValue = new String[] { theTimeCaption, String.valueOf(start), String.valueOf(stop), 2426 "" }; 2427 2428 String result = formatTime(format, variableValue); 2429 setVariableValue(theNameAttribut, result); 2430 return; 2431 } 2432 2433 if ("start".equals(action)) { 2434 final String[] theTime = new String[] { theTimeCaption, String.valueOf(System.currentTimeMillis()), "", 2435 "" }; 2436 setVariableValue(theNameAttribut, theTime); 2437 return; 2438 } 2439 2440 if ("continue".equals(action)) { 2441 String[] theTime = (String[]) getVariableValue(theNameAttribut); 2442 if (theTime == null) { 2443 theTime = new String[] { theTimeCaption, String.valueOf(System.currentTimeMillis()), "", 2444 String.valueOf(System.currentTimeMillis()) }; 2445 } 2446 2447 if (theTime == null || theTime[3] == null || theTime[3].length() == 0) { 2448 throw new Exception("Timer is not paused."); 2449 } 2450 2451 if (theTimeCaption.equals(theTime[0])) { 2452 final long theStart = Long.parseLong(theTime[1]); 2453 final long thePaused = Long.parseLong(theTime[3]); 2454 theTime[1] = String.valueOf(theStart - (System.currentTimeMillis() - thePaused)); 2455 theTime[3] = ""; 2456 setVariableValue(theNameAttribut, theTime); 2457 } else { 2458 throw new Exception("Incorrect type."); 2459 } 2460 return; 2461 } 2462 2463 if ("pause".equals(action)) { 2464 final String[] theTime = (String[]) getVariableValue(theNameAttribut); 2465 2466 if (theTime[3] != null && theTime[3].length() > 0) { 2467 throw new Exception("Timer is paused."); 2468 } 2469 if (theTimeCaption.equals(theTime[0])) { 2470 theTime[3] = String.valueOf(System.currentTimeMillis()); 2471 setVariableValue(theNameAttribut, theTime); 2472 } else { 2473 throw new Exception("Incorrect type."); 2474 } 2475 return; 2476 } 2477 2478 if ("stop".equals(action)) { 2479 final String[] theTime = (String[]) getVariableValue(theNameAttribut); 2480 2481 final long theStart = Long.parseLong(theTime[1]); 2482 2483 if (theTime[3] != null && theTime[3].length() > 0) { 2484 final long thePaused = Long.parseLong(theTime[3]); 2485 theTime[1] = String.valueOf(theStart + (System.currentTimeMillis() - thePaused)); 2486 theTime[3] = ""; 2487 } 2488 2489 if (theTimeCaption.equals(theTime[0])) { 2490 theTime[2] = String.valueOf(System.currentTimeMillis()); 2491 setVariableValue(theNameAttribut, theTime); 2492 } else { 2493 throw new Exception("Incorrect type."); 2494 } 2495 return; 2496 } 2497 2498 if ("format".equals(action)) { 2499 Object variableValue = getVariableValue(theNameAttribut); 2500 String theResult = formatTime(format, variableValue); 2501 setVariableValue(theNameAttribut, theResult); 2502 return; 2503 } 2504 } 2505 2506 private String formatTime(final String format, Object variableValue) throws Exception { 2507 final String[] theTime; 2508 if (variableValue instanceof String[]) { 2509 theTime = (String[]) variableValue; 2510 } else { 2511 theTime = new String[] { "", "0", (String) variableValue }; 2512 } 2513 2514 if (ArrayUtils.getLength(theTime) < 2) { 2515 throw new Exception("Timer is not defined."); 2516 } 2517 2518 if (ArrayUtils.getLength(theTime) < 3) { 2519 throw new Exception("Timer is not stoped."); 2520 } 2521 2522 String theResult = null; 2523 if (format == null) { 2524 theResult = String.valueOf(Long.parseLong(theTime[2]) - Long.parseLong(theTime[1])); 2525 } else { 2526 final DateFormat dateFormat = new SimpleDateFormat(format); 2527 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 2528 long date = Long.parseLong(theTime[2]) - Long.parseLong(theTime[1]); 2529 theResult = dateFormat.format(new Date(date)); 2530 } 2531 return theResult; 2532 } 2533 2534 @CommandDescription("The ArrayElement command is used to get an element from an array by element ID (elementId).") 2535 @CommandExamples({ "<ArrayElement name='type:property' array='type:property' elementId='type:number'/>" }) 2536 public void runCommandArrayElement(final Node aCurrentVar) throws Throwable { 2537 final String name = replaceProperties(aCurrentVar.getAttribute("name")); 2538 final String arrayName = replaceProperties(aCurrentVar.getAttribute("array")); 2539 if (arrayName == null) { 2540 throw new IllegalArgumentException("'array' attribute required."); 2541 } 2542 2543 final String theElementAttribut = replaceProperties(aCurrentVar.getAttribute("elementId")); 2544 if (theElementAttribut == null) { 2545 throw new IllegalArgumentException("'elementId' attribute required."); 2546 } 2547 2548 final String type = replaceProperties(aCurrentVar.getAttribute("type")); 2549 Object theValue = getVariableValue(arrayName); 2550 2551 final int theElementId = Integer.parseInt(theElementAttribut); 2552 2553 theValue = convert(theValue, type); 2554 2555 Object theResult = null; 2556 if (theValue instanceof JSONArray) { 2557 JSONArray jsonArray = (JSONArray) theValue; 2558 theResult = jsonArray.get(theElementId); 2559 } else if (theValue instanceof String[]) { 2560 theResult = ((String[]) theValue)[theElementId]; 2561 } else if (theValue instanceof List) { 2562 theResult = ((List) theValue).get(theElementId); 2563 } else if (theValue instanceof String) { 2564 if ("string".equalsIgnoreCase(type)) { 2565 theResult = new String(new char[] { ((String) theValue).charAt(theElementId) }); 2566 } else if (theElementId == 0) { 2567 theResult = theValue; 2568 } 2569 } else if (theElementId == 0) { 2570 theResult = theValue; 2571 } 2572 2573 setVariableValue(name, theResult); 2574 } 2575 2576 @CommandDescription("The Calculate command is implemented in the Java Expression Language (JEXL), " 2577 + "for more information see: https://commons.apache.org/proper/commons-jexl. " 2578 + "The value of the expression will be stored in the variable with the name specified in the `name` attribute.") 2579 @CommandExamples({ "<Calculate name='type:property' expressions='type:string'/>", 2580 "<Calculate name='type:property'>...</Calculate>" }) 2581 public void runCommandCalculate(final Node command) throws Throwable { 2582 final String name = replaceProperties(command.getAttribute("name")); 2583 String expressions = replaceProperties(command.getAttribute("expressions")); 2584 if (expressions == null) { 2585 expressions = replaceProperties(command.getInnerText()); 2586 } 2587 2588 Map<String, Object> namespaces = new HashMap<>(); 2589 namespaces.put("Math", Math.class); 2590 JexlEngine jexl = new JexlBuilder().namespaces(namespaces).create(); 2591 2592 JexlExpression expr_c = jexl.createExpression(expressions); 2593 JexlContext context = new MapContext(); 2594 2595 Map<String, String> attributes = command.getAttributes(); 2596 for (Map.Entry<String, String> entry : attributes.entrySet()) { 2597 String key = entry.getKey(); 2598 String val = entry.getValue(); 2599 2600 Object variableValue = getVariableValue(val); 2601 if (variableValue instanceof String) { 2602 try { 2603 variableValue = Double.parseDouble((String) variableValue); 2604 } catch (NumberFormatException e) { 2605 // DO NOTHING 2606 } 2607 } 2608 2609 context.set(key, variableValue); 2610 } 2611 2612 if (expr_c != null) { 2613 Object result = expr_c.evaluate(context); 2614 2615 if (result != null) { 2616 setVariableValue(name, result); 2617 } else { 2618 setVariableValue(name, null); 2619 } 2620 } 2621 } 2622 2623 @CommandExamples({ 2624 "<Parse name='type:property' source='type:property' type='enum:array|json|csv|fixed-length-line'/>", 2625 "<Parse name='type:property' source='type:property' type='fixed-length-line' length='type:integer'/>" }) 2626 public void runCommandParse(final Node command) throws Throwable { 2627 Object source = attrValue(command, "source"); 2628 final String theNameAttribut = replaceProperties(command.getAttribute("name")); 2629 final String type = replaceProperties(command.getAttribute("type")); 2630 2631 if ("json".equalsIgnoreCase(type)) { 2632 String theValue = ObjectUtils.toString(source); 2633 if (StringUtils.isNotBlank(theValue)) { 2634 Object obj = new JSONObject(theValue); 2635 if (obj instanceof JSONObject) { 2636 JSONObject result = new JSONObject(theValue); 2637 setVariableValue(theNameAttribut, result); 2638 } else if (obj instanceof JSONArray) { 2639 JSONArray result = new JSONArray(theValue); 2640 String[] array = new String[result.length()]; 2641 for (int i = 0; i < result.length(); i++) { 2642 array[i] = ObjectUtils.toString(result.get(i)); 2643 } 2644 setVariableValue(theNameAttribut, array); 2645 } 2646 } else { 2647 setVariableValue(theNameAttribut, theValue); 2648 } 2649 2650 } else if ("array".equalsIgnoreCase(type)) { 2651 String theValue = ObjectUtils.toString(source); 2652 2653 String[] array = StringUtils.split(theValue, "\r\n"); 2654 setVariableValue(theNameAttribut, array); 2655 } else if ("csv".equalsIgnoreCase(type)) { 2656 String theValue = ObjectUtils.toString(source); 2657 2658 CSVReader reader = new CSVReader(new StringReader(theValue)); 2659 List<String[]> r = reader.readAll(); 2660 2661 setVariableValue(theNameAttribut, r); 2662 } else if ("fixed-length-line".equalsIgnoreCase(type)) { 2663 if (source instanceof String) { 2664 String input = (String) source; 2665 int lineLength = Integer.parseInt(attr(command, "length")); 2666 List<Object> result = IntStream.range(0, (input.length() + lineLength - 1) / lineLength) 2667 .mapToObj(i -> input.substring(i * lineLength, Math.min((i + 1) * lineLength, input.length()))) 2668 .collect(Collectors.toList()); 2669 setVariableValue(theNameAttribut, result); 2670 } 2671 } 2672 } 2673 2674 @CommandExamples({ "<FindObject name='type:property' source='type:property' withValue='type:string'/>" }) 2675 public void runCommandFindObject(final Node aCurrentNode) throws Throwable { 2676 Object json = getVariableValue(replaceProperties(aCurrentNode.getAttribute("source"))); 2677 String withValue = replaceProperties(aCurrentNode.getAttribute("withValue")); 2678 2679 if (json instanceof String) { 2680 String jsonStr = (String) json; 2681 if (StringUtils.startsWith(jsonStr, "{")) { 2682 json = new JSONObject(jsonStr); 2683 } else if (StringUtils.startsWith(jsonStr, "[")) { 2684 json = new JSONArray(jsonStr); 2685 } 2686 } else if (json instanceof String[]) { 2687 String[] jsonStrArray = (String[]) json; 2688 JSONArray array = new JSONArray(); 2689 for (int i = 0; i < jsonStrArray.length; i++) { 2690 String jsonStr = jsonStrArray[i]; 2691 array.put(new JSONObject(jsonStr)); 2692 } 2693 json = array; 2694 } 2695 2696 Object value = find(json, withValue); 2697 2698 final String name = replaceProperties(aCurrentNode.getAttribute("name")); 2699 applyResult(aCurrentNode, name, value); 2700 } 2701 2702 private JSONObject find(Object obj, String withValue) throws JSONException { 2703 if (obj instanceof JSONObject) { 2704 JSONObject json = (JSONObject) obj; 2705 2706 JSONArray names = json.names(); 2707 if (names != null) { 2708 for (int i = 0; i < names.length(); i++) { 2709 String name = names.getString(i); 2710 Object object = json.get(name); 2711 if (object instanceof String) { 2712 if (ObjectUtils.equals(object, withValue)) { 2713 return json; 2714 } 2715 } else { 2716 JSONObject find = find(object, withValue); 2717 if (find != null) { 2718 return find; 2719 } 2720 } 2721 } 2722 } 2723 } else if (obj instanceof JSONArray) { 2724 JSONArray array = (JSONArray) obj; 2725 2726 for (int j = 0; j < array.length(); j++) { 2727 Object item = array.get(j); 2728 2729 JSONObject find = find(item, withValue); 2730 if (find != null) { 2731 return find; 2732 } 2733 } 2734 } 2735 return null; 2736 } 2737 2738 @CommandDescription("`Var` command is used to define or update a variable.\r\n" 2739 + "Attribute `init` defines how variable will be set. This attribute is optional; if it is not defined,\r\n" 2740 + "the variable will be initialized by `value` attribute or inner data. It can be set to the following values:\r\n" 2741 + "- `default` is used to initialize the variable's value if it is not defined,\r\n" 2742 + "- `console` is used for input data by the user,\r\n" 2743 + "- `mandatory` is used for show the dialog if the variable is not defined and raises an error if the user select the 'Skip' input action.\r\n" 2744 + "- `file` is used for loadind variables from property file.\r\n" 2745 + "Attribute `type` defines a type of variable. It is used to define the variable type and special rules for the input dialog:\r\n" 2746 + "The `string` parameter is the default and is not required to define a string property, it should only be used to convert the variable's value to a string type.;\r\n" 2747 + "- `array` is used for create the value of the array type;\r\n" 2748 + "- `map` is used for create the value of the map type;\r\n" 2749 + "- `json` is used for create the property of the json type. Json should be defined as a inner text, don't use `value` attribute;\r\n" 2750 + "- `password` the user dialog text field will be masked;\r\n" 2751 + "- `path` a dialog for a dir opening will be shown; \r\n" 2752 + "- `text` type is used only for large text values, and the value is not saved in the preferences.") 2753 @CommandExamples({ 2754 "<Var name='type:property' init='enum:console|mandatory|default' type='enum:text|password|path'/>", 2755 "<Var name='type:property' type='enum:array|number|map|json|string' />", 2756 "<Var name='type:property' source='type:property' start='type:string' end='type:string'/>", 2757 "<Var name='type:property'><item>...</item></Var>", 2758 "<Var name='type:property' type='map'><item key='type:string'>...</item></Var>", 2759 "<Var name='type:property' type='enum:array|number' file='type:path'/>", 2760 "<Var init='file' file='type:path'/>", "<Var name='type:property' source='type:property' />" }) 2761 public void runCommandVar(final Node action) throws Throwable { 2762 final String name = attr(action, "name"); 2763 String description = attr(action, "description"); 2764 String theInit = attr(action, "init"); 2765 2766 Object theOldValue = getVariableValue(name); 2767 if ("default".equals(theInit) && theOldValue != null) { 2768 if (theOldValue instanceof String) { 2769 if (StringUtils.isNotBlank((String) theOldValue)) { 2770 return; 2771 } 2772 } else if (theOldValue instanceof String[] && ((String[]) theOldValue).length > 0) { 2773 return; 2774 } 2775 } 2776 2777 String file = attr(action, "file"); 2778 if ("file".equals(theInit)) { 2779 File propertiesFile = getFile(file); 2780 loadProperties(propertiesFile, this.variables, true); 2781 return; 2782 } 2783 2784 String source = replaceProperties(action.getAttribute("source"), true); 2785 Object theValue = getVariableValue(name); 2786 if (source != null) { 2787 String sourceVarName = replaceProperties(source); 2788 theValue = getVariableValue(sourceVarName); 2789 theOldValue = theValue; 2790 setVariableValue(name, theValue); 2791 } 2792 2793 String value = action.getAttribute("value"); 2794 if (value != null) { 2795 theValue = replaceProperties(value); 2796 } 2797 2798 // Item setting 2799 String type = StringUtils.defaultIfEmpty(action.getAttribute("type"), ""); 2800 if (action.size() > 0) { 2801 if (Node.TEXT_TEAG_NAME.equals(action.getNode(0).getTag())) { 2802 final Node theTextNode = action.getNode(0); 2803 theValue = replaceProperties(theTextNode.getText()); 2804 2805 } else { 2806 switch (type) { 2807 case "map": 2808 Node[] nodes = action.getNodes("item"); 2809 theValue = new LinkedHashMap<String, String>(); 2810 for (Node node : nodes) { 2811 ((Map) theValue).put(node.getAttribute("key"), replaceProperties(node.getInnerText())); 2812 } 2813 break; 2814 2815 default: 2816 List<Object> theItemArray = new ArrayList<>(); 2817 for (int i = 0; i < action.size(); i++) { 2818 final Node theCurrentAction = (Node) action.get(i); 2819 Node[] theItemNodes = theCurrentAction.getTextNodes(); 2820 if (theItemNodes != null && theItemNodes.length == 1) { 2821 final String replaceProperties = replaceProperties(theItemNodes[0].getText(), true); 2822 theItemArray.add(replaceProperties); 2823 } else { 2824 if (theCurrentAction.size() == 1) { 2825 Node node = theCurrentAction.get(0); 2826 EasyUtils.removeAllAttributes(node, Node.TAG_ID); 2827 theItemArray.add(replaceProperties(node.getXMLText(), true)); 2828 } else { 2829 theItemArray = null; 2830 } 2831 break; 2832 } 2833 } 2834 theValue = theItemArray; 2835 } 2836 } 2837 } 2838 2839 boolean contains = StringUtils.contains(theInit, "mandatory"); 2840 boolean isConsoleInput = StringUtils.contains(theInit, "console"); 2841 boolean mandatory = StringUtils.contains(theInit, "mandatory"); 2842 if (mandatory) { 2843 if (!isEmpty(theOldValue)) { 2844 setVariableValue(name, theValue); 2845 } else { 2846 isConsoleInput = true; 2847 } 2848 } 2849 2850 final boolean theIntegerType = "number".equals(type); 2851 if (theIntegerType) { 2852 String string = ObjectUtils.toString(theValue); 2853 if (StringUtils.isNotBlank(string) && !NumberUtils.isNumber(string)) { 2854 setVariableValue(name, null); 2855 throw new NumberFormatException(string); 2856 } 2857 } 2858 2859 final boolean theArrayType = "array".equals(type); 2860 if (name != null && !(theValue == null && theArrayType == false)) { 2861 if (theArrayType) { 2862 char separatorChar = ','; 2863 if (theValue instanceof String) { 2864 String[] split = StringUtils.split((String) theValue, separatorChar); 2865 theValue = split != null ? Arrays.asList(split) : null; 2866 } 2867 } 2868 if (theArrayType && theValue == null) { 2869 theValue = new String[0]; 2870 } 2871 2872 if (theArrayType && file != null) { 2873 ArrayList<String> buffer = new ArrayList<String>(); 2874 2875 if (theValue instanceof String[]) { 2876 for (String string : (String[]) theValue) { 2877 buffer.add(string); 2878 } 2879 } else if (theValue instanceof Collection) { 2880 buffer = new ArrayList<String>((Collection<String>) theValue); 2881 } 2882 2883 try (InputStream inputStream = AEUtils.getInputStream(file, getBaseDir())) { 2884 String encoding = replaceProperties(action.getAttribute("charset")); 2885 if (encoding == null) { 2886 encoding = "UTF-8"; 2887 } 2888 final BufferedReader theFileReader = new BufferedReader( 2889 new InputStreamReader(inputStream, encoding)); 2890 String readLine; 2891 while ((readLine = theFileReader.readLine()) != null) { 2892 buffer.add(readLine); 2893 } 2894 } 2895 theValue = buffer; 2896 } 2897 2898 theValue = convert(theValue, type); 2899 setVariableValue(name, theValue); 2900 } 2901 2902 if (isConsoleInput) { 2903 2904 if (theOldValue instanceof List && CollectionUtils.size(theOldValue) == 1) { 2905 theOldValue = ((List) theOldValue).get(0); 2906 } 2907 2908 if ((action.size() == 0 || !action.getInnerText().isEmpty()) 2909 && (isEmpty(theOldValue) || theOldValue instanceof String)) { 2910 Object theInitialSelectionValue = null; 2911 final Object o = getVariableValue(name); 2912 if (o instanceof String) { 2913 theInitialSelectionValue = o; 2914 } 2915 if (o instanceof String[] && ((String[]) o).length > 0) { 2916 if (this.random == null) { 2917 this.random = SecureRandom.getInstance("SHA1PRNG"); 2918 } 2919 theInitialSelectionValue = ((String[]) o)[this.random.nextInt(((String[]) o).length)]; 2920 } 2921 2922 String defaultValue = AEWorkspace.getInstance().getDefaultUserConfiguration(".inputValue." + name, 2923 (String) theInitialSelectionValue); 2924 if (this.recipeListener.getManager().isConsoleDefaultInput(name, description) 2925 && !(mandatory && defaultValue == null)) { 2926 2927 if ((o instanceof String[] && ArrayUtils.contains((String[]) o, defaultValue)) || o == null) { 2928 theInitialSelectionValue = defaultValue; 2929 } 2930 2931 setVariableValue(name, theInitialSelectionValue); 2932 } else { 2933 boolean notifyMe = this.recipeListener.isNotifyMe(); 2934 Object inputValue; 2935 2936 inputValue = this.recipeListener.getManager().inputValue(name, description, defaultValue, log, type, 2937 notifyMe, this); 2938 2939 setVariableValue(name, inputValue); 2940 } 2941 2942 } else { 2943 List<Object> possibleValues = null; 2944 if (theOldValue instanceof List) { 2945 @SuppressWarnings("unchecked") 2946 final List<Object> theStringArray = (List<Object>) theOldValue; 2947 possibleValues = theStringArray; 2948 } else if (theOldValue instanceof String[]) { 2949 possibleValues = new ArrayList<>(); 2950 String[] array = (String[]) theOldValue; 2951 for (String item : array) { 2952 possibleValues.add(item); 2953 } 2954 } else { 2955 possibleValues = new ArrayList<>(); 2956 for (int i = 0; i < action.size(); i++) { 2957 final Node theCurrentAction = (Node) action.get(i); 2958 possibleValues.add(theCurrentAction.getInnerText()); 2959 } 2960 } 2961 2962 if (this.recipeListener.getManager().isConsoleDefaultInput(name, null)) { 2963 String theInitialSelectionValue = randomSelect(possibleValues); 2964 if (!randomSelect) { 2965 theInitialSelectionValue = AEWorkspace.getInstance() 2966 .getDefaultUserConfiguration(".choiceValue." + name, theInitialSelectionValue); 2967 2968 } 2969 setVariableValue(name, theInitialSelectionValue); 2970 } else { 2971 boolean notifyMe = this.recipeListener.isNotifyMe(); 2972 final Object theValueOut = this.recipeListener.getManager().choiceValue(name, description, 2973 possibleValues.toArray(), log, notifyMe, this); 2974 setVariableValue(name, theValueOut); 2975 } 2976 } 2977 } 2978 2979 theValue = getVariableValue(name); 2980 2981 String start = replaceProperties(action.getAttribute("start")); 2982 String end = replaceProperties(action.getAttribute("end")); 2983 2984 if (StringUtils.isNotEmpty(start)) { 2985 if (theValue instanceof byte[]) { 2986 theValue = new String((byte[]) theValue); 2987 } 2988 theValue = StringUtils.substringAfter(ObjectUtils.toString(theValue), start); 2989 if (StringUtils.isBlank((String) theValue)) { 2990 theValue = null; 2991 } 2992 } 2993 if (StringUtils.isNotEmpty(end)) { 2994 theValue = StringUtils.substringBefore((String) theValue, end); 2995 if (StringUtils.isBlank((String) theValue)) { 2996 theValue = null; 2997 } 2998 } 2999 3000 applyResult(action, name, theValue); 3001 3002 if (contains && theValue == null) { 3003 stop(); 3004 } 3005 } 3006 3007 private Object convert(Object theValue, String type) throws JSONException { 3008 if ("json".equals(type)) { 3009 Object parse = theValue; 3010 if (theValue instanceof String[]) { 3011 String[] array = (String[]) theValue; 3012 List<String> asList = Arrays.asList(array); 3013 theValue = new JSONArray(asList); 3014 } else if (theValue instanceof String) { 3015 if (StringUtils.startsWith((String) theValue, "{")) { 3016 parse = new JSONObject(theValue.toString()); 3017 } else if (StringUtils.startsWith((String) theValue, "[")) { 3018 parse = new JSONArray(theValue.toString()); 3019 } 3020 } 3021 theValue = parse; 3022 } else if ("string".equals(type) && theValue instanceof String[]) { 3023 theValue = StringUtils.join((String[]) theValue); 3024 } 3025 3026 return theValue; 3027 } 3028 3029 private String randomSelect(List possibleValues) throws NoSuchAlgorithmException { 3030 final String theInitialSelectionValue; 3031 if (this.random == null) { 3032 this.random = SecureRandom.getInstance("SHA1PRNG"); 3033 } 3034 theInitialSelectionValue = (String) possibleValues.get(this.random.nextInt(possibleValues.size())); 3035 return theInitialSelectionValue; 3036 } 3037 3038 @CommandExamples({ "<Note name='type:string' connection='type:string'>...</Note>" }) 3039 public void runCommandNote(final Node aCurrentAction) throws Throwable { 3040 } 3041 3042 @CommandExamples({ "<!-- ... -->" }) 3043 public void runCommandComment(final Node aCurrentAction) throws Throwable { 3044 } 3045 3046 @CommandExamples({ "<Extern class='type:processor'>\n...\n</Extern>" }) 3047 public void runCommandExtern(final Node command) throws Throwable { 3048 final Processor newInstance = makeProcessor(command); 3049 boolean success = false; 3050 try { 3051 this.externProcessor = newInstance; 3052 externProcessor.setTestName(getTestName()); 3053 newInstance.init(this, command); 3054 boolean rootRecipe = isRootRecipe(); 3055 newInstance.setRootContext(rootRecipe); 3056 newInstance.stackTask.addAll(this.stackTask); 3057 newInstance.taskNode(command, false); 3058 3059 this.variables = newInstance.variables; 3060 success = true; 3061 } finally { 3062 externProcessor.complete(success); 3063 externProcessor = null; 3064 } 3065 } 3066 3067 @CommandDescription("The Confirm command is used to confirm the user's decision to continue a " 3068 + "task or execute internal code. The name attribute is used as the dialog box title " 3069 + "and as a variable name if confirmation is required without displaying a dialog box " 3070 + "to the user. If the variable exists, confirmation is automatically applied if the " 3071 + "value is true, or rejected if the value is not true.") 3072 @CommandExamples({ "<Confirm message='type:string' name='type:string'/>", 3073 "<Confirm message='type:string' name='type:string'>...</Confirm>" }) 3074 public void runCommandConfirm(final Node aCurrentAction) throws Throwable { 3075 final String message = attr(aCurrentAction, "message"); 3076 final String name = attr(aCurrentAction, "name"); 3077 final Object nameVar = getVariableValue(name); 3078 3079 boolean confirmed = false; 3080 if (nameVar != null && nameVar instanceof String) { 3081 confirmed = BooleanUtils.toBoolean((String) nameVar); 3082 } else { 3083 AEManager manager = this.recipeListener.getManager(); 3084 confirmed = manager.confirmation(name, message, this, this.recipeListener.isNotifyMe()); 3085 } 3086 3087 if (aCurrentAction.size() > 0) { 3088 if (confirmed) { 3089 taskNode(aCurrentAction, false); 3090 } 3091 } else { 3092 if (!confirmed) { 3093 stop(); 3094 } 3095 } 3096 } 3097 3098 @CommandDescription("The WhileRun command is used to notify the user that a process has started. " 3099 + "The user can close the WhileRun dialog box without affecting the process's execution. " 3100 + "The dialog box closes after the code within the WhileRun tag completes execution. " 3101 + "The name attribute is used as the dialog box title and variable name. " 3102 + "If the dialog box should not be opened, a variable with a name corresponding to " 3103 + "the dialog box name should be assigned a value other than `true`.") 3104 @CommandExamples({ "<WhileRun name='type:string' message='type:string'>...</WhileRun>" }) 3105 public void runCommandWhileRun(final Node aCurrentAction) throws Throwable { 3106 final String message = attr(aCurrentAction, "message"); 3107 final String name = attr(aCurrentAction, "name"); 3108 final Object nameVar = getVariableValue(name); 3109 3110 if (!(nameVar instanceof String) || BooleanUtils.toBoolean((String) nameVar)) { 3111 AEManager manager = this.recipeListener.getManager(); 3112 MessageHandler handler = manager.message(this, name, message, this.recipeListener.isNotifyMe()); 3113 taskNode(aCurrentAction, false); 3114 handler.close(); 3115 } 3116 }; 3117 3118 @CommandDescription("The `Server` command initializes and starts a server socket listener, " 3119 + "operating as a blocking command that prevents the execution of subsequent commands until it completes.") 3120 @CommandExamples({ "<Server port='type:integer' request='type:property' response='type:property' > ... </Server>", 3121 "<Server port='type:integer' numbers='type:integer' request='type:property' response='type:property' > ... </Server>" }) 3122 public void runCommandServer(final Node aCurrentAction) throws Throwable { 3123 3124 String encoding = replaceProperties(aCurrentAction.getAttribute("charset")); 3125 if (encoding == null) { 3126 encoding = "UTF-8"; 3127 } 3128 3129 final int thePort = Integer.parseInt(replaceProperties(aCurrentAction.getAttribute("port"))); 3130 int maxNumber = 0; 3131 3132 final String theMaxNumbers = replaceProperties(aCurrentAction.getAttribute("numbers")); 3133 if (theMaxNumbers != null) { 3134 maxNumber = Integer.parseInt(theMaxNumbers); 3135 } 3136 final String request = attr(aCurrentAction, "request"); 3137 final String response = attr(aCurrentAction, "response"); 3138 3139 ServerAction server = new ServerAction(this, aCurrentAction, thePort, request, response, encoding, maxNumber); 3140 server.perform(); 3141 } 3142 3143 @CommandExamples({ "<Restore/>", "<Restore name='type:property'> ... </Restore>", 3144 "<Restore except='type:property'> ... </Restore>" }) 3145 public void runCommandRestore(final Node command) throws Throwable { 3146 String name = attr(command, "name"); 3147 String except = attr(command, "except"); 3148 3149 if (CollectionUtils.isEmpty(command)) { 3150 final Map<String, Object> systemVariables = getListener().getManager().getSystemVariables(); 3151 this.variables.clear(); 3152 this.variables.putAll(systemVariables); 3153 debug("Task variables has been restored."); 3154 } else if (name != null) { 3155 Object savedVariable = getVariableValue(name); 3156 taskNode(command, false); 3157 setVariableValue(name, savedVariable); 3158 3159 } else if (except != null) { 3160 Map<String, Object> savedVariables = this.variables; 3161 this.variables = new HashMap<String, Object>(this.variables); 3162 3163 taskNode(command, false); 3164 3165 Object object = getVariableValue(except); 3166 this.variables.clear(); 3167 this.variables = savedVariables; 3168 setVariableValue(except, object); 3169 } 3170 3171 } 3172 3173 @CommandDescription("Use the Finally command tag before protected code to ensure it is executed " 3174 + "before exiting the current parent tag, as usual Finally is defined as a first executable command.\n" 3175 + "Example: `<Recipe><Finally> ...finally code... </Finally> ...some code... </Recipe>`") 3176 @CommandExamples({ "<Finally> ... </Finally>" }) 3177 public void runCommandFinally(final Node aCurrentAction) throws Throwable { 3178 } 3179 3180 @CommandExamples({ "<Break/>" }) 3181 public void runCommandBreak(final Node aCurrentAction) throws Throwable { 3182 } 3183 3184 @CommandExamples({ "<Stop/>", "<Stop ifNull='type:property'/>" }) 3185 public void runCommandStop(final Node aCurrentAction) throws Throwable { 3186 } 3187 3188 private boolean isEmpty(Object theOldValue) { 3189 boolean result = false; 3190 if (theOldValue == null) { 3191 result = true; 3192 } else if (theOldValue instanceof String[] && ((String[]) theOldValue).length == 0) { 3193 result = true; 3194 } else if (theOldValue instanceof Map && ((Map) theOldValue).size() == 0) { 3195 result = true; 3196 } 3197 return result; 3198 } 3199 3200 public void setVariableValue(String name, final Object value) { 3201 if (name != null) { 3202 super.setVariableValue(name, value); 3203 if (this.recipeListener != null && name.startsWith("!") == false) { 3204 this.recipeListener.changeVariable(name, value); 3205 } 3206 } 3207 } 3208 3209}