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 Object variableValue = getVariableValue(theArrayName); 974 if (variableValue instanceof List) { 975 variableValue = ((List) variableValue).toArray(new String[0]); 976 } 977 if (variableValue instanceof String[]) { 978 final String[] variableValueArray = (String[]) variableValue; 979 for (int i = 0; i < variableValueArray.length; i++) { 980 if (theValue.equals(variableValueArray[i])) { 981 setVariableValue(theName, "true"); 982 return; 983 } 984 } 985 } 986 987 if (variableValue instanceof String) { 988 final String value = (String) variableValue; 989 if (theValue.equals(value)) { 990 setVariableValue(theName, "true"); 991 return; 992 } 993 } 994 setVariableValue(theName, "false"); 995 } else { 996 if (theText == null || theValue == null || theName == null) { 997 return; 998 } 999 final int indexOf = theText.indexOf(theValue); 1000 setVariableValue(theName, indexOf >= 0 ? "true" : "false"); 1001 } 1002 } 1003 1004 @CommandExamples({ "<Load name='type:property' init='console'/>", 1005 "<Load name='type:property' init='console'/>...</Load>", "<Load name='type:property' file='type:path'/>", 1006 "<Load name='type:property' file='type:path' timeout='type:time' mode='enum:default|noreplace|escapeJS|bytes'/>", 1007 "<Load name='type:property' url='type:url' timeout='type:time' mode='enum:default|noreplace|escapeJS|bytes'/>" }) 1008 public void runCommandLoad(final Node action) throws Throwable { 1009 final boolean noreplace = "noreplace".equals(action.getAttribute("mode")); 1010 final boolean isArray = "array".equals(action.getAttribute("mode")); 1011 final boolean defaultMode = "default".equals(action.getAttribute("mode")); 1012 final boolean escapeJSValue = "escapeJS".equals(action.getAttribute("mode")); 1013 final boolean bytes = "bytes".equals(action.getAttribute("mode")); 1014 final boolean base64 = "base64".equals(action.getAttribute("mode")); 1015 1016 String theEncoding = replaceProperties(action.getAttribute("encoding")); 1017 if (theEncoding == null) { 1018 theEncoding = "UTF-8"; 1019 } 1020 1021 final String name = replaceProperties(action.getAttribute("name")); 1022 int timeout = (int) attrTime(action, "timeout", "0"); 1023 1024 final String theUrl = replaceProperties(action.getAttribute("url")); 1025 if (theUrl != null) { 1026 URL url = new URL(theUrl); 1027 long startTime = System.currentTimeMillis(); 1028 while (timeout == 0 || System.currentTimeMillis() < startTime + timeout) { 1029 if (isStopped()) { 1030 break; 1031 } 1032 try { 1033 URLConnection openConnection = url.openConnection(); 1034 openConnection.setReadTimeout(timeout); 1035 byte[] byteArray = IOUtils.toByteArray(openConnection.getInputStream()); 1036 if (escapeJSValue) { 1037 String data = StringEscapeUtils.escapeJavaScript(new String(byteArray)); 1038 setVariableValue(name, data); 1039 } else { 1040 setVariableValue(name, new String(byteArray, theEncoding)); 1041 } 1042 return; 1043 1044 } catch (IOException e) { 1045 if (timeout == 0) { 1046 throw e; 1047 } 1048 quietWait(200); 1049 } 1050 } 1051 } 1052 1053 String filePath = (String) attr(action, "file"); 1054 final boolean theDialog = isActiveInitFor(action, "console"); 1055 1056 if (theDialog) { 1057 File theFile = null; 1058 if (filePath != null) { 1059 theFile = getFile(filePath); 1060 } 1061 filePath = this.recipeListener.getManager().inputFile(name, null, theFile, log, this); 1062 } 1063 1064 if (filePath != null) { 1065 if (base64 || bytes) { 1066 File file = getFile(filePath); 1067 debug("Loading file: " + file); 1068 if (file.exists()) { 1069 try (FileInputStream input = new FileInputStream(file)) { 1070 Object byteArray = IOUtils.toByteArray(input); 1071 if (base64) { 1072 byteArray = Base64.getEncoder().encodeToString((byte[]) byteArray); 1073 } 1074 setVariableValue(name, byteArray); 1075 } 1076 } else { 1077 throw new FileNotFoundException(file.getAbsolutePath()); 1078 } 1079 return; 1080 } 1081 1082 int iterations = timeout / 1000; 1083 if (iterations == 0) { 1084 iterations = 1; 1085 } 1086 for (int i = 0; i < iterations; i++) { 1087 if (isStopped()) { 1088 break; 1089 } 1090 try { 1091 1092 if (!action.isEmpty()) { 1093 File file = getFile(filePath); 1094 try (BufferedReader reader = new BufferedReader(new FileReader(file))) { 1095 String line; 1096 while ((line = reader.readLine()) != null) { 1097 if (this.breakFlag > 0) { 1098 break; 1099 } 1100 setVariableValue(name, line); 1101 taskNode(action, false); 1102 } 1103 } finally { 1104 if (this.breakFlag > 0) { 1105 breakFlag--; 1106 } 1107 } 1108 1109 } else { 1110 1111 String text = AEUtils.loadResource(filePath, getBaseDir(), theEncoding); 1112 if (!noreplace) { 1113 text = replaceProperties(text, defaultMode); 1114 } 1115 1116 if (escapeJSValue) { 1117 String data = StringEscapeUtils.escapeJavaScript(text); 1118 setVariableValue(name, data); 1119 } else { 1120 Object val = text; 1121 if (isArray) { 1122 val = StringUtils.split(text, "\n\r"); 1123 } 1124 1125 setVariableValue(name, val); 1126 } 1127 } 1128 1129 break; 1130 1131 } catch (final FileNotFoundException e) { 1132 if (timeout == 0) { 1133 throw e; 1134 } 1135 quietWait(1000); 1136 } catch (final Exception e) { 1137 throw e; 1138 } 1139 } 1140 } else { 1141 if (isActiveInitFor(action, "mandatory")) { 1142 stop(); 1143 } 1144 } 1145 1146 } 1147 1148 @CommandExamples({ "<Remove name='type:property'/>", "<Remove file='type:path'/>", 1149 "<Remove name='type:property' history='type:boolean'/>" }) 1150 public void runCommandRemove(final Node aCurrentAction) throws Throwable { 1151 String theFileName = aCurrentAction.getAttribute("file"); 1152 if (theFileName != null) { 1153 theFileName = replaceProperties(theFileName); 1154 final File theFile = getFile(theFileName); 1155 if (theFile.isDirectory()) { 1156 FileUtils.deleteDirectory(theFile); 1157 } else { 1158 if (theFile.delete() == false) { 1159 new Exception("File '" + theFile.getAbsolutePath() + "' is not deleted."); 1160 } 1161 } 1162 } 1163 String theVarName = aCurrentAction.getAttribute("name"); 1164 if (theVarName != null) { 1165 theVarName = replaceProperties(theVarName); 1166 setVariableValue(theVarName, null); 1167 if (Boolean.parseBoolean((attr(aCurrentAction, "history")))) { 1168 AEWorkspace.getInstance().setDefaultUserConfiguration(".inputValue." + theVarName, null); 1169 } 1170 } 1171 } 1172 1173 @CommandDescription("Load the web page by `url`. The response is stored in the variable defined by the `name` attribute.\r\n" 1174 + "The optional `auth` attribute set the 'Authorization' header of request.") 1175 @CommandExamples({ "<Get name='type:property' url='type:url' /> ", 1176 "<Get name='type:property' url='type:url' timeout='type:time'/> ", 1177 "<Get name='type:property' url='type:url' auth='type:string' mediaType='type:string' retries='1' timeout='type:time'/> " }) 1178 public void runCommandGet(final Node action) throws Throwable { 1179 1180 String mediaTypeStr = attr(action, "mediaType"); 1181 mediaTypeStr = StringUtils.defaultIfEmpty(mediaTypeStr, "application/json"); 1182 1183 String url = attr(action, "url"); 1184 String auth = attr(action, "auth"); 1185 int retries = Integer.parseInt(attr(action, "retries", "1")); 1186 1187 Builder builder = new okhttp3.Request.Builder().url(url).get(); 1188 1189 if (mediaTypeStr != null) { 1190 builder.addHeader("Content-Type", mediaTypeStr); 1191 } 1192 if (auth != null) { 1193 builder.addHeader("Authorization", auth); 1194 } 1195 1196 okhttp3.Request request = builder.build(); 1197 1198 for (int i = 0; i < retries; i++) { 1199 if (isStopped()) { 1200 break; 1201 } 1202 1203 try { 1204 okhttp3.OkHttpClient.Builder newBuilder = new OkHttpClient().newBuilder(); 1205 long timeout = attrTime(action, "timeout", "0"); 1206 if (timeout > 0) { 1207 newBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS); 1208 newBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS); 1209 newBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS); 1210 } 1211 OkHttpClient client = newBuilder.build(); 1212 1213 try (Response response = client.newCall(request).execute()) { 1214 String value = new String(response.body().bytes(), "UTF-8"); 1215 String name = attr(action, "name"); 1216 if (name != null) { 1217 setVariableValue(name, value); 1218 } 1219 } 1220 1221 break; 1222 } catch (SocketTimeoutException e) { 1223 if (i == 2) { 1224 throw e; 1225 } 1226 } 1227 } 1228 } 1229 1230 @CommandDescription("Post the body defined by the `body` attribute to `url`. " 1231 + "The response is stored in the variable defined by the `name` attribute. " 1232 + "If the body is not simple, you should create a special variable and pass its value via $var{}.\r\n" 1233 + "The optional `auth` attribute set the 'Authorization' header of request.") 1234 @CommandExamples({ "<Post name='type:property' url='type:url' body='type:string'/> ", 1235 "<Post name='type:property' url='type:url' body='type:string' timeout='type:time'/> ", 1236 "<Post name='type:property' url='type:url' auth='type:string' body='type:string' mediaType='type:string' timeout='type:time'/> " }) 1237 public void runCommandPost(final Node aCurrentAction) throws Throwable { 1238 String mediaTypeStr = attr(aCurrentAction, "mediaType"); 1239 mediaTypeStr = StringUtils.defaultIfEmpty(mediaTypeStr, "application/json"); 1240 1241 String url = attr(aCurrentAction, "url"); 1242 String auth = attr(aCurrentAction, "auth"); 1243 String bodyStr = StringUtils.defaultIfEmpty(replaceProperties(aCurrentAction.getAttribute("body")), ""); 1244 1245 MediaType mediaType = MediaType.parse(mediaTypeStr); 1246 okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType, bodyStr); 1247 1248 Builder addHeader = new okhttp3.Request.Builder().url(url).method("POST", body).addHeader("Content-Type", 1249 mediaTypeStr); 1250 if (auth != null) { 1251 addHeader = addHeader.addHeader("Authorization", auth); 1252 } 1253 okhttp3.Request request = addHeader.addHeader("Accept", "*/*").build(); 1254 okhttp3.OkHttpClient.Builder newBuilder = new OkHttpClient().newBuilder(); 1255 1256 long timeout = attrTime(aCurrentAction, "timeout", "0"); 1257 if (timeout > 0) { 1258 newBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS); 1259 newBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS); 1260 newBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS); 1261 } 1262 OkHttpClient client = newBuilder.build(); 1263 try (Response response = client.newCall(request).execute()) { 1264 String value = new String(response.body().bytes(), "UTF-8"); 1265 1266// if(response.code() == 404) { 1267// 1268// } 1269 1270 String name = attr(aCurrentAction, "name"); 1271 if (name != null) { 1272 setVariableValue(name, value); 1273 } 1274 } 1275 } 1276 1277 @CommandDescription("This method allows you to send either HTTP(S) requests or raw socket messages, " 1278 + "with support for parameters, headers, authentication, and response handling." 1279 + "The `Request` command executes a network request based on the configuration attributes:\r\n" 1280 + "- `method`: Specifies the type of network request to perform. Common values are `get` for HTTP GET requests, " 1281 + "`post` for HTTP POST requests, or `socket` for raw socket communication.\r\n" 1282 + "- `request`: is the name of the request map variable which contains the data to be sent with the request. " 1283 + "For HTTP requests, this may include the body and headers. For socket communication, " 1284 + "it can be a string or byte array representing the message to send. \r\n" 1285 + "- `response` is the name of the variable where the response data will be stored after the request is executed. " 1286 + "This allows later access to the result of the network operation.\r\n" 1287 + "- `host` is the target URL or host address for the network request. " 1288 + "For HTTP(S), this is the full URL. For socket communication, " 1289 + "it is typically in the format `hostname:port`\r\n" 1290 + "- `timeout` is the maximum time to wait for the network request to complete before aborting.\r\n" 1291 + "- `userName` is the username used for HTTP Basic Authentication, if required by the target server.\r\n" 1292 + "- `password` is the password used for HTTP Basic Authentication, paired with `userName`.\r\n") 1293 @CommandExamples({ 1294 "<Request method='enum:socket|get|post' request='type:property[Map{header;body}]' " 1295 + "response='type:property[Map{status;body}]' host='type:url' timeout='type:time'/>", 1296 "<Request method='get' response='type:property[Map{status;body}]' host='type:url' " 1297 + "userName='type:string' password='type:string'><param name='type:string' value='type:string'/></Request>" }) 1298 public void runCommandRequest(final Node aCurrentAction) throws Throwable { 1299 Object request = attrValue(aCurrentAction, "request"); 1300 final String responseName = attr(aCurrentAction, "response"); 1301 final String method = StringUtils.defaultIfEmpty(attr(aCurrentAction, "method"), "socket"); 1302 String theUrl = attr(aCurrentAction, "host"); 1303 1304 String userName = attr(aCurrentAction, "userName"); 1305 String password = attr(aCurrentAction, "password"); 1306 HttpClientContext localContext = null; 1307 if (userName != null && password != null) { 1308 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 1309 credentialsProvider.setCredentials(AuthScope.ANY, 1310 new UsernamePasswordCredentials(userName + ":" + password)); 1311 localContext = HttpClientContext.create(); 1312 localContext.setCredentialsProvider(credentialsProvider); 1313 } 1314 1315 HttpRequestBase httpRequest = null; 1316 1317 final Node[] paramNodes = aCurrentAction.getNodes("param"); 1318 if (paramNodes != null && paramNodes.length > 0) { 1319 List<NameValuePairImplementation> parameters = new ArrayList<>(); 1320 for (Node node : paramNodes) { 1321 String name = attr(node, "name"); 1322 String value = attr(node, "value"); 1323 1324 NameValuePairImplementation nameValuePair = new NameValuePairImplementation(name, value); 1325 parameters.add(nameValuePair); 1326 } 1327 1328 String query = URLEncodedUtils.format(parameters, "UTF-8"); 1329 theUrl = theUrl + "?" + query; 1330 } 1331 1332 if (theUrl.toLowerCase().startsWith("http")) { 1333 if ("get".equalsIgnoreCase(method)) { 1334 httpRequest = new HttpGet(theUrl); 1335 1336 } else if ("post".equalsIgnoreCase(method)) { 1337 HttpPost httpPost = new HttpPost(theUrl); 1338 if (request != null) { 1339 @SuppressWarnings("unchecked") 1340 final Map<String, Object> requestMap = (Map<String, Object>) request; 1341 Object bodyObject = requestMap.get("body"); 1342 byte[] body = null; 1343 if (bodyObject instanceof byte[]) { 1344 body = (byte[]) bodyObject; 1345 } else if (bodyObject instanceof String) { 1346 body = ((String) bodyObject).getBytes(); 1347 } 1348 httpPost.setEntity(new ByteArrayEntity(body)); 1349 } 1350 httpRequest = httpPost; 1351 1352 } 1353 int timeout = (int) attrTime(aCurrentAction, "timeout", "5000"); 1354 1355 RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout) 1356 .setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build(); 1357 httpRequest.setConfig(config); 1358 if (request != null) { 1359 @SuppressWarnings("unchecked") 1360 Object headerObj = ((Map<String, Object>) request).get("header"); 1361 if (headerObj instanceof Map) { 1362 @SuppressWarnings("unchecked") 1363 Map<String, String> header = (Map<String, String>) headerObj; 1364 if (header != null) { 1365 Set<Entry<String, String>> entrySet = header.entrySet(); 1366 for (Entry<String, String> entry : entrySet) { 1367 httpRequest.setHeader(entry.getKey(), entry.getValue()); 1368 } 1369 } 1370 } else { 1371 String header = String.valueOf(headerObj); 1372 httpRequest.setHeader(StringUtils.substringBefore(header, ":").trim(), 1373 StringUtils.substringAfter(header, ":").trim()); 1374 } 1375 } 1376 1377 CloseableHttpResponse response; 1378 CloseableHttpClient httpclient = HttpClients.createDefault(); 1379 1380 long duration = System.currentTimeMillis(); 1381 if (localContext == null) { 1382 response = httpclient.execute(httpRequest); 1383 } else { 1384 response = httpclient.execute(httpRequest, localContext); 1385 } 1386 ByteArrayOutputStream body = new ByteArrayOutputStream(); 1387 response.getEntity().writeTo(body); 1388 1389 HashMap<String, Object> aValue = new HashMap<String, Object>(); 1390 aValue.put("body", body.toByteArray()); 1391 aValue.put("status", response.getStatusLine().getStatusCode()); 1392 aValue.put("header", response.getAllHeaders()); 1393 aValue.put("duration", Long.toString(System.currentTimeMillis() - duration)); 1394 setVariableValue(responseName, aValue); 1395 1396 } else { 1397 1398 Socket socket = null; 1399 try { 1400 String host = StringUtils.substringBefore(theUrl, ":"); 1401 int port = Integer.parseInt(StringUtils.defaultIfBlank(StringUtils.substringAfter(theUrl, ":"), "80")); 1402 socket = new Socket(host, port); 1403 OutputStream outputStream = socket.getOutputStream(); 1404 if (request instanceof byte[]) { 1405 IOUtils.write((byte[]) request, outputStream); 1406 } else if (request instanceof String) { 1407 IOUtils.write(((String) request).getBytes(), outputStream); 1408 } 1409 InputStream inputStream = socket.getInputStream(); 1410 byte[] response = IOUtils.toByteArray(inputStream); 1411 setVariableValue(responseName, response); 1412 } finally { 1413 if (socket != null) { 1414 socket.close(); 1415 } 1416 } 1417 1418 } 1419 1420 } 1421 1422 @CommandExamples({ "<Table name='The table of variables'><var name='type:property' value='type:string'/></Table>", 1423 "<Table name='The table of variables' file='type:path'><var name='type:property' value='type:string'/></Table>" }) 1424 public void runCommandTable(final Node aCurrentVar) throws Throwable { 1425 AEManager manager = this.recipeListener.getManager(); 1426 boolean notifyMe = this.recipeListener.isNotifyMe(); 1427 1428 manager.inputDataTable(this, aCurrentVar, notifyMe); 1429 } 1430 1431 @SuppressWarnings({ "unchecked" }) 1432 @CommandExamples({ "<Loop name='type:property' source='type:property' numbers='type:integer'> ... </Loop>", 1433 "<Loop numbers='type:integer'> ... </Loop>", 1434 "<Loop name='type:property' query='select * from ...' maxCount='type:integer'> ... </Loop>" }) 1435 public void runCommandLoop(final Node aCurrentVar) throws Throwable { 1436 String theDescript = replaceProperties(aCurrentVar.getAttribute("description")); 1437 final String theNameAttribut = attr(aCurrentVar, "name"); 1438 boolean mainLoopCommand = false; 1439 1440 final String theNumbers = StringUtils.trimToNull(replaceProperties(aCurrentVar.getAttribute("numbers"))); 1441 if (theDescript == null) { 1442 theDescript = "Loop"; 1443 } 1444 String sourceAttribute = aCurrentVar.getAttribute("source"); 1445 final String theAttribut = replaceProperties(sourceAttribute); 1446 Object loopArrayVar = null; 1447 if (theAttribut != null) { 1448 loopArrayVar = getVariableValue(theAttribut); 1449 } 1450 List<Object> theLoopArray = new ArrayList<>(); 1451 if (loopArrayVar != null) { 1452 if (loopArrayVar instanceof String) { 1453 theLoopArray.add((String) loopArrayVar); 1454 } else if (loopArrayVar instanceof String[]) { 1455 List<String> asList = Arrays.asList((String[]) loopArrayVar); 1456 theLoopArray.addAll(asList); 1457 } else if (loopArrayVar instanceof List) { 1458 theLoopArray = (List<Object>) loopArrayVar; 1459 } else if (loopArrayVar instanceof Map) { 1460 Map map = (Map) loopArrayVar; 1461 theLoopArray.addAll(map.keySet()); 1462 } else if (loopArrayVar instanceof JSONArray) { 1463 JSONArray array = (JSONArray) loopArrayVar; 1464 theLoopArray = array.toList(); 1465 } else { 1466 return; 1467 } 1468 } 1469 int theCount = 0; 1470 boolean infinityLoop = false; 1471 if (StringUtils.isNotBlank(theNumbers)) { 1472 theCount = (int) Double.parseDouble(theNumbers); 1473 } else { 1474 if (!theLoopArray.isEmpty()) { 1475 theCount = theLoopArray.size(); 1476 } else { 1477 infinityLoop = sourceAttribute == null; 1478 } 1479 } 1480 if (isLoopActivated() == false) { 1481 this.mainLoopCommand = true; 1482 mainLoopCommand = true; 1483 } 1484 1485 if (infinityLoop) { 1486 this.mainLoopCommand = false; 1487 } 1488 try { 1489 for (int i = 0; (i < theCount || infinityLoop) && isStopped() == false; i++) { 1490 if (isStopped()) { 1491 break; 1492 } 1493 if (this.breakFlag > 0) { 1494 break; 1495 } 1496 if ((mainLoopCommand && !infinityLoop) || theCount > 5) { 1497 this.recipeListener.setProgress(theDescript, theCount, i, false); 1498 } 1499 startCommandInformation(aCurrentVar); 1500 if (theLoopArray != null && theLoopArray.size() > 0) { 1501 if (i < theLoopArray.size()) { 1502 setVariableValue(theNameAttribut, theLoopArray.get(i)); 1503 } 1504 } else { 1505 setVariableValue(theNameAttribut, ObjectUtils.toString(i)); 1506 } 1507 taskNode(aCurrentVar, false); 1508 } 1509 } finally { 1510 if (this.breakFlag > 0) { 1511 breakFlag--; 1512 } 1513 if (mainLoopCommand) { 1514 this.mainLoopCommand = false; 1515 } 1516 } 1517 } 1518 1519 @CommandExamples({ "<Append name='type:property' value='type:string' type='enum:all|unique'/>", 1520 "<Append name='type:property'>...</Append>" }) 1521 public synchronized void runCommandAppend(final Node aCurrentVar) throws Throwable { 1522 final String name = aCurrentVar.getAttribute("name"); 1523 Object value = attr(aCurrentVar, "value"); 1524 if (value == null) { 1525 value = replaceProperties(aCurrentVar.getInnerText()); 1526 } 1527 1528 String source = aCurrentVar.getAttribute("source"); 1529 if (source != null) { 1530 value = getVariableValue(replaceProperties(source)); 1531 } 1532 1533 Object theOldValue = getVariableValue(name, false); 1534 if (theOldValue == null) { 1535 return; 1536 } 1537 1538 theOldValue = checkForArray(theOldValue); 1539 1540 if (theOldValue instanceof String) { 1541 final String theValue1 = (String) theOldValue; 1542 value = theValue1 + value; 1543 setVariableValue(name, value); 1544 1545 } else if (theOldValue instanceof Object[]) { 1546 final boolean theUniqueTypeAttribut = "unique".equals(aCurrentVar.getAttribute("type")); 1547 1548 final Object[] theValue1 = (Object[]) theOldValue; 1549 Object[] theValue2 = null; 1550 1551 if (theUniqueTypeAttribut) { 1552 Set<Object> targetSet = new LinkedHashSet<>(Arrays.asList(theValue1)); 1553 if (value instanceof String) { 1554 targetSet.add((String) value); 1555 } else if (value instanceof String[]) { 1556 for (String val : (String[]) value) { 1557 targetSet.add(val); 1558 } 1559 } 1560 1561 String[] array = new String[targetSet.size()]; 1562 Iterator<Object> iterator = targetSet.iterator(); 1563 int i = 0; 1564 while (iterator.hasNext()) { 1565 String object = (String) iterator.next(); 1566 array[i++] = object; 1567 } 1568 1569 theValue2 = array; 1570 1571 } else { 1572 List<Object> targetList = new ArrayList<>(Arrays.asList(theValue1)); 1573 if (value instanceof String[]) { 1574 for (String val : (String[]) value) { 1575 targetList.add(val); 1576 } 1577 } else { 1578 targetList.add((String) value); 1579 } 1580 1581 String[] array = new String[targetList.size()]; 1582 int i = 0; 1583 for (Object val : targetList) { 1584 if (val instanceof String) { 1585 array[i++] = (String) val; 1586 } else { 1587 array[i++] = ObjectUtils.toString(val); 1588 } 1589 } 1590 1591 theValue2 = array; 1592 } 1593 1594 applyResult(aCurrentVar, name, theValue2); 1595 } 1596 } 1597 1598 private Object checkForArray(Object theOldValue) { 1599 if (theOldValue instanceof List) { 1600 theOldValue = ((List) theOldValue).toArray(); 1601 } 1602 return theOldValue; 1603 } 1604 1605 @CommandExamples({ "<Rnd name='type:property' symbols='type:integer' type='enum:number|default'/>", 1606 "<Rnd name='type:property' type='uuid'/>", 1607 "<Rnd name='type:property' symbols='type:integer' startCode='type:integer' endCode='type:integer'/>", 1608 "<Rnd name='type:property' source='type:property'/>" }) 1609 public void runCommandRnd(final Node aCurrentVar) throws Throwable { 1610 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 1611 String result = null; 1612 String type = replaceProperties(aCurrentVar.getAttribute("type")); 1613 1614 if ("uuid".equalsIgnoreCase(type)) { 1615 result = UUID.randomUUID().toString(); 1616 1617 } else { 1618 final String source = replaceProperties(aCurrentVar.getAttribute("source")); 1619 if (this.random == null) { 1620 this.random = SecureRandom.getInstance("SHA1PRNG"); 1621 } 1622 if (source != null) { 1623 Object value = getVariableValue(source); 1624 if (value instanceof String[]) { 1625 String[] strings = (String[]) value; 1626 int index = this.random.nextInt(strings.length); 1627 value = strings[index]; 1628 } 1629 setVariableValue(theNameAttribut, value); 1630 return; 1631 } 1632 1633 final String theSymbolsAttribut = replaceProperties(aCurrentVar.getAttribute("symbols")); 1634 1635 final long theBegNum = Long.parseLong(theSymbolsAttribut); 1636 int startCode = 0x41; 1637 final String startCodeStr = replaceProperties(aCurrentVar.getAttribute("startCode")); 1638 if (startCodeStr != null) { 1639 if (startCodeStr.indexOf('x') > 0) { 1640 startCode = Integer.parseInt(startCodeStr.substring(2), 16); 1641 } else { 1642 startCode = Integer.parseInt(startCodeStr); 1643 } 1644 type = "-number"; 1645 } 1646 1647 int endCode = 0x05A; 1648 final String endCodeStr = replaceProperties(aCurrentVar.getAttribute("endCode")); 1649 if (endCodeStr != null) { 1650 if (endCodeStr.indexOf('x') > 0) { 1651 endCode = Integer.parseInt(endCodeStr.substring(2), 16); 1652 } else { 1653 endCode = Integer.parseInt(endCodeStr); 1654 } 1655 type = "-number"; 1656 } 1657 1658 if ("number".equals(type) == false) { 1659 final StringBuffer theBuffer = new StringBuffer(); 1660 for (int i = 0; i < theBegNum; i++) { 1661 theBuffer.append((char) (this.random.nextInt(endCode - startCode) + startCode)); 1662 } 1663 result = theBuffer.toString(); 1664 } else { 1665 final StringBuffer theBuffer = new StringBuffer(); 1666 for (int i = 0; i < theBegNum; i++) { 1667 theBuffer.append(this.random.nextInt(9)); 1668 } 1669 result = theBuffer.toString(); 1670 } 1671 } 1672 1673 setVariableValue(theNameAttribut, result); 1674 } 1675 1676 @CommandExamples({ "<Inc name='type:property' increase='type:number'/>" }) 1677 public void runCommandInc(final Node aCurrentVar) throws Throwable { 1678 String theValueAttribut = aCurrentVar.getAttribute("increase"); 1679 theValueAttribut = replaceProperties(theValueAttribut); 1680 long theIncLong = 1; 1681 if (theValueAttribut != null) { 1682 theIncLong = Long.parseLong(theValueAttribut); 1683 } 1684 final String theAttribut = aCurrentVar.getAttribute("name"); 1685 Object theOldValue = getVariableValue(theAttribut); 1686 1687 if (!(theOldValue instanceof Object[])) { 1688 theOldValue = ObjectUtils.toString(theOldValue, null); 1689 } 1690 1691 if (theOldValue instanceof String) { 1692 final long theLongValue = Long.parseLong((String) theOldValue) + theIncLong; 1693 setVariableValue(theAttribut, Long.toString(theLongValue)); 1694 } 1695 if (theOldValue instanceof String[] && ((String[]) theOldValue).length > 0) { 1696 final long theLongValue = Long.parseLong(((String[]) theOldValue)[0]) + theIncLong; 1697 setVariableValue(theAttribut, Long.toString(theLongValue)); 1698 } else { 1699 new ClassCastException("Tag <Inc> enabled only one number argument."); 1700 } 1701 } 1702 1703 @CommandExamples({ 1704 "<If name='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... </If>", 1705 "<If value='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... <Else> ... </Else></If>", 1706 "<If expression='type:string'> ... </If>", "<If isNull='type:property'> ... </If>", 1707 "<If isNotNull='type:property'> ... </If>" }) 1708 public void runCommandIf(final Node action) throws Throwable { 1709 runIf(action); 1710 } 1711 1712 private boolean runIf(final Node action) throws CommandException, Throwable, ClassNotFoundException { 1713 String theExpressionAttribut = action.getAttribute("expression"); 1714 String isNull = action.getAttribute("isNull"); 1715 String isNotNull = action.getAttribute("isNotNull"); 1716 String nameAttr = attr(action, "name"); 1717 1718 String valueAttr = action.getAttribute("value"); 1719 1720 String theValue1Attribut = action.getAttribute("value1"); 1721 String theValue2Attribut = action.getAttribute("value2"); 1722 String theConditionAttribut = action.getAttribute("condition"); 1723 1724 boolean result = false; 1725 if (nameAttr != null || valueAttr != null) { 1726 Object variableValue = getVariableValue(nameAttr); 1727 String value; 1728 1729 if (variableValue == null) { 1730 value = replaceProperties(valueAttr); 1731 } else { 1732 if (variableValue instanceof String[]) { 1733 value = StringUtils.join((String[]) variableValue, "\n"); 1734 } else { 1735 value = ObjectUtils.toString(variableValue); 1736 } 1737 } 1738 1739 String startsWith = attr(action, "startsWith"); 1740 String endsWith = attr(action, "endsWith"); 1741 String contains = attr(action, "contains"); 1742 String equals = attr(action, "equals"); 1743 String regex = attr(action, "regex"); 1744 String notEqual = attr(action, "notEqual"); 1745 1746 boolean ignoreCase = Boolean.valueOf(attr(action, "ignoreCase", "false")); 1747 1748 boolean condition1 = startsWith == null || (ignoreCase ? StringUtils.startsWithIgnoreCase(value, startsWith) 1749 : StringUtils.startsWith(value, startsWith)); 1750 boolean condition2 = endsWith == null || (ignoreCase ? StringUtils.endsWithIgnoreCase(value, endsWith) 1751 : StringUtils.endsWith(value, endsWith)); 1752 boolean condition3 = contains == null || (ignoreCase ? StringUtils.containsIgnoreCase(value, contains) 1753 : StringUtils.contains(value, contains)); 1754 boolean condition4 = equals == null 1755 || (ignoreCase ? StringUtils.equalsIgnoreCase(value, equals) : StringUtils.equals(value, equals)); 1756 1757 boolean condition5 = true; 1758 if (regex != null) { 1759 final Pattern p = Pattern.compile(regex); 1760 final Matcher m = p.matcher(value); 1761 condition5 = m.find(); 1762 } 1763 1764 boolean condition6 = notEqual == null || (ignoreCase ? !StringUtils.equalsIgnoreCase(value, notEqual) 1765 : !StringUtils.equals(value, notEqual)); 1766 1767 result = condition1 && condition2 && condition3 && condition4 && condition5 && condition6; 1768 execIf(action, result); 1769 return result; 1770 } else if (isNull != null) { 1771 result = getVariableValue(replaceProperties(isNull)) == null; 1772 execIf(action, result); 1773 return result; 1774 } else if (isNotNull != null) { 1775 Object variableValue = getVariableValue(replaceProperties(isNotNull)); 1776 result = variableValue != null; 1777 execIf(action, result); 1778 return result; 1779 } else if (theExpressionAttribut != null) { 1780 theExpressionAttribut = replaceProperties(theExpressionAttribut); 1781 JexlEngine jexl = new JexlBuilder().create(); 1782 1783 JexlExpression expr_c = jexl.createExpression(theExpressionAttribut); 1784 JexlContext context = new MapContext(); 1785 1786 if (expr_c != null) { 1787 Object val = expr_c.evaluate(context); 1788 result = "true".equals(val.toString()); 1789 } 1790 1791 execIf(action, result); 1792 return result; 1793 } else if (theValue1Attribut != null || theValue2Attribut != null) { 1794 if (theConditionAttribut == null) { 1795 theConditionAttribut = "=="; 1796 } 1797 theValue1Attribut = replaceProperties(theValue1Attribut); 1798 theValue2Attribut = replaceProperties(theValue2Attribut); 1799 1800 if ("==".equals(theConditionAttribut) && theValue1Attribut.equals(theValue2Attribut)) { 1801 taskNode(action, false); 1802 result = true; 1803 } else if (("!=".equals(theConditionAttribut) || "unequal".equals(theConditionAttribut)) 1804 && (theValue1Attribut.equals(theValue2Attribut) == false)) { 1805 taskNode(action, false); 1806 result = true; 1807 } else if ("less".equals(theConditionAttribut) 1808 && (Long.parseLong(theValue1Attribut) < Long.parseLong(theValue2Attribut))) { 1809 taskNode(action, false); 1810 result = true; 1811 } else if ("bigger".equals(theConditionAttribut) 1812 && (Long.parseLong(theValue1Attribut) > Long.parseLong(theValue2Attribut))) { 1813 taskNode(action, false); 1814 result = true; 1815 } else { 1816 for (Node command : action.getNodes("Else")) { 1817 taskNode(command, false); 1818 } 1819 } 1820 } 1821 1822 return result; 1823 } 1824 1825 private void execIf(final Node action, boolean result) throws CommandException, Throwable { 1826 if (result) { 1827 taskNode(action, false); 1828 } else { 1829 for (Node command : action.getNodes("Else")) { 1830 if (command.getAttribute("name") == null && action.getAttribute("name") != null) { 1831 command.setAttribute("name", action.getAttribute("name")); 1832 } 1833 if (command.getAttribute("value") == null && action.getAttribute("value") != null) { 1834 command.setAttribute("value", action.getAttribute("value")); 1835 } 1836 if (command.getAttribute("name") != null || command.getAttribute("value") != null) { 1837 if (runIf(command)) { 1838 break; 1839 } 1840 } else { 1841 taskNode(command); 1842 } 1843 } 1844 } 1845 } 1846 1847 @CommandDescription("") 1848 @CommandExamples({ 1849 "<While name='type:property' startsWith='type:string' endsWith='type:string' contains='type:string' equals='type:string' notEqual='type:string'> ... </While>", 1850 "<While value1='type:string' value2='type:string' condition='unequal'> ... <Else value1='type:string' value2='type:string' condition='enum:unequal|equal'> ... </Else></While>", 1851 "<While expression='type:string'> ... </While>", "<While isNull='type:property'> ... </While>", 1852 "<While isNotNull='type:property'> ... </While>" }) 1853 public void runCommandWhile(final Node aCurrentVar) throws Throwable { 1854 try { 1855 for (; runIf(aCurrentVar);) { 1856 if (this.breakFlag > 0) { 1857 break; 1858 } 1859 } 1860 } finally { 1861 if (this.breakFlag > 0) { 1862 breakFlag--; 1863 } 1864 } 1865 } 1866 1867 @CommandExamples({ "<Pragma event='console-input' action='default'/>", 1868 "<Pragma event='random-select' action='on'/>", "<Pragma event='log-window' action='single'/>" }) 1869 public void runCommandPragma(final Node aCurrentVar) throws Throwable { 1870 final String theEventAttribut = aCurrentVar.getAttribute("event"); 1871 final String theActionAttribut = aCurrentVar.getAttribute("action"); 1872 1873 if ("console-input".equals(theEventAttribut)) { 1874 boolean consoleDefaultInput = "default".equals(theActionAttribut); 1875 getListener().getManager().setConsoleDefaultInput(consoleDefaultInput); 1876 } else if ("random-select".equals(theEventAttribut)) { 1877 randomSelect = "on".equals(theActionAttribut); 1878 } else { 1879 this.log.error("Pragma ignored. Event: " + theEventAttribut); 1880 } 1881 } 1882 1883 @SuppressWarnings("unchecked") 1884 @CommandDescription("The `name` attribute is a name of variable which provides Runnable or List<Runnable> value. " 1885 + "To define this value, you should use the Runnable command tag.") 1886 @CommandExamples({ 1887 "<Threads name='type:property' threadLog='type:boolean' numbers='type:integer' multi='enum:true|false' />", 1888 "<Threads numbers='type:integer' multi='enum:true|false' mode='enum:wait|nowait'><Out name='" 1889 + TaskProcessorThread.THREAD_ID + "'/></Threads>", 1890 "<Threads multi='enum:true|false'><Out name='" + TaskProcessorThread.THREAD_ID + "'/></Threads>" }) 1891 public void runCommandThreads(final Node aCurrentAction) throws InterruptedException, CommandException { 1892 1893 boolean threadLog = Boolean 1894 .parseBoolean(StringUtils.defaultIfBlank(attr(aCurrentAction, "threadLog"), "false")); 1895 int numbers = Integer.parseInt(StringUtils.defaultIfBlank(attr(aCurrentAction, "numbers"), "1")); 1896 boolean multi = Boolean.parseBoolean(StringUtils.defaultIfBlank(attr(aCurrentAction, "multi"), "true")); 1897 1898 ThreadPoolExecutor threadPool; 1899 if (multi) { 1900 if (numbers > 0) { 1901 threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(numbers); 1902 } else { 1903 threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); 1904 } 1905 } else { 1906 threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); 1907 } 1908 1909 String callableListName = attr(aCurrentAction, "name"); 1910 if (callableListName == null) { 1911 final Iterator<?> theIterator = aCurrentAction.iterator(); 1912 1913 if (numbers == 0) { 1914 while (theIterator.hasNext()) { 1915 final Node theNode = (Node) theIterator.next(); 1916 1917 String theNameThreads = getTestName(theNode); 1918 if (theNameThreads == null) { 1919 theNameThreads = "Thread"; 1920 } 1921 1922 ILogger theLog = this.log; 1923 if (threadLog) { 1924 theLog = this.recipeListener.createLog(theNameThreads, false); 1925 } 1926 1927 final TaskProcessorThread runnable = new TaskProcessorThread(this, theNode, getBaseDir(), theLog); 1928 1929 threadPool.execute(runnable); 1930 1931 } 1932 } else { 1933 final Node theNode = (Node) aCurrentAction.clone(); 1934 theNode.setTag("Task"); 1935 theNode.removeAttribute(callableListName); 1936 1937 for (int i = 0; i < numbers; i++) { 1938 String theNameThreads = getTestName(theNode); 1939 String logName = "Thread #" + i; 1940 if (theNameThreads != null) { 1941 logName = theNameThreads + " #" + i; 1942 } 1943 1944 ILogger theLog = this.log; 1945 if (threadLog) { 1946 theLog = this.recipeListener.createLog(logName, false); 1947 } 1948 1949 final TaskProcessorThread runnable = new TaskProcessorThread(this, theNode, getBaseDir(), theLog); 1950 runnable.getVaribleValue(TaskProcessorThread.THREAD_ID, String.valueOf(i)); 1951 1952 threadPool.execute(runnable); 1953 } 1954 } 1955 1956 } else { 1957 Object callableList = getVariableValue(callableListName); 1958 if (callableList instanceof List) { 1959 for (Runnable runnable : (List<Runnable>) callableList) { 1960 threadPool.execute(runnable); 1961 } 1962 } else if (callableList instanceof Runnable) { 1963 for (int i = 0; i < numbers; i++) { 1964 if (callableList instanceof TaskProcessorThread) { 1965 threadPool.execute(((TaskProcessorThread) callableList).clone(i)); 1966 } else { 1967 threadPool.execute((Runnable) callableList); 1968 } 1969 } 1970 } else { 1971 throw new IllegalArgumentException( 1972 "Variable of type must be Runnable or List<Runnable>, actually: " + callableList); 1973 } 1974 } 1975 1976 String description = replaceProperties(aCurrentAction.getAttribute("description")); 1977 1978 long completedTaskCount = 0; 1979 long size = threadPool.getTaskCount(); 1980 1981 threadPoolList.add(threadPool); 1982 do { 1983 completedTaskCount = threadPool.getCompletedTaskCount(); 1984 progress(size, completedTaskCount, description, false); 1985 Thread.sleep(200); 1986 1987 } while (completedTaskCount < size); 1988 threadPoolList.remove(threadPool); 1989 } 1990 1991 @CommandExamples({ "Run recipe by name: <Task name='type:recipe' />", 1992 "Run recipe by name in async mode: <Task name='type:recipe' mode='enum:sync|async' optional='enum:false|true'/>", 1993 "Recipe command grouping: <Task> ... </Task>", "Run recipe by file path: <Task file='type:path' />" }) 1994 public void runCommandTask(final Node action) throws Throwable { 1995 1996 final boolean optional = Boolean.valueOf(attr(action, "optional", "false")); 1997 1998 final String taskName = replaceProperties(action.getAttribute("name")); 1999 String taskFile = replaceProperties(action.getAttribute("file")); 2000 2001 if (taskFile == null) { 2002 if (taskName != null) { 2003 taskFile = getListener().getManager().getTestPath(taskName); 2004 2005 if (taskFile == null) { 2006 if (optional) { 2007 return; 2008 } else { 2009 throw new Exception("Task with name: '" + taskName + "' not found."); 2010 } 2011 } 2012 } 2013 } else { 2014 if (!(new File(taskFile)).exists()) { 2015 if (optional) { 2016 return; 2017 } else { 2018 throw new Exception("Task with name: '" + taskFile + "' not found."); 2019 } 2020 } 2021 } 2022 2023 final File theBaseDir = getBaseDir(); 2024 try { 2025 if (taskName != null) { 2026 String mode = replaceProperties(action.getAttribute("mode")); 2027 if ("async".equals(mode)) { 2028 getListener().getManager().runTask(taskName, true); 2029 2030 } else { 2031 Properties taskParameters = MapUtils.toProperties(action.getAttributes()); 2032 Set<Entry<Object, Object>> entrySet = taskParameters.entrySet(); 2033 2034 String currentLogLevel = (String) this.variables.get(DEFAULT_LOG_LEVEL_NAME); 2035 String level = (String) taskParameters.get("level"); 2036 2037 for (Entry<Object, Object> entry : entrySet) { 2038 String name = (String) entry.getKey(); 2039 if (!"$ID".equals(name) && !"name".equals(name) && !"file".equals(name) 2040 && !"level".equals(name)) { 2041 String value = replaceProperties((String) entry.getValue()); 2042 this.variables.put(toUpperCaseName(name), value); 2043 } 2044 } 2045 2046 this.variables.put(DEFAULT_LOG_LEVEL_NAME, level); 2047 this.variables = processTesting(taskFile, this.variables, getBaseDir()); 2048 this.variables.put(DEFAULT_LOG_LEVEL_NAME, currentLogLevel); 2049 } 2050 } else { 2051 taskNode(action, false); 2052 } 2053 } finally { 2054 setBaseDir(theBaseDir); 2055 } 2056 } 2057 2058 @CommandExamples({ "<IterationRules name='type:property'><Out name='Iteration'/></IterationRules>", 2059 "<IterationRules name='type:property' timeLimit='type:time' start='type:integer'> ... </IterationRules>", 2060 "<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>" 2061 + "<IterationRules name='rules'><Out name='Iteration'/></IterationRules>", }) 2062 public void runCommandIterationRules(final Node action) throws IOException, CommandException { 2063 final String name = (String) attrValue(action, "name"); 2064 String theStartAttribut = action.getAttribute("start"); 2065 long theLimit = attrTime(action, "timeLimit", "180000"); 2066 2067 if (theStartAttribut == null) { 2068 theStartAttribut = "0"; 2069 } 2070 2071 final int theStart = Integer.parseInt(theStartAttribut); 2072 if (name == null) { 2073 throw new CommandException("In tag <IterationRules> variable rules is not defined.", this); 2074 } 2075 try { 2076 this.iteratorMode = true; 2077 final TestIterator theTestIterator = new TestIterator(this, name); 2078 final int theMax = theTestIterator.count(); 2079 progress(theMax, 0, "Iteration process", false); 2080 debug("Iteration block. Total count: " + theMax); 2081 final long theStertTime = System.currentTimeMillis(); 2082 for (int i = theStart; i < theMax; i++) { 2083 if (isStopped()) { 2084 break; 2085 } 2086 setVariableValue("Iteration", Integer.toString(i + 1)); 2087 theTestIterator.nextIteration(this); 2088 2089 taskNode(action, false); 2090 2091 progress(theMax, i, "Iteration process", false); 2092 if (i == 0) { 2093 long theTotalTime = ((System.currentTimeMillis() - theStertTime) * theMax) / 60000; 2094 debug("Total Iteration time: " + Long.toString(theTotalTime) + " min."); 2095 if (theTotalTime > theLimit) { 2096 int theConfirm = 0; 2097 if (this.recipeListener.getManager() != null) { 2098 if (this.recipeListener.getManager().isConsoleDefaultInput(attr(action, "name"), 2099 attr(action, "description")) == false) { 2100 if (theTotalTime < 60) { 2101 theConfirm = JOptionPane.showConfirmDialog( 2102 JOptionPane.getRootFrame(), "Total Iteration time: " 2103 + Long.toString(theTotalTime) + " min. \nContinue?", 2104 "Warning", JOptionPane.YES_NO_OPTION); 2105 } else { 2106 theTotalTime /= 60; 2107 theConfirm = JOptionPane.showConfirmDialog(JOptionPane.getRootFrame(), 2108 "Total Iteration time: " + Long.toString(theTotalTime) + " h. \nContinue?", 2109 "Warning", JOptionPane.YES_NO_OPTION); 2110 } 2111 } 2112 if (theConfirm != JOptionPane.YES_OPTION) { 2113 break; 2114 } 2115 } 2116 } 2117 } 2118 } 2119 } finally { 2120 this.iteratorMode = false; 2121 } 2122 } 2123 2124 @CommandExamples({ "<Listdir path='type:path' name='type:property'/>", 2125 "<Listdir name='type:property' path='type:path' filter='type:regex'/>", 2126 "<Listdir name='type:property' path='type:path' filter='type:regex' dirFilter='type:regex' />", 2127 "<Listdir path='type:path' name='type:property' />" }) 2128 public void runCommandListdir(final Node aCurrentVar) throws Throwable { 2129 final String pathAttribut = replaceProperties(aCurrentVar.getAttribute("path")); 2130 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2131 final String fileFilterRegex = attr(aCurrentVar, "filter"); 2132 final String dirFilterRegex = attr(aCurrentVar, "dirFilter"); 2133 File path = getFile(pathAttribut); 2134 2135 if (isActiveInitFor(aCurrentVar, "console")) { 2136 String pathStr = this.recipeListener.getManager().inputFile(theNameAttribut, null, path, log, this); 2137 if (pathStr != null) { 2138 path = new File(pathStr); 2139 } 2140 } 2141 2142 if (path != null) { 2143 debug("Listing of directory: " + path.getAbsolutePath()); 2144 2145 IOFileFilter fileFilter = TrueFileFilter.TRUE; 2146 2147 if (fileFilterRegex != null) { 2148 fileFilter = new RegexPathFilter(fileFilterRegex, dirFilterRegex); 2149 } 2150 2151 Collection<File> files = FileUtils.listFiles(path, fileFilter, 2152 dirFilterRegex != null ? TrueFileFilter.TRUE : null); 2153 List<String> result = files.stream().map(f -> f.getAbsolutePath()).collect(Collectors.toList()); 2154 2155 if (!result.isEmpty()) { 2156 Collections.sort(result); 2157 setVariableValue(theNameAttribut, result); 2158 } 2159 } else { 2160 throw new TaskCancelingException(); 2161 } 2162 } 2163 2164 @CommandExamples({ "<NetworkInterfaces name='type:property' />", 2165 "<NetworkInterfaces name='type:property' host='type:string' filterFor='type:string'/>" }) 2166 public void runCommandNetworkInterfaces(final Node aCurrentAction) throws SocketException, UnknownHostException { 2167 final String name = replaceProperties(aCurrentAction.getAttribute("name")); 2168 String host = replaceProperties(aCurrentAction.getAttribute("host")); 2169 2170 if (host == null) { 2171 Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); 2172 Set<String> hostIps = new HashSet<>(); 2173 while (networkInterfaces.hasMoreElements()) { 2174 NetworkInterface nextElement = networkInterfaces.nextElement(); 2175 Enumeration<InetAddress> inetAddresses = nextElement.getInetAddresses(); 2176 while (inetAddresses.hasMoreElements()) { 2177 InetAddress nextElement2 = inetAddresses.nextElement(); 2178 if (nextElement2 instanceof Inet4Address) { 2179 Inet4Address address = (Inet4Address) nextElement2; 2180 String hostAddress = address.getHostAddress(); 2181 hostIps.add(hostAddress); 2182 } 2183 } 2184 } 2185 setVariableValue(name, new ArrayList<>(hostIps)); 2186 } else { 2187 try { 2188 URL url = new URL(host); 2189 host = url.getHost(); 2190 } catch (Exception e) { 2191 2192 } 2193 2194 InetAddress address1 = InetAddress.getByName(host); 2195 setVariableValue(name, address1.getHostAddress()); 2196 } 2197 } 2198 2199 @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. " 2200 + "The `dir` attribute specifies the current directory for the command being executed. This attribute can use `~` as the directory for the current recipe.") 2201 @CommandExamples({ "<Command os='type:regex'>...</Command>", 2202 "<Command os='type:regex' env='type:property'>...</Command>", 2203 "<Command name='type:property' os='type:regex' exitValue='type:property' cmd='type:string' noCommandLog='type:boolean' dir='type:path'/>", 2204 "<Command name='type:property' os='type:regex' exitValue='type:property' cmd='type:string' noCommandLog='type:boolean' dir='type:path'/>...</Command>" }) 2205 public void runCommandCommand(final Node action) throws Throwable { 2206 2207 String command = attr(action, "cmd"); 2208 final String name = attr(action, "name"); 2209 final String dir = attr(action, "dir"); 2210 final String os = attr(action, "os"); 2211 2212 String osName = SystemUtils.OS_NAME; 2213 2214 if (os == null || Pattern.compile(os).matcher(osName).matches()) { 2215 if (command == null) { 2216 final Node[] theNodes = action.getTextNodes(); 2217 if (theNodes.length > 0) { 2218 command = replaceProperties(theNodes[0].getText()); 2219 } 2220 } 2221 2222 Object env = attrValue(action, "env"); 2223 if (env != null) { 2224 if (!(env instanceof Map)) { 2225 throw new CommandException("'env' attribute shuld be Map", this); 2226 } 2227 } 2228 @SuppressWarnings("unchecked") 2229 Map<String, String> environment = (Map<String, String>) env; 2230 runSystemCommand(command, name, action, dir, environment); 2231 } 2232 } 2233 2234 private void runSystemCommand(final String command, final String theNameAttribut, final Node aCurrentNode, 2235 String dir, Map<? extends String, ? extends String> env) throws Throwable { 2236 final String prefix = "start "; 2237 if (command.startsWith(prefix) == false) { 2238 2239 String regExp = "\"(\\\"|[^\"])*?\"|[^ ]+"; 2240 Pattern pattern = Pattern.compile(regExp, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); 2241 Matcher matcher = pattern.matcher(command); 2242 List<String> commandTokens = new ArrayList<String>(); 2243 2244 if (SystemUtils.IS_OS_WINDOWS) { 2245 commandTokens.add("cmd.exe"); 2246 commandTokens.add("/c"); 2247 } 2248 2249 while (matcher.find()) { 2250 String trim = matcher.group().trim(); 2251 if (StringUtils.isNotBlank(trim)) { 2252 commandTokens.add(trim); 2253 } 2254 } 2255 String[] parsedCommand = commandTokens.toArray(new String[] {}); 2256 2257 ProcessBuilder builder = new ProcessBuilder(parsedCommand); 2258 2259 if (env != null) { 2260 Map<String, String> environment = builder.environment(); 2261 environment.putAll(env); 2262 } 2263 2264 if (dir != null) { 2265 File directory; 2266 if (dir.startsWith("~/")) { 2267 dir = dir.substring(1); 2268 String recipeFile = this.recipeListener.getManager().getTestPath(testName); 2269 if (recipeFile != null) { 2270 directory = new File(new File(recipeFile).getParent(), dir); 2271 builder.directory(directory); 2272 } 2273 } else { 2274 directory = new File(dir); 2275 builder.directory(directory); 2276 } 2277 } 2278 2279 builder.redirectErrorStream(true); 2280 Process process = builder.start(); 2281 2282 final String stdin = replaceProperties(aCurrentNode.getAttribute("stdin")); 2283 if (stdin != null) { 2284 process.getOutputStream().write(stdin.getBytes()); 2285 process.getOutputStream().close(); 2286 } 2287 2288 processes.add(process); 2289 2290 final BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getInputStream())); 2291 boolean noCommandLog = Boolean.valueOf(attr(aCurrentNode, "noCommandLog")); 2292 if (!noCommandLog) { 2293 debug("Command: " + command); 2294 } else { 2295 debug("Command: ****** **** *****"); 2296 } 2297 2298 try { 2299 final BufferedReader theOutputStream = new BufferedReader( 2300 new InputStreamReader(process.getInputStream())); 2301 String theLine; 2302 2303 boolean line_output = aCurrentNode.size() == 0; 2304 final StringBuffer theBuffer = new StringBuffer(); 2305 while ((theLine = theOutputStream.readLine()) != null && !isStoppedTest()) { 2306 if (breakFlag > 0) { 2307 break; 2308 } 2309 2310 if (line_output) { 2311 theBuffer.append(theLine); 2312 theBuffer.append('\n'); 2313 } else { 2314 setVariableValue(theNameAttribut, theLine); 2315 taskNode(aCurrentNode, false); 2316 } 2317 } 2318 2319 if (this.breakFlag > 0) { 2320 breakFlag--; 2321 } 2322 2323 String exitValueName = attr(aCurrentNode, "exitValue"); 2324 if (exitValueName != null) { 2325 int exitValue = 0; 2326 try { 2327 exitValue = process.exitValue(); 2328 if (exitValue > 0) { 2329 String error = IOUtils.toString(errorStream); 2330 if (StringUtils.isNotBlank(error)) { 2331 throw new RuntimeException(error); 2332 } 2333 } 2334 } catch (IllegalThreadStateException e) { 2335 // 2336 } 2337 setVariableValue(exitValueName, Integer.toString(exitValue)); 2338 } 2339 2340 if (line_output) { 2341 if (theNameAttribut == null) { 2342 debug("System output:" + theBuffer.toString()); 2343 } else { 2344 setVariableValue(theNameAttribut, theBuffer.toString()); 2345 } 2346 } 2347 } finally { 2348 processes.remove(process); 2349 process.destroyForcibly(); 2350 try { 2351 if (!process.waitFor(5, TimeUnit.SECONDS)) { 2352 process.destroyForcibly(); 2353 } 2354 } catch (InterruptedException e) { 2355 Thread.currentThread().interrupt(); 2356 process.destroyForcibly(); 2357 } 2358 } 2359 2360 } else { 2361 final Thread thread = new SystemCommandStart(command.substring(prefix.length()), this.log); 2362 thread.start(); 2363 } 2364 } 2365 2366 @CommandExamples({ "<ArraySize name = 'type:property' array = 'type:property'/>" }) 2367 public void runCommandArraySize(final Node aCurrentVar) throws Throwable { 2368 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2369 final String theArrayNameAttribut = replaceProperties(aCurrentVar.getAttribute("array")); 2370 final Object theValue = getVariableValue(theArrayNameAttribut); 2371 2372 int theResult = 0; 2373 if (theValue instanceof String[]) { 2374 theResult = ((String[]) theValue).length; 2375 } else if (theValue instanceof List) { 2376 theResult = ((List<String>) theValue).size(); 2377 } else if (theValue instanceof byte[]) { 2378 theResult = ((byte[]) theValue).length; 2379 } else if (theValue instanceof String && StringUtils.isNotBlank((String) theValue)) { 2380 theResult = 1; 2381 } else { 2382 theResult = 0; 2383 } 2384 2385 setVariableValue(theNameAttribut, String.valueOf(theResult)); 2386 } 2387 2388 @CommandExamples({ "Get string or array length: <Size name = 'type:property' source = 'type:property'/>" }) 2389 public void runCommandSize(final Node aCurrentVar) throws Throwable { 2390 final String theNameAttribut = replaceProperties(aCurrentVar.getAttribute("name")); 2391 final String theArrayNameAttribut = replaceProperties(aCurrentVar.getAttribute("source")); 2392 final Object theValue = getVariableValue(theArrayNameAttribut); 2393 2394 int theResult = 0; 2395 if (theValue instanceof String[]) { 2396 String[] theValue2 = (String[]) theValue; 2397 for (int i = 0; i < theValue2.length; i++) { 2398 theResult += theValue2[i].length(); 2399 } 2400 } else if (theValue instanceof byte[]) { 2401 theResult = ((byte[]) theValue).length; 2402 } else if (theValue instanceof Collection) { 2403 theResult = ((Collection) theValue).size(); 2404 } else if (theValue instanceof String && StringUtils.isNotBlank((String) theValue)) { 2405 theResult = ((String) theValue).length(); 2406 } else { 2407 theResult = 0; 2408 } 2409 2410 setVariableValue(theNameAttribut, String.valueOf(theResult)); 2411 } 2412 2413 @CommandExamples({ "<Time name = 'type:property' action = 'enum:start|continue|pause|stop'/>", 2414 "<Time name = 'type:property' action = 'format' format='mm:ss:SSS'/>", 2415 "<Time name = 'type:property' action = 'duration' >...code whose execution time is to be measured...</Time>" }) 2416 public void runCommandTime(final Node command) throws Throwable { 2417 final String theNameAttribut = replaceProperties(command.getAttribute("name")); 2418 final String format = replaceProperties(command.getAttribute("format")); 2419 final String theTimeCaption = "Time"; 2420 String action = replaceProperties(command.getAttribute("action")); 2421 2422 if ("duration".equals(action)) { 2423 long start = System.currentTimeMillis(); 2424 2425 taskNode(command, false); 2426 2427 long stop = System.currentTimeMillis(); 2428 final String[] variableValue = new String[] { theTimeCaption, String.valueOf(start), String.valueOf(stop), 2429 "" }; 2430 2431 String result = formatTime(format, variableValue); 2432 setVariableValue(theNameAttribut, result); 2433 return; 2434 } 2435 2436 if ("start".equals(action)) { 2437 final String[] theTime = new String[] { theTimeCaption, String.valueOf(System.currentTimeMillis()), "", 2438 "" }; 2439 setVariableValue(theNameAttribut, theTime); 2440 return; 2441 } 2442 2443 if ("continue".equals(action)) { 2444 String[] theTime = (String[]) getVariableValue(theNameAttribut); 2445 if (theTime == null) { 2446 theTime = new String[] { theTimeCaption, String.valueOf(System.currentTimeMillis()), "", 2447 String.valueOf(System.currentTimeMillis()) }; 2448 } 2449 2450 if (theTime == null || theTime[3] == null || theTime[3].length() == 0) { 2451 throw new Exception("Timer is not paused."); 2452 } 2453 2454 if (theTimeCaption.equals(theTime[0])) { 2455 final long theStart = Long.parseLong(theTime[1]); 2456 final long thePaused = Long.parseLong(theTime[3]); 2457 theTime[1] = String.valueOf(theStart - (System.currentTimeMillis() - thePaused)); 2458 theTime[3] = ""; 2459 setVariableValue(theNameAttribut, theTime); 2460 } else { 2461 throw new Exception("Incorrect type."); 2462 } 2463 return; 2464 } 2465 2466 if ("pause".equals(action)) { 2467 final String[] theTime = (String[]) getVariableValue(theNameAttribut); 2468 2469 if (theTime[3] != null && theTime[3].length() > 0) { 2470 throw new Exception("Timer is paused."); 2471 } 2472 if (theTimeCaption.equals(theTime[0])) { 2473 theTime[3] = String.valueOf(System.currentTimeMillis()); 2474 setVariableValue(theNameAttribut, theTime); 2475 } else { 2476 throw new Exception("Incorrect type."); 2477 } 2478 return; 2479 } 2480 2481 if ("stop".equals(action)) { 2482 final String[] theTime = (String[]) getVariableValue(theNameAttribut); 2483 2484 final long theStart = Long.parseLong(theTime[1]); 2485 2486 if (theTime[3] != null && theTime[3].length() > 0) { 2487 final long thePaused = Long.parseLong(theTime[3]); 2488 theTime[1] = String.valueOf(theStart + (System.currentTimeMillis() - thePaused)); 2489 theTime[3] = ""; 2490 } 2491 2492 if (theTimeCaption.equals(theTime[0])) { 2493 theTime[2] = String.valueOf(System.currentTimeMillis()); 2494 setVariableValue(theNameAttribut, theTime); 2495 } else { 2496 throw new Exception("Incorrect type."); 2497 } 2498 return; 2499 } 2500 2501 if ("format".equals(action)) { 2502 Object variableValue = getVariableValue(theNameAttribut); 2503 String theResult = formatTime(format, variableValue); 2504 setVariableValue(theNameAttribut, theResult); 2505 return; 2506 } 2507 } 2508 2509 private String formatTime(final String format, Object variableValue) throws Exception { 2510 final String[] theTime; 2511 if (variableValue instanceof String[]) { 2512 theTime = (String[]) variableValue; 2513 } else { 2514 theTime = new String[] { "", "0", (String) variableValue }; 2515 } 2516 2517 if (ArrayUtils.getLength(theTime) < 2) { 2518 throw new Exception("Timer is not defined."); 2519 } 2520 2521 if (ArrayUtils.getLength(theTime) < 3) { 2522 throw new Exception("Timer is not stoped."); 2523 } 2524 2525 String theResult = null; 2526 if (format == null) { 2527 theResult = String.valueOf(Long.parseLong(theTime[2]) - Long.parseLong(theTime[1])); 2528 } else { 2529 final DateFormat dateFormat = new SimpleDateFormat(format); 2530 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 2531 long date = Long.parseLong(theTime[2]) - Long.parseLong(theTime[1]); 2532 theResult = dateFormat.format(new Date(date)); 2533 } 2534 return theResult; 2535 } 2536 2537 @CommandDescription("The ArrayElement command is used to get an element from an array by element ID (elementId).") 2538 @CommandExamples({ "<ArrayElement name='type:property' array='type:property' elementId='type:number'/>" }) 2539 public void runCommandArrayElement(final Node aCurrentVar) throws Throwable { 2540 final String name = replaceProperties(aCurrentVar.getAttribute("name")); 2541 final String arrayName = replaceProperties(aCurrentVar.getAttribute("array")); 2542 if (arrayName == null) { 2543 throw new IllegalArgumentException("'array' attribute required."); 2544 } 2545 2546 final String theElementAttribut = replaceProperties(aCurrentVar.getAttribute("elementId")); 2547 if (theElementAttribut == null) { 2548 throw new IllegalArgumentException("'elementId' attribute required."); 2549 } 2550 2551 final String type = replaceProperties(aCurrentVar.getAttribute("type")); 2552 Object theValue = getVariableValue(arrayName); 2553 2554 final int theElementId = Integer.parseInt(theElementAttribut); 2555 2556 theValue = convert(theValue, type); 2557 2558 Object theResult = null; 2559 if (theValue instanceof JSONArray) { 2560 JSONArray jsonArray = (JSONArray) theValue; 2561 theResult = jsonArray.get(theElementId); 2562 } else if (theValue instanceof String[]) { 2563 theResult = ((String[]) theValue)[theElementId]; 2564 } else if (theValue instanceof List) { 2565 theResult = ((List) theValue).get(theElementId); 2566 } else if (theValue instanceof String) { 2567 if ("string".equalsIgnoreCase(type)) { 2568 theResult = new String(new char[] { ((String) theValue).charAt(theElementId) }); 2569 } else if (theElementId == 0) { 2570 theResult = theValue; 2571 } 2572 } else if (theElementId == 0) { 2573 theResult = theValue; 2574 } 2575 2576 setVariableValue(name, theResult); 2577 } 2578 2579 @CommandDescription("The Calculate command is implemented in the Java Expression Language (JEXL), " 2580 + "for more information see: https://commons.apache.org/proper/commons-jexl. " 2581 + "The value of the expression will be stored in the variable with the name specified in the `name` attribute.") 2582 @CommandExamples({ "<Calculate name='type:property' expressions='type:string'/>", 2583 "<Calculate name='type:property'>...</Calculate>" }) 2584 public void runCommandCalculate(final Node command) throws Throwable { 2585 final String name = replaceProperties(command.getAttribute("name")); 2586 String expressions = replaceProperties(command.getAttribute("expressions")); 2587 if (expressions == null) { 2588 expressions = replaceProperties(command.getInnerText()); 2589 } 2590 2591 Map<String, Object> namespaces = new HashMap<>(); 2592 namespaces.put("Math", Math.class); 2593 JexlEngine jexl = new JexlBuilder().namespaces(namespaces).create(); 2594 2595 JexlExpression expr_c = jexl.createExpression(expressions); 2596 JexlContext context = new MapContext(); 2597 2598 Map<String, String> attributes = command.getAttributes(); 2599 for (Map.Entry<String, String> entry : attributes.entrySet()) { 2600 String key = entry.getKey(); 2601 String val = entry.getValue(); 2602 2603 Object variableValue = getVariableValue(val); 2604 if (variableValue instanceof String) { 2605 try { 2606 variableValue = Double.parseDouble((String) variableValue); 2607 } catch (NumberFormatException e) { 2608 // DO NOTHING 2609 } 2610 } 2611 2612 context.set(key, variableValue); 2613 } 2614 2615 if (expr_c != null) { 2616 Object result = expr_c.evaluate(context); 2617 2618 if (result != null) { 2619 setVariableValue(name, result); 2620 } else { 2621 setVariableValue(name, null); 2622 } 2623 } 2624 } 2625 2626 @CommandExamples({ 2627 "<Parse name='type:property' source='type:property' type='enum:array|json|csv|fixed-length-line'/>", 2628 "<Parse name='type:property' source='type:property' type='fixed-length-line' length='type:integer'/>" }) 2629 public void runCommandParse(final Node command) throws Throwable { 2630 Object source = attrValue(command, "source"); 2631 final String theNameAttribut = replaceProperties(command.getAttribute("name")); 2632 final String type = replaceProperties(command.getAttribute("type")); 2633 2634 if ("json".equalsIgnoreCase(type)) { 2635 String theValue = ObjectUtils.toString(source); 2636 if (StringUtils.isNotBlank(theValue)) { 2637 Object obj = new JSONObject(theValue); 2638 if (obj instanceof JSONObject) { 2639 JSONObject result = new JSONObject(theValue); 2640 setVariableValue(theNameAttribut, result); 2641 } else if (obj instanceof JSONArray) { 2642 JSONArray result = new JSONArray(theValue); 2643 String[] array = new String[result.length()]; 2644 for (int i = 0; i < result.length(); i++) { 2645 array[i] = ObjectUtils.toString(result.get(i)); 2646 } 2647 setVariableValue(theNameAttribut, array); 2648 } 2649 } else { 2650 setVariableValue(theNameAttribut, theValue); 2651 } 2652 2653 } else if ("array".equalsIgnoreCase(type)) { 2654 String theValue = ObjectUtils.toString(source); 2655 2656 String[] array = StringUtils.split(theValue, "\r\n"); 2657 setVariableValue(theNameAttribut, array); 2658 } else if ("csv".equalsIgnoreCase(type)) { 2659 String theValue = ObjectUtils.toString(source); 2660 2661 CSVReader reader = new CSVReader(new StringReader(theValue)); 2662 List<String[]> r = reader.readAll(); 2663 2664 setVariableValue(theNameAttribut, r); 2665 } else if ("fixed-length-line".equalsIgnoreCase(type)) { 2666 if (source instanceof String) { 2667 String input = (String) source; 2668 int lineLength = Integer.parseInt(attr(command, "length")); 2669 List<Object> result = IntStream.range(0, (input.length() + lineLength - 1) / lineLength) 2670 .mapToObj(i -> input.substring(i * lineLength, Math.min((i + 1) * lineLength, input.length()))) 2671 .collect(Collectors.toList()); 2672 setVariableValue(theNameAttribut, result); 2673 } 2674 } 2675 } 2676 2677 @CommandExamples({ "<FindObject name='type:property' source='type:property' withValue='type:string'/>" }) 2678 public void runCommandFindObject(final Node aCurrentNode) throws Throwable { 2679 Object json = getVariableValue(replaceProperties(aCurrentNode.getAttribute("source"))); 2680 String withValue = replaceProperties(aCurrentNode.getAttribute("withValue")); 2681 2682 if (json instanceof String) { 2683 String jsonStr = (String) json; 2684 if (StringUtils.startsWith(jsonStr, "{")) { 2685 json = new JSONObject(jsonStr); 2686 } else if (StringUtils.startsWith(jsonStr, "[")) { 2687 json = new JSONArray(jsonStr); 2688 } 2689 } else if (json instanceof String[]) { 2690 String[] jsonStrArray = (String[]) json; 2691 JSONArray array = new JSONArray(); 2692 for (int i = 0; i < jsonStrArray.length; i++) { 2693 String jsonStr = jsonStrArray[i]; 2694 array.put(new JSONObject(jsonStr)); 2695 } 2696 json = array; 2697 } 2698 2699 Object value = find(json, withValue); 2700 2701 final String name = replaceProperties(aCurrentNode.getAttribute("name")); 2702 applyResult(aCurrentNode, name, value); 2703 } 2704 2705 private JSONObject find(Object obj, String withValue) throws JSONException { 2706 if (obj instanceof JSONObject) { 2707 JSONObject json = (JSONObject) obj; 2708 2709 JSONArray names = json.names(); 2710 if (names != null) { 2711 for (int i = 0; i < names.length(); i++) { 2712 String name = names.getString(i); 2713 Object object = json.get(name); 2714 if (object instanceof String) { 2715 if (ObjectUtils.equals(object, withValue)) { 2716 return json; 2717 } 2718 } else { 2719 JSONObject find = find(object, withValue); 2720 if (find != null) { 2721 return find; 2722 } 2723 } 2724 } 2725 } 2726 } else if (obj instanceof JSONArray) { 2727 JSONArray array = (JSONArray) obj; 2728 2729 for (int j = 0; j < array.length(); j++) { 2730 Object item = array.get(j); 2731 2732 JSONObject find = find(item, withValue); 2733 if (find != null) { 2734 return find; 2735 } 2736 } 2737 } 2738 return null; 2739 } 2740 2741 @CommandDescription("`Var` command is used to define or update a variable.\r\n" 2742 + "Attribute `init` defines how variable will be set. This attribute is optional; if it is not defined,\r\n" 2743 + "the variable will be initialized by `value` attribute or inner data. It can be set to the following values:\r\n" 2744 + "- `default` is used to initialize the variable's value if it is not defined,\r\n" 2745 + "- `console` is used for input data by the user,\r\n" 2746 + "- `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" 2747 + "- `file` is used for loadind variables from property file.\r\n" 2748 + "Attribute `type` defines a type of variable. It is used to define the variable type and special rules for the input dialog:\r\n" 2749 + "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" 2750 + "- `array` is used for create the value of the array type;\r\n" 2751 + "- `map` is used for create the value of the map type;\r\n" 2752 + "- `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" 2753 + "- `password` the user dialog text field will be masked;\r\n" 2754 + "- `path` a dialog for a dir opening will be shown; \r\n" 2755 + "- `text` type is used only for large text values, and the value is not saved in the preferences.") 2756 @CommandExamples({ 2757 "<Var name='type:property' init='enum:console|mandatory|default' type='enum:text|password|path'/>", 2758 "<Var name='type:property' type='enum:array|number|map|json|string' />", 2759 "<Var name='type:property' source='type:property' start='type:string' end='type:string'/>", 2760 "<Var name='type:property'><item>...</item></Var>", 2761 "<Var name='type:property' type='map'><item key='type:string'>...</item></Var>", 2762 "<Var name='type:property' type='enum:array|number' file='type:path'/>", 2763 "<Var init='file' file='type:path'/>", "<Var name='type:property' source='type:property' />" }) 2764 public void runCommandVar(final Node action) throws Throwable { 2765 final String name = attr(action, "name"); 2766 String description = attr(action, "description"); 2767 String theInit = attr(action, "init"); 2768 2769 Object theOldValue = getVariableValue(name); 2770 if ("default".equals(theInit) && theOldValue != null) { 2771 if (theOldValue instanceof String) { 2772 if (StringUtils.isNotBlank((String) theOldValue)) { 2773 return; 2774 } 2775 } else if (theOldValue instanceof String[] && ((String[]) theOldValue).length > 0) { 2776 return; 2777 } 2778 } 2779 2780 String file = attr(action, "file"); 2781 if ("file".equals(theInit)) { 2782 File propertiesFile = getFile(file); 2783 loadProperties(propertiesFile, this.variables, true); 2784 return; 2785 } 2786 2787 String source = replaceProperties(action.getAttribute("source"), true); 2788 Object theValue = getVariableValue(name); 2789 if (source != null) { 2790 String sourceVarName = replaceProperties(source); 2791 theValue = getVariableValue(sourceVarName); 2792 theOldValue = theValue; 2793 setVariableValue(name, theValue); 2794 } 2795 2796 String value = action.getAttribute("value"); 2797 if (value != null) { 2798 theValue = replaceProperties(value); 2799 } 2800 2801 // Item setting 2802 String type = StringUtils.defaultIfEmpty(action.getAttribute("type"), ""); 2803 if (action.size() > 0) { 2804 if (Node.TEXT_TEAG_NAME.equals(action.getNode(0).getTag())) { 2805 final Node theTextNode = action.getNode(0); 2806 theValue = replaceProperties(theTextNode.getText()); 2807 2808 } else { 2809 switch (type) { 2810 case "map": 2811 Node[] nodes = action.getNodes("item"); 2812 theValue = new LinkedHashMap<String, String>(); 2813 for (Node node : nodes) { 2814 ((Map) theValue).put(node.getAttribute("key"), replaceProperties(node.getInnerText())); 2815 } 2816 break; 2817 2818 default: 2819 List<Object> theItemArray = new ArrayList<>(); 2820 for (int i = 0; i < action.size(); i++) { 2821 final Node theCurrentAction = (Node) action.get(i); 2822 Node[] theItemNodes = theCurrentAction.getTextNodes(); 2823 if (theItemNodes != null && theItemNodes.length == 1) { 2824 final String replaceProperties = replaceProperties(theItemNodes[0].getText(), true); 2825 theItemArray.add(replaceProperties); 2826 } else { 2827 if (theCurrentAction.size() == 1) { 2828 Node node = theCurrentAction.get(0); 2829 EasyUtils.removeAllAttributes(node, Node.TAG_ID); 2830 theItemArray.add(replaceProperties(node.getXMLText(), true)); 2831 } else { 2832 theItemArray = null; 2833 } 2834 break; 2835 } 2836 } 2837 theValue = theItemArray; 2838 } 2839 } 2840 } 2841 2842 boolean contains = StringUtils.contains(theInit, "mandatory"); 2843 boolean isConsoleInput = StringUtils.contains(theInit, "console"); 2844 boolean mandatory = StringUtils.contains(theInit, "mandatory"); 2845 if (mandatory) { 2846 if (!isEmpty(theOldValue)) { 2847 setVariableValue(name, theValue); 2848 } else { 2849 isConsoleInput = true; 2850 } 2851 } 2852 2853 final boolean theIntegerType = "number".equals(type); 2854 if (theIntegerType) { 2855 String string = ObjectUtils.toString(theValue); 2856 if (StringUtils.isNotBlank(string) && !NumberUtils.isNumber(string)) { 2857 setVariableValue(name, null); 2858 throw new NumberFormatException(string); 2859 } 2860 } 2861 2862 final boolean theArrayType = "array".equals(type); 2863 if (name != null && !(theValue == null && theArrayType == false)) { 2864 if (theArrayType) { 2865 char separatorChar = ','; 2866 if (theValue instanceof String) { 2867 String[] split = StringUtils.split((String) theValue, separatorChar); 2868 theValue = split != null ? Arrays.asList(split) : null; 2869 } 2870 } 2871 if (theArrayType && theValue == null) { 2872 theValue = new String[0]; 2873 } 2874 2875 if (theArrayType && file != null) { 2876 ArrayList<String> buffer = new ArrayList<String>(); 2877 2878 if (theValue instanceof String[]) { 2879 for (String string : (String[]) theValue) { 2880 buffer.add(string); 2881 } 2882 } else if (theValue instanceof Collection) { 2883 buffer = new ArrayList<String>((Collection<String>) theValue); 2884 } 2885 2886 try (InputStream inputStream = AEUtils.getInputStream(file, getBaseDir())) { 2887 String encoding = replaceProperties(action.getAttribute("charset")); 2888 if (encoding == null) { 2889 encoding = "UTF-8"; 2890 } 2891 final BufferedReader theFileReader = new BufferedReader( 2892 new InputStreamReader(inputStream, encoding)); 2893 String readLine; 2894 while ((readLine = theFileReader.readLine()) != null) { 2895 buffer.add(readLine); 2896 } 2897 } 2898 theValue = buffer; 2899 } 2900 2901 theValue = convert(theValue, type); 2902 setVariableValue(name, theValue); 2903 } 2904 2905 if (isConsoleInput) { 2906 2907 if (theOldValue instanceof List && CollectionUtils.size(theOldValue) == 1) { 2908 theOldValue = ((List) theOldValue).get(0); 2909 } 2910 2911 if ((action.size() == 0 || !action.getInnerText().isEmpty()) 2912 && (isEmpty(theOldValue) || theOldValue instanceof String)) { 2913 Object theInitialSelectionValue = null; 2914 final Object o = getVariableValue(name); 2915 if (o instanceof String) { 2916 theInitialSelectionValue = o; 2917 } 2918 if (o instanceof String[] && ((String[]) o).length > 0) { 2919 if (this.random == null) { 2920 this.random = SecureRandom.getInstance("SHA1PRNG"); 2921 } 2922 theInitialSelectionValue = ((String[]) o)[this.random.nextInt(((String[]) o).length)]; 2923 } 2924 2925 String defaultValue = AEWorkspace.getInstance().getDefaultUserConfiguration(".inputValue." + name, 2926 (String) theInitialSelectionValue); 2927 if (this.recipeListener.getManager().isConsoleDefaultInput(name, description) 2928 && !(mandatory && defaultValue == null)) { 2929 2930 if ((o instanceof String[] && ArrayUtils.contains((String[]) o, defaultValue)) || o == null) { 2931 theInitialSelectionValue = defaultValue; 2932 } 2933 2934 setVariableValue(name, theInitialSelectionValue); 2935 } else { 2936 boolean notifyMe = this.recipeListener.isNotifyMe(); 2937 Object inputValue; 2938 2939 inputValue = this.recipeListener.getManager().inputValue(name, description, defaultValue, log, type, 2940 notifyMe, this); 2941 2942 setVariableValue(name, inputValue); 2943 } 2944 2945 } else { 2946 List<Object> possibleValues = null; 2947 if (theOldValue instanceof List) { 2948 @SuppressWarnings("unchecked") 2949 final List<Object> theStringArray = (List<Object>) theOldValue; 2950 possibleValues = theStringArray; 2951 } else if (theOldValue instanceof String[]) { 2952 possibleValues = new ArrayList<>(); 2953 String[] array = (String[]) theOldValue; 2954 for (String item : array) { 2955 possibleValues.add(item); 2956 } 2957 } else { 2958 possibleValues = new ArrayList<>(); 2959 for (int i = 0; i < action.size(); i++) { 2960 final Node theCurrentAction = (Node) action.get(i); 2961 possibleValues.add(theCurrentAction.getInnerText()); 2962 } 2963 } 2964 2965 if (this.recipeListener.getManager().isConsoleDefaultInput(name, null)) { 2966 String theInitialSelectionValue = randomSelect(possibleValues); 2967 if (!randomSelect) { 2968 theInitialSelectionValue = AEWorkspace.getInstance() 2969 .getDefaultUserConfiguration(".choiceValue." + name, theInitialSelectionValue); 2970 2971 } 2972 setVariableValue(name, theInitialSelectionValue); 2973 } else { 2974 boolean notifyMe = this.recipeListener.isNotifyMe(); 2975 final Object theValueOut = this.recipeListener.getManager().choiceValue(name, description, 2976 possibleValues.toArray(), log, notifyMe, this); 2977 setVariableValue(name, theValueOut); 2978 } 2979 } 2980 } 2981 2982 theValue = getVariableValue(name); 2983 2984 String start = replaceProperties(action.getAttribute("start")); 2985 String end = replaceProperties(action.getAttribute("end")); 2986 2987 if (StringUtils.isNotEmpty(start)) { 2988 if (theValue instanceof byte[]) { 2989 theValue = new String((byte[]) theValue); 2990 } 2991 theValue = StringUtils.substringAfter(ObjectUtils.toString(theValue), start); 2992 if (StringUtils.isBlank((String) theValue)) { 2993 theValue = null; 2994 } 2995 } 2996 if (StringUtils.isNotEmpty(end)) { 2997 theValue = StringUtils.substringBefore((String) theValue, end); 2998 if (StringUtils.isBlank((String) theValue)) { 2999 theValue = null; 3000 } 3001 } 3002 3003 applyResult(action, name, theValue); 3004 3005 if (contains && theValue == null) { 3006 stop(); 3007 } 3008 } 3009 3010 private Object convert(Object theValue, String type) throws JSONException { 3011 if ("json".equals(type)) { 3012 Object parse = theValue; 3013 if (theValue instanceof String[]) { 3014 String[] array = (String[]) theValue; 3015 List<String> asList = Arrays.asList(array); 3016 theValue = new JSONArray(asList); 3017 } else if (theValue instanceof String) { 3018 if (StringUtils.startsWith((String) theValue, "{")) { 3019 parse = new JSONObject(theValue.toString()); 3020 } else if (StringUtils.startsWith((String) theValue, "[")) { 3021 parse = new JSONArray(theValue.toString()); 3022 } 3023 } 3024 theValue = parse; 3025 } else if ("string".equals(type) && theValue instanceof String[]) { 3026 theValue = StringUtils.join((String[]) theValue); 3027 } 3028 3029 return theValue; 3030 } 3031 3032 private String randomSelect(List possibleValues) throws NoSuchAlgorithmException { 3033 final String theInitialSelectionValue; 3034 if (this.random == null) { 3035 this.random = SecureRandom.getInstance("SHA1PRNG"); 3036 } 3037 theInitialSelectionValue = (String) possibleValues.get(this.random.nextInt(possibleValues.size())); 3038 return theInitialSelectionValue; 3039 } 3040 3041 @CommandExamples({ "<Note name='type:string' connection='type:string'>...</Note>" }) 3042 public void runCommandNote(final Node aCurrentAction) throws Throwable { 3043 } 3044 3045 @CommandExamples({ "<!-- ... -->" }) 3046 public void runCommandComment(final Node aCurrentAction) throws Throwable { 3047 } 3048 3049 @CommandExamples({ "<Extern class='type:processor'>\n...\n</Extern>" }) 3050 public void runCommandExtern(final Node command) throws Throwable { 3051 final Processor newInstance = makeProcessor(command); 3052 boolean success = false; 3053 try { 3054 this.externProcessor = newInstance; 3055 externProcessor.setTestName(getTestName()); 3056 newInstance.init(this, command); 3057 boolean rootRecipe = isRootRecipe(); 3058 newInstance.setRootContext(rootRecipe); 3059 newInstance.stackTask.addAll(this.stackTask); 3060 newInstance.taskNode(command, false); 3061 3062 this.variables = newInstance.variables; 3063 success = true; 3064 } finally { 3065 externProcessor.complete(success); 3066 externProcessor = null; 3067 } 3068 } 3069 3070 @CommandDescription("The Confirm command is used to confirm the user's decision to continue a " 3071 + "task or execute internal code. The name attribute is used as the dialog box title " 3072 + "and as a variable name if confirmation is required without displaying a dialog box " 3073 + "to the user. If the variable exists, confirmation is automatically applied if the " 3074 + "value is true, or rejected if the value is not true.") 3075 @CommandExamples({ "<Confirm message='type:string' name='type:string'/>", 3076 "<Confirm message='type:string' name='type:string'>...</Confirm>" }) 3077 public void runCommandConfirm(final Node aCurrentAction) throws Throwable { 3078 final String message = attr(aCurrentAction, "message"); 3079 final String name = attr(aCurrentAction, "name"); 3080 final Object nameVar = getVariableValue(name); 3081 3082 boolean confirmed = false; 3083 if (nameVar != null && nameVar instanceof String) { 3084 confirmed = BooleanUtils.toBoolean((String) nameVar); 3085 } else { 3086 AEManager manager = this.recipeListener.getManager(); 3087 confirmed = manager.confirmation(name, message, this, this.recipeListener.isNotifyMe()); 3088 } 3089 3090 if (aCurrentAction.size() > 0) { 3091 if (confirmed) { 3092 taskNode(aCurrentAction, false); 3093 } 3094 } else { 3095 if (!confirmed) { 3096 stop(); 3097 } 3098 } 3099 } 3100 3101 @CommandDescription("The WhileRun command is used to notify the user that a process has started. " 3102 + "The user can close the WhileRun dialog box without affecting the process's execution. " 3103 + "The dialog box closes after the code within the WhileRun tag completes execution. " 3104 + "The name attribute is used as the dialog box title and variable name. " 3105 + "If the dialog box should not be opened, a variable with a name corresponding to " 3106 + "the dialog box name should be assigned a value other than `true`.") 3107 @CommandExamples({ "<WhileRun name='type:string' message='type:string'>...</WhileRun>" }) 3108 public void runCommandWhileRun(final Node aCurrentAction) throws Throwable { 3109 final String message = attr(aCurrentAction, "message"); 3110 final String name = attr(aCurrentAction, "name"); 3111 final Object nameVar = getVariableValue(name); 3112 3113 if (!(nameVar instanceof String) || BooleanUtils.toBoolean((String) nameVar)) { 3114 AEManager manager = this.recipeListener.getManager(); 3115 MessageHandler handler = manager.message(this, name, message, this.recipeListener.isNotifyMe()); 3116 taskNode(aCurrentAction, false); 3117 handler.close(); 3118 } 3119 }; 3120 3121 @CommandDescription("The `Server` command initializes and starts a server socket listener, " 3122 + "operating as a blocking command that prevents the execution of subsequent commands until it completes.") 3123 @CommandExamples({ "<Server port='type:integer' request='type:property' response='type:property' > ... </Server>", 3124 "<Server port='type:integer' numbers='type:integer' request='type:property' response='type:property' > ... </Server>" }) 3125 public void runCommandServer(final Node aCurrentAction) throws Throwable { 3126 3127 String encoding = replaceProperties(aCurrentAction.getAttribute("charset")); 3128 if (encoding == null) { 3129 encoding = "UTF-8"; 3130 } 3131 3132 final int thePort = Integer.parseInt(replaceProperties(aCurrentAction.getAttribute("port"))); 3133 int maxNumber = 0; 3134 3135 final String theMaxNumbers = replaceProperties(aCurrentAction.getAttribute("numbers")); 3136 if (theMaxNumbers != null) { 3137 maxNumber = Integer.parseInt(theMaxNumbers); 3138 } 3139 final String request = attr(aCurrentAction, "request"); 3140 final String response = attr(aCurrentAction, "response"); 3141 3142 ServerAction server = new ServerAction(this, aCurrentAction, thePort, request, response, encoding, maxNumber); 3143 server.perform(); 3144 } 3145 3146 @CommandExamples({ "<Restore/>", "<Restore name='type:property'> ... </Restore>", 3147 "<Restore except='type:property'> ... </Restore>" }) 3148 public void runCommandRestore(final Node command) throws Throwable { 3149 String name = attr(command, "name"); 3150 String except = attr(command, "except"); 3151 3152 if (CollectionUtils.isEmpty(command)) { 3153 final Map<String, Object> systemVariables = getListener().getManager().getSystemVariables(); 3154 this.variables.clear(); 3155 this.variables.putAll(systemVariables); 3156 debug("Task variables has been restored."); 3157 } else if (name != null) { 3158 Object savedVariable = getVariableValue(name); 3159 taskNode(command, false); 3160 setVariableValue(name, savedVariable); 3161 3162 } else if (except != null) { 3163 Map<String, Object> savedVariables = this.variables; 3164 this.variables = new HashMap<String, Object>(this.variables); 3165 3166 taskNode(command, false); 3167 3168 Object object = getVariableValue(except); 3169 this.variables.clear(); 3170 this.variables = savedVariables; 3171 setVariableValue(except, object); 3172 } 3173 3174 } 3175 3176 @CommandDescription("Use the Finally command tag before protected code to ensure it is executed " 3177 + "before exiting the current parent tag, as usual Finally is defined as a first executable command.\n" 3178 + "Example: `<Recipe><Finally> ...finally code... </Finally> ...some code... </Recipe>`") 3179 @CommandExamples({ "<Finally> ... </Finally>" }) 3180 public void runCommandFinally(final Node aCurrentAction) throws Throwable { 3181 } 3182 3183 @CommandExamples({ "<Break/>" }) 3184 public void runCommandBreak(final Node aCurrentAction) throws Throwable { 3185 } 3186 3187 @CommandExamples({ "<Stop/>", "<Stop ifNull='type:property'/>" }) 3188 public void runCommandStop(final Node aCurrentAction) throws Throwable { 3189 } 3190 3191 private boolean isEmpty(Object theOldValue) { 3192 boolean result = false; 3193 if (theOldValue == null) { 3194 result = true; 3195 } else if (theOldValue instanceof String[] && ((String[]) theOldValue).length == 0) { 3196 result = true; 3197 } else if (theOldValue instanceof Map && ((Map) theOldValue).size() == 0) { 3198 result = true; 3199 } 3200 return result; 3201 } 3202 3203 public void setVariableValue(String name, final Object value) { 3204 if (name != null) { 3205 super.setVariableValue(name, value); 3206 if (this.recipeListener != null && name.startsWith("!") == false) { 3207 this.recipeListener.changeVariable(name, value); 3208 } 3209 } 3210 } 3211 3212}