X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fjp%2Fsourceforge%2Fdvibrowser%2Fdvicore%2Futil%2FCommandShell.java;fp=src%2Fmain%2Fjava%2Fjp%2Fsourceforge%2Fdvibrowser%2Fdvicore%2Futil%2FCommandShell.java;h=e48f93842f08dae272982cc55c371f4b1e212893;hb=93efc067c74bbead8d4e430193690888da0e49c2;hp=0000000000000000000000000000000000000000;hpb=df94a623b684b25842e0a18636bbaa284feb88e2;p=dvibrowser%2Fdvi2epub.git diff --git a/src/main/java/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java b/src/main/java/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java new file mode 100644 index 0000000..e48f938 --- /dev/null +++ b/src/main/java/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2009, Takeyuki Nagao + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the + * following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +package jp.sourceforge.dvibrowser.dvicore.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jp.sourceforge.dvibrowser.dvicore.DviException; + + +public class CommandShell +{ + public static final int RETCODE_SUCCESS = 0; + + private final ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor + (new DaemonThreadFactory()); + + private static final Logger LOGGER = Logger.getLogger(CommandShell.class.getName()); + private ArrayList commandLine = new ArrayList(); + + public void setCommandLine(String ... commandLine) + { + ArrayList list = new ArrayList(); + for (String arg : commandLine) { + list.add(arg); + } + this.commandLine = list; + } + + public void setCommandLine(Collection cmdLine) + { + ArrayList list = new ArrayList(); + list.addAll(cmdLine); + this.commandLine = list; + } + + public Collection getCommandLine() + { + return Collections.unmodifiableCollection(commandLine); + } + + private ArrayList envs = null; + public void setEnvironment(Collection envs) + { + if (envs == null) { + this.envs = null; + return; + } else { + ArrayList list = new ArrayList(); + list.addAll(envs); + this.envs = list; + } + } + + public void setEnvironment(String ... envs) + { + ArrayList list = new ArrayList(); + for (String arg : envs) { + list.add(arg); + } + this.envs = list; + } + + public Collection getEnvironment() + { + if (envs == null) return null; + return Collections.unmodifiableCollection(envs); + } + + private File dir = null; + public void setWorkingDirectory(File dir) + { + this.dir = dir; + } + + public File getWorkingDirectory() + { + return dir; + } + + private TimeUnit timeUnit; + private long timeout = 0; + public void setTimeout(long timeout, TimeUnit timeUnit) + { + if (timeout < 0) { + throw new IllegalArgumentException("timeout is negative"); + } + this.timeout = timeout; + this.timeUnit = timeUnit; + } + + private CommandShellHandler handler = null; + public void setHandler(CommandShellHandler handler) + { + this.handler = handler; + } + + public CommandShellHandler getHandler() + { + return handler; + } + + protected void checkVars() + { + if (commandLine == null) + throw new IllegalArgumentException + ("commandLine can't be null"); + if (commandLine.size() < 1) + throw new IllegalArgumentException + ("commandLine can't be empty"); + } + + private volatile Process p = null; + private Thread stdoutThread = null; + private Thread stderrThread = null; + private Thread stdinThread = null; + private volatile Throwable stdoutThrowable = null; + private volatile Throwable stderrThrowable = null; + private volatile Throwable stdinThrowable = null; + + public int execute() + throws IOException, InterruptedException, DviException + { + int result = -1; + + p = null; + checkVars(); + + try { + final String commandLineStr = DviUtils.join(" ", commandLine); + LOGGER.fine("Running command: " + commandLineStr); + p = Runtime.getRuntime().exec( + commandLine.toArray(new String[commandLine.size()]), + getEnvironmentAsArray(), dir); + processStreams(); + ScheduledFuture future = null; + if (timeUnit != null && timeout > 0) { + LOGGER.fine("Starting timer: timeout=" + timeout + " timeUnit=" + timeUnit); + future = exe.schedule(new Runnable() { + public void run() { + LOGGER.warning("Command timed out: " + commandLineStr); + p.destroy(); + } + }, timeout, timeUnit); + } + LOGGER.fine("waiting for the process to terminate."); + result = p.waitFor(); + LOGGER.fine("process exit with retcode " + result); + if (future != null) { + future.cancel(false); + } + } catch (InterruptedException ex) { + if (p != null) { + p.destroy(); + } + throw ex; + } catch (IOException ex) { + if (p != null) { + p.destroy(); + } + throw ex; + } finally { + reapThread(stderrThread, "stderr"); + stderrThread = null; + reapThread(stdoutThread, "stdout"); + stdoutThread = null; + reapThread(stdinThread, "stdin"); + stdinThread = null; + + if (p != null) { + DviUtils.silentClose(p.getErrorStream()); + DviUtils.silentClose(p.getInputStream()); + DviUtils.silentClose(p.getOutputStream()); + } + } + + if (stdinThrowable != null) { + throw new DviException(stdinThrowable); + } + if (stdoutThrowable != null) { + throw new DviException(stdoutThrowable); + } + if (stderrThrowable != null) { + throw new DviException(stderrThrowable); + } + + p = null; + + return result; + } + + private String[] getEnvironmentAsArray() { + if (envs == null) return null; + return envs.toArray(new String[envs.size()]); + } + + private void reapThread(Thread thread, String name) + { + try { + LOGGER.fine("waiting for the " + name + " thread"); + if (thread != null) + thread.join(); + } catch (InterruptedException ex) { + DviUtils.logStackTrace(LOGGER, Level.WARNING, ex); + } + } + + protected void processStreams() + throws IOException + { + if (handler != null) { + stdinThread = new Thread(new Runnable() { + public void run() { + OutputStream os = p.getOutputStream(); + try { + handler.handleStdin(os); + } catch (Throwable ex) { + DviUtils.logStackTrace(LOGGER, Level.WARNING, ex); + stdinThrowable = ex; + p.destroy(); + } finally { + DviUtils.silentClose(os); + } + } + }); + stderrThread = new Thread(new Runnable() { + public void run() { + InputStream is = p.getErrorStream(); + try { + handler.handleStderr(is); + } catch (Throwable ex) { + DviUtils.logStackTrace(LOGGER, Level.WARNING, ex); + stderrThrowable = ex; + p.destroy(); + } finally { + DviUtils.silentClose(is); + } + } + }); + stdoutThread = new Thread(new Runnable() { + public void run() { + InputStream is = p.getInputStream(); + try { + handler.handleStdout(is); + } catch (Throwable ex) { + DviUtils.logStackTrace(LOGGER, Level.WARNING, ex); + stdoutThrowable = ex; + p.destroy(); + } finally { + DviUtils.silentClose(is); + } + } + }); + + stdinThread .start(); + stderrThread.start(); + stdoutThread.start(); + } else { + DviUtils.silentClose(p.getOutputStream()); + DviUtils.silentClose(p.getInputStream()); + DviUtils.silentClose(p.getErrorStream()); + } + } +}