All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.exactpro.sf.scriptrunner.AsyncScriptRunner Maven / Gradle / Ivy

There is a newer version: 3.4.260
Show newest version
/******************************************************************************
 * Copyright 2009-2018 Exactpro (Exactpro Systems Limited)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.exactpro.sf.scriptrunner;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.exactpro.sf.aml.generator.GeneratedScript;
import com.exactpro.sf.common.adapting.IAdapterManager;
import com.exactpro.sf.configuration.IDictionaryManager;
import com.exactpro.sf.configuration.IEnvironmentManager;
import com.exactpro.sf.configuration.workspace.IWorkspaceDispatcher;
import com.exactpro.sf.embedded.statistics.StatisticsService;
import com.exactpro.sf.scriptrunner.actionmanager.IActionManager;
import com.exactpro.sf.scriptrunner.impl.DefaultScriptConfig;
import com.exactpro.sf.scriptrunner.languagemanager.LanguageManager;
import com.exactpro.sf.scriptrunner.services.IStaticServiceManager;
import com.exactpro.sf.scriptrunner.state.ScriptState;
import com.exactpro.sf.scriptrunner.state.ScriptStatus;
import com.exactpro.sf.scriptrunner.utilitymanager.IUtilityManager;
import com.exactpro.sf.storage.ITestScriptStorage;

public class AsyncScriptRunner extends AbstractScriptRunner {

    private static final Logger logger = LoggerFactory.getLogger(AsyncScriptRunner.class);

	public AsyncScriptRunner(
            IWorkspaceDispatcher wd,
            IDictionaryManager dictionaryManager,
            IActionManager actionManager,
            IUtilityManager utilityManager,
            LanguageManager languageManager,
            PreprocessorLoader preprocessorLoader,
            ValidatorLoader validatorLoader,
            ScriptRunnerSettings settings,
            StatisticsService statisticsService,
            IEnvironmentManager environmentManager,
            ITestScriptStorage testScriptStorage,
            IAdapterManager adapterManager,
            IStaticServiceManager staticServiceManager,
            String compilerClassPath) {
		super(wd, dictionaryManager, actionManager, utilityManager, languageManager, preprocessorLoader, validatorLoader, settings, statisticsService, environmentManager, testScriptStorage, adapterManager, staticServiceManager, compilerClassPath);
		tScriptCompiler = new Thread(new ScriptCompiler(), "ScriptCompiler");
		tScriptExecutor = new Thread(new ScriptExecutor(), "ScriptExecutor");
		tScriptCompiler.start();
		tScriptExecutor.start();
	}

	class ScriptCompiler implements Runnable {
		private final ExecutorService executorService = Executors.newFixedThreadPool(2);
        private final Queue> scriptsForCompileQueue = new ConcurrentLinkedQueue<>();

		@Override
        public void run() {
			while (!isDisposing) {
				try {
					if (!scriptsForCompileQueue.isEmpty()) {
                        Entry entry = scriptsForCompileQueue.poll();
                        TestScriptDescription descrForCompile = testScripts.get(entry.getKey());

	        			if (descrForCompile == null) {
	        				logger.warn("Can't find script [{}]. Probably it was removed", entry.getKey());
	        				continue;
	        			}

						executorService.submit(new Runnable() {
							@Override
							public void run() {
								try {
									compileScript(entry.getValue(), descrForCompile);
									if (descrForCompile.getAutoRun()) {
									    synchronized (preparedTestScripts) {
                                            preparedTestScripts.add(entry.getKey());
                                        }
									} else {
									    synchronized (pendingTestScriptsToRun) {
                                            pendingTestScriptsToRun.add(entry.getKey());
                                        }
									}
									descrForCompile.scriptReady();
								} catch (Exception e) {
									scriptExceptionProcessing(descrForCompile, e);
								}
							}
						});
					}

					Thread.sleep(100);

                    Long testScript;
                    synchronized (addedTestScripts) {
                        testScript = addedTestScripts.poll();
                    }

					if (testScript == null) {
						continue;
					}

                    TestScriptDescription descrForPrep = testScripts.get(testScript);

        			if (descrForPrep == null) {
        				logger.warn("Can't find script [{}]. Probably it was removed", testScript);
        				continue;
        			}

					if (descrForPrep.getState() == ScriptState.CANCELED) {
						continue;
					}

                    if (descrForPrep.isSetCancelFlag()) {
                        cancelScript(descrForPrep);
                        continue;
                    }

					logger.info("TestScript {} is being prepared", testScript);
					descrForPrep.scriptPreparing();
					executorService.submit(new Runnable() {
						@Override
						public void run() {
							try {
								GeneratedScript generatedScript = prepareScript(descrForPrep);
                                Entry entry = new SimpleEntry<>(testScript, generatedScript);
								scriptsForCompileQueue.add(entry);
							} catch (Exception e) {
								scriptExceptionProcessing(descrForPrep, e);
							}
						}
					});
				} catch (InterruptedException e) {
					logger.error(e.getMessage(), e);
					break;
				}
			}
		}
	}

	class ScriptExecutor implements Runnable {
	    private final int MAX_THREADS = 3;
		private final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2);
        private final Set locksServices = new HashSet<>();
        private final List prepared = new ArrayList<>();

		@Override
        public void run() {

			Map> runningScriptMap = new HashMap<>();
			try {

				while (!isDisposing) {
					try {
					    pullScripts();

						stopScripts(runningScriptMap);

						startScript(runningScriptMap);

						resultScript(runningScriptMap);

                        filterCancelledScripts();

						Thread.sleep(DEFAULT_TIMEOUT);
					} catch (InterruptedException e) {
						if (isDisposing) {
							interuptScripts(runningScriptMap);
						}
					}
				}
				interuptScripts(runningScriptMap);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			} finally {
				scheduledThreadPool.shutdown();
                try {
					scheduledThreadPool.awaitTermination(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    logger.error("", e);
                }
				logger.info("AsyncScriptRunner thread finished");
			}
		}

        private boolean skipScriptExecution(TestScriptDescription description) {
            if (description == null) {
                logger.warn("Script description is NULL");
            } else if (description.getStatus() == ScriptStatus.CANCELED) {
                logger.warn("Script {} already has state CANCELED but it shouldn't", description.getId());
            }
            return description == null
                    || description.isSetCancelFlag();
        }

        private void filterCancelledScripts() {
            for (Iterator iterator = prepared.iterator();
                    iterator.hasNext(); ) {
                Long id = iterator.next();
                TestScriptDescription description = testScripts.get(id);
                if (skipScriptExecution(description)) {
                    if (description != null) {
                        if (description.getStatus() != ScriptStatus.CANCELED) {
                            cancelScript(description);
                            logger.info("Script {} has been filtered from the prepared list and cancelled", id);
                        } else {
                            logger.info("Script {} has been filtered from the prepared list because it was already cancelled", id);
                        }
                    } else {
                        logger.warn("Can't find script {}", id);
                    }
                    iterator.remove();
                }
            }
        }

        private void  stopScripts(Map> runningScriptMap) throws InterruptedException {
			boolean localShutdown = shutdown;

			for (Entry> scriptFeature : runningScriptMap.entrySet()) {
				Long scriptId = scriptFeature.getKey();

				TestScriptDescription description = testScripts.get(scriptId);
				if (localShutdown || (description != null && description.isSetCancelFlag())) {
					logger.warn("Shutdown script {}", scriptId);
					Future future = scriptFeature.getValue();

					if (!future.isDone()) {
						future.cancel(true);
					}
				}
			}

			if (localShutdown) {
				shutdown = false;
			}
		}

		private void interuptScripts(Map> runningScriptMap) {
			for (Entry> scriptFeature : runningScriptMap.entrySet()) {
				Long currentTestScript = scriptFeature.getKey();
				Future future = scriptFeature.getValue();

				TestScriptDescription descr = testScripts.get(currentTestScript);

				if (future.cancel(true)) {
					descr.scriptInterrupted();
					logger.info("TestScript {} was interrupted", currentTestScript);
				} else {
					descr.scriptExecuted();
					logger.info("TestScript {} was executed", currentTestScript);
				}
                onRunFinished(descr);
			}

			pullScripts();
            for(Long tsId : prepared) {
				TestScriptDescription descr = testScripts.get(tsId);
                descr.scriptNotStarted();
                onRunFinished(descr);
				logger.info("TestScript {} was not started", tsId);
			}
            prepared.clear();
		}

		private void startScript(Map> runningScriptMap) throws InterruptedException {
            if(prepared.isEmpty()) {
                return;
            }
            if(runningScriptMap.size() >= MAX_THREADS) {
                return;
            }

            Iterator iterator = prepared.iterator();
    		    while (iterator.hasNext() && runningScriptMap.size() < MAX_THREADS) {
    		        Long currentTestScript = iterator.next();

        			TestScriptDescription descr = testScripts.get(currentTestScript);

                    logger.info("Get script {}", currentTestScript);
                    if (skipScriptExecution(descr)) {
                        continue;
                    }

        			if (tryToLockServices(descr)) {
        			    iterator.remove();
            			logger.info("TestScript {} was taken to prepare for run", currentTestScript);

            			try {
            					onRunStarted(descr);

            					Class testCaseClass = descr.getClassLoader().loadClass(descr.getClassName()).asSubclass(SailFishTestCase.class);

            					DefaultScriptConfig scriptConfiguration = new DefaultScriptConfig(
            					        descr.getScriptSettings(),
            							descr.getWorkFolder(),
            							descr.getDescription(),
            							descr.getScriptLogger());

            					logger.info("TestScript {} is being run", currentTestScript);

            					descr.getContext().setScriptConfig(scriptConfiguration);

            					runningScriptMap.put(
            							currentTestScript,
            							scheduledThreadPool.submit(new InternalScript(testCaseClass, descr.getContext())));

            					descr.scriptRan();

            			} catch (Exception e) {
                            descr.scriptInitFailed(e);
                            onRunFinished(descr);

            				logger.error("TestScript [{}] was failed during preparation", currentTestScript, e);
            			}
        			}
    		    }
		}

		private void resultScript(Map> runningScriptMap) throws InterruptedException {
			Iterator>> iterator = runningScriptMap.entrySet().iterator();

			while (iterator.hasNext()) {
				Entry> scriptFeature = iterator.next();
				Future future = scriptFeature.getValue();

				if (future.isDone()) {
					iterator.remove();
					Long currentTestScript = scriptFeature.getKey();
					TestScriptDescription descr = testScripts.get(currentTestScript);
					unlockServices(descr);

					Throwable result;
					try {
						result = future.get();
					} catch (Exception e) {
						logger.warn("Interrupt of matrix execution, reason : {}", e.getMessage(), e);
						result = e;
					}

					if (future.isCancelled()) {
						descr.scriptInterrupted();
					} else if (result != null) {
						descr.scriptRunFailed(result);
					} else {
						descr.scriptExecuted();
					}
                    onRunFinished(descr);

					logger.info("TestScript {} was executed", currentTestScript);
				}
			}
		}

        private void pullScripts() {
            Long testScriptId;
            do {
                synchronized (preparedTestScripts) {
                    testScriptId = preparedTestScripts.poll();
                    if (testScriptId != null) {
                        logger.info("TestScript {} was taken from waiting queue", testScriptId);
                        prepared.add(testScriptId);
                    }
                }
            }
            while (testScriptId != null);
        }

		private boolean tryToLockServices(TestScriptDescription descr) {
		    List services = descr.getContext().getServiceList();
            if(Collections.disjoint(locksServices, services)) {
                locksServices.addAll(services);
		        logger.info("TestScript {} locked services {}", descr.getId(), services);
		        return true;
		    }
		    return false;
		}

		private void unlockServices(TestScriptDescription descr) {
            List services = descr.getContext().getServiceList();
            locksServices.removeAll(services);
            logger.info("TestScript {} unlocked services {}", descr.getId(), services);
        }
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy