package com.griddynamics.jagger.engine.e1.process; import com.griddynamics.jagger.coordinator.NodeContext; import com.griddynamics.jagger.engine.e1.Provider; import com.griddynamics.jagger.engine.e1.ProviderUtil; import com.griddynamics.jagger.engine.e1.collector.Validator; import com.griddynamics.jagger.engine.e1.collector.invocation.InvocationListener; import com.griddynamics.jagger.engine.e1.scenario.KernelSideObjectProvider; import com.griddynamics.jagger.engine.e1.scenario.NodeSideInitializable; import com.griddynamics.jagger.engine.e1.scenario.ScenarioCollector; import com.griddynamics.jagger.engine.e1.services.JaggerPlace; import com.griddynamics.jagger.exception.TechnicalException; import com.griddynamics.jagger.invoker.Scenario; import com.griddynamics.jagger.util.Futures; import com.griddynamics.jagger.util.TimeoutsConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Service; import java.util.Collection; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; /** * Abstract workload process to simplify status collection process. */ public abstract class AbstractWorkloadProcess implements WorkloadProcess { // list of current active workload services protected Collection<WorkloadService> threads; // executor to execute workload services protected final ThreadPoolExecutor executor; protected final String sessionId; protected final StartWorkloadProcess command; protected final NodeContext context; protected final TimeoutsConfiguration timeoutsConfiguration; protected volatile int samplesCountStartedFromTerminatedThreads = 0; protected volatile int samplesCountFinishedFromTerminatedThreads = 0; protected volatile long emptyTransactionsFromTerminatedThreads = 0; private static final Logger log = LoggerFactory.getLogger(AbstractWorkloadProcess.class); public AbstractWorkloadProcess(ThreadPoolExecutor executor, String sessionId, StartWorkloadProcess command, NodeContext context, TimeoutsConfiguration timeoutsConfiguration) { this.executor = executor; this.sessionId = sessionId; this.command = command; this.context = context; this.timeoutsConfiguration = timeoutsConfiguration; } @Override public void start() throws TechnicalException { threads = getRunningWorkloadServiceCollection(); for (KernelSideObjectProvider<ScenarioCollector<Object, Object, Object>> provider : command.getCollectors()) { if (provider instanceof NodeSideInitializable) { ((NodeSideInitializable) provider).init(sessionId, command.getTaskId(), context); } } for (Provider<InvocationListener<Object, Object, Object>> provider : command.getListeners()){ ProviderUtil.injectContext(provider, sessionId, command.getTaskId(), context, JaggerPlace.INVOCATION_LISTENER); } doStart(); } /** * @return implementation of collection to be used to collect statistics for status. */ protected abstract Collection<WorkloadService> getRunningWorkloadServiceCollection(); /** * Actually starts workload after initialising providers. */ protected abstract void doStart(); /** * Stops all current running workload services, and shutdown workload executor. */ public void stop() { log.debug("Going to terminate"); List<ListenableFuture<Service.State>> futures = Lists.newLinkedList(); for (WorkloadService thread : threads) { ListenableFuture<Service.State> stop = thread.stop(); futures.add(stop); } for (ListenableFuture<Service.State> future : futures) { Service.State state = Futures.get(future, timeoutsConfiguration.getWorkloadStopTimeout()); log.debug("stopped workload thread with status {}", state); } log.debug("All threads were terminated"); executor.shutdown(); log.debug("Shutting down executor"); } @Override public WorkloadStatus getStatus() { int started = samplesCountStartedFromTerminatedThreads; int finished = samplesCountFinishedFromTerminatedThreads; long emptyTrn = emptyTransactionsFromTerminatedThreads; int runningThreads = 0; for (WorkloadService thread : threads) { started += thread.getStartedSamples(); finished += thread.getFinishedSamples(); emptyTrn += thread.getEmptyTransactions(); if (thread.isRunning()) { runningThreads ++; } } return new WorkloadStatus(started, finished, runningThreads, emptyTrn); } /** * Common method to add new workload service with all listeners. * * @param delay delay in milliseconds that will be used in {@code WorkloadService} */ protected void startNewThread(int delay) { log.debug("Adding new workload thread"); Scenario<Object, Object, Object> scenario = command.getScenarioFactory().get(context, command.getKernelInfo()); List<InvocationListener<?, ?, ?>> listeners = Lists.newArrayList(); for (Provider<InvocationListener<Object, Object, Object>> listener : command.getListeners()){ listeners.add(listener.provide()); } List<ScenarioCollector<?, ?, ?>> collectors = Lists.newLinkedList(); for (KernelSideObjectProvider<ScenarioCollector<Object, Object, Object>> provider : command.getCollectors()) { collectors.add(provider.provide(sessionId, command.getTaskId(), context)); } List<Validator> validators = Lists.newLinkedList(); for (KernelSideObjectProvider<Validator> provider : command.getValidators()){ validators.add(provider.provide(sessionId, command.getTaskId(), context)); } AbstractWorkloadService.WorkloadServiceBuilder builder = AbstractWorkloadService .builder(scenario) .addCollectors(collectors) .addValidators(validators) .addListeners(listeners) .useExecutor(executor); WorkloadService thread = getService(builder); thread.changeDelay(delay); log.debug("Starting workload"); Future<Service.State> future = thread.start(); Service.State state = Futures.get(future, timeoutsConfiguration.getWorkloadStartTimeout()); log.debug("Workload thread was started with state {}", state); threads.add(thread); } /** * Determines type of workload service to be executed. * * @return workload service to be started while adding new thread. */ protected abstract WorkloadService getService(AbstractWorkloadService.WorkloadServiceBuilder serviceBuilder); }