Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* $Id: ProcessUtil.java 402 2008-01-14 21:09:00Z hohwille $
* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.process;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.mmm.util.component.AlreadyInitializedException;
import net.sf.mmm.util.concurrent.SimpleExecutor;
import net.sf.mmm.util.concurrent.Stoppable;
import net.sf.mmm.util.io.AsyncTransferrer;
import net.sf.mmm.util.io.StreamUtil;
import net.sf.mmm.util.nls.base.NlsIllegalArgumentException;
/**
* This class is a collection of utility functions to deal with {@link Process}es.
*
* @author Joerg Hohwiller (hohwille at users.sourceforge.net)
*/
public class ProcessUtil {
/** @see #getInstance() */
private static ProcessUtil instance;
/** @see #getStreamUtil() */
private StreamUtil streamUtil;
/** @see #getLogger() */
private Log logger;
/** @see #getExecutor() */
private Executor executor;
/**
* The constructor.
*/
public ProcessUtil() {
super();
}
/**
* This method gets the singleton instance of this {@link ProcessUtil}.
* This design is the best compromise between easy access (via this
* indirection you have direct, static access to all offered functionality)
* and IoC-style design which allows extension and customization.
* For IoC usage, simply ignore all static {@link #getInstance()} methods and
* construct new instances via the container-framework of your choice (like
* plexus, pico, springframework, etc.). To wire up the dependent components
* everything is properly annotated using common-annotations (JSR-250). If
* your container does NOT support this, you should consider using a better
* one.
*
* @return the singleton instance.
*/
public static ProcessUtil getInstance() {
if (instance == null) {
synchronized (ProcessUtil.class) {
if (instance == null) {
instance = new ProcessUtil();
// instance.setLogger(new Jdk14Logger(ProcessUtil.class.getName()));
// even more ugly...
instance.setLogger(LogFactory.getLog(ProcessUtil.class));
instance.setExecutor(SimpleExecutor.INSTANCE);
instance.setStreamUtil(StreamUtil.getInstance());
}
}
}
return instance;
}
/**
* @return the logger
*/
protected Log getLogger() {
return this.logger;
}
/**
* @param logger the logger to set
*/
@Resource
public void setLogger(Log logger) {
this.logger = logger;
}
/**
* This method gets the {@link Executor} used to run asynchronous tasks. It
* may use a thread-pool.
*
* @return the executor.
*/
protected Executor getExecutor() {
return this.executor;
}
/**
* This method sets the {@link #getExecutor() executor}.
*
* @param executor the executor to set.
*/
@Resource
public void setExecutor(Executor executor) {
if (this.executor != null) {
throw new AlreadyInitializedException();
}
this.executor = executor;
}
/**
* This method gets the stream-util that is used by this process-util.
*
* @return the streamUtil
*/
protected StreamUtil getStreamUtil() {
return this.streamUtil;
}
/**
* This method sets the {@link #getStreamUtil() stream-util}.
*
* @param streamUtil the streamUtil to set
*/
@Resource
public void setStreamUtil(StreamUtil streamUtil) {
this.streamUtil = streamUtil;
}
/**
* This method executes the external {@link Process}es configured by the
* given builders. If more than one
* {@link ProcessBuilder builder} is given, the according processes are piped.
* ATTENTION:
* This method spins up multiple {@link Thread threads}, especially when
* multiple processes are piped (2*n+1[+1] threads). Therefore you should NOT
* use the {@link #getInstance() singleton} variant of this util except you
* are writing a simple command-line client that does a simple job and then
* terminates. When writing a server-application or library, that makes such
* calls repetitive, you should create your own instance of
* {@link ProcessUtil} and configure a thread-pool as
* {@link java.util.concurrent.Executor}.
*
* @param context is the context of the process pipe (fist stdin,
* last stdout and stderr for all
* processes as well as a potential timeout).
* @param builders are the configurations of the {@link Process}(es) to
* execute. The array needs to have a length greater than zero.
* @return the {@link Process#waitFor() exit-code} of the {@link Process}-pipe
* configured by the given builders.
* @throws IOException if an input/output-error occurred.
* @throws InterruptedException if the calling {@link Thread} was interrupted
* while {@link Process#waitFor() waiting for} a {@link Process} to
* complete.
*/
public int execute(ProcessContext context, ProcessBuilder... builders) throws IOException,
InterruptedException {
ProcessExecutor processExecutor = new ProcessExecutor(context, builders);
return processExecutor.call().intValue();
}
/**
* This method executes the external {@link Process}es configured by the
* given builders. If more than one
* {@link ProcessBuilder builder} is given, the according processes are piped.
* ATTENTION:
* This method spins up multiple {@link Thread threads}, especially when
* multiple processes are piped (2*n+1[+1] threads). Therefore you should NOT
* use the {@link #getInstance() singleton} variant of this util except you
* are writing a simple command-line client that does a simple job and then
* terminates. When writing a server-application or library, that makes such
* calls repetitive, you should create your own instance of
* {@link ProcessUtil} and configure a thread-pool as
* {@link java.util.concurrent.Executor}.
*
* @param context is the context of the process pipe (fist stdin,
* last stdout and stderr for all
* processes as well as a potential timeout).
* @param timeout is the maximum amount of time to wait for the
* {@link Process}-pipe to finish.
* @param unit is the {@link TimeUnit} of the given timeout
* argument.
* @param builders are the configurations of the {@link Process}(es) to
* execute. The array needs to have a length greater than zero.
* @return the {@link Process#waitFor() exit-code} of the {@link Process}-pipe
* configured by the given builders.
* @throws IOException if an input/output-error occurred.
* @throws TimeoutException if the {@link Process}-pipe did NOT complete
* before the given timeout (according to
* unit).
* @throws InterruptedException if the calling {@link Thread} was interrupted
* while waiting for the {@link Process}-pipe to complete and before
* the timeout occurred.
*/
public int execute(ProcessContext context, long timeout, TimeUnit unit,
ProcessBuilder... builders) throws IOException, TimeoutException, InterruptedException {
AsyncProcessExecutor processExecutor = executeAsync(context, builders);
try {
return processExecutor.get(timeout, unit).intValue();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if ((cause != null) && (cause instanceof InterruptedException)) {
throw (InterruptedException) cause;
} else {
// should NOT happen...
throw new IllegalStateException(e);
}
} finally {
processExecutor.cancel(true);
}
}
/**
* This method executes the external {@link Process}es configured by the
* given builders as async task. If more than one
* {@link ProcessBuilder builder} is given, the according processes are piped.
* ATTENTION:
* This method spins up multiple {@link Thread threads}, especially when
* multiple processes are piped (2*n+1[+1] threads). Therefore you should NOT
* use the {@link #getInstance() singleton} variant of this util except you
* are writing a simple command-line client that does a simple job and then
* terminates. When writing a server-application or library, that makes such
* calls repetitive, you should create your own instance of
* {@link ProcessUtil} and configure a thread-pool as
* {@link java.util.concurrent.Executor}.
*
* @param context is the context of the process pipe (fist stdin,
* last stdout and stderr for all
* processes as well as a potential timeout).
* @param builders are the configurations of the {@link Process}(es) to
* execute. The array needs to have a length greater than zero.
* @return the {@link Process#waitFor() exit-code} of the {@link Process}-pipe
* configured by the given builders.
* @throws IOException if an input/output-error occurred while setting up the
* {@link Process}(es).
*/
public AsyncProcessExecutor executeAsync(ProcessContext context, ProcessBuilder... builders)
throws IOException {
ProcessExecutor processExecutor = new ProcessExecutor(context, builders);
AsyncProcessExecutorImpl asyncExecutor = new AsyncProcessExecutorImpl(processExecutor);
getExecutor().execute(asyncExecutor);
return asyncExecutor;
}
/**
* This inner class is the default implementation of the AsyncProcessExecutor.
*/
protected static class AsyncProcessExecutorImpl extends FutureTask implements
AsyncProcessExecutor {
/** The actual task to run. */
private final ProcessExecutor executor;
/**
* The constructor.
*
* @param executor is the process-executor to call.
*/
public AsyncProcessExecutorImpl(ProcessExecutor executor) {
super(executor);
this.executor = executor;
}
/**
* {@inheritDoc}
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
this.executor.stop();
return super.cancel(mayInterruptIfRunning);
}
}
/**
* This inner class is does the actual execution of the {@link Process}(es).
*/
protected class ProcessExecutor implements Callable, Stoppable {
/** @see ProcessExecutor#ProcessExecutor(ProcessContext, ProcessBuilder[]) */
private final ProcessContext context;
/** @see ProcessExecutor#ProcessExecutor(ProcessContext, ProcessBuilder[]) */
private final Process[] processes;
/** @see ProcessExecutor#ProcessExecutor(ProcessContext, ProcessBuilder[]) */
private final AsyncTransferrer[] transferrers;
/**
* The constructor.
*
* @param context is the context of the process pipe.
* @param builders are the configurations of the {@link Process}(es) to
* execute. The array needs to have a length greater than zero.
* @throws IOException if an input/output error occurred.
*/
public ProcessExecutor(ProcessContext context, ProcessBuilder[] builders) throws IOException {
super();
StreamUtil streamUtility = getStreamUtil();
if (builders.length == 0) {
streamUtility.close(context.getInStream());
streamUtility.close(context.getOutStream());
streamUtility.close(context.getErrStream());
throw new NlsIllegalArgumentException("builders must NOT be empty!");
}
this.context = context;
this.processes = new Process[builders.length];
// n*(process.err->context.err), (n+1)*(in->out)
this.transferrers = new AsyncTransferrer[builders.length + builders.length + 1];
boolean success = false;
try {
InputStream in = context.getInStream();
OutputStream err = context.getErrStream();
for (int i = 0; i < builders.length; i++) {
Process process = builders[i].start();
AsyncTransferrer inOutTransferrer = streamUtility.transferAsync(in, process
.getOutputStream(), false);
AsyncTransferrer errTransferrer = streamUtility.transferAsync(process.getErrorStream(),
err, true);
this.processes[i] = process;
in = this.processes[i].getInputStream();
int transferrersIndex = i + i;
this.transferrers[transferrersIndex] = inOutTransferrer;
this.transferrers[transferrersIndex + 1] = errTransferrer;
}
this.transferrers[builders.length + builders.length] = streamUtility.transferAsync(in,
context.getOutStream(), false);
success = true;
} finally {
if (!success) {
stop();
}
}
}
/**
* This method disposes this executor. All processes are
* {@link Process#destroy() destroyed} and all streams are closed.
*/
protected void dispose() {
for (int i = 0; i < this.processes.length; i++) {
if (this.processes[i] != null) {
try {
this.processes[i].destroy();
} catch (RuntimeException e) {
getLogger().warn(e);
}
this.processes[i] = null;
}
}
for (int i = 0; i < this.transferrers.length; i++) {
if (this.transferrers[i] != null) {
try {
this.transferrers[i].cancel(true);
} catch (RuntimeException e) {
getLogger().warn(e);
}
this.transferrers[i] = null;
}
}
StreamUtil streamUtility = getStreamUtil();
streamUtility.close(this.context.getInStream());
streamUtility.close(this.context.getOutStream());
streamUtility.close(this.context.getErrStream());
}
/**
* {@inheritDoc}
*/
public void stop() {
dispose();
}
/**
* {@inheritDoc}
*
* This method executes the {@link Process}(es).
*
* @return the return-code of the (last) process.
* @throws InterruptedException if a process was interrupted.
*/
public Integer call() throws InterruptedException {
try {
int returnCode = 0;
for (int i = 0; i < this.processes.length; i++) {
returnCode = this.processes[i].waitFor();
}
return Integer.valueOf(returnCode);
} finally {
dispose();
}
}
}
}