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