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

org.kuali.common.util.execute.impl.ConcurrentExecutables Maven / Gradle / Ivy

There is a newer version: 4.4.17
Show newest version
/**
 * Copyright 2010-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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 org.kuali.common.util.execute.impl;

import static com.google.common.base.Optional.absent;
import static com.google.common.base.Stopwatch.createStarted;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Math.ceil;
import static java.lang.Math.max;
import static org.kuali.common.util.FormatUtils.getTime;
import static org.kuali.common.util.base.Precondition.checkNotNull;
import static org.kuali.common.util.log.Loggers.newLogger;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.List;

import org.kuali.common.util.base.Threads;
import org.kuali.common.util.execute.Executable;
import org.slf4j.Logger;

import com.google.common.base.Optional;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

/**
 * Create a new thread for each executable in the list and run them all concurrently.
 */
public final class ConcurrentExecutables implements Executable, UncaughtExceptionHandler {

	private static final Logger logger = newLogger();

	private final ImmutableList executables;
	private final boolean skip;
	private final boolean timed;
	private final Optional maxThreads;

	// If any thread throws an exception, this gets filled in
	private Optional uncaughtException = absent();

	public static void execute(Executable... executables) {
		create(executables).execute();
	}

	public static void execute(List executables) {
		create(executables).execute();
	}

	public static void executeConcurrently(List executables, int maxThreads) {
		builder(executables).withMaxThreads(maxThreads).build().execute();
	}

	public static ConcurrentExecutables create(Executable... executables) {
		return builder(executables).build();
	}

	public static ConcurrentExecutables create(List executables) {
		return builder(executables).build();
	}

	public static Builder builder(Executable... executables) {
		return new Builder(executables);
	}

	public static Builder builder(List executables) {
		return new Builder(executables);
	}

	public static class Builder implements org.apache.commons.lang3.builder.Builder {

		// Required
		private final List executables;

		// Optional
		private boolean skip = false;
		private boolean timed = false;
		private Optional maxThreads = absent();

		public Builder(Executable... executables) {
			this(ImmutableList.copyOf(executables));
		}

		public Builder(List executables) {
			this.executables = ImmutableList.copyOf(executables);
		}

		public Builder timed(boolean timed) {
			this.timed = timed;
			return this;
		}

		public Builder skip(boolean skip) {
			this.skip = skip;
			return this;
		}

		public Builder withMaxThreads(int maxThreads) {
			this.maxThreads = Optional.of(maxThreads);
			return this;
		}

		@Override
		public ConcurrentExecutables build() {
			ConcurrentExecutables instance = new ConcurrentExecutables(this);
			validate(instance);
			return instance;
		}

		private static void validate(ConcurrentExecutables instance) {
			checkNotNull(instance.executables, "executables");
			checkNotNull(instance.uncaughtException, "uncaughtException");
		}
	}

	private ConcurrentExecutables(Builder builder) {
		this.executables = ImmutableList.copyOf(builder.executables);
		this.skip = builder.skip;
		this.timed = builder.timed;
		this.maxThreads = builder.maxThreads;
	}

	@Override
	public void execute() {
		if (skip) {
			logger.info("Skipping execution of {} executables", executables.size());
			return;
		}
		List threads = getThreads(executables, maxThreads);
		Stopwatch stopwatch = createStarted();
		Threads.start(threads);
		Threads.join(threads);
		if (uncaughtException.isPresent()) {
			throw uncaughtException.get();
		}
		if (timed) {
			logger.info("------------------------------------------------------------------------");
			logger.info("Total Time: {} (Wall Clock)", getTime(stopwatch));
			logger.info("------------------------------------------------------------------------");
		}
	}

	protected List getThreads(List executables, Optional maxThreads) {
		int max = maxThreads.isPresent() ? maxThreads.get() : executables.size();
		int size = (int) max(ceil(executables.size() / (max * 1D)), 1);
		List> partitions = Lists.partition(executables, size);
		List threads = newArrayList();
		for (List partition : partitions) {
			Runnable runnable = new ExecutablesRunner(partition);
			Thread thread = new Thread(runnable, "Executable");
			thread.setUncaughtExceptionHandler(this);
			threads.add(thread);
		}
		return threads;
	}

	@Override
	public synchronized void uncaughtException(Thread thread, Throwable uncaughtException) {
		// Only report back on the first uncaught exception reported by any thread
		// Any exceptions after the first one get ignored
		if (!this.uncaughtException.isPresent()) {
			String context = "Exception in thread [" + thread.getId() + ":" + thread.getName() + "]";
			this.uncaughtException = Optional.of(new IllegalStateException(context, uncaughtException));
		}
	}

	public ImmutableList getExecutables() {
		return executables;
	}

	public boolean isSkip() {
		return skip;
	}

	public boolean isTimed() {
		return timed;
	}

	public Optional getUncaughtException() {
		return uncaughtException;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy