001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.utils.command; 021 022 import java.io.BufferedReader; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.io.InputStreamReader; 026 import java.util.concurrent.*; 027 028 import org.apache.commons.io.IOUtils; 029 import org.slf4j.Logger; 030 import org.slf4j.LoggerFactory; 031 032 /** 033 * Synchronously execute a native command line. It's much more limited than the Apache Commons Exec library. 034 * For example it does not allow to get process output, to run asynchronously or to automatically quote 035 * command-line arguments. 036 * 037 * @since 2.7 038 */ 039 public final class CommandExecutor { 040 041 private static final CommandExecutor INSTANCE = new CommandExecutor(); 042 043 private CommandExecutor() { 044 } 045 046 public static CommandExecutor create() { 047 // stateless object, so a single singleton can be shared 048 return INSTANCE; 049 } 050 051 public int execute(Command command, long timeoutMilliseconds) { 052 ExecutorService executorService = null; 053 Process process = null; 054 StreamGobbler outputGobbler = null; 055 StreamGobbler errorGobbler = null; 056 try { 057 LoggerFactory.getLogger(getClass()).debug("Executing command: " + command); 058 ProcessBuilder builder = new ProcessBuilder(command.toStrings()); 059 if (command.getDirectory() != null) { 060 builder.directory(command.getDirectory()); 061 } 062 process = builder.start(); 063 064 // consume and display the error and output streams 065 outputGobbler = new StreamGobbler(process.getInputStream()); 066 errorGobbler = new StreamGobbler(process.getErrorStream()); 067 outputGobbler.start(); 068 errorGobbler.start(); 069 070 final Process finalProcess = process; 071 Callable<Integer> call = new Callable<Integer>() { 072 public Integer call() throws Exception { 073 return finalProcess.waitFor(); 074 } 075 }; 076 077 executorService = Executors.newSingleThreadExecutor(); 078 Future<Integer> ft = executorService.submit(call); 079 return ft.get(timeoutMilliseconds, TimeUnit.MILLISECONDS); 080 081 } catch (TimeoutException te) { 082 process.destroy(); 083 throw new CommandException(command, "Timeout exceeded: " + timeoutMilliseconds + " ms", te); 084 085 } catch (Exception e) { 086 throw new CommandException(command, e); 087 088 } finally { 089 waitUntilFinish(outputGobbler); 090 waitUntilFinish(errorGobbler); 091 closeStreams(process); 092 093 if (executorService != null) { 094 executorService.shutdown(); 095 } 096 } 097 } 098 099 private void closeStreams(Process process) { 100 if (process != null) { 101 IOUtils.closeQuietly(process.getInputStream()); 102 IOUtils.closeQuietly(process.getOutputStream()); 103 IOUtils.closeQuietly(process.getErrorStream()); 104 } 105 } 106 107 private void waitUntilFinish(StreamGobbler thread) { 108 if (thread != null) { 109 try { 110 thread.join(); 111 } catch (InterruptedException e) { 112 // ignore 113 } 114 } 115 } 116 117 private static class StreamGobbler extends Thread { 118 InputStream is; 119 120 StreamGobbler(InputStream is) { 121 super("ProcessStreamGobbler"); 122 this.is = is; 123 } 124 125 @Override 126 public void run() { 127 Logger logger = LoggerFactory.getLogger(CommandExecutor.class); 128 InputStreamReader isr = new InputStreamReader(is); 129 BufferedReader br = new BufferedReader(isr); 130 try { 131 String line; 132 while ((line = br.readLine()) != null) { 133 logger.info(line); 134 } 135 } catch (IOException ioe) { 136 logger.error("Error while reading stream", ioe); 137 138 } finally { 139 IOUtils.closeQuietly(br); 140 IOUtils.closeQuietly(isr); 141 } 142 } 143 } 144 }