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.net.URL;
014import java.net.URLClassLoader;
015import java.security.GeneralSecurityException;
016import java.security.SecureRandom;
017import java.security.cert.X509Certificate;
018import java.util.ArrayList;
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;
030import java.util.jar.Attributes;
031import java.util.jar.JarFile;
032import java.util.jar.Manifest;
033
034import javax.net.ssl.HttpsURLConnection;
035import javax.net.ssl.SSLContext;
036import javax.net.ssl.TrustManager;
037import javax.net.ssl.X509TrustManager;
038
039import org.apache.commons.collections.MapUtils;
040import org.apache.commons.lang.ObjectUtils;
041import org.apache.commons.lang.StringUtils;
042
043import com.ganteater.ae.AELogRecord;
044import com.ganteater.ae.AEManager;
045import com.ganteater.ae.CommandException;
046import com.ganteater.ae.ConfigConstants;
047import com.ganteater.ae.ILogger;
048import com.ganteater.ae.JarClassLoader;
049import com.ganteater.ae.RecipeListener;
050import com.ganteater.ae.TaskCancelingException;
051import com.ganteater.ae.TemplateProcessor;
052import com.ganteater.ae.util.AssertionFailedError;
053import com.ganteater.ae.util.xml.easyparser.EasyParser;
054import com.ganteater.ae.util.xml.easyparser.Node;
055import com.ganteater.ae.util.xml.easyparser.Node.TreeVector;
056
057@SuppressWarnings("deprecation")
058public abstract class Processor extends TemplateProcessor {
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> aStartVariables,
160                        final RecipeListener aMaxTestListener, final File aStartDir, final ILogger aLog) {
161                this.log = aLog;
162
163                this.configNode = action;
164                this.startVariables = aStartVariables;
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 aParent, Node action) throws CommandException {
178                this.mainProcessor = false;
179                this.log = aParent.log;
180                this.configNode = aParent.configNode;
181                this.startVariables = aParent.startVariables;
182                this.startBaseDir = aParent.startBaseDir;
183                setBaseDir(aParent.getBaseDir());
184                this.variables = aParent.variables;
185                setTestListener(aParent.recipeListener);
186                this.parent = aParent;
187        }
188
189        protected Map<String, Object> processTesting(final String aTestFileName, final Map<String, Object> aVariables,
190                        final File aBaseDir) throws Throwable {
191                this.variables = aVariables;
192                final File theBaseDir = getBaseDir();
193                setBaseDir(aBaseDir);
194                processTesting(aTestFileName);
195                setBaseDir(theBaseDir);
196                return this.variables;
197        }
198
199        protected void processTesting(final String aTestFileName) throws CommandException {
200                if (aTestFileName == null) {
201                        return;
202                }
203
204                Node theNode;
205                try {
206                        theNode = new EasyParser().load(aTestFileName);
207                        theNode.getVector(true);
208
209                } catch (Exception e) {
210                        throw new CommandException("Recipe is not found: " + aTestFileName, this);
211
212                }
213
214                this.recipeListener.startTask(getTestName(theNode));
215                processTesting(theNode, this.variables, new File(aTestFileName).getParentFile());
216        }
217
218        public void processTesting(final Node theNode, final Map<String, Object> aVariables, final File aBaseDir)
219                        throws CommandException {
220
221                this.variables = new HashMap<String, Object>();
222                if (aVariables != null) {
223                        this.variables.putAll(aVariables);
224                }
225                setBaseDir(aBaseDir);
226
227                if (this.startBaseDir == null) {
228                        this.startBaseDir = getBaseDir();
229                }
230                final String theDepends = theNode.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 = theNode.getAttribute("name");
239                if (taskName != null) {
240                        debug("Recipe: \"" + taskName + "\"");
241                }
242
243                taskNode(theNode);
244
245                if (taskName != null) {
246                        debug("Recipe: \"" + taskName + "\" done");
247                }
248        }
249
250        public void taskNode(final Node aNode) throws CommandException {
251                taskNode(aNode, true);
252        }
253
254        public void taskNode(final Node aNode, 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> theFinallyVector = new Vector<Node>();
267
268                String description = getTaskDescription(aNode);
269
270                int i = 0;
271                int theNumberOperation = -1;
272                String recipeName = aNode.getAttribute("name");
273
274                if (root) {
275                        pushToStack(aNode, recipeName);
276                }
277
278                Node command = null;
279                try {
280                        if (aNode != null)
281                                while (++theNumberOperation < aNode.size() && isStopped() == false) {
282
283                                        command = (Node) aNode.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(aNode.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                                                        theFinallyVector.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(aNode.size(), i, description, theTroubles != null);
391                                                }
392                                        }
393                                }
394                } finally {
395                        boolean stoppedTest = isStopped();
396                        setStopped(false);
397                        for (Node node : theFinallyVector) {
398                                try {
399                                        taskNode(node, 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                progress(100, 100, "", false);
412
413                if (theTroubles != null) {
414                        if (isMainProcessor() && this.stackTask.size() == 0) {
415
416                                final String theExceptionMode = replaceProperties(aNode.getAttribute("exception"));
417                                if ("ignored".equals(theExceptionMode) || "ignore".equals(theExceptionMode)) {
418                                        this.recipeListener.exceptionIgnored(theTroubles);
419                                        return;
420
421                                } else {
422                                        if (theTroubles.getCause() instanceof AssertionFailedError) {
423                                                this.recipeListener.checkFailure(theTroubles, this);
424                                                theTroubles = null;
425                                        } else {
426                                                this.recipeListener.criticalError(theTroubles, this);
427                                        }
428                                }
429                        }
430
431                        if (theTroubles != null) {
432                                throw theTroubles;
433                        }
434                }
435
436        }
437
438        private void pushToStack(Node command, String recipeName) {
439                Node clone = (Node) command.clone();
440                clone.clear();
441                if (recipeName != null) {
442                        clone.setAttribute("recipe", recipeName);
443                }
444                this.stackTask.push(clone);
445        }
446
447        private void executeCommand(Node action) throws IllegalAccessException, InvocationTargetException {
448                Node command;
449                Object paramsMap = attrValue(action, "attr-map");
450                if (paramsMap instanceof Map) {
451                        command = (Node) action.clone();
452                        command.removeAttribute("attr-map");
453                        @SuppressWarnings({ "unchecked", "rawtypes" })
454                        Set<Entry<String, String>> entrySet = ((Map) paramsMap).entrySet();
455                        for (Entry<String, String> entry : entrySet) {
456                                String key = entry.getKey();
457                                String value = entry.getValue();
458                                if (command.getAttribute(key) == null) {
459                                        command.setAttribute(key, value);
460                                }
461                        }
462                } else {
463                        command = action;
464                }
465
466                final String tagName = command.getTag();
467
468                try {
469                        final Method theMethod = getClass().getMethod(COMMAND_PREFIX + tagName.replace('$', '_'),
470                                        new Class[] { Node.class });
471
472                        theMethod.invoke(this, new Object[] { command });
473                } catch (NoSuchMethodException e) {
474                }
475        }
476
477        public boolean isMainProcessor() {
478                return this.mainProcessor;
479        }
480
481        public String getTestName() {
482                return this.testName;
483        }
484
485        public void setTestName(String testName) {
486                this.testName = testName;
487        }
488
489        public void regChildProcess(final TaskProcessorThread aMaxTestRunnable) {
490                this.recipeListener.startChildProcess(aMaxTestRunnable);
491                this.childThreads.add(aMaxTestRunnable);
492        }
493
494        public void unregChildProcess(final TaskProcessorThread aMaxTestRunnable) {
495                this.recipeListener.stopChildProcess(aMaxTestRunnable);
496                this.childThreads.remove(aMaxTestRunnable);
497        }
498
499        public Node getConfigNode() {
500                return this.configNode;
501        }
502
503        public Map<String, Object> getStartVariables() {
504                return this.startVariables;
505        }
506
507        public RecipeListener getListener() {
508                return this.recipeListener;
509        }
510
511        public Map<String, Object> getVariables() {
512                return this.variables;
513        }
514
515        public int getChildThreads() {
516                return this.childThreads.size();
517        }
518
519        public ILogger getLog() {
520                return log;
521        }
522
523        public void runNodes(final Node[] aTheNodes) throws CommandException {
524
525                for (int i = 0; i < aTheNodes.length; i++) {
526                        if (isStopped()) {
527                                break;
528                        }
529                        final Node theNode = aTheNodes[i];
530                        taskNode(theNode, false);
531                }
532        }
533
534        public void setTestListener(final RecipeListener testListener) {
535                this.recipeListener = testListener;
536        }
537
538        public void startCommandInformation(final Node command) {
539                final String theID = command.getAttribute(Node.TAG_ID);
540
541                boolean isRootRecipe = isRootRecipe();
542
543                if (this.recipeListener != null && theID != null && isRootRecipe) {
544                        this.recipeListener.startCommand(Integer.parseInt(theID));
545                }
546        }
547
548        protected boolean isRootRecipe() {
549                int recipeNodeCount = 0;
550                for (Object node : this.stackTask.toArray()) {
551                        if ("Recipe".equals(((Node)node).getTag())) {
552                                recipeNodeCount++;
553                        }
554                }
555                boolean isRootRecipe = recipeNodeCount == 1 || isRootContext;
556                return isRootRecipe;
557        }
558
559        protected String getTestName(final Node aNode) {
560                String theAttribut = aNode.getAttribute("name");
561                if (theAttribut == null || theAttribut.trim().length() == 0) {
562                        theAttribut = aNode.getAttribute("description");
563                }
564                return replaceProperties(theAttribut);
565        }
566
567        private String getTaskDescription(final Node aNode) {
568                if (aNode == null) {
569                        return null;
570                }
571                String theTaskDescription = aNode.getAttribute("name");
572                if (theTaskDescription == null || theTaskDescription.trim().length() == 0) {
573                        theTaskDescription = aNode.getAttribute("description");
574                }
575                final String theTag = aNode.getTag();
576                if (theTag.equals("Recipe")) {
577                        startTask(theTaskDescription, aNode.getAttribute("description"));
578                } else if (theTag.equals(SpecialCommands.STARTUP)) {
579                        if (theTaskDescription == null) {
580                                theTaskDescription = SpecialCommands.STARTUP;
581                        }
582                        startTask(theTaskDescription, SpecialCommands.STARTUP);
583                } else {
584                        theTaskDescription = aNode.getAttribute("description");
585                }
586                return theTaskDescription;
587        }
588
589        private void startTask(String name, String description) {
590                this.testName = name;
591                this.recipeListener.startTask(name);
592        }
593
594        protected String getTrimmedLines(String theValue1) throws IOException {
595                final BufferedReader theStringReader = new BufferedReader(new StringReader(theValue1));
596                String theLine = "";
597                final StringBuffer theBuffer = new StringBuffer();
598                while ((theLine = theStringReader.readLine()) != null) {
599                        final String theTrimmedLine = theLine.trim();
600                        if (theTrimmedLine.length() > 0) {
601                                theBuffer.append(theTrimmedLine);
602                                theBuffer.append('\n');
603                        }
604                }
605                theValue1 = theBuffer.toString();
606                return theValue1;
607        }
608
609        protected String[] randomSort(final String[] aArray) {
610                final Vector<String> theVector = new Vector<String>();
611                for (int i = 0; i < aArray.length; i++) {
612                        theVector.add(aArray[i]);
613                }
614                final String[] theResult = new String[theVector.size()];
615
616                for (int l = 0; l < aArray.length; l++) {
617                        final int theNumber = (int) (Math.random() * (theVector.size()));
618                        theResult[l] = theVector.elementAt(theNumber);
619                        theVector.removeElementAt(theNumber);
620                }
621
622                return theResult;
623        }
624
625        static public String prepareXml(String theCurrentValue) {
626                final int head = theCurrentValue.indexOf("<?");
627                if (head >= 0) {
628                        final int end = theCurrentValue.indexOf("?>");
629                        theCurrentValue = theCurrentValue.substring(end + 2).trim();
630                }
631                return theCurrentValue;
632        }
633
634        protected Properties replaceAttributes(Node aCurrentAction) {
635                Map<String, String> attributes = aCurrentAction.getAttributes();
636                Properties properties = MapUtils.toProperties(attributes);
637                Set<Entry<Object, Object>> entrySet = properties.entrySet();
638                Properties props = new Properties();
639                for (Entry<Object, Object> entry : entrySet) {
640                        String key = (String) entry.getKey();
641                        String value = (String) entry.getValue();
642
643                        String replaceProperties = replaceProperties(value);
644                        props.setProperty(key, replaceProperties);
645                }
646                return props;
647        }
648
649        protected void outputLog(String varName, final String theLevelAttribut, final Object theTextOut, String type) {
650
651                AELogRecord logRecord = new AELogRecord(theTextOut, type, varName);
652
653                if ("info".equals(theLevelAttribut)) {
654                        this.log.info(logRecord);
655                }
656                if ("error".equals(theLevelAttribut)) {
657                        this.log.error(logRecord);
658                }
659                if ("debug".equals(theLevelAttribut)) {
660                        debug(logRecord);
661                }
662                if ("warn".equals(theLevelAttribut)) {
663                        this.log.warn(logRecord);
664                }
665        }
666
667        protected boolean isActiveInitFor(final Node action, String value) {
668                return StringUtils.contains(attr(action, "init"), value);
669        }
670
671        protected String getFields(final Properties theOutArrayList) {
672                final StringBuffer theResult = new StringBuffer();
673                for (final Enumeration<?> e = theOutArrayList.propertyNames(); e.hasMoreElements();) {
674                        final String theName = (String) e.nextElement();
675                        final String theValue = theOutArrayList.getProperty(theName);
676                        theResult.append(theName);
677                        theResult.append("=");
678                        theResult.append(theValue);
679                        theResult.append(";");
680                }
681                return theResult.toString();
682        }
683
684        protected void applyResult(final Node commandNode, final String name, final Object value) {
685                setVariableValue(name, value);
686
687                String level = replaceProperties(commandNode.getAttribute("level"));
688                String type = replaceProperties(commandNode.getAttribute("type"));
689                if (level != null) {
690                        outputLog(name, level, value, type);
691                }
692        }
693
694        public void setStartDir(final File aStartBaseDir) {
695                this.startBaseDir = aStartBaseDir;
696        }
697
698        protected void progress(final long theSize, final long aValue, String aDescription, final boolean aErrorState) {
699                try {
700                        if (aDescription == null || aDescription.length() == 0) {
701                                aDescription = new String(" ");
702                        }
703
704                        boolean loopActivated = isLoopActivated();
705
706                        if (!loopActivated) {
707                                this.recipeListener.setProgress(aDescription, theSize, aValue, aErrorState);
708                        }
709                } catch (final Throwable e) {
710                }
711        }
712
713        protected boolean isLoopActivated() {
714                boolean result = false;
715                Processor parent = getParent();
716                if (parent != null) {
717                        result = parent.isLoopActivated();
718                }
719                if (!result) {
720                        result = mainLoopCommand;
721                }
722                return result;
723        }
724
725        @Override
726        public File getFile(String path) {
727                path = replaceProperties(path);
728                return this.recipeListener.getManager().getFile(path);
729        }
730
731        public void make(final String aTestFile) throws CommandException {
732                this.testFile = aTestFile;
733                try {
734                        if (this.recipeListener != null && isStopped() == false) {
735                                this.recipeListener.startTask(this.testFile);
736                                processTesting(this.testFile);
737                                this.recipeListener.endTask(false);
738                        } else {
739                                throw new RuntimeException();
740                        }
741
742                } finally {
743
744                        if (this.testName != null) {
745                                debug("Recipe '" + this.testName + "' is stopped.");
746                        }
747                }
748        }
749
750        public void stop() {
751                super.stop();
752
753                if (getChildThreads() > 0) {
754                        debug("Child's threads: " + getChildThreads());
755                }
756
757                if (this.externProcessor != null && this.externProcessor != this && !externProcessor.isStoppedTest()) {
758                        this.externProcessor.stop();
759                }
760
761                if (this.configNode == null) {
762                        return;
763                }
764
765                synchronized (monitor) {
766                        monitor.notify();
767                }
768
769                for (Process process : processes) {
770                        debug("Request to destroy a child process:" + process.destroyForcibly());
771                }
772                processes.clear();
773
774                stopChilds();
775
776                for (ThreadPoolExecutor pool : threadPoolList) {
777                        pool.shutdownNow();
778                }
779
780                while (!isStoppedTest()) {
781                        try {
782                                synchronized (monitor) {
783                                        monitor.wait(100);
784                                }
785                        } catch (InterruptedException e) {
786                                Thread.currentThread().interrupt();
787                        }
788                }
789
790                RecipeListener listener = getListener();
791                if (listener != null) {
792                        listener.endTask(false);
793                }
794
795                if (parent != null && !parent.isStoppedTest()) {
796                        parent.stop();
797                }
798        }
799
800        private void stopChilds() {
801                List<TaskProcessorThread> fChildThreads2 = new ArrayList<>(childThreads);
802                for (TaskProcessorThread child : fChildThreads2) {
803                        child.stopTest();
804
805                        while (!child.getProcessor().isStoppedTest()) {
806                                try {
807                                        synchronized (monitor) {
808                                                monitor.wait(100);
809                                        }
810                                } catch (InterruptedException e) {
811                                        Thread.currentThread().interrupt();
812                                }
813                        }
814                }
815        }
816
817        static class SystemCommandStart extends Thread {
818                private final String fCommandAttribut;
819
820                ILogger fLog;
821
822                public SystemCommandStart(final String aCommandAttribut, final ILogger aLog) {
823                        this.fLog = aLog;
824                        this.fCommandAttribut = aCommandAttribut;
825                }
826
827                @Override
828                public void run() {
829                        try {
830                                Runtime.getRuntime().exec(this.fCommandAttribut);
831                        } catch (final IOException e) {
832                                this.fLog.error("System command starting is failed.", e);
833                        }
834                }
835
836        }
837
838        protected String replaceXmlReference(String aValue) throws Exception {
839                aValue = replaceFile(aValue);
840                final String theStartKey = "&#";
841                final String theEndKey = ";";
842                int theBeginPos = 0;
843                int theEndPos = 0;
844                if (aValue == null) {
845                        return aValue;
846                }
847                final StringBuffer theStringBuffer = new StringBuffer();
848                while (true) {
849                        theBeginPos = aValue.indexOf(theStartKey, theEndPos);
850                        if (theBeginPos < 0) {
851                                break;
852                        }
853                        theStringBuffer.append(aValue.substring(theEndPos, theBeginPos));
854                        theEndPos = aValue.indexOf(theEndKey, theBeginPos);
855                        if (theEndPos < 0) {
856                                throw new Exception("Variable getting incorrect syntax, absent symbol '" + theEndKey + "'.");
857                        }
858                        String theNameProperty = aValue.substring(theBeginPos + theStartKey.length(), theEndPos);
859                        final int defaultPos = theNameProperty.indexOf(',');
860                        if (defaultPos >= 0) {
861                                theNameProperty = theNameProperty.substring(0, defaultPos);
862                        }
863                        final String theValue = new String(new char[] { (char) Integer.parseInt(theNameProperty) });
864                        theStringBuffer.append(theValue);
865                        theEndPos += theEndKey.length();
866                }
867                theStringBuffer.append(aValue.substring(theEndPos));
868                return theStringBuffer.toString();
869        }
870
871        public Processor makeProcessor(final Node aCurrentAction)
872                        throws IOException, ClassNotFoundException, MalformedURLException, FileNotFoundException,
873                        NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, CommandException {
874                String className = replaceProperties(aCurrentAction.getAttribute("class"));
875
876                if (StringUtils.indexOf(className, '.') < 0) {
877                        className = "com.ganteater.ae.processor." + className;
878                }
879
880                final String pluginJar = replaceProperties(aCurrentAction.getAttribute("plugin"));
881
882                Class<?> theClass = null;
883                if (pluginJar != null) {
884                        final File file = getFile(pluginJar);
885
886                        if (file.isFile()) {
887                                final JarClassLoader jarLoader = new JarClassLoader(file);
888                                if (className == null) {
889                                        final JarFile jarFile = new JarFile(file);
890                                        try {
891                                                final Manifest manifest = jarFile.getManifest();
892                                                final Attributes mainAttributes = manifest.getMainAttributes();
893                                                className = mainAttributes.getValue("AE-Class");
894                                        } finally {
895                                                jarFile.close();
896                                        }
897                                }
898                                theClass = jarLoader.loadClass(className, true);
899                        } else if (file.isDirectory()) {
900                                try (URLClassLoader classLoader = new URLClassLoader(new URL[] { file.toURL() })) {
901                                        theClass = classLoader.loadClass(className);
902                                }
903                        } else {
904                                throw new FileNotFoundException(pluginJar);
905                        }
906
907                } else {
908                        theClass = getClass().getClassLoader().loadClass(className);
909                }
910
911                final Constructor<?> constructor = theClass.getConstructor();
912                final Processor newInstance = (Processor) constructor.newInstance();
913                if (isRootRecipe()) {
914                        newInstance.setRootContext(true);
915                        newInstance.setTestListener(recipeListener);
916                }
917                newInstance.init(this);
918                return newInstance;
919        }
920
921        public void loadProperties(final String aParameterRule, final String aDelim,
922                        final Map<String, Object> aMapProperties, final boolean aNameUpperCase) throws Exception {
923                final StringTokenizer theStringTokenizer = new StringTokenizer(aParameterRule, aDelim);
924                while (theStringTokenizer.hasMoreTokens()) {
925                        final String theLine = theStringTokenizer.nextToken();
926                        if (theLine == null || theLine.length() == 0 || (theLine.length() > 0 && theLine.trim().charAt(0) == '#')) {
927                                continue;
928                        }
929                        final int thePbrk = theLine.indexOf('=');
930                        String theName = replaceProperties(theLine.substring(0, thePbrk));
931                        final String theValue = replaceProperties(theLine.substring(thePbrk + 1));
932                        if (aNameUpperCase) {
933                                theName = theName.toUpperCase();
934                        }
935                        aMapProperties.put(theName, theValue);
936                }
937        }
938
939        public void loadProperties(final File aFile, final Map<String, Object> theProperties, final boolean aNameUpperCase)
940                        throws Exception {
941                final BufferedReader theFileReader = new BufferedReader(new FileReader(aFile));
942                try {
943                        String theLine = null;
944                        while ((theLine = theFileReader.readLine()) != null) {
945                                if (theLine == null || theLine.length() == 0
946                                                || (theLine.length() > 0 && theLine.trim().charAt(0) == '#')) {
947                                        continue;
948                                }
949                                final int thePbrk = theLine.indexOf('=');
950                                if (thePbrk < 0) {
951                                        try {
952                                                String theExtLine = replaceProperties(theLine);
953                                                theExtLine = theExtLine.replace('\r', '\n');
954                                                loadProperties(theExtLine, "\n", theProperties, aNameUpperCase);
955                                        } catch (final StringIndexOutOfBoundsException e) {
956                                                throw new Exception("Incorrect data in properties file. File: " + aFile.getAbsolutePath()
957                                                                + "\nLine: " + theLine);
958                                        }
959                                        continue;
960                                }
961                                String theName = replaceProperties(theLine.substring(0, thePbrk));
962                                final String theValue = replaceProperties(theLine.substring(thePbrk + 1));
963                                if (aNameUpperCase) {
964                                        theName = theName.toUpperCase();
965                                }
966                                theProperties.put(theName, theValue);
967                        }
968                } finally {
969                        theFileReader.close();
970                }
971        }
972
973        public static String[] listToArray(final ArrayList<?> theResult) {
974                final String[] theResultArray = new String[theResult.size()];
975                for (int i = 0; i < theResultArray.length; i++) {
976                        theResultArray[i] = (String) theResult.get(i);
977                }
978                return theResultArray;
979        }
980
981        protected void setRootContext(boolean isRootContext) {
982                this.isRootContext = isRootContext;
983        }
984
985        public Processor getParent() {
986                return this.parent;
987        }
988
989        public void complete(boolean success) {
990                stopChilds();
991        }
992
993        public Stack<Node> getLocalTaskTrace() {
994                return this.stackTask;
995        }
996
997        public Vector<Object> getCallTaskTrace() {
998                final TreeVector result = new TreeVector(getTestName());
999                final Vector<Node> trace = getLocalTaskTrace();
1000                result.addAll(trace);
1001                return result;
1002        }
1003
1004        public ILogger getLogger() {
1005                return this.log;
1006        }
1007
1008        @Override
1009        protected String replaceCall(final String aValue) {
1010                final String theStartKey = "$call{";
1011                final String theEndKey = "}";
1012                int theBeginPos = 0;
1013                int theEndPos = 0;
1014                if (aValue == null) {
1015                        return aValue;
1016                }
1017
1018                final StringBuffer theStringBuffer = new StringBuffer();
1019                while (true) {
1020                        theBeginPos = aValue.indexOf(theStartKey, theEndPos);
1021                        if (theBeginPos < 0) {
1022                                break;
1023                        }
1024                        theStringBuffer.append(aValue.substring(theEndPos, theBeginPos));
1025                        theEndPos = aValue.indexOf(theEndKey, theBeginPos);
1026                        if (theEndPos < 0) {
1027                                throw new RuntimeException("Tag created is incorrect syntax, absent symbol '" + theEndKey + "'.");
1028                        }
1029
1030                        final String theLine = aValue.substring(theBeginPos + theStartKey.length(), theEndPos);
1031                        final int thePbrk = theLine.indexOf(',');
1032                        String theTagName = theLine;
1033                        String theNameVar = theLine;
1034                        if (thePbrk > 0) {
1035                                theTagName = theLine.substring(0, thePbrk);
1036                                theNameVar = theLine.substring(thePbrk + 1);
1037                        } else {
1038                                theTagName = theLine;
1039                                theNameVar = "RETURN";
1040                        }
1041
1042                        Object theObjValue = null;
1043                        if (StringUtils.startsWith(theTagName, "fn:")) {
1044
1045                                if (StringUtils.equals(theTagName, "fn:currentTimeMillis")) {
1046                                        theObjValue = Long.toString(System.currentTimeMillis());
1047                                }
1048
1049                        } else {
1050
1051                                final String theFileAttribut = getListener().getManager().getTestPath(theTagName);
1052
1053                                try {
1054                                        final Processor theTestUnit = new BaseProcessor(this);
1055                                        theTestUnit.init(this.configNode, this.variables, this.recipeListener, this.startBaseDir,
1056                                                        this.log);
1057                                        final Map<String, Object> theVariables = theTestUnit.processTesting(theFileAttribut,
1058                                                        this.variables, getBaseDir());
1059                                        String name = StringUtils.upperCase(theNameVar);
1060                                        theObjValue = theVariables.get(name);
1061
1062                                } catch (final Throwable e) {
1063                                        throw new RuntimeException(e);
1064                                }
1065                        }
1066
1067                        String theValue = null;
1068                        if (theObjValue instanceof String) {
1069                                theValue = (String) theObjValue;
1070                        } else if (theObjValue instanceof String[] && ((String[]) theObjValue).length > 0) {
1071                                theValue = ((String[]) theObjValue)[0];
1072                        } else {
1073                                theValue = ObjectUtils.toString(theObjValue);
1074                        }
1075                        theStringBuffer.append(theValue);
1076                        theEndPos += theEndKey.length();
1077                }
1078
1079                theStringBuffer.append(aValue.substring(theEndPos));
1080                return theStringBuffer.toString();
1081        }
1082
1083        public void pause() {
1084                this.pauseOn = true;
1085                if (parent != null) {
1086                        parent.pauseOn = this.pauseOn;
1087                }
1088                if (externProcessor != null) {
1089                        externProcessor.pauseOn = this.pauseOn;
1090                }
1091                for (final Enumeration<TaskProcessorThread> theEnum = this.childThreads.elements(); theEnum
1092                                .hasMoreElements();) {
1093                        final TaskProcessorThread theRunnTest = theEnum.nextElement();
1094                        theRunnTest.getProcessor().pause();
1095                }
1096                getListener().pause();
1097        }
1098
1099        public void resume() {
1100                this.pauseOn = false;
1101                if (externProcessor != null) {
1102                        externProcessor.pauseOn = this.pauseOn;
1103                }
1104                if (parent != null) {
1105                        parent.pauseOn = this.pauseOn;
1106                }
1107                for (final Enumeration<TaskProcessorThread> theEnum = this.childThreads.elements(); theEnum
1108                                .hasMoreElements();) {
1109                        final TaskProcessorThread theRunnTest = theEnum.nextElement();
1110                        theRunnTest.getProcessor().resume();
1111                }
1112                getListener().resume();
1113        }
1114
1115        private void doPause() {
1116                while (this.pauseOn) {
1117                        try {
1118                                synchronized (monitor) {
1119                                        monitor.wait(100);
1120                                }
1121                        } catch (final InterruptedException e) {
1122                                Thread.currentThread().interrupt();
1123                        }
1124                }
1125        }
1126
1127        protected void debug(Object message) {
1128                log.debug(message);
1129        }
1130
1131        protected void debug(Object message, Exception e) {
1132                log.debug(message, e);
1133        }
1134
1135        public void setLogger(final ILogger logger) {
1136                this.log = logger;
1137        }
1138
1139        public boolean isStoppedTest() {
1140                return isStopped();
1141        }
1142
1143        public String attr(Node action, String name, String defaultValue) {
1144                String value = attr(action, name);
1145                return StringUtils.defaultIfEmpty(value, defaultValue);
1146        }
1147
1148        public String attr(Node action, String name) {
1149                name = replaceProperties(name);
1150                String attribute = action.getAttribute(name);
1151                return replaceProperties(attribute);
1152        }
1153
1154        public Object attrValue(Node action, String name) {
1155                name = replaceProperties(name);
1156                String value = attr(action, name);
1157                return getVariableValue(value);
1158        }
1159
1160        public static void setSilentMode(Processor unit, boolean silentMode) {
1161                if (unit != null) {
1162                        unit.silentMode = silentMode;
1163                }
1164        }
1165
1166        public static boolean isSilentMode(Processor unit) {
1167                boolean result;
1168                if (unit != null) {
1169                        result = unit.silentMode;
1170                } else {
1171                        result = Boolean.parseBoolean(System.getProperty(ConfigConstants.TAKE_IT_EASY_MODE, "false"));
1172                }
1173
1174                return result;
1175        }
1176
1177        protected void wait(final String value) {
1178                synchronized (monitor) {
1179                        try {
1180                                monitor.wait(Integer.parseInt(value));
1181                        } catch (InterruptedException e) {
1182                                getLog().debug(e.getMessage());
1183                                stop();
1184                        }
1185                }
1186        }
1187
1188}