aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-runner-api
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-runner-api')
-rw-r--r--sonar-runner-api/pom.xml65
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/BootstrapClassLoader.java124
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java264
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/Main.java325
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/Runner.java397
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java202
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/Stats.java60
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java123
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java30
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java195
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java (renamed from sonar-runner-api/src/main/java/org/sonar/runner/Logs.java)46
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java118
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java (renamed from sonar-runner-api/src/main/java/org/sonar/runner/RunnerException.java)34
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java102
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java (renamed from sonar-runner-api/src/main/java/org/sonar/runner/Version.java)28
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java (renamed from sonar-runner-api/src/main/java/org/sonar/runner/VersionUtils.java)36
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java (renamed from sonar-runner-api/src/main/java/org/sonar/runner/package-info.java)8
-rw-r--r--sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt1
-rw-r--r--sonar-runner-api/src/main/resources/org/sonar/runner/version.txt1
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/BootstrapClassLoaderTest.java62
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java178
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java83
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java131
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java227
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java157
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java75
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java54
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java (renamed from sonar-runner-api/src/test/java/org/sonar/runner/StatsTest.java)19
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java47
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/RunnerVersionTest.java (renamed from sonar-runner-api/src/test/java/org/sonar/runner/VersionTest.java)12
-rw-r--r--sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java33
-rw-r--r--sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project/sonar-project.properties4
-rw-r--r--sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner/conf/sonar-runner.properties2
-rw-r--r--sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties1
-rw-r--r--sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/conf/sonar-runner.properties2
-rw-r--r--sonar-runner-api/src/test/resources/org/sonar/runner/RunnerTest/shouldInitDirs/fake.txt1
-rwxr-xr-xsonar-runner-api/src/test/scripts/echo.bat4
-rwxr-xr-xsonar-runner-api/src/test/scripts/echo.sh6
-rwxr-xr-xsonar-runner-api/src/test/scripts/forever.bat6
-rwxr-xr-xsonar-runner-api/src/test/scripts/forever.sh6
-rwxr-xr-xsonar-runner-api/src/test/scripts/output.bat5
-rwxr-xr-xsonar-runner-api/src/test/scripts/output.sh6
42 files changed, 1079 insertions, 2201 deletions
diff --git a/sonar-runner-api/pom.xml b/sonar-runner-api/pom.xml
index af33a64..9a7208f 100644
--- a/sonar-runner-api/pom.xml
+++ b/sonar-runner-api/pom.xml
@@ -1,48 +1,48 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar.runner</groupId>
<artifactId>sonar-runner</artifactId>
- <version>2.1-SNAPSHOT</version>
+ <version>2.2-SNAPSHOT</version>
</parent>
<artifactId>sonar-runner-api</artifactId>
<name>Sonar Runner - API</name>
<dependencies>
- <!-- Dependencies will be shaded -->
+ <!-- Dependencies with scope "compile" are shaded and removed from transitive dependencies-->
<dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.2</version>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>sonar-runner-impl</artifactId>
+ <version>${pom.version}</version>
</dependency>
<dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.4</version>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <scope>provided</scope>
</dependency>
+
+ <!-- Unit tests -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
- <version>2.4</version>
+ <version>2.6</version>
+ <scope>test</scope>
</dependency>
- <!-- Unit tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
- <version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
- <version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -54,7 +54,6 @@
<filtering>true</filtering>
</resource>
</resources>
-
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -69,6 +68,34 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>sonar-runner-impl</artifactId>
+ <version>${pom.version}</version>
+ <type>jar</type>
+ <overWrite>false</overWrite>
+ <outputDirectory>${project.build.outputDirectory}</outputDirectory>
+ <destFileName>sonar-runner-impl.jar</destFileName>
+ </artifactItem>
+ </artifactItems>
+ <overWriteReleases>true</overWriteReleases>
+ <overWriteSnapshots>true</overWriteSnapshots>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
@@ -81,14 +108,6 @@
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
- <pattern>org.apache.commons.io</pattern>
- <shadedPattern>org.sonar.runner.commonsio</shadedPattern>
- </relocation>
- <relocation>
- <pattern>org.apache.commons.codec</pattern>
- <shadedPattern>org.sonar.runner.commonscodec</shadedPattern>
- </relocation>
- <relocation>
<pattern>org.apache.commons.lang</pattern>
<shadedPattern>org.sonar.runner.commonslang</shadedPattern>
</relocation>
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/BootstrapClassLoader.java b/sonar-runner-api/src/main/java/org/sonar/runner/BootstrapClassLoader.java
deleted file mode 100644
index 75d2a10..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/BootstrapClassLoader.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Enumeration;
-
-/**
- * Special {@link URLClassLoader} to execute Sonar, which restricts loading from parent.
- */
-class BootstrapClassLoader extends URLClassLoader {
-
- private final String[] unmaskedPackages;
-
- BootstrapClassLoader(ClassLoader parent, String... unmaskedPackages) {
- super(new URL[0], parent);
- this.unmaskedPackages = unmaskedPackages;
- }
-
- /**
- * {@inheritDoc} Visibility of a method has been relaxed to public.
- */
- @Override
- public void addURL(URL url) {
- super.addURL(url);
- }
-
- /**
- * {@inheritDoc} Visibility of a method has been relaxed to public.
- */
- @Override
- public Class<?> findClass(String name) throws ClassNotFoundException {
- return super.findClass(name);
- }
-
- /**
- * @return true, if class can be loaded from parent ClassLoader
- */
- boolean canLoadFromParent(String name) {
- if (name.startsWith("org.sonar.runner.") && !name.startsWith("org.sonar.runner.internal.batch.")) {
- return true;
- }
- for (String pkg : unmaskedPackages) {
- if (name.startsWith(pkg + ".")) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Same behavior as in {@link URLClassLoader#loadClass(String, boolean)}, except loading from parent.
- */
- @Override
- protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- // First, check if the class has already been loaded
- Class<?> c = findLoadedClass(name);
- if (c == null) {
- try {
- // Load from parent
- if (getParent() != null && canLoadFromParent(name)) {
- c = getParent().loadClass(name);
- } else {
- // Load from system
-
- // I don't know for other vendors, but for Oracle JVM :
- // - ClassLoader.getSystemClassLoader() is sun.misc.Launcher$AppClassLoader. It contains app classpath.
- // - ClassLoader.getSystemClassLoader().getParent() is sun.misc.Launcher$ExtClassLoader. It contains core JVM
- ClassLoader systemClassLoader = getSystemClassLoader();
- if (systemClassLoader.getParent() != null) {
- systemClassLoader = systemClassLoader.getParent();
- }
- c = systemClassLoader.loadClass(name);
- }
- } catch (ClassNotFoundException e) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
-
- /**
- * Unlike {@link URLClassLoader#getResource(String)} don't return resource from parent.
- * See http://jira.codehaus.org/browse/SONAR-2276
- */
- @Override
- public URL getResource(String name) {
- return findResource(name);
- }
-
- /**
- * Unlike {@link URLClassLoader#getResources(String)} don't return resources from parent.
- * See http://jira.codehaus.org/browse/SONAR-2276
- */
- @Override
- public Enumeration<URL> getResources(String name) throws IOException {
- return findResources(name);
- }
-
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java b/sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java
deleted file mode 100644
index 64e9694..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.ConnectException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Bootstrapper used to download everything from the server and create the correct classloader required to execute a Sonar analysis in isolation.
- */
-class Bootstrapper {
-
- static final String VERSION_PATH = "/api/server/version";
- static final String BATCH_PATH = "/batch/";
- static final String BOOTSTRAP_INDEX_PATH = "/batch_bootstrap/index";
- static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
- static final int READ_TIMEOUT_MILLISECONDS = 60000;
- private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)");
-
- private static final String[] UNSUPPORTED_VERSIONS_FOR_CACHE = {"1", "2", "3.0", "3.1", "3.2", "3.3", "3.4"};
-
- private File bootDir;
- private String serverUrl;
- private String productToken;
- private String serverVersion;
- private SonarCache cache;
-
- /**
- * @param productToken part of User-Agent request-header field - see http://tools.ietf.org/html/rfc1945#section-10.15
- */
- Bootstrapper(String productToken, String serverUrl, File workDir, SonarCache cache) {
- this.productToken = productToken;
- this.cache = cache;
- bootDir = new File(workDir, "batch");
- bootDir.mkdirs();
- if (serverUrl.endsWith("/")) {
- this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1);
- } else {
- this.serverUrl = serverUrl;
- }
- }
-
- /**
- * @return server url
- */
- String getServerUrl() {
- return serverUrl;
- }
-
- /**
- * @return server version
- */
- String getServerVersion() {
- if (serverVersion == null) {
- try {
- serverVersion = remoteContent(VERSION_PATH);
- } catch (ConnectException e) {
- Logs.error("Sonar server '" + serverUrl + "' can not be reached");
- throw new RunnerException("Fail to request server version", e);
- } catch (UnknownHostException e) {
- Logs.error("Sonar server '" + serverUrl + "' can not be reached");
- throw new RunnerException("Fail to request server version", e);
- } catch (IOException e) {
- throw new RunnerException("Fail to request server version", e);
- }
- }
- return serverVersion;
- }
-
- /**
- * Download batch files from server and creates {@link BootstrapClassLoader}.
- * To use this method version of Sonar should be at least 2.6.
- *
- * @param urls additional URLs for loading classes and resources
- * @param parent parent ClassLoader
- * @param unmaskedPackages only classes and resources from those packages would be available for loading from parent
- */
- BootstrapClassLoader createClassLoader(URL[] urls, ClassLoader parent, String... unmaskedPackages) {
- BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, unmaskedPackages);
- List<File> files = downloadBatchFiles();
- for (URL url : urls) {
- classLoader.addURL(url);
- }
- for (File file : files) {
- try {
- classLoader.addURL(file.toURI().toURL());
- } catch (MalformedURLException e) {
- throw new IllegalStateException("Fail to create classloader", e);
- }
- }
- return classLoader;
- }
-
- private void remoteContentToFile(String path, File toFile) {
- InputStream input = null;
- FileOutputStream output = null;
- String fullUrl = serverUrl + path;
- if (Logs.isDebugEnabled()) {
- Logs.debug("Downloading " + fullUrl + " to " + toFile.getAbsolutePath());
- }
- // Don't log for old versions without cache to not pollute logs
- else if (!isUnsupportedVersionForCache(getServerVersion())) {
- Logs.info("Downloading " + path.substring(path.lastIndexOf("/") + 1));
- }
- try {
- HttpURLConnection connection = newHttpConnection(new URL(fullUrl));
- output = new FileOutputStream(toFile, false);
- input = connection.getInputStream();
- IOUtils.copyLarge(input, output);
- } catch (IOException e) {
- IOUtils.closeQuietly(output);
- FileUtils.deleteQuietly(toFile);
- throw new IllegalStateException("Fail to download the file: " + fullUrl, e);
- } finally {
- IOUtils.closeQuietly(input);
- IOUtils.closeQuietly(output);
- }
- }
-
- String remoteContent(String path) throws IOException {
- String fullUrl = serverUrl + path;
- HttpURLConnection conn = newHttpConnection(new URL(fullUrl));
- String charset = getCharsetFromContentType(conn.getContentType());
- if (charset == null || "".equals(charset)) {
- charset = "UTF-8";
- }
- Reader reader = new InputStreamReader(conn.getInputStream(), charset);
- try {
- int statusCode = conn.getResponseCode();
- if (statusCode != HttpURLConnection.HTTP_OK) {
- throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode);
- }
- return IOUtils.toString(reader);
- } finally {
- IOUtils.closeQuietly(reader);
- conn.disconnect();
- }
- }
-
- /**
- * By convention, the product tokens are listed in order of their significance for identifying the application.
- */
- String getUserAgent() {
- return "sonar-bootstrapper/" + Version.getVersion() + " " + productToken;
- }
-
- HttpURLConnection newHttpConnection(URL url) throws IOException {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS);
- connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS);
- connection.setInstanceFollowRedirects(true);
- connection.setRequestMethod("GET");
- connection.setRequestProperty("User-Agent", getUserAgent());
- return connection;
- }
-
- private List<File> downloadBatchFiles() {
- try {
- List<File> files = new ArrayList<File>();
- if (isUnsupportedVersionForCache(getServerVersion())) {
- getBootstrapFilesFromOldURL(files);
- }
- else {
- getBootstrapFiles(files);
- }
- return files;
- } catch (Exception e) {
- throw new IllegalStateException("Fail to download libraries from server", e);
- }
- }
-
- private void getBootstrapFilesFromOldURL(List<File> files) throws IOException {
- String libs = remoteContent(BATCH_PATH);
- for (String lib : libs.split(",")) {
- File file = new File(bootDir, lib);
- remoteContentToFile(BATCH_PATH + lib, file);
- files.add(file);
- }
- }
-
- private void getBootstrapFiles(List<File> files) throws IOException {
- String libs = remoteContent(BOOTSTRAP_INDEX_PATH);
- String[] lines = libs.split("[\r\n]+");
- for (String line : lines) {
- line = line.trim();
- if ("".equals(line)) {
- continue;
- }
- String[] libAndMd5 = line.split("\\|");
- String libName = libAndMd5[0];
- String remoteMd5 = libAndMd5.length > 0 ? libAndMd5[1] : null;
- File libInCache = null;
- if (remoteMd5 != null && !"".equals(remoteMd5)) {
- libInCache = cache.getFileFromCache(libName, remoteMd5);
- }
- if (libInCache == null) {
- File tmpLocation = cache.getTemporaryFile();
- remoteContentToFile(BATCH_PATH + libName, tmpLocation);
- String md5 = cache.cacheFile(tmpLocation, libName);
- libInCache = cache.getFileFromCache(libName, md5);
- if (!md5.equals(remoteMd5)) {
- throw new RunnerException("INVALID CHECKSUM: File " + libInCache.getAbsolutePath() + " was expected to have checksum " + remoteMd5
- + " but was downloaded with checksum " + md5);
- }
- }
- files.add(libInCache);
- }
- }
-
- static boolean isUnsupportedVersionForCache(String version) {
- return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS_FOR_CACHE);
- }
-
- /**
- * Parse out a charset from a content type header.
- *
- * @param contentType e.g. "text/html; charset=EUC-JP"
- * @return "EUC-JP", or null if not found. Charset is trimmed and uppercased.
- */
- static String getCharsetFromContentType(String contentType) {
- if (contentType == null) {
- return null;
- }
-
- Matcher m = CHARSET_PATTERN.matcher(contentType);
- if (m.find()) {
- return m.group(1).trim().toUpperCase();
- }
- return null;
- }
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Main.java b/sonar-runner-api/src/main/java/org/sonar/runner/Main.java
deleted file mode 100644
index a9a653a..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Main.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Locale;
-import java.util.Properties;
-
-/**
- * Arguments :
- * <ul>
- * <li>runner.home: optional path to runner home (root directory with sub-directories bin, lib and conf)</li>
- * <li>runner.settings: optional path to runner global settings, usually ${runner.home}/conf/sonar-runner.properties.
- * This property is used only if ${runner.home} is not defined</li>
- * <li>project.home: path to project root directory. If not set, then it's supposed to be the directory where the runner is executed</li>
- * <li>project.settings: optional path to project settings. Default value is ${project.home}/sonar-project.properties.</li>
- * </ul>
- *
- * @since 1.0
- */
-public final class Main {
-
- private static final String RUNNER_HOME = "runner.home";
- private static final String RUNNER_SETTINGS = "runner.settings";
- private static final String PROJECT_HOME = "project.home";
- private static final String PROJECT_SETTINGS = "project.settings";
- // TODO Remove this after everything is updated to support tasks
- private static final String TASK_COMMAND = "sonar.task";
-
- boolean debugMode = false;
- boolean displayVersionOnly = false;
- boolean displayStackTrace = false;
- String command;
- Properties globalProperties;
- Properties projectProperties;
-
- /**
- * Entry point of the program.
- */
- public static void main(String[] args) {
- new Main().execute(args);
- }
-
- Main() {
- }
-
- private void execute(String[] args) {
- Properties argsProperties = parseArguments(args);
- System.out.println("Runner version: " + Version.getVersion());
- System.out.println("Java version: " + System.getProperty("java.version", "<unknown>")
- + ", vendor: " + System.getProperty("java.vendor", "<unknown>"));
- System.out
- .println("OS name: \"" + System.getProperty("os.name") + "\", version: \"" + System.getProperty("os.version") + "\", arch: \"" + System.getProperty("os.arch") + "\"");
- if (!displayVersionOnly) {
- int result = execute(argsProperties);
- System.exit(result);
- }
- }
-
- private int execute(Properties argsProperties) {
- if (displayStackTrace) {
- Logs.info("Error stacktraces are turned on.");
- }
- Stats stats = new Stats().start();
- try {
- loadProperties(argsProperties);
- Runner runner = Runner.create(command, globalProperties, projectProperties);
- Logs.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + runner.getSourceCodeEncoding() + "\""
- + (runner.isEncodingPlatformDependant() ? " (analysis is platform dependent)" : ""));
- Logs.debug("Other system properties:");
- Logs.debug(" - sun.arch.data.model: \"" + System.getProperty("sun.arch.data.model") + "\"");
- Logs.info("Server: " + runner.getSonarServerURL());
- try {
- Logs.info("Work directory: " + runner.getWorkDir().getCanonicalPath());
- Logs.info("Cache directory: " + runner.getCache().getCacheLocation());
- } catch (IOException e) {
- throw new RunnerException("Unable to resolve directory", e);
- }
- runner.execute();
- } catch (Exception e) {
- displayExecutionResult(stats, "FAILURE");
- showError("Error during Sonar runner execution", e, displayStackTrace);
- return 1;
- }
- displayExecutionResult(stats, "SUCCESS");
- return 0;
- }
-
- private void displayExecutionResult(Stats stats, String resultMsg) {
- Logs.info("------------------------------------------------------------------------");
- Logs.info("EXECUTION " + resultMsg);
- Logs.info("------------------------------------------------------------------------");
- stats.stop();
- Logs.info("------------------------------------------------------------------------");
- }
-
- public void showError(String message, Throwable e, boolean showStackTrace) {
- if (showStackTrace) {
- Logs.error(message, e);
- if (!debugMode) {
- Logs.error("");
- suggestDebugMode();
- }
- }
- else {
- Logs.error(message);
- if (e != null) {
- Logs.error(e.getMessage());
- String previousMsg = "";
- for (Throwable cause = e.getCause(); cause != null
- && cause.getMessage() != null
- && !cause.getMessage().equals(previousMsg); cause = cause.getCause()) {
- Logs.error("Caused by: " + cause.getMessage());
- previousMsg = cause.getMessage();
- }
- }
- Logs.error("");
- Logs.error("To see the full stack trace of the errors, re-run Sonar Runner with the -e switch.");
- if (!debugMode) {
- suggestDebugMode();
- }
- }
- }
-
- private void suggestDebugMode() {
- Logs.error("Re-run Sonar Runner using the -X switch to enable full debug logging.");
- }
-
- void loadProperties(Properties argsProperties) {
- globalProperties = loadGlobalProperties(argsProperties);
- projectProperties = loadProjectProperties(argsProperties);
- }
-
- Properties loadGlobalProperties(Properties argsProperties) {
- Properties commandLineProps = new Properties();
- commandLineProps.putAll(System.getProperties());
- commandLineProps.putAll(argsProperties);
-
- Properties result = new Properties();
- result.putAll(loadRunnerConfiguration(commandLineProps));
- result.putAll(commandLineProps);
-
- return result;
- }
-
- Properties loadProjectProperties(Properties argsProperties) {
- Properties commandLineProps = new Properties();
- commandLineProps.putAll(System.getProperties());
- commandLineProps.putAll(argsProperties);
-
- Properties result = new Properties();
- result.putAll(loadProjectConfiguration(commandLineProps));
- result.putAll(commandLineProps);
-
- if (result.containsKey(PROJECT_HOME)) {
- // the real property of the Sonar Runner is "sonar.projectDir"
- String baseDir = result.getProperty(PROJECT_HOME);
- result.remove(PROJECT_HOME);
- result.put(Runner.PROPERTY_SONAR_PROJECT_BASEDIR, baseDir);
- }
-
- return result;
- }
-
- Properties loadRunnerConfiguration(Properties props) {
- File settingsFile = locatePropertiesFile(props, RUNNER_HOME, "conf/sonar-runner.properties", RUNNER_SETTINGS);
- if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
- Logs.info("Runner configuration file: " + settingsFile.getAbsolutePath());
- return toProperties(settingsFile);
- }
- Logs.info("Runner configuration file: NONE");
- return new Properties();
- }
-
- private Properties loadProjectConfiguration(Properties props) {
- File settingsFile = locatePropertiesFile(props, PROJECT_HOME, "sonar-project.properties", PROJECT_SETTINGS);
- if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
- Logs.info("Project configuration file: " + settingsFile.getAbsolutePath());
- return toProperties(settingsFile);
- }
- Logs.info("Project configuration file: NONE");
- return new Properties();
- }
-
- private File locatePropertiesFile(Properties props, String homeKey, String relativePathFromHome, String settingsKey) {
- File settingsFile = null;
- String runnerHome = props.getProperty(homeKey);
- if (runnerHome != null && !"".equals(runnerHome)) {
- settingsFile = new File(runnerHome, relativePathFromHome);
- }
-
- if (settingsFile == null || !settingsFile.exists()) {
- String settingsPath = props.getProperty(settingsKey);
- if (settingsPath != null && !"".equals(settingsPath)) {
- settingsFile = new File(settingsPath);
- }
- }
- return settingsFile;
- }
-
- private Properties toProperties(File file) {
- InputStream in = null;
- Properties properties = new Properties();
- try {
- in = new FileInputStream(file);
- properties.load(in);
- return properties;
-
- } catch (Exception e) {
- throw new IllegalStateException("Fail to load file: " + file.getAbsolutePath(), e);
-
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- Properties parseArguments(String[] args) {
- int i = 0;
- if (args.length > 0 && !args[0].startsWith("-")) {
- command = args[0];
- i++;
- }
- else {
- command = null;
- }
- Properties props = new Properties();
- for (; i < args.length; i++) {
- String arg = args[i];
- if ("-h".equals(arg) || "--help".equals(arg)) {
- printUsage();
- }
- else if ("-v".equals(arg) || "--version".equals(arg)) {
- displayVersionOnly = true;
- }
- else if ("-e".equals(arg) || "--errors".equals(arg)) {
- displayStackTrace = true;
- }
- else if ("-X".equals(arg) || "--debug".equals(arg)) {
- props.setProperty(Runner.PROPERTY_VERBOSE, "true");
- displayStackTrace = true;
- debugMode = true;
- Logs.setDebugEnabled(true);
- }
- else if ("-D".equals(arg) || "--define".equals(arg)) {
- i++;
- if (i >= args.length) {
- printError("Missing argument for option --define");
- }
- arg = args[i];
- appendPropertyTo(arg, props);
-
- }
- else if (arg.startsWith("-D")) {
- arg = arg.substring(2);
- appendPropertyTo(arg, props);
-
- }
- else {
- printError("Unrecognized option: " + arg);
- }
- }
- return props;
- }
-
- private void appendPropertyTo(String arg, Properties props) {
- final String key, value;
- int j = arg.indexOf('=');
- if (j == -1) {
- key = arg;
- value = "true";
- } else {
- key = arg.substring(0, j);
- value = arg.substring(j + 1);
- }
- if (TASK_COMMAND.equals(key)) {
- command = value;
- }
- else {
- props.setProperty(key, value);
- }
- }
-
- private void printError(String message) {
- Logs.error(message);
- printUsage();
- }
-
- private void printUsage() {
- Logs.info("");
- Logs.info("usage: sonar-runner [command] [options]");
- Logs.info("");
- Logs.info("Command:");
- Logs.info(" analyse-project Run Sonar analysis task on the current project (default)");
- Logs.info(" list-tasks Display all tasks available");
- Logs.info("Options:");
- Logs.info(" -D,--define <arg> Define property");
- Logs.info(" -e,--errors Produce execution error messages");
- Logs.info(" -h,--help Display help information");
- Logs.info(" -v,--version Display version information");
- Logs.info(" -X,--debug Produce execution debug output");
- System.exit(0);
- }
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Runner.java b/sonar-runner-api/src/main/java/org/sonar/runner/Runner.java
deleted file mode 100644
index 5b1a436..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Runner.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-/**
- * <p>
- * Sonar Runner class that can be used to launch Sonar analyses.
- * </p>
- * <p>
- * Configuration is all done through properties:
- * </p>
- * <ul>
- * <li>"sonar.projectDir": the base directory of the project to analyse (this can also be passed via the {@link #create(Properties, File)} constructor)</li>
- * <li>"sonar.working.directory": the working directory, which is "${sonar.projectDir}/.sonar" by default.</li>
- * <li>"sonar.verbose": if set to "true", more information is displayed in the log</li>
- * <li>"sonar.environment.information.key" and "sonar.environment.information.version": can be used to overwrite environment information (can also be
- * set via {@link #setEnvironmentInformation(String, String)} method)</li>
- * <li>... plus all the other Sonar and Sonar plugins properties.</li>
- * </ul>
- *
- * @since 1.1
- */
-public final class Runner {
-
- /**
- * Old property used to activate debug level for logging.
- *
- * @deprecated Replaced by sonar.verbose since 1.2
- */
- @Deprecated
- public static final String PROPERTY_OLD_DEBUG_MODE = "runner.debug";
-
- /**
- * Property used to increase logging information.
- *
- * @since 1.2
- */
- public static final String PROPERTY_VERBOSE = "sonar.verbose";
-
- /**
- * Property used to specify the working directory for the runner. May be a relative or absolute path.
- *
- * @since 1.4
- */
- public static final String PROPERTY_WORK_DIRECTORY = "sonar.working.directory";
-
- /**
- * Default value of the working directory.
- */
- public static final String DEF_VALUE_WORK_DIRECTORY = ".sonar";
-
- /**
- * Property used to specify the base directory of the project to analyse.
- *
- * @since 1.5
- */
- public static final String PROPERTY_SONAR_PROJECT_BASEDIR = "sonar.projectBaseDir";
-
- /**
- * Property used to specify the name of the tool that will run a Sonar analysis.
- *
- * @since 1.5
- */
- public static final String PROPERTY_ENVIRONMENT_INFORMATION_KEY = "sonar.environment.information.key";
-
- /**
- * Property used to specify the version of the tool that will run a Sonar analysis.
- *
- * @since 1.5
- */
- public static final String PROPERTY_ENVIRONMENT_INFORMATION_VERSION = "sonar.environment.information.version";
-
- /**
- * Property used to define cache location (default to ~/.sonar/cache).
- *
- * @since 2.1
- */
- String ENV_SONAR_USER_HOME = "SONAR_USER_HOME";
- String PROPERTY_SONAR_USER_HOME = "sonar.userHome";
-
- /**
- * Array of prefixes of versions of Sonar without support of this runner.
- */
- private static final String[] UNSUPPORTED_VERSIONS = {"1", "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "2.10"};
- private static final String[] UNSUPPORTED_VERSIONS_FOR_TASKS = {"1", "2", "3.0", "3.1", "3.2", "3.3", "3.4"};
-
- private static final String PROPERTY_SOURCE_ENCODING = "sonar.sourceEncoding";
-
- private String command;
- private File projectDir;
- private File sonarUserHomeDir;
- private File workDir;
- private String[] unmaskedPackages;
- private List<Object> containerExtensions = new ArrayList<Object>();
- private Properties globalProperties;
- private Properties projectProperties;
- private boolean isEncodingPlatformDependant;
- private SonarCache cache;
-
- private Runner(String command, Properties globalProperties, Properties projectProperties) {
- this.command = command;
- this.globalProperties = globalProperties;
- this.projectProperties = projectProperties;
- this.unmaskedPackages = new String[0];
- // set the default values for the Sonar Runner - they can be overriden with #setEnvironmentInformation
- this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_KEY, "Runner");
- this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_VERSION, Version.getVersion());
- // sets the encoding if not forced
- if (!globalProperties.containsKey(PROPERTY_SOURCE_ENCODING) && !projectProperties.containsKey(PROPERTY_SOURCE_ENCODING)) {
- isEncodingPlatformDependant = true;
- globalProperties.setProperty(PROPERTY_SOURCE_ENCODING, Charset.defaultCharset().name());
- }
- // and init the directories
- initDirs();
- // init the cache
- // Try to get Sonar user home from property
- cache = SonarCache.create(getSonarUserHomeDir()).build();
- }
-
- /**
- * Creates a Runner based only on the given properties.
- * @deprecated Use {@link Runner#create(String, Properties, Properties)}
- */
- @Deprecated
- public static Runner create(Properties props) {
- return create(null, new Properties(), props);
- }
-
- /**
- * Creates a Runner based only on the given properties.
- */
- public static Runner create(String command, Properties globalProperties, Properties projectProperties) {
- return new Runner(command, globalProperties, projectProperties);
- }
-
- /**
- * Creates a Runner based only on the properties and with the given base directory.
- * @deprecated Use {@link Runner#create(String, Properties, Properties, File)}
- */
- @Deprecated
- public static Runner create(Properties props, File basedir) {
- return create(null, new Properties(), props, basedir);
- }
-
- /**
- * Creates a Runner based only on the properties and with the given base directory.
- */
- public static Runner create(String command, Properties globalProperties, Properties projectProperties, File basedir) {
- projectProperties.put(PROPERTY_SONAR_PROJECT_BASEDIR, basedir.getAbsolutePath());
- return new Runner(command, globalProperties, projectProperties);
- }
-
- /**
- * Runs a Sonar analysis.
- */
- public void execute() {
- Bootstrapper bootstrapper = new Bootstrapper("SonarRunner/" + Version.getVersion(), getSonarServerURL(), getWorkDir(), getCache());
- checkSonarVersion(bootstrapper);
- delegateExecution(createClassLoader(bootstrapper));
- }
-
- public String getSonarServerURL() {
- return projectProperties.getProperty("sonar.host.url", globalProperties.getProperty("sonar.host.url", "http://localhost:9000"));
- }
-
- public SonarCache getCache() {
- return cache;
- }
-
- private void initDirs() {
- projectDir = initProjectDir();
- // project home exists: add its absolute path as "sonar.projectBaseDir" property
- projectProperties.put(PROPERTY_SONAR_PROJECT_BASEDIR, projectDir.getAbsolutePath());
- workDir = initWorkDir();
- sonarUserHomeDir = initSonarUserHomeDir();
- }
-
- private File initSonarUserHomeDir() {
- String sonarUserHome = globalProperties.getProperty(PROPERTY_SONAR_USER_HOME);
- if (StringUtils.isBlank(sonarUserHome)) {
- // Try to get Sonar user home from environment variable
- sonarUserHome = System.getenv(ENV_SONAR_USER_HOME);
- }
- if (StringUtils.isBlank(sonarUserHome)) {
- // Default Sonar user home
- sonarUserHome = System.getProperty("user.home") + File.separator + ".sonar";
- }
- return new File(sonarUserHome);
- }
-
- private File initProjectDir() {
- String path = projectProperties.getProperty(PROPERTY_SONAR_PROJECT_BASEDIR, ".");
- File dir = new File(path);
- if (!dir.isDirectory()) {
- throw new RunnerException("Project home must be an existing directory: " + path);
- }
- return dir;
- }
-
- private File initWorkDir() {
- File newWorkDir;
- String customWorkDir = projectProperties.getProperty(PROPERTY_WORK_DIRECTORY, globalProperties.getProperty(PROPERTY_WORK_DIRECTORY));
- if (customWorkDir == null || "".equals(customWorkDir.trim())) {
- newWorkDir = new File(getProjectDir(), DEF_VALUE_WORK_DIRECTORY);
- }
- else {
- newWorkDir = defineCustomizedWorkDir(new File(customWorkDir));
- }
- FileUtils.deleteQuietly(newWorkDir);
- return newWorkDir;
- }
-
- private File defineCustomizedWorkDir(File customWorkDir) {
- if (customWorkDir.isAbsolute()) {
- return customWorkDir;
- }
- return new File(getProjectDir(), customWorkDir.getPath());
- }
-
- /**
- * @return the project base directory
- */
- public File getProjectDir() {
- return projectDir;
- }
-
- /**
- * @return work directory, default is ".sonar" in project directory
- */
- public File getWorkDir() {
- return workDir;
- }
-
- /**
- * @return sonar user home directory
- */
- public File getSonarUserHomeDir() {
- return sonarUserHomeDir;
- }
-
- /**
- * @return the source code encoding that will be used by Sonar
- */
- public String getSourceCodeEncoding() {
- return projectProperties.getProperty(PROPERTY_SOURCE_ENCODING, globalProperties.getProperty(PROPERTY_SOURCE_ENCODING));
- }
-
- /**
- * @return true if the property "sonar.sourceEncoding" hasn't been forced
- */
- public boolean isEncodingPlatformDependant() {
- return isEncodingPlatformDependant;
- }
-
- public String getCommand() {
- return command;
- }
-
- /**
- * @return global properties, project properties and command-line properties
- */
- public Properties getProperties() {
- Properties props = new Properties();
- props.putAll(globalProperties);
- props.putAll(projectProperties);
- return props;
- }
-
- protected void checkSonarVersion(Bootstrapper bootstrapper) {
- String serverVersion = bootstrapper.getServerVersion();
- if (isUnsupportedVersion(serverVersion)) {
- throw new RunnerException("Sonar " + serverVersion
- + " is not supported. Please upgrade Sonar to version 2.11 or more.");
- }
- if (command != null && isUnsupportedVersionForTasks(serverVersion)) {
- throw new RunnerException("Sonar " + serverVersion
- + " doesn't support tasks. Please upgrade Sonar to version 3.5 or more.");
- }
- }
-
- private BootstrapClassLoader createClassLoader(Bootstrapper bootstrapper) {
- URL url = getJarPath();
- return bootstrapper.createClassLoader(
- // Add JAR with Sonar Runner - it's a Jar which contains this class
- new URL[] {url},
- getClass().getClassLoader(),
- unmaskedPackages);
- }
-
- /**
- * For unknown reasons <code>getClass().getProtectionDomain().getCodeSource().getLocation()</code> doesn't work under Ant 1.7.0.
- * So this is a workaround.
- *
- * @return Jar which contains this class
- */
- public static URL getJarPath() {
- String pathToClass = "/" + Runner.class.getName().replace('.', '/') + ".class";
- URL url = Runner.class.getResource(pathToClass);
- if (url != null) {
- String path = url.toString();
- String uri = null;
- if (path.startsWith("jar:file:")) {
- int bang = path.indexOf('!');
- uri = path.substring(4, bang);
- } else if (path.startsWith("file:")) {
- int tail = path.indexOf(pathToClass);
- uri = path.substring(0, tail);
- }
- if (uri != null) {
- try {
- return new URL(uri);
- } catch (MalformedURLException e) {
- }
- }
- }
- return null;
- }
-
- static boolean isUnsupportedVersion(String version) {
- return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS);
- }
-
- static boolean isUnsupportedVersionForTasks(String version) {
- return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS_FOR_TASKS);
- }
-
- private void delegateExecution(BootstrapClassLoader sonarClassLoader) {
- ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
- try {
- Thread.currentThread().setContextClassLoader(sonarClassLoader);
- Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.runner.internal.batch.Launcher");
- Constructor<?> constructor = launcherClass.getConstructor(String.class, Properties.class, Properties.class, List.class);
- Object launcher = constructor.newInstance(getCommand(), globalProperties, projectProperties, containerExtensions);
- Method method = launcherClass.getMethod("execute");
- method.invoke(launcher);
- } catch (InvocationTargetException e) {
- // Unwrap original exception
- throw new RunnerException("Unable to execute Sonar", e.getTargetException());
- } catch (Exception e) {
- // Catch all other exceptions, which relates to reflection
- throw new RunnerException("Unable to execute Sonar", e);
- } finally {
- Thread.currentThread().setContextClassLoader(oldContextClassLoader);
- }
- }
-
- /**
- * Allows to overwrite the environment information when Sonar Runner is embedded in a specific tool (for instance, with the Sonar Ant Task).
- *
- * @param key the key of the tool that embeds Sonar Runner
- * @param version the version of this tool
- */
- public void setEnvironmentInformation(String key, String version) {
- this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_KEY, key);
- this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_VERSION, version);
- }
-
- public void setUnmaskedPackages(String... unmaskedPackages) {
- this.unmaskedPackages = unmaskedPackages;
- }
-
- public void addContainerExtension(Object extension) {
- containerExtensions.add(extension);
- }
-
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java b/sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java
deleted file mode 100644
index cd8ae28..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-/**
- * This class is responsible for managing Sonar batch file cache. You can put file into cache and
- * later try to retrieve them. MD5 is used to differentiate files (name is not secure as files may come
- * from different Sonar servers and have same name but be actually different, and same for SNAPSHOTs).
- * Default location of cache is
- * @author Julien HENRY
- *
- */
-public class SonarCache {
-
- private static final int TEMP_FILE_ATTEMPTS = 10000;
-
- private File cacheLocation;
- /**
- * Temporary directory where files should be stored before be inserted in the cache.
- * Having a temporary close to the final location (read on same FS) will assure
- * the move will be atomic.
- */
- private File tmpDir;
-
- private SonarCache(File cacheLocation) {
- this.cacheLocation = cacheLocation;
- tmpDir = new File(cacheLocation, "tmp");
- if (!cacheLocation.exists()) {
- Logs.debug("Creating cache directory: " + cacheLocation.getAbsolutePath());
- try {
- FileUtils.forceMkdir(cacheLocation);
- } catch (IOException e) {
- throw new RuntimeException("Unable to create cache directory " + cacheLocation.getAbsolutePath(), e);
- }
- }
- }
-
- public static class Builder {
-
- private File sonarUserHomeLocation;
- private File cacheLocation;
-
- public Builder(File sonarUserHomeLocation) {
- this.sonarUserHomeLocation = sonarUserHomeLocation;
- }
-
- public Builder setCacheLocation(File cacheLocation) {
- this.cacheLocation = cacheLocation;
- return this;
- }
-
- public SonarCache build() {
- if (cacheLocation == null) {
- return new SonarCache(new File(sonarUserHomeLocation, "cache"));
- }
- else {
- return new SonarCache(cacheLocation);
- }
- }
- }
-
- public static Builder create(File sonarUserHomeLocation) {
- if (sonarUserHomeLocation == null) {
- throw new RunnerException("Sonar user home directory should not be null");
- }
- return new Builder(sonarUserHomeLocation);
- }
-
- /**
- * Move the given file inside the cache. Return the MD5 of the cached file.
- * @param sourceFile
- * @throws IOException
- */
- public String cacheFile(File sourceFile, String filename) throws IOException {
- Logs.debug("Trying to cache file " + sourceFile.getAbsolutePath() + " with filename " + filename);
- File tmpFileName = null;
- try {
- if (!sourceFile.getParentFile().equals(getTmpDir())) {
- // Provided file is not close to the cache so we will move it first in a temporary file (could be non atomic)
- tmpFileName = getTemporaryFile();
- FileUtils.moveFile(sourceFile, tmpFileName);
- }
- else {
- tmpFileName = sourceFile;
- }
- // Now compute the md5 to find the final destination
- String md5;
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(tmpFileName);
- md5 = DigestUtils.md5Hex(fis);
- } finally {
- IOUtils.closeQuietly(fis);
- }
- File finalDir = new File(cacheLocation, md5);
- File finalFileName = new File(finalDir, filename);
- // Try to create final destination folder
- FileUtils.forceMkdir(finalDir);
- // Now try to move the file from temporary folder to final location
- boolean rename = tmpFileName.renameTo(finalFileName);
- if (!rename) {
- // Check if the file was already in cache
- if (!finalFileName.exists()) {
- Logs.warn("Unable to rename " + tmpFileName.getAbsolutePath() + " to " + finalFileName.getAbsolutePath());
- Logs.warn("A copy/delete will be tempted but with no garantee of atomicity");
- FileUtils.moveFile(tmpFileName, finalFileName);
- }
- }
- Logs.debug("File cached at " + finalFileName.getAbsolutePath());
- return md5;
- } finally {
- FileUtils.deleteQuietly(tmpFileName);
- }
-
- }
-
- /**
- * Look for a file in the cache by its filename and md5 checksum. If the file is not
- * present then return null.
- */
- public File getFileFromCache(String filename, String md5) {
- File location = new File(new File(cacheLocation, md5), filename);
- Logs.debug("Looking for " + location.getAbsolutePath());
- if (location.exists()) {
- return location;
- }
- Logs.debug("No file found in the cache with name " + filename + " and checksum " + md5);
- return null;
- }
-
- /**
- * Return a temporary file that caller can use to store file content before
- * asking for caching it with {@link #cacheFile(File)}.
- * This is to avoid extra copy.
- * @return
- * @throws IOException
- */
- public File getTemporaryFile() throws IOException {
- return createTempFile(getTmpDir());
- }
-
- /**
- * Create a temporary file in the given directory.
- * @param baseDir
- * @return
- * @throws IOException
- */
- private static File createTempFile(File baseDir) throws IOException {
- String baseName = System.currentTimeMillis() + "-";
-
- for (int counter = 0; counter < TEMP_FILE_ATTEMPTS; counter++) {
- File tempFile = new File(baseDir, baseName + counter);
- if (tempFile.createNewFile()) {
- return tempFile;
- }
- }
- throw new IOException("Failed to create temporary file in " + baseDir.getAbsolutePath() + " within "
- + TEMP_FILE_ATTEMPTS + " attempts (tried "
- + baseName + "0 to " + baseName + (TEMP_FILE_ATTEMPTS - 1) + ')');
- }
-
- public File getTmpDir() {
- if (!tmpDir.exists()) {
- Logs.debug("Creating temporary cache directory: " + tmpDir.getAbsolutePath());
- try {
- FileUtils.forceMkdir(tmpDir);
- } catch (IOException e) {
- throw new RuntimeException("Unable to create temporary cache directory " + tmpDir.getAbsolutePath(), e);
- }
- }
- return tmpDir;
- }
-
- public File getCacheLocation() {
- return cacheLocation;
- }
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Stats.java b/sonar-runner-api/src/main/java/org/sonar/runner/Stats.java
deleted file mode 100644
index b5ce8d5..0000000
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Stats.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-class Stats {
- private long startTime;
-
- Stats() {
- }
-
- Stats start() {
- startTime = System.currentTimeMillis();
- return this;
- }
-
- Stats stop() {
- long stopTime = System.currentTimeMillis() - startTime;
- Logs.info("Total time: " + formatTime(stopTime));
-
- System.gc();
- Runtime r = Runtime.getRuntime();
- long mb = 1024L * 1024;
- Logs.info("Final Memory: " + (r.totalMemory() - r.freeMemory()) / mb + "M/" + r.totalMemory() / mb + "M");
-
- return this;
- }
-
- static String formatTime(long time) {
- long h = time / (60 * 60 * 1000);
- long m = (time - h * 60 * 60 * 1000) / (60 * 1000);
- long s = (time - h * 60 * 60 * 1000 - m * 60 * 1000) / 1000;
- long ms = time % 1000;
- final String format;
- if (h > 0) {
- format = "%1$d:%2$02d:%3$02d.%4$03ds";
- } else if (m > 0) {
- format = "%2$d:%3$02d.%4$03ds";
- } else {
- format = "%3$d.%4$03ds";
- }
- return String.format(format, h, m, s, ms);
- }
-}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java
new file mode 100644
index 0000000..89f9fbe
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java
@@ -0,0 +1,123 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import java.io.File;
+import java.util.*;
+
+class Command {
+ private final String executable;
+ private final List<String> arguments;
+ private final Map<String, String> env;
+ private final File directory;
+
+ private Command(Builder builder) {
+ this.executable = builder.executable;
+ this.arguments = Collections.unmodifiableList(builder.arguments);
+ this.env = Collections.unmodifiableMap(builder.env);
+ this.directory = builder.directory;
+ }
+
+ File directory() {
+ return directory;
+ }
+
+ String executable() {
+ return executable;
+ }
+
+ List<String> arguments() {
+ return arguments;
+ }
+
+ /**
+ * Environment variables that are propagated during command execution.
+ *
+ * @return a non-null and immutable map of variables
+ */
+ Map<String, String> envVariables() {
+ return env;
+ }
+
+ String[] toStrings() {
+ String[] strings = new String[1 + arguments.size()];
+ strings[0] = executable;
+ for (int index = 0; index < arguments.size(); index++) {
+ strings[index + 1] = arguments.get(index);
+ }
+ return strings;
+ }
+
+ @Override
+ public String toString() {
+ return Utils.join(toStrings(), " ");
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+ private String executable;
+ private final List<String> arguments = new ArrayList<String>();
+ private final Map<String, String> env = new HashMap<String, String>();
+ private File directory;
+
+ private Builder() {
+ }
+
+ Builder setExecutable(String s) {
+ this.executable = s;
+ return this;
+ }
+
+ Builder addArguments(String... args) {
+ arguments.addAll(Arrays.asList(args));
+ return this;
+ }
+
+ Builder addArguments(List<String> args) {
+ arguments.addAll(args);
+ return this;
+ }
+
+ Builder setEnvVariable(String key, String value) {
+ env.put(key, value);
+ return this;
+ }
+
+ Builder addEnvVariables(Map<String, String> map) {
+ env.putAll(map);
+ return this;
+ }
+
+ Builder setDirectory(File d) {
+ this.directory = d;
+ return this;
+ }
+
+ Command build() {
+ if (executable == null) {
+ throw new IllegalArgumentException("Command executable is not defined");
+ }
+ return new Command(this);
+ }
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java
new file mode 100644
index 0000000..ba068e3
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java
@@ -0,0 +1,30 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import javax.annotation.Nullable;
+
+class CommandException extends RuntimeException {
+
+ CommandException(String message, Command command, @Nullable Throwable throwable) {
+ super(message + " [command: " + command + "]", throwable);
+ }
+
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java
new file mode 100644
index 0000000..e3ac470
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java
@@ -0,0 +1,195 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Synchronously execute a native command line. It's much more limited than the Apache Commons Exec library.
+ * For example it does not allow to run asynchronously or to automatically quote command-line arguments.
+ *
+ * @since 2.7
+ */
+class CommandExecutor {
+
+ private static final CommandExecutor INSTANCE = new CommandExecutor();
+
+ private CommandExecutor() {
+ }
+
+ static CommandExecutor create() {
+ // stateless object, so a single singleton can be shared
+ return INSTANCE;
+ }
+
+ int execute(Command command, StreamConsumer stdOut, StreamConsumer stdErr, long timeoutMilliseconds) {
+ ExecutorService executorService = null;
+ Process process = null;
+ StreamGobbler outputGobbler = null;
+ StreamGobbler errorGobbler = null;
+ try {
+ ProcessBuilder builder = new ProcessBuilder(command.toStrings());
+ if (command.directory() != null) {
+ builder.directory(command.directory());
+ }
+ builder.environment().putAll(command.envVariables());
+ process = builder.start();
+
+ outputGobbler = new StreamGobbler(process.getInputStream(), stdOut);
+ errorGobbler = new StreamGobbler(process.getErrorStream(), stdErr);
+ outputGobbler.start();
+ errorGobbler.start();
+
+ final Process finalProcess = process;
+ executorService = Executors.newSingleThreadExecutor();
+ Future<Integer> ft = executorService.submit(new Callable<Integer>() {
+ public Integer call() throws Exception {
+ return finalProcess.waitFor();
+ }
+ });
+ int exitCode = ft.get(timeoutMilliseconds, TimeUnit.MILLISECONDS);
+ waitUntilFinish(outputGobbler);
+ waitUntilFinish(errorGobbler);
+ verifyGobbler(command, outputGobbler, "stdOut");
+ verifyGobbler(command, errorGobbler, "stdErr");
+ return exitCode;
+
+ } catch (TimeoutException te) {
+ process.destroy();
+ throw new CommandException("Timeout exceeded: " + timeoutMilliseconds + " ms", command, te);
+
+ } catch (CommandException e) {
+ throw e;
+
+ } catch (Exception e) {
+ throw new CommandException("Fail to execute command", command, e);
+
+ } finally {
+ waitUntilFinish(outputGobbler);
+ waitUntilFinish(errorGobbler);
+ closeStreams(process);
+
+ if (executorService != null) {
+ executorService.shutdown();
+ }
+ }
+ }
+
+ private void verifyGobbler(Command command, StreamGobbler gobbler, String type) {
+ if (gobbler.getException() != null) {
+ throw new CommandException("Error inside " + type + " stream", command, gobbler.getException());
+ }
+ }
+
+ /**
+ * Execute command and display error and output streams in log.
+ * Method {@link #execute(Command, StreamConsumer, StreamConsumer, long)} is preferable,
+ * when fine-grained control of output of command required.
+ */
+ int execute(Command command, long timeoutMilliseconds) {
+ return execute(command, new DefaultConsumer(), new DefaultConsumer(), timeoutMilliseconds);
+ }
+
+ private void closeStreams(Process process) {
+ if (process != null) {
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getOutputStream());
+ IOUtils.closeQuietly(process.getErrorStream());
+ }
+ }
+
+ private void waitUntilFinish(StreamGobbler thread) {
+ if (thread != null) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ System.err.println("InterruptedException while waiting finish of " + thread.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static class StreamGobbler extends Thread {
+ private final InputStream is;
+ private final StreamConsumer consumer;
+ private volatile Exception exception;
+
+ StreamGobbler(InputStream is, StreamConsumer consumer) {
+ super("ProcessStreamGobbler");
+ this.is = is;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void run() {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ consumeLine(line);
+ }
+ } catch (IOException ioe) {
+ exception = ioe;
+
+ } finally {
+ IOUtils.closeQuietly(br);
+ IOUtils.closeQuietly(isr);
+ }
+ }
+
+ private void consumeLine(String line) {
+ if (exception == null) {
+ try {
+ consumer.consumeLine(line);
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+ }
+
+
+ interface StreamConsumer {
+ void consumeLine(String line);
+ }
+
+ private static class DefaultConsumer implements StreamConsumer {
+ public void consumeLine(String line) {
+ System.out.println(line);
+ }
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Logs.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
index b7da73c..3357e9a 100644
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Logs.java
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
@@ -17,44 +17,40 @@
* 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.runner;
+package org.sonar.runner.api;
-final class Logs {
- private Logs() {
- }
+import org.sonar.runner.impl.BatchLauncher;
- private static boolean debugEnabled = false;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
- public static void setDebugEnabled(boolean debugEnabled) {
- Logs.debugEnabled = debugEnabled;
- }
+public class EmbeddedRunner extends Runner<EmbeddedRunner> {
+
+ private final List<Object> extensions = new ArrayList<Object>();
- public static boolean isDebugEnabled() {
- return debugEnabled;
+ private EmbeddedRunner() {
}
- static void debug(String message) {
- if (isDebugEnabled()) {
- System.out.println("DEBUG: " + message);
- }
+ public static EmbeddedRunner create() {
+ return new EmbeddedRunner();
}
- static void info(String message) {
- System.out.println("INFO: " + message);
+ public EmbeddedRunner setUnmaskedPackages(String... packages) {
+ return setProperty("sonarRunner.unmaskedPackages", Utils.join(packages, ","));
}
- static void warn(String message) {
- System.out.println("WARN: " + message);
+ public EmbeddedRunner addExtensions(Object... objects) {
+ extensions.addAll(Arrays.asList(objects));
+ return this;
}
- static void error(String message) {
- System.err.println("ERROR: " + message);
+ List<Object> extensions() {
+ return extensions;
}
- static void error(String message, Throwable t) {
- System.err.println("ERROR: " + message);
- if (t != null) {
- t.printStackTrace(System.err);
- }
+ @Override
+ public void doExecute() {
+ new BatchLauncher().execute(properties(), extensions);
}
}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
new file mode 100644
index 0000000..7a6c3cc
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
@@ -0,0 +1,118 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.apache.commons.io.IOUtils;
+import org.sonar.runner.impl.BatchLauncherMain;
+import org.sonar.runner.impl.JarExtractor;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class ForkedRunner extends Runner<ForkedRunner> {
+
+ private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
+
+ private final Command.Builder commandBuilder;
+ private final JarExtractor jarExtractor;
+
+ ForkedRunner(Command.Builder commandBuilder, JarExtractor jarExtractor) {
+ this.commandBuilder = commandBuilder;
+ this.jarExtractor = jarExtractor;
+ }
+
+ public static ForkedRunner create() {
+ Os os = new Os();
+ Command.Builder builder = Command.builder().setExecutable(os.usedJavaExe().getAbsolutePath());
+ return new ForkedRunner(builder, new JarExtractor());
+ }
+
+ @Override
+ public void doExecute() {
+ File propertiesFile = writeProperties();
+ File jarFile = extractJar();
+ fork(jarFile, propertiesFile);
+ }
+
+ private File extractJar() {
+ return jarExtractor.extract("sonar-runner-impl");
+ }
+
+ private void fork(File jarFile, File propertiesFile) {
+ // java -jar sonar-runner-impl.jar path/to/propertiesFile
+ Command command = commandBuilder
+ .addArguments("-cp", jarFile.getAbsolutePath())
+ .addArguments(BatchLauncherMain.class.getName())
+ .addArguments(propertiesFile.getAbsolutePath())
+ .build();
+ System.out.println("---------- execute: " + command);
+ int status = CommandExecutor.create().execute(command, ONE_DAY_IN_MILLISECONDS);
+ if (status != 0) {
+ throw new IllegalStateException("TODO");
+ }
+ }
+
+ private File writeProperties() {
+ OutputStream output = null;
+ try {
+ File file = File.createTempFile("sonar-project", ".properties");
+ output = new FileOutputStream(file);
+ properties().store(output, "Generated by sonar-runner");
+ return file;
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to export sonar-runner properties", e);
+
+ } finally {
+ IOUtils.closeQuietly(output);
+ }
+ }
+
+ public ForkedRunner setJavaCommand(String s) {
+ commandBuilder.setExecutable(s);
+ return this;
+ }
+
+ public ForkedRunner addJvmArgument(String... s) {
+ commandBuilder.addArguments(Arrays.asList(s));
+ return this;
+ }
+
+ public ForkedRunner addJvmArguments(List<String> args) {
+ commandBuilder.addArguments(args);
+ return this;
+ }
+
+ public ForkedRunner setJvmEnvVariable(String key, String value) {
+ commandBuilder.setEnvVariable(key, value);
+ return this;
+ }
+
+ public ForkedRunner addJvmEnvVariables(Map<String, String> map) {
+ commandBuilder.addEnvVariables(map);
+ return this;
+ }
+
+
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/RunnerException.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
index a676f57..da51361 100644
--- a/sonar-runner-api/src/main/java/org/sonar/runner/RunnerException.java
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
@@ -17,36 +17,24 @@
* 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.runner;
+package org.sonar.runner.api;
-/**
- * Exception thrown by the Sonar Runner when something bad happens.
- *
- * @since 1.2
- */
-public class RunnerException extends RuntimeException {
-
- private static final long serialVersionUID = 4810407777585753030L;
+import java.io.File;
- /**
- * See {@link RuntimeException}
- */
- public RunnerException(String message) {
- super(message);
+class Os {
+ boolean isWindows() {
+ return System.getProperty("os.name").contains("Windows");
}
- /**
- * See {@link RuntimeException}
- */
- public RunnerException(Throwable cause) {
- super(cause);
+ File usedJavaHome() {
+ return new File(System.getProperty("java.home"));
}
/**
- * See {@link RuntimeException}
+ * Path to the java executable used by this VM
*/
- public RunnerException(String message, Throwable cause) {
- super(message, cause);
+ File usedJavaExe() {
+ File bin = new File(usedJavaHome(), "bin");
+ return new File(bin, isWindows() ? "java.exe" : "java");
}
-
}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
new file mode 100644
index 0000000..079cdb9
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
@@ -0,0 +1,102 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.sonar.runner.impl.Constants;
+import org.sonar.runner.impl.Logs;
+
+import javax.annotation.Nullable;
+
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Properties;
+
+/**
+ * @since 2.2
+ */
+public abstract class Runner<T extends Runner> {
+
+ private final Properties properties = new Properties();
+
+ protected Runner() {
+ initProperties();
+ }
+
+ private void initProperties() {
+ // default values
+ properties.put(Constants.HOST_URL, "http://localhost:9000");
+ properties.put(Constants.TASK, "scan");
+ properties.put(Constants.PROP_APP, "SonarRunner");
+ properties.put(Constants.PROP_APP_VERSION, RunnerVersion.version());
+ }
+
+ public Properties properties() {
+ Properties clone = new Properties();
+ clone.putAll(properties);
+ return clone;
+ }
+
+ public T addProperties(Properties p) {
+ properties.putAll(p);
+ return (T) this;
+ }
+
+ public T setProperty(String key, String value) {
+ properties.setProperty(key, value);
+ return (T) this;
+ }
+
+ public String property(String key, @Nullable String defaultValue) {
+ return properties.getProperty(key, defaultValue);
+ }
+
+ public T setApp(String app, String version) {
+ setProperty(Constants.PROP_APP, app);
+ setProperty(Constants.PROP_APP_VERSION, version);
+ return (T) this;
+ }
+
+ public String app() {
+ return property(Constants.PROP_APP, null);
+ }
+
+ public String appVersion() {
+ return property(Constants.PROP_APP_VERSION, null);
+ }
+
+ public void execute() {
+ initSourceEncoding();
+ doExecute();
+ }
+
+ private void initSourceEncoding() {
+ String sourceEncoding = property(Constants.SOURCE_ENCODING, null);
+ boolean platformDependent = false;
+ if (sourceEncoding == null || sourceEncoding.equals("")) {
+ sourceEncoding = Charset.defaultCharset().name();
+ platformDependent = true;
+ setProperty(Constants.SOURCE_ENCODING, sourceEncoding);
+ }
+ Logs.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + sourceEncoding + "\""
+ + (platformDependent ? " (analysis is platform dependent)" : ""));
+ }
+
+ protected abstract void doExecute();
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/Version.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
index e0447c0..d15f72f 100644
--- a/sonar-runner-api/src/main/java/org/sonar/runner/Version.java
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
@@ -17,38 +17,26 @@
* 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.runner;
+package org.sonar.runner.api;
-import org.apache.commons.io.IOUtils;
+import java.util.Scanner;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-public enum Version {
+public enum RunnerVersion {
INSTANCE;
- private static final String PROPERTIES_PATH = "/org/sonar/runner/version.txt";
private String version;
- public static String getVersion() {
+ public static String version() {
return INSTANCE.version;
}
- private Version() {
- InputStream input = getClass().getResourceAsStream(PROPERTIES_PATH);
+ private RunnerVersion() {
+ Scanner scanner = new Scanner(getClass().getResourceAsStream("/org/sonar/runner/api/version.txt"), "UTF-8");
try {
- Properties properties = new Properties();
- properties.load(input);
- this.version = properties.getProperty("version");
-
- } catch (IOException e) {
- // Can not load the version
- this.version = "";
-
+ this.version = scanner.next();
} finally {
- IOUtils.closeQuietly(input);
+ scanner.close();
}
}
}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/VersionUtils.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java
index 3e19df8..9be34b8 100644
--- a/sonar-runner-api/src/main/java/org/sonar/runner/VersionUtils.java
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java
@@ -17,29 +17,25 @@
* 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.runner;
+package org.sonar.runner.api;
-/**
- * Internal class used only by the Runner.
- * This class should not be used by Sonar Runner consumers.
- */
-final class VersionUtils {
-
- private VersionUtils() {
- // only static methods
- }
+import java.util.Arrays;
+import java.util.Iterator;
- static boolean isUnsupportedVersion(String version, String[] unsuportedVersions) {
- for (String unsupportedVersion : unsuportedVersions) {
- if (isVersion(version, unsupportedVersion)) {
- return true;
+class Utils {
+ /**
+ * Similar to org.apache.commons.lang.StringUtils#join()
+ */
+ static String join(String[] array, String delimiter) {
+ StringBuilder sb = new StringBuilder();
+ Iterator it = Arrays.asList(array).iterator();
+ while (it.hasNext()) {
+ sb.append(it.next());
+ if (!it.hasNext()) {
+ break;
}
+ sb.append(delimiter);
}
- return false;
+ return sb.toString();
}
-
- private static boolean isVersion(String version, String prefix) {
- return version.startsWith(prefix + ".") || version.equals(prefix);
- }
-
}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/package-info.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java
index e837d88..a362ee1 100644
--- a/sonar-runner-api/src/main/java/org/sonar/runner/package-info.java
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java
@@ -17,7 +17,7 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
-/**
- * API package of the Sonar Runner.
- */
-package org.sonar.runner; \ No newline at end of file
+@ParametersAreNonnullByDefault
+package org.sonar.runner.api;
+
+import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file
diff --git a/sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt b/sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt
new file mode 100644
index 0000000..f2ab45c
--- /dev/null
+++ b/sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt
@@ -0,0 +1 @@
+${project.version} \ No newline at end of file
diff --git a/sonar-runner-api/src/main/resources/org/sonar/runner/version.txt b/sonar-runner-api/src/main/resources/org/sonar/runner/version.txt
deleted file mode 100644
index defbd48..0000000
--- a/sonar-runner-api/src/main/resources/org/sonar/runner/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-version=${project.version}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapClassLoaderTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapClassLoaderTest.java
deleted file mode 100644
index 7917d2d..0000000
--- a/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapClassLoaderTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class BootstrapClassLoaderTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void should_restrict_loading_from_parent() throws Exception {
- BootstrapClassLoader classLoader = new BootstrapClassLoader(getClass().getClassLoader(), "org.apache.ant");
- assertThat(classLoader.canLoadFromParent("org.sonar.runner.internal.batch.Launcher")).isFalse();
- assertThat(classLoader.canLoadFromParent("org.sonar.runner.Runner")).isTrue();
- assertThat(classLoader.canLoadFromParent("org.objectweb.asm.ClassVisitor")).isFalse();
- assertThat(classLoader.canLoadFromParent("org.apache.ant.project.Project")).isTrue();
- }
-
- @Test
- public void should_use_isolated_system_classloader_when_parent_is_excluded() throws ClassNotFoundException {
- thrown.expect(ClassNotFoundException.class);
- thrown.expectMessage("org.junit.Test");
- ClassLoader parent = getClass().getClassLoader();
- BootstrapClassLoader classLoader = new BootstrapClassLoader(parent);
-
- // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM
- assertThat(classLoader.loadClass("java.lang.String", false)).isNotNull();
- classLoader.loadClass("org.junit.Test", false);
- }
-
- @Test
- public void should_find_in_parent_when_matches_unmasked_packages() throws ClassNotFoundException {
- ClassLoader parent = getClass().getClassLoader();
- BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, "org.junit");
-
- // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM
- assertThat(classLoader.loadClass("org.junit.Test", false)).isNotNull();
- }
-}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java
deleted file mode 100644
index 37a5425..0000000
--- a/sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.apache.commons.io.IOUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class BootstrapperTest {
-
- @Rule
- public TemporaryFolder tempFolder = new TemporaryFolder();
-
- @Test
- public void shouldRemoveLastUrlSlash() {
- Bootstrapper bootstrapper = new Bootstrapper("", "http://test/", new File("target/tmp"), null);
- assertThat(bootstrapper.getServerUrl()).isEqualTo("http://test");
- }
-
- @Test(expected = Exception.class)
- public void shouldFailIfCanNotConnectServer() {
- Bootstrapper bootstrapper = new Bootstrapper("", "http://unknown.foo", new File("target/tmp"), null);
- bootstrapper.getServerVersion();
- }
-
- @Test
- public void shouldReturnUserAgent() {
- Bootstrapper bootstrapper = new Bootstrapper("test/0.1", "http://unknown.foo", new File("target/tmp"), null);
- String userAgent = bootstrapper.getUserAgent();
-
- assertThat(userAgent.length()).isGreaterThan(0);
- assertThat(userAgent).startsWith("sonar-bootstrapper/");
- assertThat(userAgent).endsWith(" test/0.1");
- }
-
- @Test
- public void shouldReturnValidVersion() {
- Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), null) {
- @Override
- String remoteContent(String path) throws IOException {
- return "2.6";
- }
- };
- assertThat(bootstrapper.getServerVersion()).isEqualTo("2.6");
- }
-
- @Test
- public void shouldParseEncodingFromContentType() {
- assertThat(Bootstrapper.getCharsetFromContentType("text/html; charset=EUC-JP")).isEqualTo("EUC-JP");
- assertThat(Bootstrapper.getCharsetFromContentType("text/html")).isNull();
- }
-
- @Test
- public void shouldCheckVersionForCache() {
- assertThat(Bootstrapper.isUnsupportedVersionForCache("1.0")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.0")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.1")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.2")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.3")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.4")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.4.1")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.5")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("2.11")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.0")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.1")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.2")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.3")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.4")).isTrue();
- assertThat(Bootstrapper.isUnsupportedVersionForCache("3.5")).isFalse();
- }
-
- @Test
- public void shouldCacheWhenNecessary() throws Exception {
- File sonarUserHome = tempFolder.newFolder();
- SonarCache cache = SonarCache.create(sonarUserHome).build();
- final MockedConnectionFactory connections = new MockedConnectionFactory("http://test");
- connections.register("/api/server/version", "3.5");
- connections.register("/batch_bootstrap/index", "foo.jar|922afef30ca31573d7131347d01b76c4\nbar.jar|69155f65900fbabbf21e28abb33dd06a");
- connections.register("/batch/foo.jar", "fakecontent1");
- connections.register("/batch/bar.jar", "fakecontent2");
- Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), cache) {
- @Override
- HttpURLConnection newHttpConnection(URL url) throws IOException {
- return connections.get(url);
- }
- };
- bootstrapper.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
- assertThat(new File(new File(cache.getCacheLocation(), "922afef30ca31573d7131347d01b76c4"), "foo.jar")).exists();
- assertThat(new File(new File(cache.getCacheLocation(), "69155f65900fbabbf21e28abb33dd06a"), "bar.jar")).exists();
-
- // Should not download during the second execution
- final MockedConnectionFactory connections2 = new MockedConnectionFactory("http://test");
- connections2.register("/api/server/version", "3.5");
- connections2.register("/batch_bootstrap/index", "foo.jar|922afef30ca31573d7131347d01b76c4\nbar.jar|69155f65900fbabbf21e28abb33dd06a");
- Bootstrapper bootstrapper2 = new Bootstrapper("", "http://test", new File("target/tmp"), cache) {
- @Override
- HttpURLConnection newHttpConnection(URL url) throws IOException {
- return connections2.get(url);
- }
- };
- bootstrapper2.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
- }
-
- @Test
- public void shouldDownloadFromOldURL() throws Exception {
- File sonarUserHome = tempFolder.newFolder();
- final MockedConnectionFactory connections = new MockedConnectionFactory("http://test");
- connections.register("/api/server/version", "3.4");
- connections.register("/batch/", "foo.jar,bar.jar");
- connections.register("/batch/foo.jar", "fakecontent1");
- connections.register("/batch/bar.jar", "fakecontent2");
- Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), SonarCache.create(sonarUserHome).build()) {
- @Override
- HttpURLConnection newHttpConnection(URL url) throws IOException {
- return connections.get(url);
- }
- };
- bootstrapper.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
- verify(connections.get("/batch/foo.jar")).getInputStream();
- verify(connections.get("/batch/bar.jar")).getInputStream();
- }
-
- private class MockedConnectionFactory {
- private Map<URL, HttpURLConnection> mockedConnections = new HashMap<URL, HttpURLConnection>();
- private String serverUrl;
-
- public MockedConnectionFactory(String serverUrl) {
- this.serverUrl = serverUrl;
- }
-
- public void register(String path, String content) throws Exception {
- HttpURLConnection mockConnection = mock(HttpURLConnection.class);
- when(mockConnection.getInputStream()).thenReturn(IOUtils.toInputStream(content));
- when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
- mockedConnections.put(new URL(serverUrl + path), mockConnection);
- }
-
- public HttpURLConnection get(URL url) {
- return mockedConnections.get(url);
- }
-
- public HttpURLConnection get(String path) throws MalformedURLException {
- return mockedConnections.get(new URL(serverUrl + path));
- }
-
- }
-}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java
deleted file mode 100644
index 1131300..0000000
--- a/sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class LogsTest {
-
- private PrintStream oldSysout;
- private PrintStream oldSyserr;
-
- private ByteArrayOutputStream baosOut;
- private ByteArrayOutputStream baosErr;
-
- @Before
- public void prepare() {
- oldSysout = System.out;
- oldSyserr = System.err;
- baosOut = new ByteArrayOutputStream();
- System.setOut(new PrintStream(baosOut));
- baosErr = new ByteArrayOutputStream();
- System.setErr(new PrintStream(baosErr));
- }
-
- @After
- public void restore() {
- System.setOut(oldSysout);
- System.setErr(oldSyserr);
- }
-
- @Test
- public void shouldLogInfo() {
- Logs.info("info");
- assertThat(baosOut.toString()).contains("INFO: info");
- assertThat(baosErr.toString()).isEmpty();
- }
-
- @Test
- public void shouldLogError() {
- Logs.error("error");
- assertThat(baosOut.toString()).isEmpty();
- assertThat(baosErr.toString()).contains("ERROR: error");
- }
-
- @Test
- public void shouldLogErrorWithoutThrowable() {
- Logs.error("error", null);
- assertThat(baosOut.toString()).isEmpty();
- assertThat(baosErr.toString()).contains("ERROR: error");
- }
-
- @Test
- public void shouldLogErrorWithThrowable() {
- Logs.error("error", new RuntimeException());
- assertThat(baosOut.toString()).isEmpty();
- assertThat(baosErr.toString()).contains("ERROR: error").contains("RuntimeException");
- }
-
-}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java
deleted file mode 100644
index de7f17c..0000000
--- a/sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.util.Properties;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class MainTest {
-
- private Main main;
-
- @Before
- public void prepare() {
- main = new Main();
- }
-
- @Test
- public void shouldParseEmptyArguments() {
- Properties props = main.parseArguments(new String[] {});
- assertThat(props).isEmpty();
- }
-
- @Test
- public void shouldParseArguments() {
- Properties props = main.parseArguments(new String[] {"-D", "foo=bar", "--define", "hello=world", "-Dboolean"});
- assertThat(props).hasSize(3);
- assertThat(props.getProperty("foo")).isEqualTo("bar");
- assertThat(props.getProperty("hello")).isEqualTo("world");
- assertThat(props.getProperty("boolean")).isEqualTo("true");
- }
-
- @Test
- public void shouldParseCommand() {
-
- Properties props = main.parseArguments(new String[] {"cmd", "-D", "foo=bar", "--define", "hello=world", "-Dboolean"});
- assertThat(main.command).isEqualTo("cmd");
- assertThat(props).hasSize(3);
- }
-
- @Test
- public void shouldEnableDebugMode() {
- Properties props = main.parseArguments(new String[] {"-X"});
- assertThat(main.debugMode).isTrue();
- assertThat(main.displayStackTrace).isTrue();
- assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isEqualTo("true");
- }
-
- @Test
- public void shouldEnableError() {
- Properties props = main.parseArguments(new String[] {"-e"});
- assertThat(main.debugMode).isFalse();
- assertThat(main.displayStackTrace).isTrue();
- assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isNull();
- }
-
- @Test
- public void shouldDisableDebugModeAndStackByDefault() {
- Properties props = main.parseArguments(new String[] {});
- assertThat(main.debugMode).isFalse();
- assertThat(main.displayStackTrace).isFalse();
- assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isNull();
- }
-
- @Test
- public void shouldLoadRunnerSettingsByHome() throws Exception {
- File home = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/").toURI());
- Properties args = new Properties();
- args.setProperty("runner.home", home.getCanonicalPath());
-
- Properties props = new Main().loadRunnerConfiguration(args);
-
- assertThat(props.getProperty("sonar.host.url")).isEqualTo("http://moon/sonar");
- }
-
- @Test
- public void shouldNotFailIfNoHome() throws Exception {
- Properties args = new Properties();
- Properties props = new Main().loadRunnerConfiguration(args);
-
- assertThat(props).isEmpty();
- }
-
- @Test
- public void shouldLoadRunnerSettingsByDirectPath() throws Exception {
- File settings = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties").toURI());
- Properties args = new Properties();
- args.setProperty("runner.settings", settings.getCanonicalPath());
- Properties props = new Main().loadRunnerConfiguration(args);
-
- assertThat(props.getProperty("sonar.host.url")).isEqualTo("http://other/sonar");
- }
-
- @Test
- public void shouldLoadCompleteConfiguration() throws Exception {
- File runnerHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner").toURI());
- File projectHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project").toURI());
- Main main = new Main();
- Properties args = main.parseArguments(new String[] {
- "-D", "runner.home=" + runnerHome.getCanonicalPath(),
- "-D", "project.home=" + projectHome.getCanonicalPath()
- });
- main.loadProperties(args);
-
- assertThat(main.projectProperties.getProperty("project.prop")).isEqualTo("foo");
- assertThat(main.projectProperties.getProperty("overridden.prop")).isEqualTo("project scope");
- assertThat(main.globalProperties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar");
- }
-
-}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java
deleted file mode 100644
index 545565f..0000000
--- a/sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Sonar Runner - API
- * Copyright (C) 2011 SonarSource
- * 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.runner;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.nio.charset.Charset;
-import java.util.Properties;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class RunnerTest {
-
- @Rule
- public TemporaryFolder tempFolder = new TemporaryFolder();
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void shouldHaveDefaultEncodingIfNotForce() {
- Runner runner = Runner.create(new Properties());
- assertThat(runner.getSourceCodeEncoding()).isEqualTo(Charset.defaultCharset().name());
- assertThat(runner.isEncodingPlatformDependant()).isTrue();
- }
-
- @Test
- public void shouldKeepEncodingIfSpecified() {
- Properties props = new Properties();
- // Yeah, windows charset!
- props.setProperty("sonar.sourceEncoding", "cp1252");
- Runner runner = Runner.create(props);
- assertThat(runner.getSourceCodeEncoding()).isEqualTo("cp1252");
- assertThat(runner.isEncodingPlatformDependant()).isFalse();
- }
-
- @Test
- public void shouldHaveDefaultEnvironmentInformationValues() {
- Runner runner = Runner.create(new Properties());
- assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_KEY)).isEqualTo("Runner");
- assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).contains(".");
- assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).doesNotContain("$");
- }
-
- @Test
- public void shouldOverwriteDefaultEnvironmentInformationValues() {
- Runner runner = Runner.create(new Properties());
- runner.setEnvironmentInformation("Ant", "1.2.3");
- assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_KEY)).isEqualTo("Ant");
- assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).isEqualTo("1.2.3");
- }
-
- @Test
- public void shouldCheckVersion() {
- assertThat(Runner.isUnsupportedVersion("1.0")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.0")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.1")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.2")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.3")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.4")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.4.1")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.5")).isTrue();
- assertThat(Runner.isUnsupportedVersion("2.11")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.0")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.1")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.2")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.3")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.4")).isFalse();
- assertThat(Runner.isUnsupportedVersion("3.5")).isFalse();
- }
-
- @Test
- public void shouldCheckVersionForTasks() {
- assertThat(Runner.isUnsupportedVersionForTasks("1.0")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.0")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.1")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.2")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.3")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.4")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.4.1")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.5")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("2.11")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.0")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.1")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.2")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.3")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.4")).isTrue();
- assertThat(Runner.isUnsupportedVersionForTasks("3.5")).isFalse();
- }
-
- @Test
- public void shouldGetServerUrl() {
- Properties properties = new Properties();
- Runner runner = Runner.create(properties);
- assertThat(runner.getSonarServerURL()).isEqualTo("http://localhost:9000");
- properties.setProperty("sonar.host.url", "foo");
- assertThat(runner.getSonarServerURL()).isEqualTo("foo");
- }
-
- @Test
- public void shouldInitDirs() throws Exception {
- Properties props = new Properties();
- File home = tempFolder.newFolder("shouldInitDirs").getCanonicalFile();
- props.setProperty(Runner.PROPERTY_SONAR_PROJECT_BASEDIR, home.getCanonicalPath());
- Runner runner = Runner.create(props);
- assertThat(runner.getProperties().get(Runner.PROPERTY_SONAR_PROJECT_BASEDIR)).isEqualTo(home.getCanonicalPath());
-
- assertThat(runner.getProjectDir().getCanonicalFile()).isEqualTo(home);
- assertThat(runner.getWorkDir().getCanonicalFile()).isEqualTo(new File(home, ".sonar"));
- }
-
- @Test
- public void shouldInitProjectDirWithCurrentDir() throws Exception {
- Runner runner = Runner.create(new Properties());
-
- assertThat(runner.getProjectDir().isDirectory()).isTrue();
- assertThat(runner.getProjectDir().exists()).isTrue();
- }
-
- @Test
- public void shouldSetValidBaseDirOnConstructor() {
- File baseDir = tempFolder.newFolder("shouldInitDirs");
- Runner runner = Runner.create(new Properties(), baseDir);
- assertThat(runner.getProjectDir()).isEqualTo(baseDir);
- }
-
- @Test
- public void shouldFailIfBaseDirDoesNotExist() {
- File fakeBasedir = new File("fake");
-
- thrown.expect(RunnerException.class);
- thrown.expectMessage("Project home must be an existing directory: " + fakeBasedir.getAbsolutePath());
-
- Runner.create(new Properties(), fakeBasedir);
- }
-
- @Test
- public void shouldSpecifyWorkingDirectory() {
- Properties properties = new Properties();
- Runner runner = Runner.create(properties);
- assertThat(runner.getWorkDir()).isEqualTo(new File(".", ".sonar"));
-
- // empty string
- properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, " ");
- runner = Runner.create(properties);
- assertThat(runner.getWorkDir()).isEqualTo(new File(".", ".sonar").getAbsoluteFile());
-
- // real relative path
- properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, "temp-dir");
- runner = Runner.create(properties);
- assertThat(runner.getWorkDir()).isEqualTo(new File(".", "temp-dir").getAbsoluteFile());
-
- // real absolute path
- properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, new File("target", "temp-dir2").getAbsolutePath());
- runner = Runner.create(properties);
- assertThat(runner.getWorkDir()).isEqualTo(new File("target", "temp-dir2").getAbsoluteFile());
- }
-
- @Test
- public void shouldDeleteWorkingDirectory() {
- Properties properties = new Properties();
- File workDir = new File("target", "temp-dir-should-be-deleted");
- workDir.mkdirs();
- assertThat(workDir.exists()).isTrue();
- // real absolute path
- properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, workDir.getAbsolutePath());
- Runner.create(properties);
- assertThat(workDir.exists()).isFalse();
- }
-
- @Test
- public void shouldCheckSonarVersion() {
- Properties properties = new Properties();
- Runner runner = Runner.create(properties);
- Bootstrapper bootstrapper = mock(Bootstrapper.class);
-
- // nothing happens, OK
- when(bootstrapper.getServerVersion()).thenReturn("3.0");
- runner.checkSonarVersion(bootstrapper);
-
- // but fails with older versions
- when(bootstrapper.getServerVersion()).thenReturn("2.1");
- thrown.expect(RunnerException.class);
- thrown.expectMessage("Sonar 2.1 is not supported. Please upgrade Sonar to version 2.11 or more.");
- runner.checkSonarVersion(bootstrapper);
- }
-
- @Test
- public void shouldCheckSonarVersionForTasks() {
- Properties properties = new Properties();
- Runner runner = Runner.create("foo-cmd", properties, properties);
- Bootstrapper bootstrapper = mock(Bootstrapper.class);
-
- // nothing happens, OK
- when(bootstrapper.getServerVersion()).thenReturn("3.5");
- runner.checkSonarVersion(bootstrapper);
-
- // but fails with older versions
- when(bootstrapper.getServerVersion()).thenReturn("3.4");
- thrown.expect(RunnerException.class);
- thrown.expectMessage("Sonar 3.4 doesn't support tasks. Please upgrade Sonar to version 3.5 or more.");
- runner.checkSonarVersion(bootstrapper);
- }
-}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java
new file mode 100644
index 0000000..d63a6a3
--- /dev/null
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java
@@ -0,0 +1,157 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class CommandExecutorTest {
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Rule
+ public TestName testName = new TestName();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private File workDir;
+
+ @Before
+ public void setUp() throws IOException {
+ workDir = tempFolder.newFolder(testName.getMethodName());
+ }
+
+ @Test
+ public void should_consume_StdOut_and_StdErr() throws Exception {
+ final StringBuilder stdOutBuilder = new StringBuilder();
+ CommandExecutor.StreamConsumer stdOutConsumer = new CommandExecutor.StreamConsumer() {
+ public void consumeLine(String line) {
+ stdOutBuilder.append(line).append(System.getProperty("line.separator"));
+ }
+ };
+ final StringBuilder stdErrBuilder = new StringBuilder();
+ CommandExecutor.StreamConsumer stdErrConsumer = new CommandExecutor.StreamConsumer() {
+ public void consumeLine(String line) {
+ stdErrBuilder.append(line).append(System.getProperty("line.separator"));
+ }
+ };
+ Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
+ int exitCode = CommandExecutor.create().execute(command, stdOutConsumer, stdErrConsumer, 1000L);
+ assertThat(exitCode).isEqualTo(0);
+
+ String stdOut = stdOutBuilder.toString();
+ String stdErr = stdErrBuilder.toString();
+ assertThat(stdOut).contains("stdOut: first line");
+ assertThat(stdOut).contains("stdOut: second line");
+ assertThat(stdErr).contains("stdErr: first line");
+ assertThat(stdErr).contains("stdErr: second line");
+ }
+
+ @Test
+ public void stdOut_consumer_can_throw_exception() throws Exception {
+ Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
+ thrown.expect(CommandException.class);
+ thrown.expectMessage("Error inside stdOut stream");
+ CommandExecutor.create().execute(command, BAD_CONSUMER, NOP_CONSUMER, 1000L);
+ }
+
+ @Test
+ public void stdErr_consumer_can_throw_exception() throws Exception {
+ Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
+ thrown.expect(CommandException.class);
+ thrown.expectMessage("Error inside stdErr stream");
+ CommandExecutor.create().execute(command, NOP_CONSUMER, BAD_CONSUMER, 1000L);
+ }
+
+ private static final CommandExecutor.StreamConsumer NOP_CONSUMER = new CommandExecutor.StreamConsumer() {
+ public void consumeLine(String line) {
+ // nop
+ }
+ };
+
+ private static final CommandExecutor.StreamConsumer BAD_CONSUMER = new CommandExecutor.StreamConsumer() {
+ public void consumeLine(String line) {
+ throw new RuntimeException();
+ }
+ };
+
+ @Test
+ public void should_use_working_directory_to_store_argument_and_environment_variable() throws Exception {
+ Command command = Command.builder()
+ .setDirectory(workDir)
+ .setExecutable(getScript("echo"))
+ .addArguments("1")
+ .setEnvVariable("ENVVAR", "2")
+ .build();
+ int exitCode = CommandExecutor.create().execute(command, 1000L);
+ assertThat(exitCode).isEqualTo(0);
+ File logFile = new File(workDir, "echo.log");
+ assertThat(logFile).exists();
+ String log = FileUtils.readFileToString(logFile);
+ assertThat(log).contains(workDir.getAbsolutePath());
+ assertThat(log).contains("Parameter: 1");
+ assertThat(log).contains("Environment variable: 2");
+ }
+
+ @Test
+ public void should_stop_after_timeout() throws IOException {
+ String executable = getScript("forever");
+ long start = System.currentTimeMillis();
+ try {
+ CommandExecutor.create().execute(Command.builder().setExecutable(executable).setDirectory(workDir).build(), 300L);
+ fail();
+ } catch (CommandException e) {
+ long duration = System.currentTimeMillis() - start;
+ // should test >= 300 but it strangly fails during build on windows.
+ // The timeout is raised after 297ms (??)
+ assertThat(duration).as(e.getMessage()).isGreaterThan(290L);
+ }
+ }
+
+ @Test
+ public void should_fail_if_script_not_found() {
+ thrown.expect(CommandException.class);
+ CommandExecutor.create().execute(Command.builder().setExecutable("notfound").setDirectory(workDir).build(), 1000L);
+ }
+
+ private static String getScript(String name) throws IOException {
+ String filename;
+ if (new Os().isWindows()) {
+ filename = name + ".bat";
+ } else {
+ filename = name + ".sh";
+ }
+ return new File("src/test/scripts/" + filename).getCanonicalPath();
+ }
+
+}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java
new file mode 100644
index 0000000..259d1c6
--- /dev/null
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java
@@ -0,0 +1,75 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.fest.assertions.MapAssert.entry;
+
+public class CommandTest {
+ @Test
+ public void test_simple_build() throws Exception {
+ Command command = Command.builder().setExecutable("java").build();
+ assertThat(command.executable()).isEqualTo("java");
+ assertThat(command.envVariables()).isEmpty();
+ assertThat(command.arguments()).isEmpty();
+ assertThat(command.toStrings()).containsOnly("java");
+ assertThat(command.toString()).isEqualTo("java");
+ }
+
+ @Test
+ public void test_arguments_and_env_variables() throws Exception {
+ Map<String, String> env = new HashMap<String, String>();
+ env.put("USER_HOME", "/user");
+
+ Command command = Command.builder()
+ .setExecutable("java")
+ .addArguments("-Dfoo=bar", "-Djava.io.tmpdir=/tmp")
+ .addArguments(Arrays.asList("-Xmx512m"))
+ .setEnvVariable("JAVA_HOME", "/path/to/jdk")
+ .addEnvVariables(env)
+ .build();
+
+ assertThat(command.executable()).isEqualTo("java");
+ assertThat(command.envVariables()).hasSize(2).includes(
+ entry("JAVA_HOME", "/path/to/jdk"),
+ entry("USER_HOME", "/user")
+ );
+ assertThat(command.arguments()).containsSequence("-Dfoo=bar", "-Djava.io.tmpdir=/tmp", "-Xmx512m");
+ assertThat(command.toStrings()).containsOnly("java", "-Dfoo=bar", "-Djava.io.tmpdir=/tmp", "-Xmx512m");
+ assertThat(command.toString()).isEqualTo("java -Dfoo=bar -Djava.io.tmpdir=/tmp -Xmx512m");
+ }
+
+ @Test
+ public void executable_should_be_required() {
+ try {
+ Command.builder().build();
+ fail();
+ } catch (IllegalArgumentException e) {
+ // success
+ }
+ }
+}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java
new file mode 100644
index 0000000..d3d2ca2
--- /dev/null
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java
@@ -0,0 +1,54 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.junit.Test;
+import org.sonar.runner.impl.Constants;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class EmbeddedRunnerTest {
+ @Test
+ public void should_create() {
+ assertThat(EmbeddedRunner.create()).isNotNull().isInstanceOf(EmbeddedRunner.class);
+ }
+
+ @Test
+ public void should_set_unmasked_packages() {
+ EmbeddedRunner runner = EmbeddedRunner.create();
+ assertThat(runner.property(Constants.UNMASKED_PACKAGES, null)).isNull();
+
+ runner = EmbeddedRunner.create().setUnmaskedPackages("org.apache.ant", "org.ant");
+ assertThat(runner.property(Constants.UNMASKED_PACKAGES, null)).isEqualTo("org.apache.ant,org.ant");
+ }
+
+ @Test
+ public void should_add_extensions() {
+ EmbeddedRunner runner = EmbeddedRunner.create();
+ assertThat(runner.extensions()).isEmpty();
+
+ FakeExtension fakeExtension = new FakeExtension();
+ runner.addExtensions(fakeExtension);
+ assertThat(runner.extensions()).containsExactly(fakeExtension);
+ }
+
+ static class FakeExtension {
+ }
+}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/StatsTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java
index 7ea5d84..5c8767b 100644
--- a/sonar-runner-api/src/test/java/org/sonar/runner/StatsTest.java
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java
@@ -17,26 +17,15 @@
* 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.runner;
+package org.sonar.runner.api;
import org.junit.Test;
import static org.fest.assertions.Assertions.assertThat;
-
-public class StatsTest {
-
- @Test
- public void shouldPrintStats() {
- new Stats().start().stop();
- //TODO mock Logs
- }
-
+public class ForkedRunnerTest {
@Test
- public void shouldFormatTime() {
- assertThat(Stats.formatTime(1 * 60 * 60 * 1000 + 2 * 60 * 1000 + 3 * 1000 + 400)).isEqualTo("1:02:03.400s");
- assertThat(Stats.formatTime(2 * 60 * 1000 + 3 * 1000 + 400)).isEqualTo("2:03.400s");
- assertThat(Stats.formatTime(3 * 1000 + 400)).isEqualTo("3.400s");
- assertThat(Stats.formatTime(400)).isEqualTo("0.400s");
+ public void should_create() {
+ assertThat(ForkedRunner.create()).isNotNull().isInstanceOf(ForkedRunner.class);
}
}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java
new file mode 100644
index 0000000..20c4a91
--- /dev/null
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.apache.commons.lang.SystemUtils;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class OsTest {
+ @Test
+ public void testIsWindows() throws Exception {
+ assertThat(new Os().isWindows()).isEqualTo(SystemUtils.IS_OS_WINDOWS);
+ }
+
+ @Test
+ public void testUsedJavaHome() throws Exception {
+ File javaHome = new Os().usedJavaHome();
+ assertThat(javaHome).isNotNull().exists().isDirectory();
+ }
+
+ @Test
+ public void testUsedJavaExe() throws Exception {
+ File javaExe = new Os().usedJavaExe();
+ assertThat(javaExe).isNotNull().isFile().exists();
+ assertThat(javaExe.getName()).contains("java");
+ }
+}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/VersionTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/RunnerVersionTest.java
index 2280195..65d13c5 100644
--- a/sonar-runner-api/src/test/java/org/sonar/runner/VersionTest.java
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/RunnerVersionTest.java
@@ -17,20 +17,18 @@
* 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.runner;
+package org.sonar.runner.api;
import org.junit.Test;
-import org.sonar.runner.Version;
import static org.fest.assertions.Assertions.assertThat;
-public class VersionTest {
+public class RunnerVersionTest {
@Test
- public void shouldLoadVersion() {
- String version = Version.getVersion();
- assertThat(version).contains(".");
- assertThat(version).doesNotContain("$");
+ public void should_load_version() {
+ String version = RunnerVersion.version();
+ assertThat(version).isNotEmpty().contains(".").endsWith("-SNAPSHOT").doesNotContain("$");
}
}
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java
new file mode 100644
index 0000000..f956c75
--- /dev/null
+++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java
@@ -0,0 +1,33 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * 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.runner.api;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class UtilsTest {
+ @Test
+ public void should_join_strings() {
+ assertThat(Utils.join(new String[]{}, ",")).isEqualTo("");
+ assertThat(Utils.join(new String[]{"foo"}, ",")).isEqualTo("foo");
+ assertThat(Utils.join(new String[]{"foo", "bar"}, ",")).isEqualTo("foo,bar");
+ }
+}
diff --git a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project/sonar-project.properties b/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project/sonar-project.properties
deleted file mode 100644
index 0d1e025..0000000
--- a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project/sonar-project.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-project.prop=foo
-
-# overridden property
-overridden.prop=project scope \ No newline at end of file
diff --git a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner/conf/sonar-runner.properties b/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner/conf/sonar-runner.properties
deleted file mode 100644
index 7edfb99..0000000
--- a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner/conf/sonar-runner.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-overridden.prop=runner scope
-global.prop=jdbc:mysql:localhost/sonar
diff --git a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties b/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties
deleted file mode 100644
index 740a616..0000000
--- a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties
+++ /dev/null
@@ -1 +0,0 @@
-sonar.host.url=http://other/sonar \ No newline at end of file
diff --git a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/conf/sonar-runner.properties b/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/conf/sonar-runner.properties
deleted file mode 100644
index e7d5c09..0000000
--- a/sonar-runner-api/src/test/resources/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/conf/sonar-runner.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-sonar.host.url=http://moon/sonar
-sonar.jdbc.url=jdbc:mysql:localhost/sonar \ No newline at end of file
diff --git a/sonar-runner-api/src/test/resources/org/sonar/runner/RunnerTest/shouldInitDirs/fake.txt b/sonar-runner-api/src/test/resources/org/sonar/runner/RunnerTest/shouldInitDirs/fake.txt
deleted file mode 100644
index f0f877c..0000000
--- a/sonar-runner-api/src/test/resources/org/sonar/runner/RunnerTest/shouldInitDirs/fake.txt
+++ /dev/null
@@ -1 +0,0 @@
-fake \ No newline at end of file
diff --git a/sonar-runner-api/src/test/scripts/echo.bat b/sonar-runner-api/src/test/scripts/echo.bat
new file mode 100755
index 0000000..6622b99
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/echo.bat
@@ -0,0 +1,4 @@
+@ECHO OFF
+@ECHO %CD% > echo.log
+@ECHO Parameter: %1 >> echo.log
+@ECHO Environment variable: %ENVVAR% >> echo.log
diff --git a/sonar-runner-api/src/test/scripts/echo.sh b/sonar-runner-api/src/test/scripts/echo.sh
new file mode 100755
index 0000000..8931fd8
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/echo.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+WORKING_DIR=`pwd`
+echo $WORKING_DIR > echo.log
+echo "Parameter: $1" >> echo.log
+echo "Environment variable: $ENVVAR" >> echo.log
diff --git a/sonar-runner-api/src/test/scripts/forever.bat b/sonar-runner-api/src/test/scripts/forever.bat
new file mode 100755
index 0000000..a7d9218
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/forever.bat
@@ -0,0 +1,6 @@
+@ECHO OFF
+
+:LOOP
+ @rem Next line may lead to freeze of build process on Windows 7 due to non-terminated ping-processes
+ @rem ping 1.1.1.1 -n 2 -w 60000 > nul
+GOTO LOOP
diff --git a/sonar-runner-api/src/test/scripts/forever.sh b/sonar-runner-api/src/test/scripts/forever.sh
new file mode 100755
index 0000000..d7b6a9b
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/forever.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+while test "notempty"
+do
+ sleep 1
+done
diff --git a/sonar-runner-api/src/test/scripts/output.bat b/sonar-runner-api/src/test/scripts/output.bat
new file mode 100755
index 0000000..967bc60
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/output.bat
@@ -0,0 +1,5 @@
+@ECHO OFF
+@ECHO stdOut: first line
+@ECHO stdOut: second line
+@ECHO stdErr: first line 1>&2
+@ECHO stdErr: second line 1>&2
diff --git a/sonar-runner-api/src/test/scripts/output.sh b/sonar-runner-api/src/test/scripts/output.sh
new file mode 100755
index 0000000..cbb0fea
--- /dev/null
+++ b/sonar-runner-api/src/test/scripts/output.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo stdOut: first line
+echo stdOut: second line
+echo stdErr: first line 1>&2
+echo stdErr: second line 1>&2