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