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

com.liferay.petra.process.ProcessUtil Maven / Gradle / Ivy

There is a newer version: 7.4.3.112-ga112
Show newest version
/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library 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.
 */

package com.liferay.petra.process;

import com.liferay.petra.concurrent.BaseFutureListener;
import com.liferay.petra.concurrent.DefaultNoticeableFuture;
import com.liferay.petra.concurrent.NoticeableFuture;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;

import java.io.IOException;

import java.util.AbstractMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicMarkableReference;

/**
 * @author Shuyang Zhou
 */
public class ProcessUtil {

	public static  NoticeableFuture> execute(
			OutputProcessor outputProcessor, List arguments)
		throws ProcessException {

		if (outputProcessor == null) {
			throw new NullPointerException("Output processor is null");
		}

		if (arguments == null) {
			throw new NullPointerException("Arguments is null");
		}

		ProcessBuilder processBuilder = new ProcessBuilder(arguments);

		String threadNamePrefix = _buildThreadNamePrefix(arguments);

		try {
			Process process = processBuilder.start();

			NoticeableFuture stdOutNoticeableFuture = _submit(
				threadNamePrefix.concat("StdOut"),
				new ProcessStdOutCallable<>(outputProcessor, process));

			NoticeableFuture stdErrNoticeableFuture = _submit(
				threadNamePrefix.concat("StdErr"),
				new ProcessStdErrCallable<>(outputProcessor, process));

			return _wrapNoticeableFuture(
				stdOutNoticeableFuture, stdErrNoticeableFuture, process);
		}
		catch (IOException ioe) {
			throw new ProcessException(ioe);
		}
	}

	public static  NoticeableFuture> execute(
			OutputProcessor outputProcessor, String... arguments)
		throws ProcessException {

		return execute(outputProcessor, Arrays.asList(arguments));
	}

	private static String _buildThreadNamePrefix(List arguments) {
		StringBundler sb = new StringBundler(arguments.size() * 2 + 1);

		sb.append(StringPool.OPEN_BRACKET);

		for (String argument : arguments) {
			sb.append(argument);
			sb.append(StringPool.SPACE);
		}

		sb.setStringAt(StringPool.CLOSE_BRACKET, sb.index() - 1);

		sb.append("-");

		return sb.toString();
	}

	private static  NoticeableFuture _submit(
		String threadName, Callable callable) {

		DefaultNoticeableFuture defaultNoticeableFuture =
			new DefaultNoticeableFuture<>(callable);

		Thread thread = new Thread(defaultNoticeableFuture, threadName);

		thread.setDaemon(true);

		thread.start();

		return defaultNoticeableFuture;
	}

	private static  NoticeableFuture>
		_wrapNoticeableFuture(
			NoticeableFuture stdOutNoticeableFuture,
			NoticeableFuture stdErrNoticeableFuture, Process process) {

		DefaultNoticeableFuture> defaultNoticeableFuture =
			new DefaultNoticeableFuture<>();

		defaultNoticeableFuture.addFutureListener(
			future -> {
				if (!future.isCancelled()) {
					return;
				}

				stdOutNoticeableFuture.cancel(true);

				stdErrNoticeableFuture.cancel(true);

				process.destroy();
			});

		AtomicMarkableReference stdOutReference =
			new AtomicMarkableReference<>(null, false);

		AtomicMarkableReference stdErrReference =
			new AtomicMarkableReference<>(null, false);

		stdOutNoticeableFuture.addFutureListener(
			new BaseFutureListener() {

				@Override
				public void completeWithCancel(Future future) {
					defaultNoticeableFuture.cancel(true);
				}

				@Override
				public void completeWithException(
					Future future, Throwable throwable) {

					defaultNoticeableFuture.setException(throwable);
				}

				@Override
				public void completeWithResult(Future future, O stdOut) {
					stdOutReference.set(stdOut, true);

					boolean[] markHolder = new boolean[1];

					E stdErr = stdErrReference.get(markHolder);

					if (markHolder[0]) {
						defaultNoticeableFuture.set(
							new AbstractMap.SimpleEntry<>(stdOut, stdErr));
					}
				}

			});

		stdErrNoticeableFuture.addFutureListener(
			new BaseFutureListener() {

				@Override
				public void completeWithCancel(Future future) {
					defaultNoticeableFuture.cancel(true);
				}

				@Override
				public void completeWithException(
					Future future, Throwable throwable) {

					defaultNoticeableFuture.setException(throwable);
				}

				@Override
				public void completeWithResult(Future future, E stdErr) {
					stdErrReference.set(stdErr, true);

					boolean[] markHolder = new boolean[1];

					O stdOut = stdOutReference.get(markHolder);

					if (markHolder[0]) {
						defaultNoticeableFuture.set(
							new AbstractMap.SimpleEntry<>(stdOut, stdErr));
					}
				}

			});

		return defaultNoticeableFuture;
	}

	private static class ProcessStdErrCallable implements Callable {

		@Override
		public T call() throws Exception {
			return _outputProcessor.processStdErr(_process.getErrorStream());
		}

		private ProcessStdErrCallable(
			OutputProcessor outputProcessor, Process process) {

			_outputProcessor = outputProcessor;
			_process = process;
		}

		private final OutputProcessor _outputProcessor;
		private final Process _process;

	}

	private static class ProcessStdOutCallable implements Callable {

		@Override
		public T call() throws Exception {
			try {
				return _outputProcessor.processStdOut(
					_process.getInputStream());
			}
			finally {
				try {
					int exitCode = _process.waitFor();

					if (exitCode != 0) {
						throw new TerminationProcessException(exitCode);
					}
				}
				catch (InterruptedException ie) {
					_process.destroy();

					throw new ProcessException(
						"Forcibly killed subprocess on interruption", ie);
				}
			}
		}

		private ProcessStdOutCallable(
			OutputProcessor outputProcessor, Process process) {

			_outputProcessor = outputProcessor;
			_process = process;
		}

		private final OutputProcessor _outputProcessor;
		private final Process _process;

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy