/** * AnalyzerBeans * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.eobjects.analyzer.test; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eobjects.analyzer.job.concurrent.TaskListener; import org.eobjects.analyzer.job.concurrent.TaskRunnable; import org.eobjects.analyzer.job.concurrent.TaskRunner; import org.eobjects.analyzer.job.tasks.Task; import org.junit.Assert; /** * Multithreaded task runner appropriate for thread-testing purposes. Unlike the * regular multithreaded task runner this TaskRunner saves the futures of all * submitted tasks in order to make it possible to inspect them from the * perspective of a unittest (typically to assert that no tasks are waiting or * such). * * */ public final class ActivityAwareMultiThreadedTaskRunner implements TaskRunner { private final Map<Future<?>, Task> _tasksAndFutures = Collections.synchronizedMap(new LinkedHashMap<Future<?>, Task>()); private final ExecutorService _executorService; public ActivityAwareMultiThreadedTaskRunner() { _executorService = Executors.newFixedThreadPool(10); } @Override public void run(Task task, TaskListener listener) { Future<?> future = _executorService.submit(new TaskRunnable(task, listener)); _tasksAndFutures.put(future, task); } @Override public void run(TaskRunnable taskRunnable) { Future<?> future = _executorService.submit(taskRunnable); _tasksAndFutures.put(future, taskRunnable.getTask()); } @Override public void shutdown() { _executorService.shutdown(); } public ExecutorService getExecutorService() { return _executorService; } public Set<Future<?>> getFutures() { return _tasksAndFutures.keySet(); } public Map<Future<?>, Task> getTasksAndFutures() { return _tasksAndFutures; } /** * Asserts that all submitted tasks have been executed * * @param timeoutMillis * the amount of slack milliseconds to allow for remaining tasks * to finish * @return the amount of tasks finished * @throws Exception * any exceptions either thrown because of failing assertions or * because of exceptions thrown in the tasks */ public int assertAllBegunTasksFinished(int timeoutMillis) throws Exception { long millisBefore = System.currentTimeMillis(); int taskCount = 0; for (Future<?> future : _tasksAndFutures.keySet()) { if (future.isDone()) { taskCount++; } else { long millisNow = System.currentTimeMillis(); long millisUsed = millisNow - millisBefore; try { // using the timeout'ed get method to ensure that the future // will not just wait for the result to be ready. It SHOULD // be ready already! future.get(timeoutMillis - millisUsed, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { Task task = _tasksAndFutures.get(future); Assert.fail("Task is not finished: " + task); } } } return taskCount; } @Override public void assistExecution() { // do nothing } }