001package com.ganteater.ae.processor;
002
003import java.io.BufferedReader;
004import java.io.File;
005import java.io.FileNotFoundException;
006import java.io.FileReader;
007import java.io.IOException;
008import java.io.StringReader;
009import java.lang.reflect.Constructor;
010import java.lang.reflect.InvocationTargetException;
011import java.lang.reflect.Method;
012import java.net.MalformedURLException;
013import java.security.GeneralSecurityException;
014import java.security.SecureRandom;
015import java.security.cert.X509Certificate;
016import java.util.ArrayList;
017import java.util.Calendar;
018import java.util.Date;
019import java.util.Enumeration;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Properties;
025import java.util.Set;
026import java.util.Stack;
027import java.util.StringTokenizer;
028import java.util.Vector;
029import java.util.concurrent.ThreadPoolExecutor;
030
031import javax.net.ssl.HttpsURLConnection;
032import javax.net.ssl.SSLContext;
033import javax.net.ssl.TrustManager;
034import javax.net.ssl.X509TrustManager;
035
036import org.apache.commons.collections.MapUtils;
037import org.apache.commons.lang.ObjectUtils;
038import org.apache.commons.lang.StringUtils;
039import org.apache.commons.lang.math.NumberUtils;
040
041import com.ganteater.ae.AELogRecord;
042import com.ganteater.ae.AEManager;
043import com.ganteater.ae.CommandException;
044import com.ganteater.ae.ConfigConstants;
045import com.ganteater.ae.ILogger;
046import com.ganteater.ae.RecipeListener;
047import com.ganteater.ae.RecipeRunner;
048import com.ganteater.ae.TaskCancelingException;
049import com.ganteater.ae.TemplateProcessor;
050import com.ganteater.ae.util.AssertionFailedError;
051import com.ganteater.ae.util.xml.easyparser.EasyParser;
052import com.ganteater.ae.util.xml.easyparser.Node;
053import com.ganteater.ae.util.xml.easyparser.Node.TreeVector;
054
055@SuppressWarnings("deprecation")
056public abstract class Processor extends TemplateProcessor {
057
058        public static final String STG_PROCESSOR_PACKAGE_NAME = "com.ganteater.ae.processor.";
059
060        private static final String COMMAND_PREFIX = "runCommand";
061        protected static final String DEFAULT_LOG_LEVEL_NAME = "DEFAULT_LOG_LEVEL";
062
063        protected ILogger log;
064
065        protected String testName;
066        protected String testFile;
067        public Map<String, Object> startVariables;
068        private File startBaseDir;
069        private Processor parent = null;
070        protected Processor externProcessor = null;
071
072        protected RecipeListener recipeListener;
073        protected Vector<TaskProcessorThread> childThreads = new Vector<TaskProcessorThread>();
074        protected Stack<Node> stackTask = new Stack<>();
075        protected List<ThreadPoolExecutor> threadPoolList = new ArrayList<ThreadPoolExecutor>();
076        protected Map<Class<?>, Object> operationObjects = new HashMap<Class<?>, Object>();
077
078        private boolean mainProcessor = true;
079        protected boolean iteratorMode = false;
080        protected boolean mainLoopCommand = false;
081        private boolean pauseOn;
082        protected boolean randomSelect;
083
084        protected int breakFlag;
085
086        protected List<Process> processes = new ArrayList<>();
087        protected boolean isRootContext;
088        private boolean silentMode = isSilentMode(null);
089
090        private Object monitor = new Object();
091
092        static {
093                javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {
094                        public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
095                                return true;
096                        }
097                });
098
099                final TrustManager[] trustAllCertificates = new TrustManager[] { new X509TrustManager() {
100                        @Override
101                        public X509Certificate[] getAcceptedIssuers() {
102                                return null; // Not relevant.
103                        }
104
105                        @Override
106                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
107                                // Do nothing. Just allow them all.
108                        }
109
110                        @Override
111                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
112                                // Do nothing. Just allow them all.
113                        }
114                } };
115
116                try {
117                        SSLContext sc = SSLContext.getInstance("SSL");
118                        sc.init(null, trustAllCertificates, new SecureRandom());
119                        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
120                } catch (GeneralSecurityException e) {
121                        throw new ExceptionInInitializerError(e);
122                }
123        }
124
125        protected Processor() {
126        }
127
128        public Processor(final Processor parent) throws CommandException {
129                init(parent);
130        }
131
132        public Processor(AEManager manager, ILogger log, File baseDir) {
133                this(new HashMap<String, Object>(manager.getSystemVariables()), manager.getConfigNode(), baseDir);
134                this.log = log;
135        }
136
137        public Processor(final Node configNode, final Map<String, Object> startVariables, final RecipeListener listener,
138                        final File startDir, final ILogger aLog, final Processor parent) throws CommandException {
139                this(parent);
140                init(configNode, startVariables, listener, startDir, aLog);
141        }
142
143        public Processor(Map<String, Object> hashMap, Node node, File baseDir) {
144                super(hashMap, node, baseDir);
145        }
146
147        public void init(final Processor parent) throws CommandException {
148                this.parent = parent;
149                this.variables = new HashMap<String, Object>(parent.getListener().getManager().getSystemVariables());
150                this.configNode = parent.getConfigNode();
151                this.log = parent.getLog();
152                setBaseDir(parent.getBaseDir());
153                init();
154        }
155
156        protected void init() throws CommandException {
157        }
158
159        public void init(final Node action, final Map<String, Object> variables, final RecipeListener aMaxTestListener,
160                        final File aStartDir, final ILogger aLog) {
161                this.log = aLog;
162
163                this.configNode = action;
164                this.startVariables = variables;
165                if (aStartDir != null) {
166                        this.startBaseDir = aStartDir;
167                        setBaseDir(aStartDir);
168                }
169
170                if (this.startVariables != null) {
171                        this.variables.putAll(this.startVariables);
172                }
173
174                setTestListener(aMaxTestListener);
175        }
176
177        public void init(final Processor parentProcessor, Node action) throws CommandException {
178                this.mainProcessor = false;
179                this.log = parentProcessor.log;
180                this.configNode = parentProcessor.configNode;
181                this.startVariables = parentProcessor.startVariables;
182                this.startBaseDir = parentProcessor.startBaseDir;
183                setBaseDir(parentProcessor.getBaseDir());
184                this.variables = parentProcessor.variables;
185                setTestListener(parentProcessor.recipeListener);
186                this.parent = parentProcessor;
187        }
188
189        protected Map<String, Object> processTesting(final String testFileName, final Map<String, Object> variables,
190                        final File baseDir) throws Throwable {
191                this.variables = variables;
192                final File theBaseDir = getBaseDir();
193                setBaseDir(baseDir);
194                processTesting(testFileName);
195                setBaseDir(theBaseDir);
196                return this.variables;
197        }
198
199        protected void processTesting(final String testFileName) throws CommandException {
200                if (testFileName == null) {
201                        return;
202                }
203
204                Node theNode;
205                try {
206                        theNode = new EasyParser().load(testFileName);
207                        theNode.getVector(true);
208
209                } catch (Exception e) {
210                        throw new CommandException("Recipe is not found: " + testFileName, this);
211
212                }
213
214                this.recipeListener.startTask(getTestName(theNode));
215                processTesting(theNode, this.variables, new File(testFileName).getParentFile());
216        }
217
218        public void processTesting(final Node node, final Map<String, Object> variables, final File baseDir)
219                        throws CommandException {
220
221                this.variables = new HashMap<String, Object>();
222                if (variables != null) {
223                        this.variables.putAll(variables);
224                }
225                setBaseDir(baseDir);
226
227                if (this.startBaseDir == null) {
228                        this.startBaseDir = getBaseDir();
229                }
230                final String theDepends = node.getAttribute("depends");
231                if (theDepends != null) {
232                        final StringTokenizer theStringTokenizer = new StringTokenizer(theDepends, ";");
233                        while (theStringTokenizer.hasMoreTokens()) {
234                                processTesting(theStringTokenizer.nextToken());
235                                setBaseDir(this.startBaseDir);
236                        }
237                }
238                String taskName = node.getAttribute("name");
239                if (taskName != null) {
240                        debug("Recipe: \"" + taskName + "\"");
241                }
242
243                taskNode(node);
244
245                if (taskName != null) {
246                        debug("Recipe: \"" + taskName + "\" done");
247                }
248        }
249
250        public void taskNode(final Node node) throws CommandException {
251                taskNode(node, true);
252        }
253
254        public void taskNode(final Node node, boolean root) throws CommandException {
255
256                if (isStopped()) {
257                        return;
258                }
259
260                if (getBaseDir() != null) {
261                        setVariableValue("BASEDIR", getBaseDir().getPath());
262                }
263
264                CommandException theTroubles = null;
265
266                final Vector<Node> finallyVector = new Vector<Node>();
267
268                String description = getTaskDescription(node);
269
270                int i = 0;
271                int theNumberOperation = -1;
272                String recipeName = node.getAttribute("name");
273
274                if (root) {
275                        pushToStack(node, recipeName);
276                }
277
278                Node command = null;
279                try {
280                        if (node != null)
281                                while (++theNumberOperation < node.size() && isStopped() == false) {
282
283                                        command = (Node) node.get(theNumberOperation);
284                                        pushToStack(command, recipeName);
285
286                                        final String theRunMode = command.getAttribute("runMode");
287                                        if (theRunMode != null && theRunMode.equals(this.recipeListener.getRunMode()) == false) {
288                                                continue;
289                                        }
290
291                                        String theDescription = command.getAttribute("description");
292
293                                        theDescription = replaceProperties(theDescription);
294                                        if (theDescription != null) {
295                                                theDescription = replaceProperties(theDescription);
296                                        }
297
298                                        if (this.iteratorMode == false) {
299                                                progress(node.size(), i++, description, false);
300                                        }
301
302                                        if (SpecialCommands.LOGGER.equals(command.getTag())) {
303                                                continue;
304                                        }
305
306                                        final String exceptionVarName = replaceProperties(command.getAttribute("exception"));
307                                        try {
308                                                final String tagName = command.getTag();
309
310                                                if (SpecialCommands.ELSE.equals(tagName)) {
311                                                        continue;
312                                                }
313
314                                                if (SpecialCommands.BREAK.equals(tagName)) {
315                                                        breakFlag++;
316                                                        break;
317                                                }
318
319                                                if (SpecialCommands.STOP.equals(tagName)) {
320                                                        String ifNull = command.getAttribute("ifNull");
321                                                        if (ifNull != null) {
322                                                                String replaceProperties = replaceProperties(ifNull);
323                                                                if (getVariableValue(replaceProperties) == null) {
324                                                                        stop();
325                                                                        break;
326                                                                }
327                                                        } else {
328                                                                stop();
329                                                                break;
330                                                        }
331                                                }
332
333                                                if (SpecialCommands.FINALLY.equals(tagName)) {
334                                                        finallyVector.add(command);
335                                                        continue;
336                                                }
337
338                                                if (isStopped()) {
339                                                        return;
340                                                }
341                                                startCommandInformation(command);
342
343                                                executeCommand(command);
344
345                                                if (exceptionVarName != null) {
346                                                        setVariableValue(exceptionVarName, null);
347                                                }
348
349                                        } catch (final InvocationTargetException e) {
350                                                Throwable cause = e.getCause();
351
352                                                if (!isStoppedTest()) {
353                                                        if (exceptionVarName != null) {
354                                                                this.recipeListener.exceptionIgnored(e);
355                                                                setVariableValue(exceptionVarName, e.getCause().getMessage());
356
357                                                        } else {
358                                                                if (!(cause instanceof TaskCancelingException)
359                                                                                && !(cause instanceof CommandException)) {
360                                                                        try {
361                                                                                this.recipeListener.errorInformation(this, cause, command);
362                                                                                theTroubles = null;
363                                                                                break;
364
365                                                                        } catch (final Exception e1) {
366                                                                                cause = e1;
367                                                                        }
368                                                                }
369
370                                                                if (cause instanceof TaskCancelingException) {
371                                                                        recipeListener.stopTest();
372                                                                } else if (cause instanceof CommandException) {
373                                                                        theTroubles = (CommandException) cause;
374                                                                } else {
375                                                                        theTroubles = new CommandException(cause, this, command);
376                                                                }
377                                                                break;
378                                                        }
379                                                }
380                                        } catch (final Throwable e) {
381                                                if (!isStoppedTest()) {
382                                                        theTroubles = new CommandException(e, this, command);
383                                                }
384
385                                        } finally {
386                                                this.stackTask.pop();
387                                                doPause();
388
389                                                if (this.iteratorMode == false) {
390                                                        progress(node.size(), i, description, theTroubles != null);
391                                                }
392                                        }
393                                }
394                } finally {
395                        boolean stoppedTest = isStopped();
396                        setStopped(false);
397                        for (Node curNode : finallyVector) {
398                                try {
399                                        taskNode(curNode, false);
400                                } catch (final Throwable aThrowable) {
401                                        this.log.error("Exception in finally block.\n"
402                                                        + new CommandException(aThrowable, this, command).getTaskTrace(), aThrowable);
403                                }
404                        }
405                        setStopped(stoppedTest);
406                        if (root) {
407                                this.stackTask.pop();
408                        }
409                }
410
411                if (theTroubles == null) {
412                        progress(100, 100, "", false);
413                } else {
414                        progress(node.size(), i, description, true);
415                }
416
417                if (theTroubles != null) {
418                        if (isMainProcessor() && this.stackTask.size() == 0) {
419
420                                final String theExceptionMode = replaceProperties(node.getAttribute("exception"));
421                                if ("ignored".equals(theExceptionMode) || "ignore".equals(theExceptionMode)) {
422                                        this.recipeListener.exceptionIgnored(theTroubles);
423                                        return;
424
425                                } else {
426                                        if (theTroubles.getCause() instanceof AssertionFailedError) {
427                                                this.recipeListener.checkFailure(theTroubles, this);
428                                                theTroubles = null;
429                                        } else {
430                                                this.recipeListener.criticalError(theTroubles, this);
431                                        }
432                                }
433                        }
434
435                        if (theTroubles != null) {
436                                throw theTroubles;
437                        }
438                }
439
440        }
441
442        private void pushToStack(Node command, String recipeName) {
443                Node clone = (Node) command.clone();
444                clone.clear();
445                if (recipeName != null) {
446                        clone.setAttribute("recipe", recipeName);
447                }
448                this.stackTask.push(clone);
449        }
450
451        private void executeCommand(Node action) throws IllegalAccessException, InvocationTargetException {
452                Node command;
453                Object paramsMap = attrValue(action, "attr-map");
454                if (paramsMap instanceof Map) {
455                        command = (Node) action.clone();
456                        command.removeAttribute("attr-map");
457                        @SuppressWarnings({ "unchecked", "rawtypes" })
458                        Set<Entry<String, String>> entrySet = ((Map) paramsMap).entrySet();
459                        for (Entry<String, String> entry : entrySet) {
460                                String key = entry.getKey();
461                                String value = entry.getValue();
462                                if (command.getAttribute(key) == null) {
463                                        command.setAttribute(key, value);
464                                }
465                        }
466                } else {
467                        command = action;
468                }
469
470                final String tagName = command.getTag();
471
472                try {
473                        final Method theMethod = getClass().getMethod(COMMAND_PREFIX + tagName.replace('$', '_'),
474                                        new Class[] { Node.class });
475
476                        theMethod.invoke(this, new Object[] { command });
477                } catch (NoSuchMethodException e) {
478                }
479        }
480
481        public boolean isMainProcessor() {
482                return this.mainProcessor;
483        }
484
485        public String getTestName() {
486                return this.testName;
487        }
488
489        public void setTestName(String testName) {
490                this.testName = testName;
491        }
492
493        public void regChildProcess(final TaskProcessorThread aMaxTestRunnable) {
494                this.recipeListener.startChildProcess(aMaxTestRunnable);
495                this.childThreads.add(aMaxTestRunnable);
496        }
497
498        public void unregChildProcess(final TaskProcessorThread aMaxTestRunnable) {
499                this.recipeListener.stopChildProcess(aMaxTestRunnable);
500                this.childThreads.remove(aMaxTestRunnable);
501        }
502
503        public Node getConfigNode() {
504                return this.configNode;
505        }
506
507        public Map<String, Object> getStartVariables() {
508                return this.startVariables;
509        }
510
511        public RecipeListener getListener() {
512                return this.recipeListener;
513        }
514
515        public Map<String, Object> getVariables() {
516                return this.variables;
517        }
518
519        public int getChildThreads() {
520                return this.childThreads.size();
521        }
522
523        public ILogger getLog() {
524                return log;
525        }
526
527        public void runNodes(final Node[] aTheNodes) throws CommandException {
528
529                for (int i = 0; i < aTheNodes.length; i++) {
530                        if (isStopped()) {
531                                break;
532                        }
533                        final Node theNode = aTheNodes[i];
534                        taskNode(theNode, false);
535                }
536        }
537
538        public void setTestListener(final RecipeListener testListener) {
539                this.recipeListener = testListener;
540        }
541
542        public void startCommandInformation(final Node command) {
543                final String theID = command.getAttribute(Node.TAG_ID);
544
545                boolean isRootRecipe = isRootRecipe();
546
547                if (this.recipeListener != null && theID != null && isRootRecipe) {
548                        this.recipeListener.startCommand(Integer.parseInt(theID));
549                }
550        }
551
552        protected boolean isRootRecipe() {
553                int recipeNodeCount = 0;
554                for (Object node : this.stackTask.toArray()) {
555                        if ("Recipe".equals(((Node) node).getTag())) {
556                                recipeNodeCount++;
557                        }
558                }
559                boolean isRootRecipe = recipeNodeCount == 1 || isRootContext;
560                return isRootRecipe;
561        }
562
563        protected String getTestName(final Node aNode) {
564                String theAttribut = aNode.getAttribute("name");
565                if (theAttribut == null || theAttribut.trim().length() == 0) {
566                        theAttribut = aNode.getAttribute("description");
567                }
568                return replaceProperties(theAttribut);
569        }
570
571        private String getTaskDescription(final Node aNode) {
572                if (aNode == null) {
573                        return null;
574                }
575                String theTaskDescription = aNode.getAttribute("name");
576                if (theTaskDescription == null || theTaskDescription.trim().length() == 0) {
577                        theTaskDescription = aNode.getAttribute("description");
578                }
579                final String theTag = aNode.getTag();
580                if (theTag.equals("Recipe")) {
581                        startTask(theTaskDescription, aNode.getAttribute("description"));
582                } else if (theTag.equals(SpecialCommands.STARTUP)) {
583                        if (theTaskDescription == null) {
584                                theTaskDescription = SpecialCommands.STARTUP;
585                        }
586                        startTask(theTaskDescription, SpecialCommands.STARTUP);
587                } else {
588                        theTaskDescription = aNode.getAttribute("description");
589                }
590                return theTaskDescription;
591        }
592
593        private void startTask(String name, String description) {
594                this.testName = name;
595                this.recipeListener.startTask(name);
596        }
597
598        protected String getTrimmedLines(String theValue1) throws IOException {
599                final BufferedReader theStringReader = new BufferedReader(new StringReader(theValue1));
600                String theLine = "";
601                final StringBuffer theBuffer = new StringBuffer();
602                while ((theLine = theStringReader.readLine()) != null) {
603                        final String theTrimmedLine = theLine.trim();
604                        if (theTrimmedLine.length() > 0) {
605                                theBuffer.append(theTrimmedLine);
606                                theBuffer.append('\n');
607                        }
608                }
609                theValue1 = theBuffer.toString();
610                return theValue1;
611        }
612
613        protected String[] randomSort(final String[] aArray) {
614                final Vector<String> theVector = new Vector<String>();
615                for (int i = 0; i < aArray.length; i++) {
616                        theVector.add(aArray[i]);
617                }
618                final String[] theResult = new String[theVector.size()];
619
620                for (int l = 0; l < aArray.length; l++) {
621                        final int theNumber = (int) (Math.random() * (theVector.size()));
622                        theResult[l] = theVector.elementAt(theNumber);
623                        theVector.removeElementAt(theNumber);
624                }
625
626                return theResult;
627        }
628
629        static public String prepareXml(String theCurrentValue) {
630                final int head = theCurrentValue.indexOf("<?");
631                if (head >= 0) {
632                        final int end = theCurrentValue.indexOf("?>");
633                        theCurrentValue = theCurrentValue.substring(end + 2).trim();
634                }
635                return theCurrentValue;
636        }
637
638        protected Properties replaceAttributes(Node aCurrentAction) {
639                Map<String, String> attributes = aCurrentAction.getAttributes();
640                Properties properties = MapUtils.toProperties(attributes);
641                Set<Entry<Object, Object>> entrySet = properties.entrySet();
642                Properties props = new Properties();
643                for (Entry<Object, Object> entry : entrySet) {
644                        String key = (String) entry.getKey();
645                        String value = (String) entry.getValue();
646
647                        String replaceProperties = replaceProperties(value);
648                        props.setProperty(key, replaceProperties);
649                }
650                return props;
651        }
652
653        protected void outputLog(String varName, final String theLevelAttribut, final Object theTextOut, String type) {
654
655                AELogRecord logRecord = new AELogRecord(theTextOut, type, varName);
656
657                if ("info".equals(theLevelAttribut)) {
658                        this.log.info(logRecord);
659                }
660                if ("error".equals(theLevelAttribut)) {
661                        this.log.error(logRecord);
662                }
663                if ("debug".equals(theLevelAttribut)) {
664                        debug(logRecord);
665                }
666                if ("warn".equals(theLevelAttribut)) {
667                        this.log.warn(logRecord);
668                }
669        }
670
671        protected boolean isActiveInitFor(final Node action, String value) {
672                return StringUtils.contains(attr(action, "init"), value);
673        }
674
675        protected String getFields(final Properties theOutArrayList) {
676                final StringBuffer theResult = new StringBuffer();
677                for (final Enumeration<?> e = theOutArrayList.propertyNames(); e.hasMoreElements();) {
678                        final String theName = (String) e.nextElement();
679                        final String theValue = theOutArrayList.getProperty(theName);
680                        theResult.append(theName);
681                        theResult.append("=");
682                        theResult.append(theValue);
683                        theResult.append(";");
684                }
685                return theResult.toString();
686        }
687
688        protected void applyResult(final Node commandNode, final String name, final Object value) {
689                setVariableValue(name, value);
690
691                String level = replaceProperties(commandNode.getAttribute("level"));
692                String type = replaceProperties(commandNode.getAttribute("type"));
693                if (level != null) {
694                        outputLog(name, level, value, type);
695                }
696        }
697
698        public void setStartDir(final File aStartBaseDir) {
699                this.startBaseDir = aStartBaseDir;
700        }
701
702        protected void progress(final long theSize, final long aValue, String aDescription, final boolean aErrorState) {
703                try {
704                        if (aDescription == null || aDescription.length() == 0) {
705                                aDescription = new String(" ");
706                        }
707
708                        boolean loopActivated = isLoopActivated();
709
710                        if (!loopActivated) {
711                                this.recipeListener.setProgress(aDescription, theSize, aValue, aErrorState);
712                        }
713                } catch (final Throwable e) {
714                }
715        }
716
717        protected boolean isLoopActivated() {
718                boolean result = false;
719                Processor parent = getParent();
720                if (parent != null) {
721                        result = parent.isLoopActivated();
722                }
723                if (!result) {
724                        result = mainLoopCommand;
725                }
726                return result;
727        }
728
729        @Override
730        public File getFile(String filePath) {
731                filePath = replaceProperties(filePath);
732
733                File file = null;
734                if (!StringUtils.startsWithAny(filePath, new String[] { "https:", "http:" })) {
735                        if (filePath != null) {
736                                if (filePath.startsWith("classpath:") || filePath.startsWith("jar:") || filePath.startsWith("file:")
737                                                || '/' == filePath.charAt(0) || '\\' == filePath.charAt(0) || filePath.indexOf(":\\") > 0
738                                                || '/' == filePath.charAt(0)) {
739                                        file = new File(filePath);
740                                } else {
741                                        file = new File(getBaseDir(), filePath);
742                                }
743                        }
744                }
745
746                return file;
747        }
748
749        public void make(final String aTestFile) throws CommandException {
750                this.testFile = aTestFile;
751                try {
752                        if (this.recipeListener != null && isStopped() == false) {
753                                this.recipeListener.startTask(this.testFile);
754                                processTesting(this.testFile);
755                                this.recipeListener.endTask(false);
756                        } else {
757                                throw new RuntimeException();
758                        }
759
760                } finally {
761
762                        if (this.testName != null) {
763                                debug("Recipe '" + this.testName + "' is stopped.");
764                        }
765                }
766        }
767
768        public void stop() {
769                super.stop();
770
771                if (getChildThreads() > 0) {
772                        debug("Child's threads: " + getChildThreads());
773                }
774
775                if (this.externProcessor != null && this.externProcessor != this && !externProcessor.isStoppedTest()) {
776                        this.externProcessor.stop();
777                }
778
779                if (this.configNode == null) {
780                        return;
781                }
782
783                synchronized (monitor) {
784                        monitor.notify();
785                }
786
787                for (Process process : processes) {
788                        debug("Request to destroy a child process:" + process.destroyForcibly());
789                }
790                processes.clear();
791
792                stopChilds();
793
794                for (ThreadPoolExecutor pool : threadPoolList) {
795                        pool.shutdownNow();
796                }
797
798                while (!isStoppedTest()) {
799                        try {
800                                synchronized (monitor) {
801                                        monitor.wait(100);
802                                }
803                        } catch (InterruptedException e) {
804                                Thread.currentThread().interrupt();
805                        }
806                }
807
808                RecipeListener listener = getListener();
809                if (listener != null) {
810                        listener.endTask(false);
811                }
812
813                if (parent != null && !parent.isStoppedTest()) {
814                        parent.stop();
815                }
816        }
817
818        private void stopChilds() {
819                List<TaskProcessorThread> fChildThreads2 = new ArrayList<>(childThreads);
820                for (TaskProcessorThread child : fChildThreads2) {
821                        child.stopTest();
822
823                        while (!child.getProcessor().isStoppedTest()) {
824                                try {
825                                        synchronized (monitor) {
826                                                monitor.wait(100);
827                                        }
828                                } catch (InterruptedException e) {
829                                        Thread.currentThread().interrupt();
830                                }
831                        }
832                }
833        }
834
835        static class SystemCommandStart extends Thread {
836                private final String fCommandAttribut;
837
838                ILogger fLog;
839
840                public SystemCommandStart(final String aCommandAttribut, final ILogger aLog) {
841                        this.fLog = aLog;
842                        this.fCommandAttribut = aCommandAttribut;
843                }
844
845                @Override
846                public void run() {
847                        try {
848                                Runtime.getRuntime().exec(this.fCommandAttribut);
849                        } catch (final IOException e) {
850                                this.fLog.error("System command starting is failed.", e);
851                        }
852                }
853
854        }
855
856        protected String replaceXmlReference(String aValue) throws Exception {
857                aValue = replaceFile(aValue);
858                final String theStartKey = "&#";
859                final String theEndKey = ";";
860                int theBeginPos = 0;
861                int theEndPos = 0;
862                if (aValue == null) {
863                        return aValue;
864                }
865                final StringBuffer theStringBuffer = new StringBuffer();
866                while (true) {
867                        theBeginPos = aValue.indexOf(theStartKey, theEndPos);
868                        if (theBeginPos < 0) {
869                                break;
870                        }
871                        theStringBuffer.append(aValue.substring(theEndPos, theBeginPos));
872                        theEndPos = aValue.indexOf(theEndKey, theBeginPos);
873                        if (theEndPos < 0) {
874                                throw new Exception("Variable getting incorrect syntax, absent symbol '" + theEndKey + "'.");
875                        }
876                        String theNameProperty = aValue.substring(theBeginPos + theStartKey.length(), theEndPos);
877                        final int defaultPos = theNameProperty.indexOf(',');
878                        if (defaultPos >= 0) {
879                                theNameProperty = theNameProperty.substring(0, defaultPos);
880                        }
881                        final String theValue = new String(new char[] { (char) Integer.parseInt(theNameProperty) });
882                        theStringBuffer.append(theValue);
883                        theEndPos += theEndKey.length();
884                }
885                theStringBuffer.append(aValue.substring(theEndPos));
886                return theStringBuffer.toString();
887        }
888
889        public Processor makeProcessor(final Node aCurrentAction) throws IOException, ClassNotFoundException,
890                        MalformedURLException, FileNotFoundException, NoSuchMethodException, InstantiationException,
891                        IllegalAccessException, InvocationTargetException, CommandException {
892                String className = replaceProperties(aCurrentAction.getAttribute("class"));
893
894                className = getFullClassName(className);
895
896                Class<?> theClass = getClass().getClassLoader().loadClass(className);
897
898                final Constructor<?> constructor = theClass.getConstructor();
899                final Processor newInstance = (Processor) constructor.newInstance();
900                if (isRootRecipe()) {
901                        newInstance.setRootContext(true);
902                        newInstance.setTestListener(recipeListener);
903                }
904                newInstance.init(this);
905                return newInstance;
906        }
907
908        public static String getFullClassName(String className) {
909                if (StringUtils.indexOf(className, '.') < 0) {
910                        className = STG_PROCESSOR_PACKAGE_NAME + className;
911                }
912                return className;
913        }
914
915        public void loadProperties(final String aParameterRule, final String aDelim,
916                        final Map<String, Object> aMapProperties, final boolean aNameUpperCase) {
917                final StringTokenizer theStringTokenizer = new StringTokenizer(aParameterRule, aDelim);
918                while (theStringTokenizer.hasMoreTokens()) {
919                        final String theLine = theStringTokenizer.nextToken();
920                        if (theLine == null || theLine.length() == 0 || (theLine.length() > 0 && theLine.trim().charAt(0) == '#')) {
921                                continue;
922                        }
923                        final int thePbrk = theLine.indexOf('=');
924                        String theName = replaceProperties(theLine.substring(0, thePbrk));
925                        final String theValue = replaceProperties(theLine.substring(thePbrk + 1));
926                        if (aNameUpperCase) {
927                                theName = theName.toUpperCase();
928                        }
929                        aMapProperties.put(theName, theValue);
930                }
931        }
932
933        public void loadProperties(final File aFile, final Map<String, Object> theProperties, final boolean aNameUpperCase)
934                        throws IOException {
935                final BufferedReader theFileReader = new BufferedReader(new FileReader(aFile));
936                try {
937                        String theLine = null;
938                        while ((theLine = theFileReader.readLine()) != null) {
939                                if (theLine == null || theLine.length() == 0
940                                                || (theLine.length() > 0 && theLine.trim().charAt(0) == '#')) {
941                                        continue;
942                                }
943                                final int thePbrk = theLine.indexOf('=');
944                                if (thePbrk < 0) {
945                                        try {
946                                                String theExtLine = replaceProperties(theLine);
947                                                theExtLine = theExtLine.replace('\r', '\n');
948                                                loadProperties(theExtLine, "\n", theProperties, aNameUpperCase);
949                                        } catch (final StringIndexOutOfBoundsException e) {
950                                                throw new IOException("Incorrect data in properties file. File: " + aFile.getAbsolutePath()
951                                                                + "\nLine: " + theLine);
952                                        }
953                                        continue;
954                                }
955                                String theName = replaceProperties(theLine.substring(0, thePbrk));
956                                final String theValue = replaceProperties(theLine.substring(thePbrk + 1));
957                                if (aNameUpperCase) {
958                                        theName = theName.toUpperCase();
959                                }
960                                theProperties.put(theName, theValue);
961                        }
962                } finally {
963                        theFileReader.close();
964                }
965        }
966
967        public static String[] listToArray(final ArrayList<?> theResult) {
968                final String[] theResultArray = new String[theResult.size()];
969                for (int i = 0; i < theResultArray.length; i++) {
970                        theResultArray[i] = (String) theResult.get(i);
971                }
972                return theResultArray;
973        }
974
975        protected void setRootContext(boolean isRootContext) {
976                this.isRootContext = isRootContext;
977        }
978
979        public Processor getParent() {
980                return this.parent;
981        }
982
983        public void complete(boolean success) {
984                stopChilds();
985        }
986
987        public Stack<Node> getLocalTaskTrace() {
988                return this.stackTask;
989        }
990
991        public Vector<Object> getCallTaskTrace() {
992                final TreeVector result = new TreeVector(getTestName());
993                final Vector<Node> trace = getLocalTaskTrace();
994                result.addAll(trace);
995                return result;
996        }
997
998        public ILogger getLogger() {
999                return this.log;
1000        }
1001
1002        @Override
1003        protected String replaceCall(final String aValue) {
1004                final String theStartKey = "$call{";
1005                final String theEndKey = "}";
1006                int theBeginPos = 0;
1007                int theEndPos = 0;
1008                if (aValue == null) {
1009                        return aValue;
1010                }
1011
1012                final StringBuffer theStringBuffer = new StringBuffer();
1013                while (true) {
1014                        theBeginPos = aValue.indexOf(theStartKey, theEndPos);
1015                        if (theBeginPos < 0) {
1016                                break;
1017                        }
1018                        theStringBuffer.append(aValue.substring(theEndPos, theBeginPos));
1019                        theEndPos = aValue.indexOf(theEndKey, theBeginPos);
1020                        if (theEndPos < 0) {
1021                                throw new RuntimeException("Tag created is incorrect syntax, absent symbol '" + theEndKey + "'.");
1022                        }
1023
1024                        final String theLine = aValue.substring(theBeginPos + theStartKey.length(), theEndPos);
1025                        final int thePbrk = theLine.indexOf(',');
1026                        String theTagName = theLine;
1027                        String theNameVar = theLine;
1028                        if (thePbrk > 0) {
1029                                theTagName = theLine.substring(0, thePbrk);
1030                                theNameVar = theLine.substring(thePbrk + 1);
1031                        } else {
1032                                theTagName = theLine;
1033                                theNameVar = "RETURN";
1034                        }
1035
1036                        Object theObjValue = null;
1037                        if (StringUtils.startsWith(theTagName, "fn:")) {
1038
1039                                if (StringUtils.equals(theTagName, "fn:currentTimeMillis")) {
1040                                        theObjValue = Long.toString(System.currentTimeMillis());
1041                                }
1042
1043                        } else {
1044
1045                                final String theFileAttribut = getListener().getManager().getTestPath(theTagName);
1046
1047                                try {
1048                                        final Processor theTestUnit = new BaseProcessor(this);
1049                                        theTestUnit.init(this.configNode, this.variables, this.recipeListener, this.startBaseDir, this.log);
1050                                        final Map<String, Object> theVariables = theTestUnit.processTesting(theFileAttribut, this.variables,
1051                                                        getBaseDir());
1052                                        String name = StringUtils.upperCase(theNameVar);
1053                                        theObjValue = theVariables.get(name);
1054
1055                                } catch (final Throwable e) {
1056                                        throw new RuntimeException(e);
1057                                }
1058                        }
1059
1060                        String theValue = null;
1061                        if (theObjValue instanceof String) {
1062                                theValue = (String) theObjValue;
1063                        } else if (theObjValue instanceof String[] && ((String[]) theObjValue).length > 0) {
1064                                theValue = ((String[]) theObjValue)[0];
1065                        } else {
1066                                theValue = ObjectUtils.toString(theObjValue);
1067                        }
1068                        theStringBuffer.append(theValue);
1069                        theEndPos += theEndKey.length();
1070                }
1071
1072                theStringBuffer.append(aValue.substring(theEndPos));
1073                return theStringBuffer.toString();
1074        }
1075
1076        public void pause() {
1077                this.pauseOn = true;
1078                if (parent != null) {
1079                        parent.pauseOn = this.pauseOn;
1080                }
1081                if (externProcessor != null) {
1082                        externProcessor.pauseOn = this.pauseOn;
1083                }
1084                for (final Enumeration<TaskProcessorThread> theEnum = this.childThreads.elements(); theEnum
1085                                .hasMoreElements();) {
1086                        final TaskProcessorThread theRunnTest = theEnum.nextElement();
1087                        theRunnTest.getProcessor().pause();
1088                }
1089                getListener().pause();
1090        }
1091
1092        public void resume() {
1093                this.pauseOn = false;
1094                if (externProcessor != null) {
1095                        externProcessor.pauseOn = this.pauseOn;
1096                }
1097                if (parent != null) {
1098                        parent.pauseOn = this.pauseOn;
1099                }
1100                for (final Enumeration<TaskProcessorThread> theEnum = this.childThreads.elements(); theEnum
1101                                .hasMoreElements();) {
1102                        final TaskProcessorThread theRunnTest = theEnum.nextElement();
1103                        theRunnTest.getProcessor().resume();
1104                }
1105                getListener().resume();
1106        }
1107
1108        private void doPause() {
1109                while (this.pauseOn) {
1110                        try {
1111                                synchronized (monitor) {
1112                                        monitor.wait(100);
1113                                }
1114                        } catch (final InterruptedException e) {
1115                                Thread.currentThread().interrupt();
1116                        }
1117                }
1118        }
1119
1120        protected void debug(Object message) {
1121                log.debug(message);
1122        }
1123
1124        protected void debug(Object message, Exception e) {
1125                log.debug(message, e);
1126        }
1127
1128        public void setLogger(final ILogger logger) {
1129                this.log = logger;
1130        }
1131
1132        public boolean isStoppedTest() {
1133                return isStopped();
1134        }
1135
1136        public String attr(Node action, String name, String defaultValue) {
1137                String value = attr(action, name);
1138                return StringUtils.defaultIfEmpty(value, defaultValue);
1139        }
1140
1141        public String attr(Node action, String name) {
1142                name = replaceProperties(name);
1143                String attribute = action.getAttribute(name);
1144                return replaceProperties(attribute);
1145        }
1146
1147        public Object attrValue(Node action, String name) {
1148                name = replaceProperties(name);
1149                String value = attr(action, name);
1150                return getVariableValue(value);
1151        }
1152
1153        public static void setSilentMode(Processor unit, boolean silentMode) {
1154                if (unit != null) {
1155                        unit.silentMode = silentMode;
1156                }
1157        }
1158
1159        public static boolean isSilentMode(Processor unit) {
1160                boolean result;
1161                if (unit != null) {
1162                        result = unit.silentMode;
1163                } else {
1164                        result = Boolean.parseBoolean(System.getProperty(ConfigConstants.TAKE_IT_EASY_MODE, "false"));
1165                }
1166
1167                return result;
1168        }
1169
1170        protected void quietWait(final long value) {
1171                synchronized (monitor) {
1172                        try {
1173                                monitor.wait(value);
1174                        } catch (InterruptedException e) {
1175                                getLog().debug(e.getMessage());
1176                                stop();
1177                        }
1178                }
1179        }
1180
1181        public long attrTime(final Node action, String attrNane, String defaultValue) {
1182                String fullValue = StringUtils.defaultIfBlank(attr(action, attrNane), defaultValue);
1183                String valueStr;
1184                char typeMarker = ' ';
1185
1186                if (NumberUtils.isNumber(fullValue)) {
1187                        valueStr = fullValue;
1188                } else {
1189                        valueStr = StringUtils.substring(fullValue, 0, fullValue.length() - 1);
1190                        typeMarker = fullValue.charAt(fullValue.length() - 1);
1191                }
1192
1193                double value = Double.parseDouble(valueStr);
1194
1195                Date cdate = new Date();
1196                Calendar c = Calendar.getInstance();
1197                c.setTime(cdate);
1198
1199                long resultInMs;
1200                switch (typeMarker) {
1201                case 'Y':
1202                        c.add(Calendar.YEAR, (int) value);
1203                        resultInMs = c.getTime().getTime() - cdate.getTime();
1204                        break;
1205                case 'M':
1206                        c.add(Calendar.MONTH, (int) value);
1207                        resultInMs = c.getTime().getTime() - cdate.getTime();
1208                        break;
1209                case 'w':
1210                        value = (long) (value *= 7);
1211                case 'd':
1212                        value = (long) (value *= 24);
1213                case 'h':
1214                        value = (long) (value *= 60);
1215                case 'm':
1216                        value = (long) (value *= 60);
1217                case 's':
1218                        value = (long) (value *= 1000);
1219                default:
1220                        resultInMs = (long) value;
1221                }
1222
1223                return resultInMs;
1224        }
1225
1226}