@@ -1,4 +1,5 @@ | |||
<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-plugins</groupId> | |||
@@ -25,8 +26,9 @@ | |||
<modules> | |||
<module>sonar-runner-api</module> | |||
<module>sonar-runner-impl</module> | |||
<module>sonar-runner-batch</module> | |||
<module>sonar-runner-dist</module> | |||
<module>sonar-runner-impl</module> | |||
</modules> | |||
<organization> | |||
@@ -46,7 +48,6 @@ | |||
<connection>scm:git:git@github.com:SonarSource/sonar-runner.git</connection> | |||
<developerConnection>scm:git:git@github.com:SonarSource/sonar-runner.git</developerConnection> | |||
<url>https://github.com/SonarSource/sonar-runner</url> | |||
<tag>HEAD</tag> | |||
</scm> | |||
<issueManagement> | |||
@@ -55,7 +56,7 @@ | |||
</issueManagement> | |||
<properties> | |||
<sonar.buildVersion>3.5-RC3</sonar.buildVersion> | |||
<sonar.buildVersion>3.5</sonar.buildVersion> | |||
<maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile> | |||
</properties> | |||
@@ -94,4 +95,45 @@ | |||
</profile> | |||
</profiles> | |||
<dependencyManagement> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.google.code.findbugs</groupId> | |||
<artifactId>jsr305</artifactId> | |||
<version>2.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.11</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.easytesting</groupId> | |||
<artifactId>fest-assert</artifactId> | |||
<version>1.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||
<build> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-dependency-plugin</artifactId> | |||
<version>2.7</version> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<version>1.7.1</version> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
</build> | |||
</project> |
@@ -1,4 +1,5 @@ | |||
<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> | |||
@@ -10,39 +11,38 @@ | |||
<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> | |||
@@ -67,6 +66,34 @@ | |||
</archive> | |||
</configuration> | |||
</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> | |||
@@ -80,14 +107,6 @@ | |||
<createDependencyReducedPom>true</createDependencyReducedPom> | |||
<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> | |||
@@ -97,6 +116,36 @@ | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.felix</groupId> | |||
<artifactId>maven-bundle-plugin</artifactId> | |||
<version>2.3.7</version> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<instructions> | |||
<Import-Package>!*</Import-Package> | |||
<Export-Package>!.,org.sonar.runner.api</Export-Package> | |||
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment> | |||
</instructions> | |||
</configuration> | |||
<executions> | |||
<execution> | |||
<id>bundle-manifest</id> | |||
<phase>process-classes</phase> | |||
<goals> | |||
<goal>manifest</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<archive> | |||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> | |||
</archive> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -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; | |||
} | |||
} |
@@ -1,324 +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"; | |||
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); | |||
} | |||
} |
@@ -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 | |||
*/ | |||
public static final String ENV_SONAR_USER_HOME = "SONAR_USER_HOME"; | |||
public static final 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); | |||
} | |||
} |
@@ -1,201 +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 RunnerException("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 | |||
&& !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 RunnerException("Unable to create temporary cache directory " + tmpDir.getAbsolutePath(), e); | |||
} | |||
} | |||
return tmpDir; | |||
} | |||
public File getCacheLocation() { | |||
return cacheLocation; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,170 @@ | |||
/* | |||
* 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.*; | |||
/** | |||
* 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()); | |||
} | |||
} | |||
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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
* 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.BatchLauncher; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
/** | |||
* Implementation of {@link Runner} that is executed in the same JVM. The application can inject | |||
* some extensions into Sonar IoC container (see {@link #addExtensions(Object...)}. It can be | |||
* used for example in the Maven Sonar plugin to register Maven components like MavenProject | |||
* or MavenPluginExecutor. | |||
* @since 2.2 | |||
*/ | |||
public class EmbeddedRunner extends Runner<EmbeddedRunner> { | |||
private final BatchLauncher batchLauncher; | |||
private final List<Object> extensions = new ArrayList<Object>(); | |||
EmbeddedRunner(BatchLauncher bl) { | |||
this.batchLauncher = bl; | |||
} | |||
/** | |||
* Create a new instance. | |||
*/ | |||
public static EmbeddedRunner create() { | |||
return new EmbeddedRunner(new BatchLauncher()); | |||
} | |||
/** | |||
* Sonar is executed in an almost fully isolated classloader. The unmasked packages | |||
* define the classes of the client application that are visible from Sonar classloader. They | |||
* relate to the extensions provided by {@link #setUnmaskedPackages(String...)}. | |||
*/ | |||
public EmbeddedRunner setUnmaskedPackages(String... packages) { | |||
return setProperty("sonarRunner.unmaskedPackages", Utils.join(packages, ",")); | |||
} | |||
public EmbeddedRunner addExtensions(Object... objects) { | |||
extensions.addAll(Arrays.asList(objects)); | |||
return this; | |||
} | |||
List<Object> extensions() { | |||
return extensions; | |||
} | |||
@Override | |||
protected void doExecute() { | |||
batchLauncher.execute(properties(), extensions); | |||
} | |||
} |
@@ -0,0 +1,169 @@ | |||
/* | |||
* 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 javax.annotation.Nullable; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.OutputStream; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* Runner executed in a new JVM. | |||
* | |||
* @since 2.2 | |||
*/ | |||
public class ForkedRunner extends Runner<ForkedRunner> { | |||
private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000; | |||
private final Map<String, String> jvmEnvVariables = new HashMap<String, String>(); | |||
private final List<String> jvmArguments = new ArrayList<String>(); | |||
private String javaExecutable; | |||
private StreamConsumer stdOut = null, stdErr = null; | |||
private final JarExtractor jarExtractor; | |||
private final CommandExecutor commandExecutor; | |||
ForkedRunner(JarExtractor jarExtractor, CommandExecutor commandExecutor) { | |||
this.jarExtractor = jarExtractor; | |||
this.commandExecutor = commandExecutor; | |||
} | |||
/** | |||
* Create new instance. Never return null. | |||
*/ | |||
public static ForkedRunner create() { | |||
return new ForkedRunner(new JarExtractor(), CommandExecutor.create()); | |||
} | |||
/** | |||
* Path to the java executable. The JVM of the client app is used by default | |||
* (see the system property java.home) | |||
*/ | |||
public ForkedRunner setJavaExecutable(@Nullable String s) { | |||
this.javaExecutable = s; | |||
return this; | |||
} | |||
/** | |||
* See {@link #addJvmArguments(java.util.List)} | |||
*/ | |||
public ForkedRunner addJvmArguments(String... s) { | |||
return addJvmArguments(Arrays.asList(s)); | |||
} | |||
/** | |||
* JVM arguments, for example "-Xmx512m" | |||
*/ | |||
public ForkedRunner addJvmArguments(List<String> args) { | |||
jvmArguments.addAll(args); | |||
return this; | |||
} | |||
/** | |||
* Set a JVM environment variable. By default no variables are set. | |||
*/ | |||
public ForkedRunner setJvmEnvVariable(String key, String value) { | |||
jvmEnvVariables.put(key, value); | |||
return this; | |||
} | |||
/** | |||
* Add some JVM environment variables. By default no variables are set. | |||
*/ | |||
public ForkedRunner addJvmEnvVariables(Map<String, String> map) { | |||
jvmEnvVariables.putAll(map); | |||
return this; | |||
} | |||
/** | |||
* Subscribe to the standard output. By default output is {@link System.out} | |||
*/ | |||
public ForkedRunner setStdOut(@Nullable StreamConsumer stream) { | |||
this.stdOut = stream; | |||
return this; | |||
} | |||
/** | |||
* Subscribe to the error output. By default output is {@link System.err} | |||
*/ | |||
public ForkedRunner setStdErr(@Nullable StreamConsumer stream) { | |||
this.stdErr = stream; | |||
return this; | |||
} | |||
@Override | |||
protected void doExecute() { | |||
fork(createCommand()); | |||
} | |||
private Command createCommand() { | |||
File propertiesFile = writeProperties(); | |||
File jarFile = jarExtractor.extract("sonar-runner-impl"); | |||
if (javaExecutable == null) { | |||
javaExecutable = new Os().thisJavaExe().getAbsolutePath(); | |||
} | |||
return Command.builder() | |||
.setExecutable(javaExecutable) | |||
.addEnvVariables(jvmEnvVariables) | |||
.addArguments(jvmArguments) | |||
.addArguments("-cp", jarFile.getAbsolutePath(), BatchLauncherMain.class.getName(), propertiesFile.getAbsolutePath()) | |||
.build(); | |||
} | |||
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); | |||
} | |||
} | |||
private void fork(Command command) { | |||
if (stdOut == null) { | |||
stdOut = new PrintStreamConsumer(System.out); | |||
} | |||
if (stdErr == null) { | |||
stdErr = new PrintStreamConsumer(System.err); | |||
} | |||
int status = commandExecutor.execute(command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS); | |||
if (status != 0) { | |||
throw new IllegalStateException("Error status: " + status); | |||
} | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* 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; | |||
class Os { | |||
boolean isWindows() { | |||
return System.getProperty("os.name").contains("Windows"); | |||
} | |||
File thisJavaHome() { | |||
return new File(System.getProperty("java.home")); | |||
} | |||
/** | |||
* Path to the java executable used by this VM | |||
*/ | |||
File thisJavaExe() { | |||
File bin = new File(thisJavaHome(), "bin"); | |||
return new File(bin, isWindows() ? "java.exe" : "java"); | |||
} | |||
} |
@@ -17,29 +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; | |||
import java.io.PrintStream; | |||
/** | |||
* Internal class used only by the Runner. | |||
* This class should not be used by Sonar Runner consumers. | |||
* Implementation of StreamConsumer that prints lines to {@link java.io.PrintStream}. Generally used | |||
* to forward to {@link System.out} or {@link System.err}. | |||
* | |||
* @since 2.2 | |||
*/ | |||
final class VersionUtils { | |||
public class PrintStreamConsumer implements StreamConsumer { | |||
final PrintStream output; | |||
private VersionUtils() { | |||
// only static methods | |||
public PrintStreamConsumer(PrintStream output) { | |||
this.output = output; | |||
} | |||
static boolean isUnsupportedVersion(String version, String[] unsuportedVersions) { | |||
for (String unsupportedVersion : unsuportedVersions) { | |||
if (isVersion(version, unsupportedVersion)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
public void consumeLine(String line) { | |||
output.println(line); | |||
} | |||
private static boolean isVersion(String version, String prefix) { | |||
return version.startsWith(prefix + ".") || version.equals(prefix); | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
/* | |||
* 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; | |||
} | |||
/** | |||
* Declare Sonar properties, for example sonar.projectKey=>foo. | |||
*/ | |||
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); | |||
} | |||
/** | |||
* User-agent used in the HTTP requests to the Sonar server | |||
*/ | |||
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(); | |||
} |
@@ -17,38 +17,30 @@ | |||
* 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 { | |||
/** | |||
* Version of this sonar-runner API. | |||
* @since 2.2 | |||
*/ | |||
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(); | |||
} | |||
} | |||
} |
@@ -17,7 +17,11 @@ | |||
* 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; | |||
/** | |||
* API package of the Sonar Runner. | |||
* @since 2.2 | |||
*/ | |||
package org.sonar.runner; | |||
public interface StreamConsumer { | |||
void consumeLine(String line); | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* 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.util.Arrays; | |||
import java.util.Iterator; | |||
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 sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* 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 | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.runner.api; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -0,0 +1 @@ | |||
${project.version} |
@@ -1 +0,0 @@ | |||
version=${project.version} |
@@ -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)); | |||
} | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,161 @@ | |||
/* | |||
* 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(); | |||
PrintStreamConsumer stdout = new PrintStreamConsumer(System.out); | |||
PrintStreamConsumer stderr = new PrintStreamConsumer(System.err); | |||
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(); | |||
StreamConsumer stdOutConsumer = new StreamConsumer() { | |||
public void consumeLine(String line) { | |||
stdOutBuilder.append(line).append(System.getProperty("line.separator")); | |||
} | |||
}; | |||
final StringBuilder stdErrBuilder = new StringBuilder(); | |||
StreamConsumer stdErrConsumer = new 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 StreamConsumer NOP_CONSUMER = new StreamConsumer() { | |||
public void consumeLine(String line) { | |||
// nop | |||
} | |||
}; | |||
private static final StreamConsumer BAD_CONSUMER = new 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, stdout, stderr, 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 { | |||
Command command = Command.builder().setExecutable(executable).setDirectory(workDir).build(); | |||
CommandExecutor.create().execute(command, stdout, stderr, 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); | |||
Command command = Command.builder().setExecutable("notfound").setDirectory(workDir).build(); | |||
CommandExecutor.create().execute(command, stdout, stderr, 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(); | |||
} | |||
} |
@@ -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 | |||
} | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
/* | |||
* 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.mockito.ArgumentMatcher; | |||
import org.sonar.runner.impl.BatchLauncher; | |||
import org.sonar.runner.impl.Constants; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Matchers.argThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class EmbeddedRunnerTest { | |||
@Test | |||
public void should_create() { | |||
assertThat(EmbeddedRunner.create()).isNotNull().isInstanceOf(EmbeddedRunner.class); | |||
} | |||
@Test | |||
public void test_app() { | |||
EmbeddedRunner runner = EmbeddedRunner.create().setApp("Eclipse", "3.1"); | |||
assertThat(runner.app()).isEqualTo("Eclipse"); | |||
assertThat(runner.appVersion()).isEqualTo("3.1"); | |||
} | |||
@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); | |||
} | |||
@Test | |||
public void should_set_properties() { | |||
EmbeddedRunner runner = EmbeddedRunner.create(); | |||
runner.setProperty("sonar.projectKey", "foo"); | |||
runner.addProperties(new Properties() {{ | |||
setProperty("sonar.login", "admin"); | |||
setProperty("sonar.password", "gniark"); | |||
}}); | |||
assertThat(runner.property("sonar.projectKey", null)).isEqualTo("foo"); | |||
assertThat(runner.property("sonar.login", null)).isEqualTo("admin"); | |||
assertThat(runner.property("sonar.password", null)).isEqualTo("gniark"); | |||
assertThat(runner.property("not.set", "this_is_default")).isEqualTo("this_is_default"); | |||
} | |||
@Test | |||
public void should_launch_batch() { | |||
BatchLauncher batchLauncher = mock(BatchLauncher.class); | |||
EmbeddedRunner runner = new EmbeddedRunner(batchLauncher); | |||
final FakeExtension fakeExtension = new FakeExtension(); | |||
runner.addExtensions(fakeExtension); | |||
runner.setProperty("sonar.projectKey", "foo"); | |||
runner.execute(); | |||
verify(batchLauncher).execute(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return "foo".equals(((Properties) o).getProperty("sonar.projectKey")); | |||
} | |||
}), argThat(new ArgumentMatcher<List<Object>>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return ((List) o).contains(fakeExtension); | |||
} | |||
})); | |||
} | |||
static class FakeExtension { | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
/* | |||
* 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.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.mockito.ArgumentMatcher; | |||
import org.sonar.runner.impl.JarExtractor; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.PrintStream; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyLong; | |||
import static org.mockito.Matchers.argThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
public class ForkedRunnerTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void should_create() { | |||
ForkedRunner runner = ForkedRunner.create(); | |||
assertThat(runner).isNotNull().isInstanceOf(ForkedRunner.class); | |||
} | |||
@Test | |||
public void should_print_to_standard_outputs_by_default() throws IOException { | |||
JarExtractor jarExtractor = mock(JarExtractor.class); | |||
final File jar = temp.newFile(); | |||
when(jarExtractor.extract("sonar-runner-impl")).thenReturn(jar); | |||
CommandExecutor commandExecutor = mock(CommandExecutor.class); | |||
ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor); | |||
runner.execute(); | |||
verify(commandExecutor).execute(any(Command.class), argThat(new StdConsumerMatcher(System.out)), argThat(new StdConsumerMatcher(System.err)), anyLong()); | |||
} | |||
static class StdConsumerMatcher extends ArgumentMatcher<StreamConsumer> { | |||
PrintStream output; | |||
StdConsumerMatcher(PrintStream output) { | |||
this.output = output; | |||
} | |||
public boolean matches(Object o) { | |||
return ((PrintStreamConsumer) o).output == output; | |||
} | |||
} | |||
@Test | |||
public void test_java_command() throws IOException { | |||
JarExtractor jarExtractor = mock(JarExtractor.class); | |||
final File jar = temp.newFile(); | |||
when(jarExtractor.extract("sonar-runner-impl")).thenReturn(jar); | |||
CommandExecutor commandExecutor = mock(CommandExecutor.class); | |||
ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor); | |||
runner.setJavaExecutable("java"); | |||
runner.setProperty("sonar.dynamicAnalysis", "false"); | |||
runner.setProperty("sonar.login", "admin"); | |||
runner.addJvmArguments("-Xmx512m"); | |||
runner.addJvmEnvVariables(System.getenv()); | |||
runner.setJvmEnvVariable("SONAR_HOME", "/path/to/sonar"); | |||
runner.setStdOut(mock(StreamConsumer.class)); | |||
runner.setStdErr(mock(StreamConsumer.class)); | |||
runner.execute(); | |||
verify(commandExecutor).execute(argThat(new ArgumentMatcher<Command>() { | |||
public boolean matches(Object o) { | |||
Command command = (Command) o; | |||
assertThat(command.toStrings()).hasSize(6); | |||
assertThat(command.toStrings()[0]).isEqualTo("java"); | |||
assertThat(command.toStrings()[1]).isEqualTo("-Xmx512m"); | |||
assertThat(command.toStrings()[2]).isEqualTo("-cp"); | |||
assertThat(command.toStrings()[3]).isEqualTo(jar.getAbsolutePath()); | |||
assertThat(command.toStrings()[4]).isEqualTo("org.sonar.runner.impl.BatchLauncherMain"); | |||
// env variables | |||
assertThat(command.envVariables().size()).isGreaterThan(1); | |||
assertThat(command.envVariables().get("SONAR_HOME")).isEqualTo("/path/to/sonar"); | |||
// the properties | |||
String propsPath = command.toStrings()[5]; | |||
assertThat(propsPath).endsWith(".properties"); | |||
Properties properties = new Properties(); | |||
try { | |||
properties.load(new FileInputStream(propsPath)); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(e); | |||
} | |||
assertThat(properties.size()).isGreaterThan(2); | |||
assertThat(properties.getProperty("sonar.dynamicAnalysis")).isEqualTo("false"); | |||
assertThat(properties.getProperty("sonar.login")).isEqualTo("admin"); | |||
assertThat(properties.getProperty("-Xmx512m")).isNull(); | |||
assertThat(properties.getProperty("SONAR_HOME")).isNull(); | |||
// default values | |||
assertThat(properties.getProperty("sonar.task")).isEqualTo("scan"); | |||
assertThat(properties.getProperty("sonar.host.url")).isEqualTo("http://localhost:9000"); | |||
return true; | |||
} | |||
}), any(PrintStreamConsumer.class), any(PrintStreamConsumer.class), anyLong()); | |||
} | |||
} |
@@ -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().thisJavaHome(); | |||
assertThat(javaHome).isNotNull().exists().isDirectory(); | |||
} | |||
@Test | |||
public void testUsedJavaExe() throws Exception { | |||
File javaExe = new Os().thisJavaExe(); | |||
assertThat(javaExe).isNotNull().isFile().exists(); | |||
assertThat(javaExe.getName()).contains("java"); | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* 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.io.PrintStream; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
public class PrintStreamConsumerTest { | |||
@Test | |||
public void consumeLine() { | |||
PrintStream stream = mock(PrintStream.class); | |||
PrintStreamConsumer consumer = new PrintStreamConsumer(stream); | |||
consumer.consumeLine("foo"); | |||
consumer.consumeLine("bar"); | |||
verify(stream).println("foo"); | |||
verify(stream).println("bar"); | |||
verifyNoMoreInteractions(stream); | |||
} | |||
} |
@@ -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("$"); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
@ECHO OFF | |||
@ECHO %CD% > echo.log | |||
@ECHO Parameter: %1 >> echo.log | |||
@ECHO Environment variable: %ENVVAR% >> echo.log |
@@ -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 |
@@ -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 |
@@ -0,0 +1,6 @@ | |||
#!/bin/sh | |||
while test "notempty" | |||
do | |||
sleep 1 | |||
done |
@@ -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 |
@@ -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 |
@@ -0,0 +1,67 @@ | |||
<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.2-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>sonar-runner-batch</artifactId> | |||
<name>Sonar Runner - Batch</name> | |||
<properties> | |||
<sonarBatchVersion>3.0</sonarBatchVersion> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>ch.qos.logback</groupId> | |||
<artifactId>logback-classic</artifactId> | |||
<version>0.9.15</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-batch</artifactId> | |||
<version>${sonarBatchVersion}</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<!-- Unit tests --> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-testing-harness</artifactId> | |||
<version>${sonarBatchVersion}</version> | |||
<scope>test</scope> | |||
<exclusions> | |||
<exclusion> | |||
<artifactId>commons-configuration</artifactId> | |||
<groupId>commons-configuration</groupId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.easytesting</groupId> | |||
<artifactId>fest-assert</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<phase>package</phase> | |||
<goals> | |||
<goal>shade</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -0,0 +1,99 @@ | |||
/* | |||
* Sonar Runner - Batch | |||
* 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.batch; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.joran.JoranConfigurator; | |||
import ch.qos.logback.core.joran.spi.JoranException; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import org.apache.commons.io.IOUtils; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.bootstrap.ProjectReactor; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import java.io.InputStream; | |||
import java.util.List; | |||
import java.util.Properties; | |||
/** | |||
* This class is executed within the classloader provided by the server. It contains the installed plugins and | |||
* the same version of sonar-batch as the server. | |||
*/ | |||
public class IsolatedLauncher { | |||
public void execute(Properties properties, List<Object> extensions) { | |||
String task = properties.getProperty("sonar.task"); | |||
ProjectReactor projectReactor = null; | |||
if (task == null || "scan".equals(task)) { | |||
Properties propsClone = new Properties(); | |||
propsClone.putAll(properties); | |||
projectReactor = new ProjectReactorBuilder(propsClone).build(); | |||
} | |||
initLogging(properties); | |||
EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.userAgent"), properties.getProperty("sonarRunner.userAgentVersion")); | |||
Batch.Builder builder = Batch.builder() | |||
.setEnvironment(env) | |||
.addComponents(extensions); | |||
if (projectReactor != null) { | |||
builder.setProjectReactor(projectReactor); | |||
} | |||
builder.build().execute(); | |||
} | |||
private void initLogging(Properties props) { | |||
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); | |||
JoranConfigurator jc = new JoranConfigurator(); | |||
jc.setContext(context); | |||
context.reset(); | |||
InputStream input = Batch.class.getResourceAsStream("/org/sonar/batch/logback.xml"); | |||
System.setProperty("ROOT_LOGGER_LEVEL", isDebug(props) ? "DEBUG" : "INFO"); | |||
context.putProperty("SQL_LOGGER_LEVEL", getSqlLevel(props)); | |||
context.putProperty("SQL_RESULTS_LOGGER_LEVEL", getSqlResultsLevel(props)); | |||
try { | |||
jc.doConfigure(input); | |||
} catch (JoranException e) { | |||
throw new SonarException("can not initialize logging", e); | |||
} finally { | |||
IOUtils.closeQuietly(input); | |||
} | |||
} | |||
@VisibleForTesting | |||
protected boolean isDebug(Properties props) { | |||
return Boolean.parseBoolean(props.getProperty("sonar.verbose", "false")); | |||
} | |||
@VisibleForTesting | |||
protected static String getSqlLevel(Properties props) { | |||
boolean showSql = "true".equals(props.getProperty("sonar.showSql", "false")); | |||
return showSql ? "DEBUG" : "WARN"; | |||
} | |||
@VisibleForTesting | |||
protected static String getSqlResultsLevel(Properties props) { | |||
boolean showSql = "true".equals(props.getProperty("sonar.showSqlResults", "false")); | |||
return showSql ? "DEBUG" : "WARN"; | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* Sonar Runner - Implementation | |||
* Sonar Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -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 | |||
*/ | |||
package org.sonar.runner.internal.batch; | |||
package org.sonar.runner.batch; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.collect.Lists; | |||
@@ -29,7 +29,7 @@ import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.runner.RunnerException; | |||
import org.sonar.api.batch.bootstrap.ProjectReactor; | |||
import java.io.File; | |||
import java.io.FileFilter; | |||
@@ -46,9 +46,9 @@ import java.util.Properties; | |||
* | |||
* @since 1.5 | |||
*/ | |||
public final class SonarProjectBuilder { | |||
public final class ProjectReactorBuilder { | |||
private static final Logger LOG = LoggerFactory.getLogger(SonarProjectBuilder.class); | |||
private static final Logger LOG = LoggerFactory.getLogger(ProjectReactorBuilder.class); | |||
private static final String PROPERTY_PROJECT_BASEDIR = "sonar.projectBaseDir"; | |||
private static final String PROPERTY_PROJECT_CONFIG_FILE = "sonar.projectConfigFile"; | |||
@@ -110,47 +110,19 @@ public final class SonarProjectBuilder { | |||
*/ | |||
private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, PROPERTY_MODULES, PROPERTY_PROJECT_DESCRIPTION); | |||
private String command; | |||
private Properties properties; | |||
private File rootProjectWorkDir; | |||
private SonarProjectBuilder(String command, Properties properties) { | |||
this.command = command; | |||
public ProjectReactorBuilder(Properties properties) { | |||
this.properties = properties; | |||
} | |||
public static SonarProjectBuilder create(Properties properties) { | |||
return create(null, properties); | |||
} | |||
public static SonarProjectBuilder create(String command, Properties properties) { | |||
return new SonarProjectBuilder(command, properties); | |||
} | |||
public ProjectDefinition generateProjectDefinition() { | |||
if (StringUtils.isBlank(command) || "scan".equals(command)) { | |||
ProjectDefinition rootProject = defineProject(properties, null); | |||
rootProjectWorkDir = rootProject.getWorkDir(); | |||
defineChildren(rootProject); | |||
cleanAndCheckProjectDefinitions(rootProject); | |||
return rootProject; | |||
} | |||
else if (properties.containsKey(PROPERTY_PROJECT_KEY)) { | |||
return defineTaskContext(); | |||
} | |||
else { | |||
return null; | |||
} | |||
} | |||
private ProjectDefinition defineTaskContext() { | |||
File baseDir = new File(System.getProperty("user.home")); | |||
File workDir = initRootProjectWorkDir(baseDir); | |||
ProjectDefinition definition = ProjectDefinition.create().setProperties(properties) | |||
.setBaseDir(baseDir) | |||
.setWorkDir(workDir); | |||
return definition; | |||
public ProjectReactor build() { | |||
ProjectDefinition rootProject = defineProject(properties, null); | |||
rootProjectWorkDir = rootProject.getWorkDir(); | |||
defineChildren(rootProject); | |||
cleanAndCheckProjectDefinitions(rootProject); | |||
return new ProjectReactor(rootProject); | |||
} | |||
private ProjectDefinition defineProject(Properties properties, ProjectDefinition parent) { | |||
@@ -161,7 +133,7 @@ public final class SonarProjectBuilder { | |||
else { | |||
checkMandatoryProperties(properties, MANDATORY_PROPERTIES_FOR_SIMPLE_PROJECT); | |||
} | |||
File workDir = null; | |||
File workDir; | |||
if (parent == null) { | |||
validateDirectories(properties, baseDir, properties.getProperty(PROPERTY_PROJECT_KEY)); | |||
workDir = initRootProjectWorkDir(baseDir); | |||
@@ -199,7 +171,7 @@ public final class SonarProjectBuilder { | |||
private void defineChildren(ProjectDefinition parentProject) { | |||
Properties parentProps = parentProject.getProperties(); | |||
if (parentProps.containsKey(PROPERTY_MODULES)) { | |||
for (String module : SonarRunnerUtils.getListFromProperty(parentProps, PROPERTY_MODULES)) { | |||
for (String module : Utils.getListFromProperty(parentProps, PROPERTY_MODULES)) { | |||
Properties moduleProps = extractModuleProperties(module, parentProps); | |||
ProjectDefinition childProject = loadChildProject(parentProject, moduleProps, module); | |||
// check the uniqueness of the child key | |||
@@ -224,7 +196,7 @@ public final class SonarProjectBuilder { | |||
tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId); | |||
} | |||
} catch (IOException e) { | |||
throw new RunnerException("Error when resolving baseDir", e); | |||
throw new IllegalStateException("Error when resolving baseDir", e); | |||
} | |||
} else if (moduleProps.containsKey(PROPERTY_PROJECT_CONFIG_FILE)) { | |||
baseDir = loadPropsFile(parentProject, moduleProps, moduleId); | |||
@@ -264,7 +236,7 @@ public final class SonarProjectBuilder { | |||
setProjectBaseDir(baseDir, moduleProps, moduleId); | |||
return baseDir; | |||
} else { | |||
throw new RunnerException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath()); | |||
throw new IllegalStateException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath()); | |||
} | |||
} | |||
@@ -290,7 +262,7 @@ public final class SonarProjectBuilder { | |||
fileInputStream = new FileInputStream(propertyFile); | |||
propsFromFile.load(fileInputStream); | |||
} catch (IOException e) { | |||
throw new RunnerException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e); | |||
throw new IllegalStateException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e); | |||
} finally { | |||
IOUtils.closeQuietly(fileInputStream); | |||
} | |||
@@ -311,7 +283,7 @@ public final class SonarProjectBuilder { | |||
protected static void checkUniquenessOfChildKey(ProjectDefinition childProject, ProjectDefinition parentProject) { | |||
for (ProjectDefinition definition : parentProject.getSubProjects()) { | |||
if (definition.getKey().equals(childProject.getKey())) { | |||
throw new RunnerException("Project '" + parentProject.getKey() + "' can't have 2 modules with the following key: " + childProject.getKey()); | |||
throw new IllegalStateException("Project '" + parentProject.getKey() + "' can't have 2 modules with the following key: " + childProject.getKey()); | |||
} | |||
} | |||
} | |||
@@ -324,7 +296,7 @@ public final class SonarProjectBuilder { | |||
private static void setProjectBaseDir(File baseDir, Properties childProps, String moduleId) { | |||
if (!baseDir.isDirectory()) { | |||
throw new RunnerException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath()); | |||
throw new IllegalStateException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath()); | |||
} | |||
childProps.put(PROPERTY_PROJECT_BASEDIR, baseDir.getAbsolutePath()); | |||
} | |||
@@ -343,29 +315,29 @@ public final class SonarProjectBuilder { | |||
} | |||
String projectKey = props.getProperty(PROPERTY_PROJECT_KEY); | |||
if (missing.length() != 0) { | |||
throw new RunnerException("You must define the following mandatory properties for '" + (projectKey == null ? "Unknown" : projectKey) + "': " + missing); | |||
throw new IllegalStateException("You must define the following mandatory properties for '" + (projectKey == null ? "Unknown" : projectKey) + "': " + missing); | |||
} | |||
} | |||
private static void validateDirectories(Properties props, File baseDir, String projectId) { | |||
if (!props.containsKey(PROPERTY_MODULES)) { | |||
// SONARPLUGINS-2285 Not an aggreator project so we can validate that paths are correct if defined | |||
// SONARPLUGINS-2285 Not an aggregator project so we can validate that paths are correct if defined | |||
// We need to resolve patterns that may have been used in "sonar.libraries" | |||
for (String pattern : SonarRunnerUtils.getListFromProperty(props, PROPERTY_LIBRARIES)) { | |||
for (String pattern : Utils.getListFromProperty(props, PROPERTY_LIBRARIES)) { | |||
File[] files = getLibraries(baseDir, pattern); | |||
if (files == null || files.length == 0) { | |||
LOG.error("Invalid value of " + PROPERTY_LIBRARIES + " for " + projectId); | |||
throw new RunnerException("No file matching pattern \"" + pattern + "\" in directory \"" + baseDir + "\""); | |||
throw new IllegalStateException("No file matching pattern \"" + pattern + "\" in directory \"" + baseDir + "\""); | |||
} | |||
} | |||
// Check sonar.tests | |||
String[] testDirs = SonarRunnerUtils.getListFromProperty(props, PROPERTY_TESTS); | |||
String[] testDirs = Utils.getListFromProperty(props, PROPERTY_TESTS); | |||
checkExistenceOfDirectories(projectId, baseDir, testDirs, PROPERTY_TESTS); | |||
// Check sonar.binaries | |||
String[] binDirs = SonarRunnerUtils.getListFromProperty(props, PROPERTY_BINARIES); | |||
String[] binDirs = Utils.getListFromProperty(props, PROPERTY_BINARIES); | |||
checkExistenceOfDirectories(projectId, baseDir, binDirs, PROPERTY_BINARIES); | |||
} | |||
} | |||
@@ -389,12 +361,12 @@ public final class SonarProjectBuilder { | |||
Properties properties = project.getProperties(); | |||
// We need to check the existence of source directories | |||
String[] sourceDirs = SonarRunnerUtils.getListFromProperty(properties, PROPERTY_SOURCES); | |||
String[] sourceDirs = Utils.getListFromProperty(properties, PROPERTY_SOURCES); | |||
checkExistenceOfDirectories(project.getKey(), project.getBaseDir(), sourceDirs, PROPERTY_SOURCES); | |||
// And we need to resolve patterns that may have been used in "sonar.libraries" | |||
List<String> libPaths = Lists.newArrayList(); | |||
for (String pattern : SonarRunnerUtils.getListFromProperty(properties, PROPERTY_LIBRARIES)) { | |||
for (String pattern : Utils.getListFromProperty(properties, PROPERTY_LIBRARIES)) { | |||
for (File file : getLibraries(project.getBaseDir(), pattern)) { | |||
libPaths.add(file.getAbsolutePath()); | |||
} | |||
@@ -408,7 +380,7 @@ public final class SonarProjectBuilder { | |||
Properties properties = project.getProperties(); | |||
// SONARPLUGINS-2295 | |||
String[] sourceDirs = SonarRunnerUtils.getListFromProperty(properties, PROPERTY_SOURCES); | |||
String[] sourceDirs = Utils.getListFromProperty(properties, PROPERTY_SOURCES); | |||
for (String path : sourceDirs) { | |||
File sourceFolder = getFileFromPath(path, project.getBaseDir()); | |||
if (sourceFolder.isDirectory()) { | |||
@@ -426,7 +398,7 @@ public final class SonarProjectBuilder { | |||
// and they don't need properties related to their modules either | |||
Properties clone = (Properties) properties.clone(); | |||
List<String> moduleIds = Lists.newArrayList(SonarRunnerUtils.getListFromProperty(properties, PROPERTY_MODULES)); | |||
List<String> moduleIds = Lists.newArrayList(Utils.getListFromProperty(properties, PROPERTY_MODULES)); | |||
for (Entry<Object, Object> entry : clone.entrySet()) { | |||
String key = (String) entry.getKey(); | |||
if (isKeyPrefixedByModuleId(key, moduleIds)) { | |||
@@ -455,7 +427,7 @@ public final class SonarProjectBuilder { | |||
@VisibleForTesting | |||
protected static void mergeParentProperties(Properties childProps, Properties parentProps) { | |||
List<String> moduleIds = Lists.newArrayList(SonarRunnerUtils.getListFromProperty(parentProps, PROPERTY_MODULES)); | |||
List<String> moduleIds = Lists.newArrayList(Utils.getListFromProperty(parentProps, PROPERTY_MODULES)); | |||
for (Map.Entry<Object, Object> entry : parentProps.entrySet()) { | |||
String key = (String) entry.getKey(); | |||
if (!childProps.containsKey(key) | |||
@@ -495,7 +467,7 @@ public final class SonarProjectBuilder { | |||
File sourceFolder = getFileFromPath(path, baseDir); | |||
if (!sourceFolder.isDirectory()) { | |||
LOG.error("Invalid value of " + propName + " for " + moduleRef); | |||
throw new RunnerException("The folder '" + path + "' does not exist for '" + moduleRef + | |||
throw new IllegalStateException("The folder '" + path + "' does not exist for '" + moduleRef + | |||
"' (base directory = " + baseDir.getAbsolutePath() + ")"); | |||
} | |||
} | |||
@@ -531,7 +503,7 @@ public final class SonarProjectBuilder { | |||
try { | |||
file = new File(baseDir, path).getCanonicalFile(); | |||
} catch (IOException e) { | |||
throw new RunnerException("Unable to resolve path \"" + path + "\"", e); | |||
throw new IllegalStateException("Unable to resolve path \"" + path + "\"", e); | |||
} | |||
} | |||
return file; |
@@ -1,5 +1,5 @@ | |||
/* | |||
* Sonar Runner - Implementation | |||
* Sonar Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -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 | |||
*/ | |||
package org.sonar.runner.internal.batch; | |||
package org.sonar.runner.batch; | |||
import org.apache.commons.lang.StringUtils; | |||
@@ -26,9 +26,9 @@ import java.util.Properties; | |||
/** | |||
* Public utility that can be used by consumers of the Sonar Runner. | |||
*/ | |||
public final class SonarRunnerUtils { | |||
class Utils { | |||
private SonarRunnerUtils() { | |||
private Utils() { | |||
// only static methods | |||
} | |||
@@ -1,5 +1,5 @@ | |||
/* | |||
* Sonar Runner - Implementation | |||
* Sonar Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -21,4 +21,7 @@ | |||
* Internal package that creates the project definition and launches the analyses based on it. | |||
* Should not be used by consumers. | |||
*/ | |||
package org.sonar.runner.internal.batch; | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.runner.batch; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -0,0 +1,74 @@ | |||
/* | |||
* Sonar Runner - Batch | |||
* 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.batch; | |||
// | |||
//import com.google.common.collect.Lists; | |||
//import org.junit.Test; | |||
// | |||
//import java.util.Properties; | |||
// | |||
//import static org.fest.assertions.Assertions.assertThat; | |||
// | |||
//public class LauncherTest { | |||
// | |||
// @Test | |||
// public void testGetSqlLevel() throws Exception { | |||
// Properties conf = new Properties(); | |||
// | |||
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("WARN"); | |||
// | |||
// conf.setProperty("sonar.showSql", "true"); | |||
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("DEBUG"); | |||
// | |||
// conf.setProperty("sonar.showSql", "false"); | |||
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("WARN"); | |||
// } | |||
// | |||
// @Test | |||
// public void testGetSqlResultsLevel() throws Exception { | |||
// Properties conf = new Properties(); | |||
// | |||
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("WARN"); | |||
// | |||
// conf.setProperty("sonar.showSqlResults", "true"); | |||
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("DEBUG"); | |||
// | |||
// conf.setProperty("sonar.showSqlResults", "false"); | |||
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("WARN"); | |||
// } | |||
// | |||
// @Test | |||
// public void shouldDetermineVerboseMode() { | |||
// Properties properties = new Properties(); | |||
// Launcher launcher = new Launcher(properties, Lists.newArrayList()); | |||
// assertThat(launcher.isDebug()).isFalse(); | |||
// properties.setProperty("sonar.verbose", "true"); | |||
// assertThat(launcher.isDebug()).isTrue(); | |||
// } | |||
// | |||
// @Test | |||
// public void shouldSupportDeprecatedDebugProperty() { | |||
// Properties properties = new Properties(); | |||
// Launcher launcher = new Launcher(properties, Lists.newArrayList()); | |||
// properties.setProperty("runner.debug", "true"); | |||
// assertThat(launcher.isDebug()).isTrue(); | |||
// } | |||
// | |||
//} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* Sonar Runner - Implementation | |||
* Sonar Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -17,13 +17,13 @@ | |||
* 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.internal.batch; | |||
package org.sonar.runner.batch; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.runner.RunnerException; | |||
import org.sonar.api.batch.bootstrap.ProjectReactor; | |||
import org.sonar.test.TestUtils; | |||
import java.io.File; | |||
@@ -34,7 +34,7 @@ import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class SonarProjectBuilderTest { | |||
public class ProjectReactorBuilderTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@@ -64,9 +64,9 @@ public class SonarProjectBuilderTest { | |||
@Test | |||
public void shouldFailIfUnexistingSourceDirectory() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")"); | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")"); | |||
loadProjectDefinition("simple-project-with-unexisting-source-dir"); | |||
} | |||
@@ -302,28 +302,28 @@ public class SonarProjectBuilderTest { | |||
@Test | |||
public void shouldFailIfUnexistingModuleBaseDir() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The base directory of the module 'module1' does not exist: " | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1"); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1"); | |||
loadProjectDefinition("multi-module-with-unexisting-basedir"); | |||
} | |||
@Test | |||
public void shouldFailIfUnexistingModuleFile() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The properties file of the module 'module1' does not exist: " | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-file").getAbsolutePath() + File.separator + "any-folder" | |||
+ File.separator + "any-file.properties"); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-file").getAbsolutePath() + File.separator + "any-folder" | |||
+ File.separator + "any-file.properties"); | |||
loadProjectDefinition("multi-module-with-unexisting-file"); | |||
} | |||
@Test | |||
public void shouldFailIfUnexistingSourceFolderInheritedInMultimodule() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)"); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)"); | |||
loadProjectDefinition("multi-module-with-unexisting-source-dir"); | |||
} | |||
@@ -335,54 +335,54 @@ public class SonarProjectBuilderTest { | |||
@Test | |||
public void shouldFailIfExplicitUnexistingTestFolder() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'tests' does not exist for 'com.foo.project' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath()); | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath()); | |||
loadProjectDefinition("simple-project-with-unexisting-test-dir"); | |||
} | |||
@Test | |||
public void shouldFailIfExplicitUnexistingBinaryFolder() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'bin' does not exist for 'com.foo.project' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-binary").getAbsolutePath()); | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-binary").getAbsolutePath()); | |||
loadProjectDefinition("simple-project-with-unexisting-binary"); | |||
} | |||
@Test | |||
public void shouldFailIfExplicitUnmatchingLibFolder() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("No file matching pattern \"libs/*.txt\" in directory \"" | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-lib").getAbsolutePath()); | |||
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-lib").getAbsolutePath()); | |||
loadProjectDefinition("simple-project-with-unexisting-lib"); | |||
} | |||
@Test | |||
public void shouldFailIfExplicitUnexistingTestFolderOnModule() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)"); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)"); | |||
loadProjectDefinition("multi-module-with-explicit-unexisting-test-dir"); | |||
} | |||
@Test | |||
public void shouldFailIfExplicitUnexistingBinaryFolderOnModule() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("The folder 'bin' does not exist for 'module1' (base directory = " | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-binary-dir").getAbsolutePath() + File.separator + "module1)"); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-binary-dir").getAbsolutePath() + File.separator + "module1)"); | |||
loadProjectDefinition("multi-module-with-explicit-unexisting-binary-dir"); | |||
} | |||
@Test | |||
public void shouldFailIfExplicitUnmatchingLibFolderOnModule() throws IOException { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("No file matching pattern \"lib/*.jar\" in directory \"" | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1\""); | |||
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1\""); | |||
loadProjectDefinition("multi-module-with-explicit-unexisting-lib"); | |||
} | |||
@@ -396,14 +396,14 @@ public class SonarProjectBuilderTest { | |||
props.setProperty("foobar.tests", "src/test/java"); | |||
props.setProperty("foobar.binaries", "target/classes"); | |||
Properties moduleProps = SonarProjectBuilder.extractModuleProperties("bar", props); | |||
Properties moduleProps = ProjectReactorBuilder.extractModuleProperties("bar", props); | |||
assertThat(moduleProps.size()).isEqualTo(0); | |||
moduleProps = SonarProjectBuilder.extractModuleProperties("foo", props); | |||
moduleProps = ProjectReactorBuilder.extractModuleProperties("foo", props); | |||
assertThat(moduleProps.size()).isEqualTo(1); | |||
assertThat(moduleProps.get("sources")).isEqualTo("src/main/java"); | |||
moduleProps = SonarProjectBuilder.extractModuleProperties("foobar", props); | |||
moduleProps = ProjectReactorBuilder.extractModuleProperties("foobar", props); | |||
assertThat(moduleProps.size()).isEqualTo(2); | |||
assertThat(moduleProps.get("tests")).isEqualTo("src/test/java"); | |||
assertThat(moduleProps.get("binaries")).isEqualTo("target/classes"); | |||
@@ -415,10 +415,10 @@ public class SonarProjectBuilderTest { | |||
props.setProperty("foo1", "bla"); | |||
props.setProperty("foo4", "bla"); | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3"); | |||
SonarProjectBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"}); | |||
ProjectReactorBuilder.checkMandatoryProperties(props, new String[]{"foo1", "foo2", "foo3"}); | |||
} | |||
@Test | |||
@@ -427,10 +427,10 @@ public class SonarProjectBuilderTest { | |||
props.setProperty("foo1", "bla"); | |||
props.setProperty("sonar.projectKey", "my-project"); | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3"); | |||
SonarProjectBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"}); | |||
ProjectReactorBuilder.checkMandatoryProperties(props, new String[]{"foo1", "foo2", "foo3"}); | |||
} | |||
@Test | |||
@@ -439,7 +439,7 @@ public class SonarProjectBuilderTest { | |||
props.setProperty("foo1", "bla"); | |||
props.setProperty("foo4", "bla"); | |||
SonarProjectBuilder.checkMandatoryProperties(props, new String[] {"foo1"}); | |||
ProjectReactorBuilder.checkMandatoryProperties(props, new String[]{"foo1"}); | |||
// No exception should be thrown | |||
} | |||
@@ -447,22 +447,22 @@ public class SonarProjectBuilderTest { | |||
@Test | |||
public void shouldFilterFiles() throws Exception { | |||
File baseDir = TestUtils.getResource(this.getClass(), "shouldFilterFiles"); | |||
assertThat(SonarProjectBuilder.getLibraries(baseDir, "in*.txt").length).isEqualTo(1); | |||
assertThat(SonarProjectBuilder.getLibraries(baseDir, "*.txt").length).isEqualTo(2); | |||
assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/in*.txt").length).isEqualTo(1); | |||
assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/*.txt").length).isEqualTo(2); | |||
assertThat(ProjectReactorBuilder.getLibraries(baseDir, "in*.txt").length).isEqualTo(1); | |||
assertThat(ProjectReactorBuilder.getLibraries(baseDir, "*.txt").length).isEqualTo(2); | |||
assertThat(ProjectReactorBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/in*.txt").length).isEqualTo(1); | |||
assertThat(ProjectReactorBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/*.txt").length).isEqualTo(2); | |||
} | |||
@Test | |||
public void shouldWorkWithAbsolutePath() throws Exception { | |||
File baseDir = new File("not-exists"); | |||
String absolutePattern = TestUtils.getResource(this.getClass(), "shouldFilterFiles").getAbsolutePath() + "/in*.txt"; | |||
assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), absolutePattern).length).isEqualTo(1); | |||
assertThat(ProjectReactorBuilder.getLibraries(baseDir.getParentFile(), absolutePattern).length).isEqualTo(1); | |||
} | |||
@Test | |||
public void shouldGetRelativeFile() { | |||
assertThat(SonarProjectBuilder.getFileFromPath("shouldGetFile/foo.properties", TestUtils.getResource(this.getClass(), "/"))) | |||
assertThat(ProjectReactorBuilder.getFileFromPath("shouldGetFile/foo.properties", TestUtils.getResource(this.getClass(), "/"))) | |||
.isEqualTo(TestUtils.getResource(this.getClass(), "shouldGetFile/foo.properties")); | |||
} | |||
@@ -470,7 +470,7 @@ public class SonarProjectBuilderTest { | |||
public void shouldGetAbsoluteFile() { | |||
File file = TestUtils.getResource(this.getClass(), "shouldGetFile/foo.properties"); | |||
assertThat(SonarProjectBuilder.getFileFromPath(file.getAbsolutePath(), TestUtils.getResource(this.getClass(), "/"))) | |||
assertThat(ProjectReactorBuilder.getFileFromPath(file.getAbsolutePath(), TestUtils.getResource(this.getClass(), "/"))) | |||
.isEqualTo(file); | |||
} | |||
@@ -488,7 +488,7 @@ public class SonarProjectBuilderTest { | |||
childProps.setProperty("existingChildProp", "barChild"); | |||
childProps.setProperty("otherProp", "tutuChild"); | |||
SonarProjectBuilder.mergeParentProperties(childProps, parentProps); | |||
ProjectReactorBuilder.mergeParentProperties(childProps, parentProps); | |||
assertThat(childProps.size()).isEqualTo(3); | |||
assertThat(childProps.getProperty("toBeMergeProps")).isEqualTo("fooParent"); | |||
@@ -502,7 +502,7 @@ public class SonarProjectBuilderTest { | |||
@Test | |||
public void shouldInitRootWorkDir() { | |||
SonarProjectBuilder builder = SonarProjectBuilder.create(new Properties()); | |||
ProjectReactorBuilder builder = new ProjectReactorBuilder(new Properties()); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
File workDir = builder.initRootProjectWorkDir(baseDir); | |||
@@ -514,7 +514,7 @@ public class SonarProjectBuilderTest { | |||
public void shouldInitWorkDirWithCustomRelativeFolder() { | |||
Properties properties = new Properties(); | |||
properties.put("sonar.working.directory", ".foo"); | |||
SonarProjectBuilder builder = SonarProjectBuilder.create(properties); | |||
ProjectReactorBuilder builder = new ProjectReactorBuilder(properties); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
File workDir = builder.initRootProjectWorkDir(baseDir); | |||
@@ -526,7 +526,7 @@ public class SonarProjectBuilderTest { | |||
public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { | |||
Properties properties = new Properties(); | |||
properties.put("sonar.working.directory", new File("src").getAbsolutePath()); | |||
SonarProjectBuilder builder = SonarProjectBuilder.create(properties); | |||
ProjectReactorBuilder builder = new ProjectReactorBuilder(properties); | |||
File baseDir = new File("target/tmp/baseDir"); | |||
File workDir = builder.initRootProjectWorkDir(baseDir); | |||
@@ -539,7 +539,7 @@ public class SonarProjectBuilderTest { | |||
Properties props = new Properties(); | |||
props.put("sonar.projectKey", "my-module-key"); | |||
SonarProjectBuilder.prefixProjectKeyWithParentKey(props, "my-parent-key"); | |||
ProjectReactorBuilder.prefixProjectKeyWithParentKey(props, "my-parent-key"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("my-parent-key:my-module-key"); | |||
} | |||
@@ -557,15 +557,15 @@ public class SonarProjectBuilderTest { | |||
Properties props2 = new Properties(); | |||
props2.put("sonar.projectKey", "mod2"); | |||
ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2); | |||
SonarProjectBuilder.checkUniquenessOfChildKey(mod2, root); | |||
ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root); | |||
// Now, add it and check again | |||
root.addSubProject(mod2); | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Project 'root' can't have 2 modules with the following key: mod2"); | |||
SonarProjectBuilder.checkUniquenessOfChildKey(mod2, root); | |||
ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root); | |||
} | |||
@Test | |||
@@ -574,30 +574,29 @@ public class SonarProjectBuilderTest { | |||
props.put("sonar.projectVersion", "1.0"); | |||
// should be set | |||
SonarProjectBuilder.setProjectKeyAndNameIfNotDefined(props, "foo"); | |||
ProjectReactorBuilder.setProjectKeyAndNameIfNotDefined(props, "foo"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo"); | |||
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo"); | |||
// but not this 2nd time | |||
SonarProjectBuilder.setProjectKeyAndNameIfNotDefined(props, "bar"); | |||
ProjectReactorBuilder.setProjectKeyAndNameIfNotDefined(props, "bar"); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo"); | |||
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo"); | |||
} | |||
@Test | |||
public void shouldFailToLoadPropertiesFile() throws Exception { | |||
thrown.expect(RunnerException.class); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Impossible to read the property file"); | |||
SonarProjectBuilder.toProperties(new File("foo.properties")); | |||
ProjectReactorBuilder.toProperties(new File("foo.properties")); | |||
} | |||
private ProjectDefinition loadProjectDefinition(String projectFolder) throws FileNotFoundException, IOException { | |||
Properties props = SonarProjectBuilder.toProperties(TestUtils.getResource(this.getClass(), projectFolder + "/sonar-project.properties")); | |||
private ProjectDefinition loadProjectDefinition(String projectFolder) throws IOException { | |||
Properties props = ProjectReactorBuilder.toProperties(TestUtils.getResource(this.getClass(), projectFolder + "/sonar-project.properties")); | |||
props.put("sonar.projectBaseDir", TestUtils.getResource(this.getClass(), projectFolder).getAbsolutePath()); | |||
ProjectDefinition projectDefinition = SonarProjectBuilder.create(props) | |||
.generateProjectDefinition(); | |||
return projectDefinition; | |||
ProjectReactor projectReactor = new ProjectReactorBuilder(props).build(); | |||
return projectReactor.getRoot(); | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* Sonar Runner - Implementation | |||
* Sonar Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -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 | |||
*/ | |||
package org.sonar.runner.internal.batch; | |||
package org.sonar.runner.batch; | |||
import org.apache.commons.io.IOUtils; | |||
import org.junit.Test; | |||
@@ -29,22 +29,31 @@ import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class SonarRunnerUtilsTest { | |||
public class UtilsTest { | |||
@Test | |||
public void shouldGetList() { | |||
Properties props = new Properties(); | |||
props.put("prop", " foo , bar , \n\ntoto,tutu"); | |||
assertThat(SonarRunnerUtils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); | |||
assertThat(Utils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); | |||
} | |||
// | |||
// @Test | |||
// public void test_props() { | |||
// Properties p1 = new Properties(); | |||
// p1.setProperty("foo", "bar"); | |||
// Properties p2 = new Properties(); | |||
// p2.putAll(p1); | |||
// assertThat(p2.getProperty("foo")).isEqualTo("bar"); | |||
// } | |||
@Test | |||
public void shouldGetListFromFile() throws IOException { | |||
String filePath = "shouldGetList/foo.properties"; | |||
Properties props = loadPropsFromFile(filePath); | |||
assertThat(SonarRunnerUtils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); | |||
assertThat(Utils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu"); | |||
} | |||
private Properties loadPropsFromFile(String filePath) throws IOException { |