/* * Sonar PHP Plugin * Copyright (C) 2010 Sonar PHP Plugin * dev@sonar.codehaus.org * * This program 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 3 of the License, or (at your option) any later version. * * This program 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. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ package org.sonar.plugins.php.core; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchExtension; import org.sonar.api.profiles.ProfileExporter; import org.sonar.api.profiles.RulesProfile; /** * Abstract php plugin executor. This class handles common executor needs such as running the process, reading its common and error output * streams and logging. In nominal case implementing executor should just construct the desire command line. */ public abstract class PhpPluginAbstractExecutor implements BatchExtension { /** * The Class AsyncPipe. */ private static class AsyncPipe extends Thread { /** The logger. */ private static final Logger LOG = LoggerFactory.getLogger(AsyncPipe.class); /** The input stream. */ private BufferedReader reader; /** The output stream. */ private BufferedWriter writer; /** * Instantiates a new async pipe. * * @param input * an InputStream * @param output * an OutputStream */ public AsyncPipe(InputStream input, ByteArrayOutputStream output) { this.reader = new BufferedReader(new InputStreamReader(input)); this.writer = new BufferedWriter(new OutputStreamWriter(output)); } /** * @see java.lang.Thread#run() */ @Override public void run() { try { // Reads the process input stream and writes it to the output stream String line = reader.readLine(); while (line != null) { synchronized (writer) { writer.write(line); LOG.debug(line); } line = reader.readLine(); } } catch (IOException e) { LOG.error("Can't execute the Async Pipe", e); } } } /** The logger */ private static final Logger LOG = LoggerFactory.getLogger(PhpPluginAbstractExecutor.class); private static final int DEFAUT_BUFFER_INITIAL_SIZE = 1024; private static final String RULESET_PREFIX = "ruleset"; private static final String XML_SUFFIX = ".xml"; /** * Executes the external tool. */ public void execute() { try { // Gets the tool command line List<String> commandLine = getCommandLine(); ProcessBuilder builder = new ProcessBuilder(commandLine); LOG.info("Executing " + getExecutedTool() + " with command '{}'", prettyPrint(commandLine)); // Starts the process Process p = builder.start(); // And handles it's normal and error stream in separated threads. ByteArrayOutputStream output = new ByteArrayOutputStream(DEFAUT_BUFFER_INITIAL_SIZE); AsyncPipe outputStreamThread = new AsyncPipe(p.getInputStream(), output); outputStreamThread.start(); ByteArrayOutputStream error = new ByteArrayOutputStream(DEFAUT_BUFFER_INITIAL_SIZE); AsyncPipe errorStreamThread = new AsyncPipe(p.getErrorStream(), error); errorStreamThread.start(); LOG.info(getExecutedTool() + " ended with returned code '{}'.", p.waitFor()); } catch (IOException e) { LOG.error("Can't execute the external tool", e); throw new PhpPluginExecutionException(e); } catch (InterruptedException e) { LOG.error("Async pipe interrupted: ", e); throw new PhpPluginExecutionException(e); } } protected File getRuleset(AbstractPhpPluginConfiguration configuration, RulesProfile profile, ProfileExporter exporter) { File workingDir = configuration.createWorkingDirectory(); File ruleset = null; try { ruleset = File.createTempFile(RULESET_PREFIX, XML_SUFFIX, workingDir); Writer writer = new FileWriter(ruleset); exporter.exportProfile(profile, writer); } catch (IOException e) { String msg = "Error while creating temporary ruleset from profile: " + profile + " to file : " + ruleset + " in dir " + workingDir; LOG.error(msg); } return ruleset.length() > 0 ? ruleset : null; } /** * Returns a String where each list member is separated with a space * * @param commandLine * the external tool command line argument * @return String where each list member is separated with a space */ private String prettyPrint(List<String> commandLine) { StringBuilder sb = new StringBuilder(); for (Iterator<String> iter = commandLine.iterator(); iter.hasNext();) { String part = iter.next(); sb.append(part); if (iter.hasNext()) { sb.append(" "); } } return sb.toString(); } /** * Gets the command line. * * @return the command line */ protected abstract List<String> getCommandLine(); /** * Gets the executed tool. * * @return the executed tool */ protected abstract String getExecutedTool(); }