@@ -6,21 +6,14 @@ | |||
<version>23</version> | |||
</parent> | |||
<groupId>org.sonarsource.sonar-runner</groupId> | |||
<artifactId>sonar-runner</artifactId> | |||
<groupId>org.sonarsource.scanner</groupId> | |||
<artifactId>sonar-scanner</artifactId> | |||
<version>2.5-SNAPSHOT</version> | |||
<packaging>pom</packaging> | |||
<name>SonarQube Runner</name> | |||
<url>http://docs.codehaus.org/display/SONAR/Analyzing+with+SonarQube+Runner</url> | |||
<packaging>jar</packaging> | |||
<name>SonarQube Scanner</name> | |||
<url>http://docs.sonarqube.org/display/SONAR/Analyzing+with+SonarQube+Scanner</url> | |||
<inceptionYear>2011</inceptionYear> | |||
<modules> | |||
<module>sonar-runner-api</module> | |||
<module>sonar-runner-batch</module> | |||
<module>sonar-runner-batch-interface</module> | |||
<module>sonar-runner-cli</module> | |||
</modules> | |||
<organization> | |||
<name>SonarSource</name> | |||
<url>http://www.sonarsource.com</url> | |||
@@ -35,103 +28,142 @@ | |||
</licenses> | |||
<scm> | |||
<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> | |||
<connection>scm:git:git@github.com:SonarSource/sonar-scanner-cli.git</connection> | |||
<developerConnection>scm:git:git@github.com:SonarSource/sonar-scanner-cli.git</developerConnection> | |||
<url>https://github.com/SonarSource/sonar-scanner-cli</url> | |||
<tag>HEAD</tag> | |||
</scm> | |||
<issueManagement> | |||
<system>JIRA</system> | |||
<url>http://jira.codehaus.org/browse/SONARPLUGINS/component/14640</url> | |||
<url>https://jira.sonarsource.com/browse/SONARSCANNER</url> | |||
</issueManagement> | |||
<properties> | |||
<maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile> | |||
</properties> | |||
<profiles> | |||
<profile> | |||
<id>release</id> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-javadoc-plugin</artifactId> | |||
<version>2.9</version> | |||
<executions> | |||
<execution> | |||
<id>attach-javadocs</id> | |||
<goals> | |||
<goal>jar</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</profile> | |||
</profiles> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.sonarsource.sonar-runner</groupId> | |||
<artifactId>sonar-runner-api</artifactId> | |||
<version>2.5-RC1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.findbugs</groupId> | |||
<artifactId>jsr305</artifactId> | |||
<version>2.0.1</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependencyManagement> | |||
<dependencies> | |||
<dependency> | |||
<groupId>commons-lang</groupId> | |||
<artifactId>commons-lang</artifactId> | |||
<version>2.6</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<version>2.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.easytesting</groupId> | |||
<artifactId>fest-assert</artifactId> | |||
<version>1.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.github.kevinsawicki</groupId> | |||
<artifactId>http-request</artifactId> | |||
<version>4.1</version> | |||
</dependency> | |||
<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.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
<version>1.10</version> | |||
<!-- Unit tests --> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.11</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> | |||
</dependencyManagement> | |||
<dependency> | |||
<groupId>com.jayway.awaitility</groupId> | |||
<artifactId>awaitility</artifactId> | |||
<version>1.6.3</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-dependency-plugin</artifactId> | |||
<version>2.8</version> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<version>2.3</version> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<excludes> | |||
<exclude>org/sonar/runner/commonsio/*</exclude> | |||
</excludes> | |||
<archive> | |||
<manifest> | |||
<addClasspath>false</addClasspath> | |||
<mainClass>org.sonar.runner.cli.Main</mainClass> | |||
</manifest> | |||
</archive> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<version>2.3</version> | |||
<executions> | |||
<execution> | |||
<phase>package</phase> | |||
<goals> | |||
<goal>shade</goal> | |||
</goals> | |||
<configuration> | |||
<createDependencyReducedPom>true</createDependencyReducedPom> | |||
<minimizeJar>true</minimizeJar> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-assembly-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<phase>package</phase> | |||
<goals> | |||
<goal>single</goal> | |||
</goals> | |||
<configuration> | |||
<finalName>sonar-runner-${project.version}</finalName> | |||
<appendAssemblyId>false</appendAssemblyId> | |||
<escapeString>\</escapeString> | |||
<descriptors> | |||
<descriptor>${project.basedir}/assembly.xml</descriptor> | |||
</descriptors> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-enforcer-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>enforce-distribution-size</id> | |||
<goals> | |||
<goal>enforce</goal> | |||
</goals> | |||
<phase>verify</phase> | |||
<configuration> | |||
<rules> | |||
<requireFilesSize> | |||
<minsize>160000</minsize> | |||
<maxsize>185000</maxsize> | |||
<files> | |||
<file>${project.build.directory}/sonar-runner-${project.version}.zip</file> | |||
</files> | |||
</requireFilesSize> | |||
</rules> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,215 +0,0 @@ | |||
<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.sonarsource.sonar-runner</groupId> | |||
<artifactId>sonar-runner</artifactId> | |||
<version>2.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>sonar-runner-api</artifactId> | |||
<name>SonarQube Runner - API</name> | |||
<dependencies> | |||
<!-- Dependencies with scope "compile" are shaded and removed from transitive dependencies--> | |||
<dependency> | |||
<groupId>com.github.kevinsawicki</groupId> | |||
<artifactId>http-request</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.findbugs</groupId> | |||
<artifactId>jsr305</artifactId> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>sonar-runner-batch</artifactId> | |||
<version>${project.version}</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>sonar-runner-batch-interface</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<!-- unit tests --> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.easytesting</groupId> | |||
<artifactId>fest-assert</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.assertj</groupId> | |||
<artifactId>assertj-core</artifactId> | |||
<version>2.2.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mortbay.jetty</groupId> | |||
<artifactId>jetty</artifactId> | |||
<version>6.1.25</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-lang</groupId> | |||
<artifactId>commons-lang</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<!-- used to compare results --> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<resources> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
</resource> | |||
</resources> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<archive> | |||
<!-- Use the MANIFEST generated by maven-bundle-plugin --> | |||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> | |||
<manifest> | |||
<addClasspath>true</addClasspath> | |||
</manifest> | |||
</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>${project.groupId}</groupId> | |||
<artifactId>sonar-runner-batch</artifactId> | |||
<version>${project.version}</version> | |||
<type>jar</type> | |||
<overWrite>false</overWrite> | |||
<outputDirectory>${project.build.outputDirectory}</outputDirectory> | |||
<destFileName>sonar-runner-batch.jar</destFileName> | |||
</artifactItem> | |||
</artifactItems> | |||
<overWriteReleases>true</overWriteReleases> | |||
<overWriteSnapshots>true</overWriteSnapshots> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-shade-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<phase>package</phase> | |||
<goals> | |||
<goal>shade</goal> | |||
</goals> | |||
<configuration> | |||
<createDependencyReducedPom>true</createDependencyReducedPom> | |||
<minimizeJar>true</minimizeJar> | |||
<relocations> | |||
<relocation> | |||
<pattern>com.github.kevinsawicki.http</pattern> | |||
<shadedPattern>org.sonar.runner.kevinsawicki</shadedPattern> | |||
</relocation> | |||
<relocation> | |||
<pattern>org.apache.commons.io</pattern> | |||
<shadedPattern>org.sonar.runner.commonsio</shadedPattern> | |||
</relocation> | |||
<relocation> | |||
<pattern>org.sonar.home</pattern> | |||
<shadedPattern>org.sonar.runner.home</shadedPattern> | |||
</relocation> | |||
</relocations> | |||
</configuration> | |||
</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>JavaSE-1.7</Bundle-RequiredExecutionEnvironment> | |||
</instructions> | |||
</configuration> | |||
<executions> | |||
<execution> | |||
<id>bundle-manifest</id> | |||
<phase>process-classes</phase> | |||
<goals> | |||
<goal>manifest</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-release-plugin</artifactId> | |||
<configuration> | |||
<releaseProfiles>sign</releaseProfiles> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
<profiles> | |||
<profile> | |||
<id>sign</id> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jarsigner-plugin</artifactId> | |||
<version>1.4</version> | |||
<executions> | |||
<execution> | |||
<id>sign</id> | |||
<goals> | |||
<goal>sign</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
<configuration> | |||
<alias>sonarsource-codesign-2015-2017</alias> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</profile> | |||
</profiles> | |||
</project> |
@@ -1,78 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache.Logger; | |||
import java.io.File; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
import java.util.Properties; | |||
class Dirs { | |||
private final Logger logger; | |||
Dirs(Logger logger) { | |||
this.logger = logger; | |||
} | |||
void init(Properties p) { | |||
boolean onProject = Utils.taskRequiresProject(p); | |||
if (onProject) { | |||
initProjectDirs(p); | |||
} else { | |||
initTaskDirs(p); | |||
} | |||
} | |||
private void initProjectDirs(Properties p) { | |||
String pathString = p.getProperty(ScanProperties.PROJECT_BASEDIR, ""); | |||
Path absoluteProjectPath = Paths.get(pathString).toAbsolutePath().normalize(); | |||
if (!Files.isDirectory(absoluteProjectPath)) { | |||
throw new IllegalStateException("Project home must be an existing directory: " + pathString); | |||
} | |||
p.setProperty(ScanProperties.PROJECT_BASEDIR, absoluteProjectPath.toString()); | |||
Path workDirPath; | |||
pathString = p.getProperty(RunnerProperties.WORK_DIR, ""); | |||
if ("".equals(pathString.trim())) { | |||
workDirPath = absoluteProjectPath.resolve(".sonar"); | |||
} else { | |||
workDirPath = Paths.get(pathString); | |||
if (!workDirPath.isAbsolute()) { | |||
workDirPath = absoluteProjectPath.resolve(pathString); | |||
} | |||
} | |||
p.setProperty(RunnerProperties.WORK_DIR, workDirPath.normalize().toString()); | |||
logger.debug("Work directory: " + workDirPath.normalize().toString()); | |||
} | |||
/** | |||
* Non-scan task | |||
*/ | |||
private static void initTaskDirs(Properties p) { | |||
String path = p.getProperty(RunnerProperties.WORK_DIR, "."); | |||
File workDir = new File(path); | |||
p.setProperty(RunnerProperties.WORK_DIR, workDir.getAbsolutePath()); | |||
} | |||
} |
@@ -1,299 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache.Logger; | |||
import org.sonar.runner.impl.ClassloadRules; | |||
import java.nio.charset.Charset; | |||
import java.security.InvalidParameterException; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.util.Properties; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
import org.sonar.runner.impl.InternalProperties; | |||
import org.sonar.runner.impl.IsolatedLauncherFactory; | |||
import org.sonar.runner.impl.VersionUtils; | |||
/** | |||
* Entry point to run SonarQube analysis programmatically. | |||
* @since 2.2 | |||
*/ | |||
public class EmbeddedRunner { | |||
private final IsolatedLauncherFactory launcherFactory; | |||
private IsolatedLauncher launcher; | |||
private final LogOutput logOutput; | |||
private final Properties globalProperties = new Properties(); | |||
private final List<Object> extensions = new ArrayList<>(); | |||
private final Logger logger; | |||
private final Set<String> classloaderMask = new HashSet<>(); | |||
private final Set<String> classloaderUnmask = new HashSet<>(); | |||
EmbeddedRunner(IsolatedLauncherFactory bl, Logger logger, LogOutput logOutput) { | |||
this.logger = logger; | |||
this.launcherFactory = bl; | |||
this.logOutput = logOutput; | |||
this.classloaderUnmask.add("org.sonar.runner.batch."); | |||
} | |||
public static EmbeddedRunner create(final LogOutput logOutput) { | |||
Logger logger = new LoggerAdapter(logOutput); | |||
return new EmbeddedRunner(new IsolatedLauncherFactory(logger), logger, logOutput); | |||
} | |||
public Properties globalProperties() { | |||
Properties clone = new Properties(); | |||
clone.putAll(globalProperties); | |||
return clone; | |||
} | |||
public EmbeddedRunner unmask(String fqcnPrefix) { | |||
checkLauncherDoesntExist(); | |||
classloaderUnmask.add(fqcnPrefix); | |||
return this; | |||
} | |||
public EmbeddedRunner mask(String fqcnPrefix) { | |||
checkLauncherDoesntExist(); | |||
classloaderMask.add(fqcnPrefix); | |||
return this; | |||
} | |||
/** | |||
* Declare Sonar properties, for example sonar.projectKey=>foo. | |||
* These might be used at different stages (on {@link #start() or #runAnalysis(Properties)}, depending on the | |||
* property and SQ version. | |||
* | |||
* @see #setProperty(String, String) | |||
*/ | |||
public EmbeddedRunner addGlobalProperties(Properties p) { | |||
globalProperties.putAll(p); | |||
return this; | |||
} | |||
/** | |||
* Declare a SonarQube property. | |||
* These might be used at different stages (on {@link #start() or #runAnalysis(Properties)}, depending on the | |||
* property and SQ version. | |||
* | |||
* @see RunnerProperties | |||
* @see ScanProperties | |||
*/ | |||
public EmbeddedRunner setGlobalProperty(String key, String value) { | |||
globalProperties.setProperty(key, value); | |||
return this; | |||
} | |||
public String globalProperty(String key, @Nullable String defaultValue) { | |||
return globalProperties.getProperty(key, defaultValue); | |||
} | |||
/** | |||
* User-agent used in the HTTP requests to the SonarQube server | |||
*/ | |||
public EmbeddedRunner setApp(String app, String version) { | |||
setGlobalProperty(InternalProperties.RUNNER_APP, app); | |||
setGlobalProperty(InternalProperties.RUNNER_APP_VERSION, version); | |||
return this; | |||
} | |||
public String app() { | |||
return globalProperty(InternalProperties.RUNNER_APP, null); | |||
} | |||
/** | |||
* Add extensions to the batch's object container. | |||
* Only supported until SQ 5.1. For more recent versions, an exception is thrown | |||
* @param objs | |||
*/ | |||
public EmbeddedRunner addExtensions(Object... objs) { | |||
checkLauncherExists(); | |||
if (VersionUtils.isAtLeast52(launcher.getVersion())) { | |||
throw new IllegalStateException("not supported in current SonarQube version: " + launcher.getVersion()); | |||
} | |||
extensions.addAll(Arrays.asList(objs)); | |||
return this; | |||
} | |||
public String appVersion() { | |||
return globalProperty(InternalProperties.RUNNER_APP_VERSION, null); | |||
} | |||
/** | |||
* Launch an analysis. | |||
* Runner must have been started - see {@link #start()}. | |||
*/ | |||
public void runAnalysis(Properties analysisProperties) { | |||
runAnalysis(analysisProperties, null); | |||
} | |||
/** | |||
* Launch an analysis, providing optionally a issue listener. | |||
* Runner must have been started - see {@link #start()}. | |||
* Issue listener is supported starting in SQ 5.2. If a non-null listener is given for older versions, an exception is thrown | |||
*/ | |||
public void runAnalysis(Properties analysisProperties, @Nullable IssueListener issueListener) { | |||
checkLauncherExists(); | |||
Properties copy = new Properties(); | |||
copy.putAll(analysisProperties); | |||
initAnalysisProperties(copy); | |||
doExecute(copy, issueListener); | |||
} | |||
/** | |||
* Synchronizes the project's data in the local cache with the server, allowing analysis of the project to be done offline. | |||
* Runner must have been started - see {@link #start()}. | |||
* Only supported starting in SQ 5.2. For older versions, an exception is thrown | |||
*/ | |||
public void syncProject(String projectKey) { | |||
checkLauncherExists(); | |||
if (!VersionUtils.isAtLeast52(launcher.getVersion())) { | |||
throw new IllegalStateException("not supported in current SonarQube version: " + launcher.getVersion()); | |||
} | |||
launcher.syncProject(projectKey); | |||
} | |||
public void start() { | |||
start(false); | |||
} | |||
public void start(boolean preferCache) { | |||
initGlobalDefaultValues(); | |||
doStart(preferCache); | |||
} | |||
/** | |||
* Stops the batch. | |||
* Only supported starting in SQ 5.2. For older versions, this is a no-op. | |||
*/ | |||
public void stop() { | |||
checkLauncherExists(); | |||
doStop(); | |||
} | |||
public String serverVersion() { | |||
checkLauncherExists(); | |||
return launcher.getVersion(); | |||
} | |||
/** | |||
* @deprecated since 2.5 use {@link #start()}, {@link #runAnalysis(Properties)} and then {@link #stop()} | |||
*/ | |||
@Deprecated | |||
public final void execute() { | |||
start(); | |||
runAnalysis(new Properties()); | |||
stop(); | |||
} | |||
private void initGlobalDefaultValues() { | |||
setGlobalDefaultValue(RunnerProperties.HOST_URL, "http://localhost:9000"); | |||
setGlobalDefaultValue(InternalProperties.RUNNER_APP, "SonarQubeRunner"); | |||
setGlobalDefaultValue(InternalProperties.RUNNER_APP_VERSION, RunnerVersion.version()); | |||
} | |||
private void initAnalysisProperties(Properties p) { | |||
initSourceEncoding(p); | |||
new Dirs(logger).init(p); | |||
} | |||
void initSourceEncoding(Properties p) { | |||
boolean onProject = Utils.taskRequiresProject(p); | |||
if (onProject) { | |||
String sourceEncoding = p.getProperty(ScanProperties.PROJECT_SOURCE_ENCODING, ""); | |||
boolean platformDependent = false; | |||
if ("".equals(sourceEncoding)) { | |||
sourceEncoding = Charset.defaultCharset().name(); | |||
platformDependent = true; | |||
p.setProperty(ScanProperties.PROJECT_SOURCE_ENCODING, sourceEncoding); | |||
} | |||
logger.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + sourceEncoding + "\"" | |||
+ (platformDependent ? " (analysis is platform dependent)" : "")); | |||
} | |||
} | |||
private void setGlobalDefaultValue(String key, String value) { | |||
if (!globalProperties.containsKey(key)) { | |||
setGlobalProperty(key, value); | |||
} | |||
} | |||
protected void doStart(boolean preferCache) { | |||
checkLauncherDoesntExist(); | |||
ClassloadRules rules = new ClassloadRules(classloaderMask, classloaderUnmask); | |||
launcher = launcherFactory.createLauncher(globalProperties(), rules, preferCache); | |||
if (VersionUtils.isAtLeast52(launcher.getVersion())) { | |||
launcher.start(globalProperties(), new org.sonar.runner.batch.LogOutput() { | |||
@Override | |||
public void log(String formattedMessage, Level level) { | |||
logOutput.log(formattedMessage, LogOutput.Level.valueOf(level.name())); | |||
} | |||
}, preferCache); | |||
} | |||
} | |||
protected void doStop() { | |||
if (VersionUtils.isAtLeast52(launcher.getVersion())) { | |||
launcher.stop(); | |||
launcher = null; | |||
} | |||
} | |||
protected void doExecute(Properties analysisProperties, @Nullable IssueListener issueListener) { | |||
if (VersionUtils.isAtLeast52(launcher.getVersion())) { | |||
if (issueListener != null) { | |||
launcher.execute(analysisProperties, new IssueListenerAdapter(issueListener)); | |||
} else { | |||
launcher.execute(analysisProperties); | |||
} | |||
} else { | |||
if (issueListener != null) { | |||
throw new InvalidParameterException("Issue listeners not supported in current version: " + launcher.getVersion()); | |||
} | |||
Properties prop = new Properties(); | |||
prop.putAll(globalProperties()); | |||
prop.putAll(analysisProperties); | |||
launcher.executeOldVersion(prop, extensions); | |||
} | |||
} | |||
private void checkLauncherExists() { | |||
if (launcher == null) { | |||
throw new IllegalStateException("not started"); | |||
} | |||
} | |||
private void checkLauncherDoesntExist() { | |||
if (launcher != null) { | |||
throw new IllegalStateException("already started"); | |||
} | |||
} | |||
} |
@@ -1,290 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.concurrent.Immutable; | |||
@Immutable | |||
public final class Issue { | |||
private final String key; | |||
private final String componentKey; | |||
private final String message; | |||
private final String ruleKey; | |||
private final String ruleName; | |||
private final String status; | |||
private final String resolution; | |||
private final boolean isNew; | |||
private final String assigneeLogin; | |||
private final String assigneeName; | |||
private final String severity; | |||
private final Integer startLine; | |||
private final Integer startLineOffset; | |||
private final Integer endLine; | |||
private final Integer endLineOffset; | |||
private Issue(String key, String componentKey, String message, String ruleKey, String ruleName, String status, String resolution, boolean isNew, | |||
String assigneeLogin, String assigneeName, String severity, Integer startLine, Integer startLineOffset, Integer endLine, Integer endLineOffset) { | |||
super(); | |||
this.key = key; | |||
this.componentKey = componentKey; | |||
this.message = message; | |||
this.ruleKey = ruleKey; | |||
this.ruleName = ruleName; | |||
this.status = status; | |||
this.resolution = resolution; | |||
this.isNew = isNew; | |||
this.assigneeLogin = assigneeLogin; | |||
this.assigneeName = assigneeName; | |||
this.severity = severity; | |||
this.startLine = startLine; | |||
this.startLineOffset = startLineOffset; | |||
this.endLine = endLine; | |||
this.endLineOffset = endLineOffset; | |||
} | |||
public static class Builder { | |||
private String key; | |||
private String componentKey; | |||
private String message; | |||
private String ruleKey; | |||
private String ruleName; | |||
private String status; | |||
private String resolution; | |||
private boolean isNew; | |||
private String assigneeLogin; | |||
private String assigneeName; | |||
private String severity; | |||
private Integer startLine; | |||
private Integer startLineOffset; | |||
private Integer endLine; | |||
private Integer endLineOffset; | |||
public Integer getStartLine() { | |||
return startLine; | |||
} | |||
public Builder setStartLine(Integer startLine) { | |||
this.startLine = startLine; | |||
return this; | |||
} | |||
public Integer getStartLineOffset() { | |||
return startLineOffset; | |||
} | |||
public Builder setStartLineOffset(Integer startLineOffset) { | |||
this.startLineOffset = startLineOffset; | |||
return this; | |||
} | |||
public Integer getEndLine() { | |||
return endLine; | |||
} | |||
public Builder setEndLine(Integer endLine) { | |||
this.endLine = endLine; | |||
return this; | |||
} | |||
public Integer getEndLineOffset() { | |||
return endLineOffset; | |||
} | |||
public Builder setEndLineOffset(Integer endLineOffset) { | |||
this.endLineOffset = endLineOffset; | |||
return this; | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
public Builder setKey(String key) { | |||
this.key = key; | |||
return this; | |||
} | |||
public String getComponentKey() { | |||
return componentKey; | |||
} | |||
public Builder setComponentKey(String componentKey) { | |||
this.componentKey = componentKey; | |||
return this; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
public Builder setMessage(String message) { | |||
this.message = message; | |||
return this; | |||
} | |||
public String getRuleKey() { | |||
return ruleKey; | |||
} | |||
public Builder setRuleKey(String ruleKey) { | |||
this.ruleKey = ruleKey; | |||
return this; | |||
} | |||
public String getRuleName() { | |||
return ruleKey; | |||
} | |||
public Builder setRuleName(String ruleName) { | |||
this.ruleName = ruleName; | |||
return this; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public Builder setStatus(String status) { | |||
this.status = status; | |||
return this; | |||
} | |||
public String getResolution() { | |||
return resolution; | |||
} | |||
public Builder setResolution(String resolution) { | |||
this.resolution = resolution; | |||
return this; | |||
} | |||
public boolean isNew() { | |||
return isNew; | |||
} | |||
public Builder setNew(boolean isNew) { | |||
this.isNew = isNew; | |||
return this; | |||
} | |||
public String getAssigneeLogin() { | |||
return assigneeLogin; | |||
} | |||
public Builder setAssigneeLogin(String assigneeLogin) { | |||
this.assigneeLogin = assigneeLogin; | |||
return this; | |||
} | |||
public String getAssigneeName() { | |||
return assigneeName; | |||
} | |||
public Builder setAssigneeName(String assigneeName) { | |||
this.assigneeName = assigneeName; | |||
return this; | |||
} | |||
public String getSeverity() { | |||
return severity; | |||
} | |||
public Builder setSeverity(String severity) { | |||
this.severity = severity; | |||
return this; | |||
} | |||
public Issue build() { | |||
return new Issue(key, componentKey, message, ruleKey, ruleName, status, resolution, isNew, assigneeLogin, | |||
assigneeName, severity, startLine, startLineOffset, endLine, endLineOffset); | |||
} | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
public String getComponentKey() { | |||
return componentKey; | |||
} | |||
public Integer getStartLine() { | |||
return startLine; | |||
} | |||
/** | |||
* @return <code>null</code> if it isn't supported by the sonar-batch being used (< 5.3). | |||
*/ | |||
public Integer getStartLineOffset() { | |||
return startLineOffset; | |||
} | |||
public Integer getEndLine() { | |||
return endLine; | |||
} | |||
/** | |||
* @return <code>null</code> if it isn't supported by the sonar-batch being used (< 5.3). | |||
*/ | |||
public Integer getEndLineOffset() { | |||
return endLineOffset; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
public String getRuleKey() { | |||
return ruleKey; | |||
} | |||
public String getRuleName() { | |||
return ruleName; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public String getResolution() { | |||
return resolution; | |||
} | |||
public boolean isNew() { | |||
return isNew; | |||
} | |||
public String getAssigneeLogin() { | |||
return assigneeLogin; | |||
} | |||
public String getAssigneeName() { | |||
return assigneeName; | |||
} | |||
public String getSeverity() { | |||
return severity; | |||
} | |||
} |
@@ -1,24 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
public interface IssueListener { | |||
void handle(Issue issue); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
class IssueListenerAdapter implements org.sonar.runner.batch.IssueListener { | |||
private IssueListener apiIssueListener; | |||
public IssueListenerAdapter(IssueListener apiIssueListener) { | |||
this.apiIssueListener = apiIssueListener; | |||
} | |||
@Override | |||
public void handle(org.sonar.runner.batch.IssueListener.Issue issue) { | |||
apiIssueListener.handle(transformIssue(issue)); | |||
} | |||
private static org.sonar.runner.api.Issue transformIssue(org.sonar.runner.batch.IssueListener.Issue batchIssue) { | |||
org.sonar.runner.api.Issue.Builder issueBuilder = org.sonar.runner.api.Issue.builder(); | |||
issueBuilder.setAssigneeLogin(batchIssue.getAssigneeLogin()); | |||
issueBuilder.setAssigneeName(batchIssue.getAssigneeName()); | |||
issueBuilder.setComponentKey(batchIssue.getComponentKey()); | |||
issueBuilder.setKey(batchIssue.getKey()); | |||
issueBuilder.setMessage(batchIssue.getMessage()); | |||
issueBuilder.setNew(batchIssue.isNew()); | |||
issueBuilder.setResolution(batchIssue.getResolution()); | |||
issueBuilder.setRuleKey(batchIssue.getRuleKey()); | |||
issueBuilder.setRuleName(batchIssue.getRuleName()); | |||
issueBuilder.setSeverity(batchIssue.getSeverity()); | |||
issueBuilder.setStatus(batchIssue.getStatus()); | |||
issueBuilder.setStartLine(batchIssue.getStartLine()); | |||
issueBuilder.setStartLineOffset(batchIssue.getStartLineOffset()); | |||
issueBuilder.setEndLine(batchIssue.getEndLine()); | |||
issueBuilder.setEndLineOffset(batchIssue.getEndLineOffset()); | |||
return issueBuilder.build(); | |||
} | |||
} |
@@ -1,29 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
public interface LogOutput { | |||
void log(String formattedMessage, Level level); | |||
enum Level { | |||
ERROR, WARN, INFO, DEBUG, TRACE; | |||
} | |||
} |
@@ -1,61 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache.Logger; | |||
import java.io.PrintWriter; | |||
import java.io.StringWriter; | |||
class LoggerAdapter implements Logger { | |||
private LogOutput logOutput; | |||
LoggerAdapter(LogOutput logOutput) { | |||
this.logOutput = logOutput; | |||
} | |||
@Override | |||
public void warn(String msg) { | |||
logOutput.log(msg, LogOutput.Level.WARN); | |||
} | |||
@Override | |||
public void info(String msg) { | |||
logOutput.log(msg, LogOutput.Level.INFO); | |||
} | |||
@Override | |||
public void error(String msg, Throwable t) { | |||
StringWriter errors = new StringWriter(); | |||
t.printStackTrace(new PrintWriter(errors)); | |||
logOutput.log(msg + "\n" + errors.toString(), LogOutput.Level.ERROR); | |||
} | |||
@Override | |||
public void error(String msg) { | |||
logOutput.log(msg, LogOutput.Level.ERROR); | |||
} | |||
@Override | |||
public void debug(String msg) { | |||
logOutput.log(msg, LogOutput.Level.DEBUG); | |||
} | |||
} |
@@ -1,45 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
/** | |||
* Mostly used properties that can be passed to {@link EmbeddedRunner#addGlobalProperties(java.util.Properties)}. | |||
* See <a href="http://docs.sonarqube.org/display/SONAR/Analysis+Parameters">documentation</a> for more properties. | |||
* | |||
* @since 2.2 | |||
*/ | |||
public interface RunnerProperties { | |||
/** | |||
* HTTP URL of Sonar server, "http://localhost:9000" by default | |||
*/ | |||
String HOST_URL = "sonar.host.url"; | |||
/** | |||
* Task to execute, "scan" by default | |||
* @deprecated since 2.5 No more task starting from SQ 5.2 | |||
*/ | |||
@Deprecated | |||
String TASK = "sonar.task"; | |||
/** | |||
* Working directory containing generated reports and temporary data. | |||
*/ | |||
String WORK_DIR = "sonar.working.directory"; | |||
} |
@@ -1,48 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.Scanner; | |||
/** | |||
* Version of this sonar-runner API. | |||
* | |||
* @since 2.2 | |||
*/ | |||
public enum RunnerVersion { | |||
INSTANCE; | |||
private String version; | |||
private RunnerVersion() { | |||
Scanner scanner = new Scanner(getClass().getResourceAsStream("/org/sonar/runner/api/version.txt"), "UTF-8"); | |||
try { | |||
this.version = scanner.next(); | |||
} finally { | |||
scanner.close(); | |||
} | |||
} | |||
public static String version() { | |||
return INSTANCE.version; | |||
} | |||
} |
@@ -1,79 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
/** | |||
* Most commonly used properties for a SonarQube analysis. These properties are passed to {@link EmbeddedRunner#runAnalysis(java.util.Properties)}. | |||
* See <a href="http://docs.sonarqube.org/display/SONAR/Analysis+Parameters">documentation</a> for more properties. | |||
* | |||
* @since 2.2 | |||
*/ | |||
public interface ScanProperties { | |||
/** | |||
* Default task | |||
* | |||
* @see RunnerProperties#TASK | |||
* @deprecated since 2.5 No more task since SQ 5.2 | |||
*/ | |||
@Deprecated | |||
String SCAN_TASK = "scan"; | |||
/** | |||
* Required project key | |||
*/ | |||
String PROJECT_KEY = "sonar.projectKey"; | |||
/** | |||
* Used to define the exact key of each module. If {@link #PROJECT_KEY} is used instead on a module, then final key of the module will be <parent module key>:<PROJECT_KEY>. | |||
* @since SonarQube 4.1 | |||
*/ | |||
String MODULE_KEY = "sonar.moduleKey"; | |||
String PROJECT_NAME = "sonar.projectName"; | |||
String PROJECT_VERSION = "sonar.projectVersion"; | |||
/** | |||
* Optional description | |||
*/ | |||
String PROJECT_DESCRIPTION = "sonar.projectDescription"; | |||
/** | |||
* Required paths to source directories, separated by commas, for example: "srcDir1,srcDir2" | |||
*/ | |||
String PROJECT_SOURCE_DIRS = "sonar.sources"; | |||
/** | |||
* Optional paths to test directories, separated by commas, for example: "testDir1,testDir2" | |||
*/ | |||
String PROJECT_TEST_DIRS = "sonar.tests"; | |||
/** | |||
* Property used to specify the base directory of the project to analyse. Default is ".". | |||
*/ | |||
String PROJECT_BASEDIR = "sonar.projectBaseDir"; | |||
/** | |||
* Encoding of source and test files. By default it's the platform encoding. | |||
*/ | |||
String PROJECT_SOURCE_ENCODING = "sonar.sourceEncoding"; | |||
} |
@@ -1,39 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.PrintStream; | |||
public class StdOutLogOutput implements LogOutput { | |||
private PrintStream stdOut; | |||
public StdOutLogOutput() { | |||
this(System.out); | |||
} | |||
StdOutLogOutput(PrintStream stdOut) { | |||
this.stdOut = stdOut; | |||
} | |||
@Override | |||
public void log(String formattedMessage, org.sonar.runner.api.LogOutput.Level level) { | |||
stdOut.println(level.name() + ": " + formattedMessage); | |||
} | |||
} |
@@ -1,123 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.Closeable; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.nio.file.FileVisitResult; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.SimpleFileVisitor; | |||
import java.nio.file.attribute.BasicFileAttributes; | |||
import java.util.Arrays; | |||
import java.util.Iterator; | |||
import java.util.Properties; | |||
import javax.annotation.Nullable; | |||
class Utils { | |||
private Utils() { | |||
// only util static methods | |||
} | |||
/** | |||
* Similar to org.apache.commons.lang.StringUtils#join() | |||
*/ | |||
static String join(String[] array, String delimiter) { | |||
StringBuilder sb = new StringBuilder(); | |||
Iterator<String> it = Arrays.asList(array).iterator(); | |||
while (it.hasNext()) { | |||
sb.append(it.next()); | |||
if (!it.hasNext()) { | |||
break; | |||
} | |||
sb.append(delimiter); | |||
} | |||
return sb.toString(); | |||
} | |||
static boolean taskRequiresProject(Properties props) { | |||
Object task = props.get(RunnerProperties.TASK); | |||
return task == null || ScanProperties.SCAN_TASK.equals(task); | |||
} | |||
static void writeProperties(File outputFile, Properties p) { | |||
try (OutputStream output = new FileOutputStream(outputFile)) { | |||
p.store(output, "Generated by sonar-runner"); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to export sonar-runner properties", e); | |||
} | |||
} | |||
static void deleteQuietly(File f) { | |||
try { | |||
Files.walkFileTree(f.toPath(), new DeleteQuietlyFileVisitor()); | |||
} catch (IOException e) { | |||
// ignore | |||
} | |||
} | |||
private static class DeleteQuietlyFileVisitor extends SimpleFileVisitor<Path> { | |||
@Override | |||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { | |||
try { | |||
Files.delete(file); | |||
} catch (IOException e) { | |||
// ignore | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult visitFileFailed(Path file, IOException exc) { | |||
try { | |||
Files.delete(file); | |||
} catch (IOException e) { | |||
// ignore | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) { | |||
try { | |||
Files.delete(dir); | |||
} catch (IOException e) { | |||
// ignore | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
} | |||
static void closeQuietly(@Nullable Closeable c) { | |||
if (c == null) { | |||
return; | |||
} | |||
try { | |||
c.close(); | |||
} catch (IOException e) { | |||
// ignore | |||
} | |||
} | |||
} |
@@ -1,24 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
@@ -1,84 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
public class DeleteFileOnCloseInputStream extends InputStream { | |||
private final InputStream is; | |||
private final Path p; | |||
public DeleteFileOnCloseInputStream(InputStream stream, Path p) { | |||
this.is = stream; | |||
this.p = p; | |||
} | |||
@Override | |||
public int read() throws IOException { | |||
return is.read(); | |||
} | |||
@Override | |||
public int read(byte[] b) throws IOException { | |||
return is.read(b); | |||
} | |||
@Override | |||
public int read(byte[] b, int off, int len) throws IOException { | |||
return is.read(b, off, len); | |||
} | |||
@Override | |||
public long skip(long n) throws IOException { | |||
return is.skip(n); | |||
} | |||
@Override | |||
public synchronized void mark(int readlimit) { | |||
is.mark(readlimit); | |||
} | |||
@Override | |||
public synchronized void reset() throws IOException { | |||
is.reset(); | |||
} | |||
@Override | |||
public int available() throws IOException { | |||
return is.available(); | |||
} | |||
@Override | |||
public boolean markSupported() { | |||
return is.markSupported(); | |||
} | |||
@Override | |||
public void close() throws IOException { | |||
try { | |||
super.close(); | |||
} finally { | |||
Files.delete(p); | |||
} | |||
} | |||
} |
@@ -1,106 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.IOException; | |||
import java.io.PrintWriter; | |||
import java.io.RandomAccessFile; | |||
import java.io.StringWriter; | |||
import java.nio.channels.FileChannel; | |||
import java.nio.channels.FileLock; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
public class DirectoryLock { | |||
static final String LOCK_FILE_NAME = ".sonar_lock"; | |||
private final Path lockFilePath; | |||
private final Logger logger; | |||
private RandomAccessFile lockRandomAccessFile; | |||
private FileChannel lockChannel; | |||
private FileLock lockFile; | |||
public DirectoryLock(Path directory, Logger logger) { | |||
this.logger = logger; | |||
this.lockFilePath = directory.resolve(LOCK_FILE_NAME).toAbsolutePath(); | |||
} | |||
public String getFileLockName() { | |||
return LOCK_FILE_NAME; | |||
} | |||
public void lock() { | |||
try { | |||
lockRandomAccessFile = new RandomAccessFile(lockFilePath.toFile(), "rw"); | |||
lockChannel = lockRandomAccessFile.getChannel(); | |||
lockFile = lockChannel.lock(0, 1024, false); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to create lock in " + lockFilePath.toString(), e); | |||
} | |||
} | |||
public boolean tryLock() { | |||
try { | |||
lockRandomAccessFile = new RandomAccessFile(lockFilePath.toFile(), "rw"); | |||
lockChannel = lockRandomAccessFile.getChannel(); | |||
lockFile = lockChannel.tryLock(0, 1024, false); | |||
return lockFile != null; | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to create lock in " + lockFilePath.toString(), e); | |||
} | |||
} | |||
public void unlock() { | |||
if (lockFile != null) { | |||
try { | |||
lockFile.release(); | |||
lockFile = null; | |||
} catch (IOException e) { | |||
logger.error("Error releasing lock", e); | |||
} | |||
} | |||
if (lockChannel != null) { | |||
try { | |||
lockChannel.close(); | |||
lockChannel = null; | |||
} catch (IOException e) { | |||
logger.error("Error closing file channel", e); | |||
} | |||
} | |||
if (lockRandomAccessFile != null) { | |||
try { | |||
lockRandomAccessFile.close(); | |||
lockRandomAccessFile = null; | |||
} catch (IOException e) { | |||
logger.error("Error closing file", e); | |||
} | |||
} | |||
try { | |||
Files.delete(lockFilePath); | |||
} catch (IOException e) { | |||
// ignore, as an error happens if another process just started to acquire the same lock | |||
StringWriter errors = new StringWriter(); | |||
e.printStackTrace(new PrintWriter(errors)); | |||
logger.debug("Couldn't delete lock file: " + lockFilePath.toString() + " " + errors.toString()); | |||
} | |||
} | |||
} |
@@ -1,159 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import javax.annotation.CheckForNull; | |||
/** | |||
* 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). | |||
*/ | |||
public class FileCache { | |||
/** Maximum loop count when creating temp directories. */ | |||
private static final int TEMP_DIR_ATTEMPTS = 10000; | |||
private final File dir; | |||
private final File tmpDir; | |||
private final FileHashes hashes; | |||
private final Logger logger; | |||
FileCache(File dir, FileHashes fileHashes, Logger logger) { | |||
this.hashes = fileHashes; | |||
this.logger = logger; | |||
this.dir = createDir(dir, "user cache"); | |||
logger.info(String.format("User cache: %s", dir.getAbsolutePath())); | |||
this.tmpDir = createDir(new File(dir, "_tmp"), "temp dir"); | |||
} | |||
static FileCache create(File dir, Logger logger) { | |||
return new FileCache(dir, new FileHashes(), logger); | |||
} | |||
public File getDir() { | |||
return dir; | |||
} | |||
/** | |||
* Look for a file in the cache by its filename and md5 checksum. If the file is not | |||
* present then return null. | |||
*/ | |||
@CheckForNull | |||
public File get(String filename, String hash) { | |||
File cachedFile = new File(new File(dir, hash), filename); | |||
if (cachedFile.exists()) { | |||
return cachedFile; | |||
} | |||
logger.debug(String.format("No file found in the cache with name %s and hash %s", filename, hash)); | |||
return null; | |||
} | |||
public interface Downloader { | |||
void download(String filename, File toFile) throws IOException; | |||
} | |||
public File get(String filename, String hash, Downloader downloader) { | |||
// Does not fail if another process tries to create the directory at the same time. | |||
File hashDir = hashDir(hash); | |||
File targetFile = new File(hashDir, filename); | |||
if (!targetFile.exists()) { | |||
File tempFile = newTempFile(); | |||
download(downloader, filename, tempFile); | |||
String downloadedHash = hashes.of(tempFile); | |||
if (!hash.equals(downloadedHash)) { | |||
throw new IllegalStateException("INVALID HASH: File " + tempFile.getAbsolutePath() + " was expected to have hash " + hash | |||
+ " but was downloaded with hash " + downloadedHash); | |||
} | |||
mkdirQuietly(hashDir); | |||
renameQuietly(tempFile, targetFile); | |||
} | |||
return targetFile; | |||
} | |||
private void download(Downloader downloader, String filename, File tempFile) { | |||
try { | |||
downloader.download(filename, tempFile); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to download " + filename + " to " + tempFile, e); | |||
} | |||
} | |||
private void renameQuietly(File sourceFile, File targetFile) { | |||
boolean rename = sourceFile.renameTo(targetFile); | |||
// Check if the file was cached by another process during download | |||
if (!rename && !targetFile.exists()) { | |||
logger.warn(String.format("Unable to rename %s to %s", sourceFile.getAbsolutePath(), targetFile.getAbsolutePath())); | |||
logger.warn("A copy/delete will be tempted but with no guarantee of atomicity"); | |||
try { | |||
Files.move(sourceFile.toPath(), targetFile.toPath()); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to move " + sourceFile.getAbsolutePath() + " to " + targetFile, e); | |||
} | |||
} | |||
} | |||
private File hashDir(String hash) { | |||
return new File(dir, hash); | |||
} | |||
private static void mkdirQuietly(File hashDir) { | |||
try { | |||
Files.createDirectories(hashDir.toPath()); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to create cache directory: " + hashDir, e); | |||
} | |||
} | |||
private File newTempFile() { | |||
try { | |||
return File.createTempFile("fileCache", null, tmpDir); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to create temp file in " + tmpDir, e); | |||
} | |||
} | |||
public File createTempDir() { | |||
String baseName = System.currentTimeMillis() + "-"; | |||
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { | |||
File tempDir = new File(tmpDir, baseName + counter); | |||
if (tempDir.mkdir()) { | |||
return tempDir; | |||
} | |||
} | |||
throw new IllegalStateException("Failed to create directory in " + tmpDir); | |||
} | |||
private File createDir(File dir, String debugTitle) { | |||
if (!dir.isDirectory() || !dir.exists()) { | |||
logger.debug("Create : " + dir.getAbsolutePath()); | |||
try { | |||
Files.createDirectories(dir.toPath()); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Unable to create " + debugTitle + dir.getAbsolutePath(), e); | |||
} | |||
} | |||
return dir; | |||
} | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.File; | |||
import javax.annotation.Nullable; | |||
public class FileCacheBuilder { | |||
private final Logger logger; | |||
private File userHome; | |||
public FileCacheBuilder(Logger logger) { | |||
this.logger = logger; | |||
} | |||
public FileCacheBuilder setUserHome(File d) { | |||
this.userHome = d; | |||
return this; | |||
} | |||
public FileCacheBuilder setUserHome(@Nullable String path) { | |||
this.userHome = (path == null) ? null : new File(path); | |||
return this; | |||
} | |||
public FileCache build() { | |||
if (userHome == null) { | |||
userHome = findHome(); | |||
} | |||
File cacheDir = new File(userHome, "cache"); | |||
return FileCache.create(cacheDir, logger); | |||
} | |||
private static File findHome() { | |||
String path = System.getenv("SONAR_USER_HOME"); | |||
if (path == null) { | |||
// Default | |||
path = System.getProperty("user.home") + File.separator + ".sonar"; | |||
} | |||
return new File(path); | |||
} | |||
} |
@@ -1,73 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.math.BigInteger; | |||
import java.security.MessageDigest; | |||
/** | |||
* Hashes used to store files in the cache directory. | |||
* | |||
* @since 3.5 | |||
*/ | |||
class FileHashes { | |||
private static final int STREAM_BUFFER_LENGTH = 1024; | |||
String of(File file) { | |||
try { | |||
return of(new FileInputStream(file)); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to compute hash of: " + file.getAbsolutePath(), e); | |||
} | |||
} | |||
/** | |||
* Computes the hash of given stream. The stream is closed by this method. | |||
*/ | |||
String of(InputStream input) { | |||
try(InputStream is = input) { | |||
MessageDigest digest = MessageDigest.getInstance("MD5"); | |||
byte[] hash = digest(is, digest); | |||
return toHex(hash); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to compute hash", e); | |||
} | |||
} | |||
private static byte[] digest(InputStream input, MessageDigest digest) throws IOException { | |||
final byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; | |||
int read = input.read(buffer, 0, STREAM_BUFFER_LENGTH); | |||
while (read > -1) { | |||
digest.update(buffer, 0, read); | |||
read = input.read(buffer, 0, STREAM_BUFFER_LENGTH); | |||
} | |||
return digest.digest(); | |||
} | |||
static String toHex(byte[] bytes) { | |||
BigInteger bi = new BigInteger(1, bytes); | |||
return String.format("%0" + (bytes.length << 1) + "x", bi); | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
public interface Logger { | |||
void debug(String msg); | |||
void info(String msg); | |||
void warn(String msg); | |||
void error(String msg); | |||
void error(String msg, Throwable t); | |||
} |
@@ -1,281 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.nio.charset.Charset; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.DirectoryStream; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.StandardCopyOption; | |||
import java.security.MessageDigest; | |||
import java.security.NoSuchAlgorithmException; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nonnull; | |||
import static java.nio.file.StandardOpenOption.CREATE; | |||
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; | |||
import static java.nio.file.StandardOpenOption.WRITE; | |||
public class PersistentCache { | |||
private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); | |||
private static final Charset ENCODING = StandardCharsets.UTF_8; | |||
private static final String DIGEST_ALGO = "MD5"; | |||
private final PersistentCacheInvalidation invalidation; | |||
private final Logger logger; | |||
private final Path dir; | |||
private DirectoryLock lock; | |||
PersistentCache(Path dir, PersistentCacheInvalidation invalidation, Logger logger, DirectoryLock lock) { | |||
this.dir = dir; | |||
this.invalidation = invalidation; | |||
this.logger = logger; | |||
this.lock = lock; | |||
reconfigure(); | |||
logger.debug("cache: " + dir); | |||
} | |||
public synchronized void reconfigure() { | |||
try { | |||
Files.createDirectories(dir); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("failed to create cache dir", e); | |||
} | |||
} | |||
public Path getDirectory() { | |||
return dir; | |||
} | |||
@CheckForNull | |||
public synchronized String getString(@Nonnull String obj) throws IOException { | |||
byte[] cached = get(obj); | |||
if (cached == null) { | |||
return null; | |||
} | |||
return new String(cached, ENCODING); | |||
} | |||
@CheckForNull | |||
public synchronized InputStream getStream(@Nonnull String obj) throws IOException { | |||
String key = getKey(obj); | |||
try { | |||
lock(); | |||
Path path = getCacheCopy(key); | |||
if (path == null) { | |||
return null; | |||
} | |||
return new DeleteFileOnCloseInputStream(new FileInputStream(path.toFile()), path); | |||
} finally { | |||
unlock(); | |||
} | |||
} | |||
@CheckForNull | |||
public synchronized byte[] get(@Nonnull String obj) throws IOException { | |||
String key = getKey(obj); | |||
try { | |||
lock(); | |||
byte[] cached = getCache(key); | |||
if (cached != null) { | |||
logger.debug("cache hit for " + obj + " -> " + key); | |||
return cached; | |||
} | |||
logger.debug("cache miss for " + obj + " -> " + key); | |||
} finally { | |||
unlock(); | |||
} | |||
return null; | |||
} | |||
public synchronized void put(@Nonnull String obj, @Nonnull InputStream stream) throws IOException { | |||
String key = getKey(obj); | |||
try { | |||
lock(); | |||
putCache(key, stream); | |||
} finally { | |||
unlock(); | |||
} | |||
} | |||
public synchronized void put(@Nonnull String obj, @Nonnull byte[] value) throws IOException { | |||
String key = getKey(obj); | |||
try { | |||
lock(); | |||
putCache(key, value); | |||
} finally { | |||
unlock(); | |||
} | |||
} | |||
/** | |||
* Deletes all cache entries | |||
*/ | |||
public synchronized void clear() { | |||
logger.info("cache: clearing"); | |||
try { | |||
lock(); | |||
deleteCacheEntries(new DirectoryClearFilter()); | |||
} catch (IOException e) { | |||
logger.error("Error clearing cache", e); | |||
} finally { | |||
unlock(); | |||
} | |||
} | |||
/** | |||
* Deletes cache entries that are no longer valid according to the default expiration time period. | |||
*/ | |||
public synchronized void clean() { | |||
logger.info("cache: cleaning"); | |||
try { | |||
lock(); | |||
deleteCacheEntries(new DirectoryCleanFilter()); | |||
} catch (IOException e) { | |||
logger.error("Error cleaning cache", e); | |||
} finally { | |||
unlock(); | |||
} | |||
} | |||
private void lock() throws IOException { | |||
lock.lock(); | |||
} | |||
private void unlock() { | |||
lock.unlock(); | |||
} | |||
private static String getKey(String uri) { | |||
try { | |||
String key = uri; | |||
MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGO); | |||
digest.update(key.getBytes(StandardCharsets.UTF_8)); | |||
return byteArrayToHex(digest.digest()); | |||
} catch (NoSuchAlgorithmException e) { | |||
throw new IllegalStateException("Couldn't create hash", e); | |||
} | |||
} | |||
private void deleteCacheEntries(DirectoryStream.Filter<Path> filter) throws IOException { | |||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) { | |||
for (Path p : stream) { | |||
try { | |||
Files.delete(p); | |||
} catch (Exception e) { | |||
logger.error("Error deleting " + p, e); | |||
} | |||
} | |||
} | |||
} | |||
private class DirectoryClearFilter implements DirectoryStream.Filter<Path> { | |||
@Override | |||
public boolean accept(Path entry) throws IOException { | |||
return !lock.getFileLockName().equals(entry.getFileName().toString()); | |||
} | |||
} | |||
private class DirectoryCleanFilter implements DirectoryStream.Filter<Path> { | |||
@Override | |||
public boolean accept(Path entry) throws IOException { | |||
if (lock.getFileLockName().equals(entry.getFileName().toString())) { | |||
return false; | |||
} | |||
return invalidation.test(entry); | |||
} | |||
} | |||
private void putCache(String key, byte[] value) throws IOException { | |||
Path cachePath = getCacheEntryPath(key); | |||
Files.write(cachePath, value, CREATE, WRITE, TRUNCATE_EXISTING); | |||
} | |||
private void putCache(String key, InputStream stream) throws IOException { | |||
Path cachePath = getCacheEntryPath(key); | |||
Files.copy(stream, cachePath, StandardCopyOption.REPLACE_EXISTING); | |||
} | |||
private byte[] getCache(String key) throws IOException { | |||
Path cachePath = getCacheEntryPath(key); | |||
if (!validateCacheEntry(cachePath)) { | |||
return null; | |||
} | |||
return Files.readAllBytes(cachePath); | |||
} | |||
private Path getCacheCopy(String key) throws IOException { | |||
Path cachePath = getCacheEntryPath(key); | |||
if (!validateCacheEntry(cachePath)) { | |||
return null; | |||
} | |||
Path temp = Files.createTempFile("sonar_cache", null); | |||
Files.copy(cachePath, temp, StandardCopyOption.REPLACE_EXISTING); | |||
return temp; | |||
} | |||
private boolean validateCacheEntry(Path cacheEntryPath) throws IOException { | |||
if (!Files.exists(cacheEntryPath)) { | |||
return false; | |||
} | |||
if (invalidation.test(cacheEntryPath)) { | |||
logger.debug("cache: evicting entry"); | |||
Files.delete(cacheEntryPath); | |||
return false; | |||
} | |||
return true; | |||
} | |||
private Path getCacheEntryPath(String key) { | |||
return dir.resolve(key); | |||
} | |||
public static String byteArrayToHex(byte[] bytes) { | |||
char[] hexChars = new char[bytes.length * 2]; | |||
for (int j = 0; j < bytes.length; j++) { | |||
int v = bytes[j] & 0xFF; | |||
hexChars[j * 2] = hexArray[v >>> 4]; | |||
hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | |||
} | |||
return new String(hexChars); | |||
} | |||
} |
@@ -1,110 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.UnsupportedEncodingException; | |||
import java.net.URLEncoder; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
import java.util.concurrent.TimeUnit; | |||
import javax.annotation.Nullable; | |||
/** | |||
* Cache files will be placed in 3 areas: | |||
* <pre> | |||
* <sonar_home>/ws_cache/<server_url>-<version>/projects/<project>/ | |||
* <sonar_home>/ws_cache/<server_url>-<version>/global/ | |||
* <sonar_home>/ws_cache/<server_url>-<version>/local/ | |||
* </pre> | |||
*/ | |||
public class PersistentCacheBuilder { | |||
private static final long DEFAULT_EXPIRE_DURATION = TimeUnit.MILLISECONDS.convert(9999L, TimeUnit.DAYS); | |||
private static final String DIR_NAME = "ws_cache"; | |||
private Path cacheBasePath; | |||
private Path relativePath; | |||
private final Logger logger; | |||
public PersistentCacheBuilder(Logger logger) { | |||
this.logger = logger; | |||
} | |||
public PersistentCacheBuilder setAreaForProject(String serverUrl, String serverVersion, String projectKey) { | |||
relativePath = Paths.get(sanitizeFilename(serverUrl)) | |||
.resolve(sanitizeFilename(serverVersion)) | |||
.resolve("projects") | |||
.resolve(sanitizeFilename(projectKey)); | |||
return this; | |||
} | |||
public PersistentCacheBuilder setAreaForGlobal(String serverUrl) { | |||
relativePath = Paths.get(sanitizeFilename(serverUrl)) | |||
.resolve("global"); | |||
return this; | |||
} | |||
public PersistentCacheBuilder setAreaForLocalProject(String serverUrl, String serverVersion) { | |||
relativePath = Paths.get(sanitizeFilename(serverUrl)) | |||
.resolve(sanitizeFilename(serverVersion)) | |||
.resolve("local"); | |||
return this; | |||
} | |||
public PersistentCacheBuilder setSonarHome(@Nullable Path p) { | |||
if (p != null) { | |||
this.cacheBasePath = p.resolve(DIR_NAME); | |||
} | |||
return this; | |||
} | |||
public PersistentCache build() { | |||
if (relativePath == null) { | |||
throw new IllegalStateException("area must be set before building"); | |||
} | |||
if (cacheBasePath == null) { | |||
setSonarHome(findHome()); | |||
} | |||
Path cachePath = cacheBasePath.resolve(relativePath); | |||
DirectoryLock lock = new DirectoryLock(cacheBasePath, logger); | |||
PersistentCacheInvalidation criteria = new TTLCacheInvalidation(DEFAULT_EXPIRE_DURATION); | |||
return new PersistentCache(cachePath, criteria, logger, lock); | |||
} | |||
private static Path findHome() { | |||
String home = System.getenv("SONAR_USER_HOME"); | |||
if (home != null) { | |||
return Paths.get(home); | |||
} | |||
home = System.getProperty("user.home"); | |||
return Paths.get(home, ".sonar"); | |||
} | |||
private static String sanitizeFilename(String name) { | |||
try { | |||
return URLEncoder.encode(name, StandardCharsets.UTF_8.name()); | |||
} catch (UnsupportedEncodingException e) { | |||
throw new IllegalStateException("Couldn't sanitize filename: " + name, e); | |||
} | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
public interface PersistentCacheInvalidation { | |||
boolean test(Path cacheEntryPath) throws IOException; | |||
} |
@@ -1,48 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.attribute.BasicFileAttributes; | |||
class TTLCacheInvalidation implements PersistentCacheInvalidation { | |||
private final long durationToExpireMs; | |||
TTLCacheInvalidation(long durationToExpireMs) { | |||
this.durationToExpireMs = durationToExpireMs; | |||
} | |||
@Override | |||
public boolean test(Path cacheEntryPath) throws IOException { | |||
BasicFileAttributes attr = Files.readAttributes(cacheEntryPath, BasicFileAttributes.class); | |||
long modTime = attr.lastModifiedTime().toMillis(); | |||
long age = System.currentTimeMillis() - modTime; | |||
if (age > durationToExpireMs) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import javax.annotation.ParametersAreNonnullByDefault; | |||
@@ -1,62 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import javax.annotation.concurrent.Immutable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Set; | |||
@Immutable | |||
public class ClassloadRules { | |||
private final List<String> mask; | |||
private final List<String> unmask; | |||
public ClassloadRules(Set<String> maskRules, Set<String> unmaskRules) { | |||
this.mask = new ArrayList<>(maskRules); | |||
this.unmask = new ArrayList<>(unmaskRules); | |||
} | |||
public boolean canLoad(String className) { | |||
// if there is a tie -> block it | |||
return unmaskSize(className) > maskSize(className); | |||
} | |||
private int maskSize(String className) { | |||
return findBestMatch(mask, className); | |||
} | |||
private int unmaskSize(String className) { | |||
return findBestMatch(unmask, className); | |||
} | |||
private static int findBestMatch(List<String> list, String name) { | |||
// there can be a match of 0 ("") | |||
int bestMatch = -1; | |||
for (String s : list) { | |||
if (name.startsWith(s) && s.length() > bestMatch) { | |||
bestMatch = s.length(); | |||
} | |||
} | |||
return bestMatch; | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
public interface InternalProperties { | |||
String RUNNER_APP = "sonarRunner.app"; | |||
String RUNNER_APP_VERSION = "sonarRunner.appVersion"; | |||
String RUNNER_DUMP_TO_FILE = "sonarRunner.dumpToFile"; | |||
String RUNNER_VERSION_SIMULATION = "sonarRunner.versionSimulation"; | |||
String RUNNER_MASK_RULES = "sonarRunner.maskRules"; | |||
} |
@@ -1,109 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.Enumeration; | |||
import java.util.List; | |||
/** | |||
* Special {@link java.net.URLClassLoader} to execute batch, which restricts loading from parent. | |||
*/ | |||
class IsolatedClassloader extends URLClassLoader { | |||
private final ClassloadRules rules; | |||
/** | |||
* The parent classloader is used only for loading classes and resources in unmasked packages | |||
*/ | |||
IsolatedClassloader(ClassLoader parent, ClassloadRules rules) { | |||
super(new URL[0], parent); | |||
this.rules = rules; | |||
} | |||
void addFiles(List<File> files) { | |||
try { | |||
for (File file : files) { | |||
addURL(file.toURI().toURL()); | |||
} | |||
} catch (MalformedURLException e) { | |||
throw new IllegalStateException("Fail to create classloader", e); | |||
} | |||
} | |||
/** | |||
* Same behavior as in {@link java.net.URLClassLoader#loadClass(String, boolean)}, except loading from parent. | |||
*/ | |||
@Override | |||
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { | |||
// First, check if the class has already been loaded | |||
Class<?> c = findLoadedClass(name); | |||
if (c == null) { | |||
try { | |||
// Load from parent | |||
if (getParent() != null && rules.canLoad(name)) { | |||
c = getParent().loadClass(name); | |||
} else { | |||
// Load from system | |||
// I don't know for other vendors, but for Oracle JVM : | |||
// - ClassLoader.getSystemClassLoader() is sun.misc.Launcher$AppClassLoader. It contains app classpath. | |||
// - ClassLoader.getSystemClassLoader().getParent() is sun.misc.Launcher$ExtClassLoader. It contains core JVM | |||
ClassLoader systemClassLoader = getSystemClassLoader(); | |||
if (systemClassLoader.getParent() != null) { | |||
systemClassLoader = systemClassLoader.getParent(); | |||
} | |||
c = systemClassLoader.loadClass(name); | |||
} | |||
} catch (ClassNotFoundException e) { | |||
// If still not found, then invoke findClass in order | |||
// to find the class. | |||
c = findClass(name); | |||
} | |||
} | |||
if (resolve) { | |||
resolveClass(c); | |||
} | |||
return c; | |||
} | |||
/** | |||
* Unlike {@link java.net.URLClassLoader#getResource(String)} don't return resource from parent. | |||
* See http://jira.codehaus.org/browse/SONAR-2276 | |||
*/ | |||
@Override | |||
public URL getResource(String name) { | |||
return findResource(name); | |||
} | |||
/** | |||
* Unlike {@link java.net.URLClassLoader#getResources(String)} don't return resources from parent. | |||
* See http://jira.codehaus.org/browse/SONAR-2276 | |||
*/ | |||
@Override | |||
public Enumeration<URL> getResources(String name) throws IOException { | |||
return findResources(name); | |||
} | |||
} |
@@ -1,105 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
import org.sonar.runner.cache.Logger; | |||
import org.sonar.runner.cache.PersistentCache; | |||
import org.sonar.runner.cache.PersistentCacheBuilder; | |||
import java.io.File; | |||
import java.nio.file.Paths; | |||
import java.security.AccessController; | |||
import java.security.PrivilegedAction; | |||
import java.util.List; | |||
import java.util.Properties; | |||
public class IsolatedLauncherFactory { | |||
static final String ISOLATED_LAUNCHER_IMPL = "org.sonar.runner.batch.BatchIsolatedLauncher"; | |||
private final TempCleaning tempCleaning; | |||
private final String launcherImplClassName; | |||
private final Logger logger; | |||
/** | |||
* For unit tests | |||
*/ | |||
IsolatedLauncherFactory(String isolatedLauncherClassName, TempCleaning tempCleaning, Logger logger) { | |||
this.tempCleaning = tempCleaning; | |||
this.launcherImplClassName = isolatedLauncherClassName; | |||
this.logger = logger; | |||
} | |||
public IsolatedLauncherFactory(Logger logger) { | |||
this(ISOLATED_LAUNCHER_IMPL, new TempCleaning(logger), logger); | |||
} | |||
private PersistentCache getCache(Properties props) { | |||
PersistentCacheBuilder builder = new PersistentCacheBuilder(logger); | |||
String serverUrl = props.getProperty("sonar.host.url"); | |||
String home = props.getProperty("sonar.userHome"); | |||
builder.setAreaForGlobal(serverUrl); | |||
if (home != null) { | |||
builder.setSonarHome(Paths.get(home)); | |||
} | |||
return builder.build(); | |||
} | |||
private ClassLoader createClassLoader(List<File> jarFiles, ClassloadRules maskRules) { | |||
IsolatedClassloader classloader = new IsolatedClassloader(getClass().getClassLoader(), maskRules); | |||
classloader.addFiles(jarFiles); | |||
return classloader; | |||
} | |||
public IsolatedLauncher createLauncher(Properties props, ClassloadRules rules, boolean preferCache) { | |||
if (props.containsKey(InternalProperties.RUNNER_DUMP_TO_FILE)) { | |||
String version = props.getProperty(InternalProperties.RUNNER_VERSION_SIMULATION); | |||
if (version == null) { | |||
version = "5.2"; | |||
} | |||
return new SimulatedLauncher(version, logger); | |||
} | |||
ServerConnection serverConnection = ServerConnection.create(props, getCache(props), logger, preferCache); | |||
JarDownloader jarDownloader = new JarDownloader(serverConnection, logger, props); | |||
return createLauncher(jarDownloader, rules); | |||
} | |||
IsolatedLauncher createLauncher(final JarDownloader jarDownloader, final ClassloadRules rules) { | |||
return AccessController.doPrivileged(new PrivilegedAction<IsolatedLauncher>() { | |||
@Override | |||
public IsolatedLauncher run() { | |||
try { | |||
List<File> jarFiles = jarDownloader.download(); | |||
logger.debug("Create isolated classloader..."); | |||
ClassLoader cl = createClassLoader(jarFiles, rules); | |||
IsolatedLauncher objProxy = IsolatedLauncherProxy.create(cl, IsolatedLauncher.class, launcherImplClassName, logger); | |||
tempCleaning.clean(); | |||
return objProxy; | |||
} catch (Exception e) { | |||
// Catch all other exceptions, which relates to reflection | |||
throw new RunnerException("Unable to execute SonarQube", e); | |||
} | |||
} | |||
}); | |||
} | |||
} |
@@ -1,86 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.cache.Logger; | |||
import java.lang.reflect.InvocationHandler; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Proxy; | |||
import java.lang.reflect.UndeclaredThrowableException; | |||
public class IsolatedLauncherProxy implements InvocationHandler { | |||
private final Object proxied; | |||
private final ClassLoader cl; | |||
private final Logger logger; | |||
private IsolatedLauncherProxy(ClassLoader cl, Object proxied, Logger logger) { | |||
this.cl = cl; | |||
this.proxied = proxied; | |||
this.logger = logger; | |||
} | |||
public static <T> T create(ClassLoader cl, Class<T> interfaceClass, String proxiedClassName, Logger logger) throws ReflectiveOperationException { | |||
Object proxied = createProxiedObject(cl, proxiedClassName); | |||
// interfaceClass needs to be loaded with a parent ClassLoader (common to both ClassLoaders) | |||
// In addition, Proxy.newProxyInstance checks if the target ClassLoader sees the same class as the one given | |||
Class<?> loadedInterfaceClass = cl.loadClass(interfaceClass.getName()); | |||
return (T) create(cl, proxied, loadedInterfaceClass, logger); | |||
} | |||
public static <T> T create(ClassLoader cl, Object proxied, Class<T> interfaceClass, Logger logger) { | |||
Class<?>[] c = {interfaceClass}; | |||
return (T) Proxy.newProxyInstance(cl, c, new IsolatedLauncherProxy(cl, proxied, logger)); | |||
} | |||
@Override | |||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
ClassLoader initialContextClassLoader = Thread.currentThread().getContextClassLoader(); | |||
try { | |||
Thread.currentThread().setContextClassLoader(cl); | |||
logger.debug("Execution " + method.getName()); | |||
return method.invoke(proxied, args); | |||
} catch (UndeclaredThrowableException | InvocationTargetException e) { | |||
throw unwrapException(e); | |||
} finally { | |||
Thread.currentThread().setContextClassLoader(initialContextClassLoader); | |||
} | |||
} | |||
private static Throwable unwrapException(Throwable e) { | |||
Throwable cause = e; | |||
while (cause.getCause() != null) { | |||
if (cause instanceof UndeclaredThrowableException || cause instanceof InvocationTargetException) { | |||
cause = cause.getCause(); | |||
} else { | |||
break; | |||
} | |||
} | |||
return cause; | |||
} | |||
private static Object createProxiedObject(ClassLoader cl, String proxiedClassName) throws ClassNotFoundException, InstantiationException, IllegalAccessException { | |||
Class<?> proxiedClass = cl.loadClass(proxiedClassName); | |||
return proxiedClass.newInstance(); | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.util.List; | |||
import java.util.Properties; | |||
class JarDownloader { | |||
private final ServerConnection serverConnection; | |||
private final Logger logger; | |||
private final Properties props; | |||
JarDownloader(ServerConnection conn, Logger logger, Properties props) { | |||
this.serverConnection = conn; | |||
this.logger = logger; | |||
this.props = props; | |||
} | |||
List<File> download() { | |||
return new Jars(serverConnection, new JarExtractor(), logger, props).download(); | |||
} | |||
} |
@@ -1,41 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.apache.commons.io.FileUtils; | |||
import java.io.File; | |||
import java.net.URL; | |||
public class JarExtractor { | |||
public File extractToTemp(String filenameWithoutSuffix) { | |||
String filename = filenameWithoutSuffix + ".jar"; | |||
URL url = getClass().getResource("/" + filename); | |||
try { | |||
File copy = File.createTempFile(filenameWithoutSuffix, ".jar"); | |||
FileUtils.copyURLToFile(url, copy); | |||
return copy; | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to extract " + filename, e); | |||
} | |||
} | |||
} | |||
@@ -1,110 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.cache.FileCache; | |||
import org.sonar.runner.cache.FileCacheBuilder; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Properties; | |||
class Jars { | |||
private static final String BOOTSTRAP_INDEX_PATH = "/batch_bootstrap/index"; | |||
static final String BATCH_PATH = "/batch/"; | |||
private final FileCache fileCache; | |||
private final ServerConnection connection; | |||
private final JarExtractor jarExtractor; | |||
private final Logger logger; | |||
Jars(ServerConnection conn, JarExtractor jarExtractor, Logger logger, Properties props) { | |||
this.logger = logger; | |||
this.fileCache = new FileCacheBuilder(logger) | |||
.setUserHome(props.getProperty("sonar.userHome")) | |||
.build(); | |||
this.connection = conn; | |||
this.jarExtractor = jarExtractor; | |||
} | |||
/** | |||
* For unit tests | |||
*/ | |||
Jars(FileCache fileCache, ServerConnection conn, JarExtractor jarExtractor, Logger logger) { | |||
this.logger = logger; | |||
this.fileCache = fileCache; | |||
this.connection = conn; | |||
this.jarExtractor = jarExtractor; | |||
} | |||
/** | |||
* For unit tests | |||
*/ | |||
FileCache getFileCache() { | |||
return fileCache; | |||
} | |||
List<File> download() { | |||
List<File> files = new ArrayList<File>(); | |||
logger.debug("Extract sonar-runner-batch in temp..."); | |||
files.add(jarExtractor.extractToTemp("sonar-runner-batch")); | |||
files.addAll(dowloadFiles()); | |||
return files; | |||
} | |||
private List<File> dowloadFiles() { | |||
try { | |||
List<File> files = new ArrayList<File>(); | |||
logger.debug("Get bootstrap index..."); | |||
String libs = connection.loadString(BOOTSTRAP_INDEX_PATH); | |||
logger.debug("Get bootstrap completed"); | |||
String[] lines = libs.split("[\r\n]+"); | |||
BatchFileDownloader batchFileDownloader = new BatchFileDownloader(connection); | |||
for (String line : lines) { | |||
line = line.trim(); | |||
if (!"".equals(line)) { | |||
String[] libAndHash = line.split("\\|"); | |||
String filename = libAndHash[0]; | |||
String hash = libAndHash.length > 0 ? libAndHash[1] : ""; | |||
files.add(fileCache.get(filename, hash, batchFileDownloader)); | |||
} | |||
} | |||
return files; | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to download libraries from server", e); | |||
} | |||
} | |||
static class BatchFileDownloader implements FileCache.Downloader { | |||
private final ServerConnection connection; | |||
BatchFileDownloader(ServerConnection conn) { | |||
this.connection = conn; | |||
} | |||
@Override | |||
public void download(String filename, File toFile) throws IOException { | |||
connection.download(BATCH_PATH + filename, toFile); | |||
} | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
public class RunnerException extends RuntimeException { | |||
public RunnerException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
} |
@@ -1,218 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import com.github.kevinsawicki.http.HttpRequest; | |||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.ConnectException; | |||
import java.net.URL; | |||
import java.net.UnknownHostException; | |||
import java.text.MessageFormat; | |||
import java.util.Properties; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import org.apache.commons.io.FileUtils; | |||
import org.sonar.runner.cache.Logger; | |||
import org.sonar.runner.cache.PersistentCache; | |||
class ServerConnection { | |||
private static final String SONAR_SERVER_CAN_NOT_BE_REACHED = "SonarQube server ''{0}'' can not be reached"; | |||
private static final String STATUS_RETURNED_BY_URL_IS_INVALID = "Status returned by url : ''{0}'' is invalid : {1}"; | |||
static final int CONNECT_TIMEOUT_MILLISECONDS = 5000; | |||
static final int READ_TIMEOUT_MILLISECONDS = 60000; | |||
private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)"); | |||
private final String serverUrl; | |||
private final String userAgent; | |||
private final PersistentCache wsCache; | |||
private final boolean preferCache; | |||
private final Logger logger; | |||
private final boolean isCacheEnabled; | |||
private ServerConnection(String serverUrl, String app, String appVersion, boolean preferCache, boolean cacheEnabled, PersistentCache cache, Logger logger) { | |||
this.isCacheEnabled = cacheEnabled; | |||
this.logger = logger; | |||
this.serverUrl = removeEndSlash(serverUrl); | |||
this.userAgent = app + "/" + appVersion; | |||
this.wsCache = cache; | |||
this.preferCache = preferCache; | |||
} | |||
private static String removeEndSlash(String url) { | |||
return url.endsWith("/") ? url.substring(0, url.length() - 1) : url; | |||
} | |||
static ServerConnection create(Properties properties, PersistentCache cache, Logger logger, boolean preferCache) { | |||
String serverUrl = properties.getProperty("sonar.host.url"); | |||
String app = properties.getProperty(InternalProperties.RUNNER_APP); | |||
String appVersion = properties.getProperty(InternalProperties.RUNNER_APP_VERSION); | |||
boolean enableCache = isCacheEnabled(properties); | |||
return new ServerConnection(serverUrl, app, appVersion, preferCache, enableCache, cache, logger); | |||
} | |||
private static boolean isCacheEnabled(Properties properties) { | |||
String analysisMode = properties.getProperty("sonar.analysis.mode"); | |||
return "issues".equalsIgnoreCase(analysisMode); | |||
} | |||
/** | |||
* | |||
* @throws HttpRequestException If there is an underlying IOException related to the connection | |||
* @throws IOException If the HTTP response code is != 200 | |||
*/ | |||
private String downloadString(String url, boolean saveCache) throws HttpRequestException, IOException { | |||
logger.debug("Download: " + url); | |||
HttpRequest httpRequest = null; | |||
try { | |||
httpRequest = newHttpRequest(new URL(url)); | |||
String charset = getCharsetFromContentType(httpRequest.contentType()); | |||
if (charset == null || "".equals(charset)) { | |||
charset = "UTF-8"; | |||
} | |||
if (!httpRequest.ok()) { | |||
throw new IOException(MessageFormat.format(STATUS_RETURNED_BY_URL_IS_INVALID, url, httpRequest.code())); | |||
} | |||
byte[] body = httpRequest.bytes(); | |||
if (saveCache) { | |||
try { | |||
wsCache.put(url, body); | |||
} catch (IOException e) { | |||
logger.warn("Failed to cache WS call: " + e.getMessage()); | |||
} | |||
} | |||
return new String(body, charset); | |||
} finally { | |||
if (httpRequest != null) { | |||
httpRequest.disconnect(); | |||
} | |||
} | |||
} | |||
void download(String path, File toFile) { | |||
String fullUrl = serverUrl + path; | |||
try { | |||
logger.debug("Download " + fullUrl + " to " + toFile.getAbsolutePath()); | |||
HttpRequest httpRequest = newHttpRequest(new URL(fullUrl)); | |||
if (!httpRequest.ok()) { | |||
throw new IOException(MessageFormat.format(STATUS_RETURNED_BY_URL_IS_INVALID, fullUrl, httpRequest.code())); | |||
} | |||
httpRequest.receive(toFile); | |||
} catch (Exception e) { | |||
if (isCausedByConnection(e)) { | |||
logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl)); | |||
} | |||
FileUtils.deleteQuietly(toFile); | |||
throw new IllegalStateException("Fail to download: " + fullUrl, e); | |||
} | |||
} | |||
/** | |||
* Tries to fetch from cache and server. If both attempts fail, it throws the exception linked to the server connection failure. | |||
*/ | |||
String loadString(String path) throws IOException { | |||
String fullUrl = serverUrl + path; | |||
if (isCacheEnabled && preferCache) { | |||
return tryCacheFirst(fullUrl); | |||
} else { | |||
return tryServerFirst(fullUrl, isCacheEnabled); | |||
} | |||
} | |||
private String tryCacheFirst(String fullUrl) throws IOException { | |||
String cached = getFromCache(fullUrl); | |||
if (cached != null) { | |||
return cached; | |||
} | |||
try { | |||
return downloadString(fullUrl, preferCache); | |||
} catch (Exception e) { | |||
logger.error(MessageFormat.format("Data is not cached and " + SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl)); | |||
throw e; | |||
} | |||
} | |||
private String tryServerFirst(String fullUrl, boolean cacheEnabled) throws IOException { | |||
try { | |||
return downloadString(fullUrl, cacheEnabled); | |||
} catch (HttpRequest.HttpRequestException e) { | |||
if (cacheEnabled && isCausedByConnection(e)) { | |||
logger.info(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + ", trying cache", serverUrl)); | |||
String cached = getFromCache(fullUrl); | |||
if (cached != null) { | |||
return cached; | |||
} | |||
logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + " and data is not cached", serverUrl)); | |||
throw e; | |||
} | |||
logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl)); | |||
throw e; | |||
} | |||
} | |||
private static boolean isCausedByConnection(Exception e) { | |||
return e.getCause() instanceof ConnectException || e.getCause() instanceof UnknownHostException || | |||
e.getCause() instanceof java.net.SocketTimeoutException; | |||
} | |||
private String getFromCache(String fullUrl) { | |||
try { | |||
return wsCache.getString(fullUrl); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to access cache", e); | |||
} | |||
} | |||
private HttpRequest newHttpRequest(URL url) { | |||
HttpRequest request = HttpRequest.get(url); | |||
request.trustAllCerts().trustAllHosts(); | |||
request.acceptGzipEncoding().uncompress(true); | |||
request.connectTimeout(CONNECT_TIMEOUT_MILLISECONDS).readTimeout(READ_TIMEOUT_MILLISECONDS); | |||
request.userAgent(userAgent); | |||
return request; | |||
} | |||
/** | |||
* 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 upper-cased. | |||
*/ | |||
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,113 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
import org.sonar.runner.batch.IssueListener; | |||
import org.sonar.runner.batch.LogOutput; | |||
import org.sonar.runner.cache.Logger; | |||
import javax.annotation.Nullable; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.OutputStream; | |||
import java.util.List; | |||
import java.util.Properties; | |||
public class SimulatedLauncher implements IsolatedLauncher { | |||
private final String version; | |||
private final Logger logger; | |||
private Properties globalProperties; | |||
SimulatedLauncher(String version, Logger logger) { | |||
this.version = version; | |||
this.logger = logger; | |||
} | |||
@Override | |||
public void start(Properties properties, LogOutput logOutput, boolean preferCache) { | |||
globalProperties = properties; | |||
} | |||
@Override | |||
public void stop() { | |||
globalProperties = null; | |||
} | |||
@Override | |||
public void execute(Properties properties) { | |||
dumpProperties(globalProperties, properties); | |||
} | |||
@Override | |||
public void execute(Properties properties, IssueListener listener) { | |||
dumpProperties(globalProperties, properties); | |||
} | |||
private void dumpProperties(@Nullable Properties global, Properties analysis) { | |||
// for old versions, analysis will have global properties merged in it | |||
String filePath; | |||
String filePathGlobal = null; | |||
if (global != null) { | |||
filePath = global.getProperty(InternalProperties.RUNNER_DUMP_TO_FILE); | |||
filePathGlobal = filePath + ".global"; | |||
} else { | |||
filePath = analysis.getProperty(InternalProperties.RUNNER_DUMP_TO_FILE); | |||
} | |||
if (filePath == null) { | |||
throw new IllegalStateException("No file to dump properties"); | |||
} | |||
if (global != null) { | |||
File dumpFileGlobal = new File(filePathGlobal); | |||
writeProperties(dumpFileGlobal, global, "global properties"); | |||
} | |||
File dumpFile = new File(filePath); | |||
writeProperties(dumpFile, analysis, "analysis properties"); | |||
logger.info("Simulation mode. Configuration written to " + dumpFile.getAbsolutePath()); | |||
} | |||
private static void writeProperties(File outputFile, Properties p, String comment) { | |||
try (OutputStream output = new FileOutputStream(outputFile)) { | |||
p.store(output, "Generated by sonar-runner - " + comment); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to export sonar-runner properties", e); | |||
} | |||
} | |||
@Override | |||
public void syncProject(String projectKey) { | |||
// no op | |||
} | |||
@Override | |||
public void executeOldVersion(Properties properties, List<Object> extensions) { | |||
dumpProperties(null, properties); | |||
} | |||
@Override | |||
public String getVersion() { | |||
return version; | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.filefilter.AgeFileFilter; | |||
import org.apache.commons.io.filefilter.AndFileFilter; | |||
import org.apache.commons.io.filefilter.PrefixFileFilter; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.util.Collection; | |||
/** | |||
* The file sonar-runner-batch.jar is locked by the classloader on Windows and can't be dropped at the end of the execution. | |||
* See {@link IsolatedLauncherFactory} | |||
*/ | |||
class TempCleaning { | |||
static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000; | |||
final File tempDir; | |||
private final Logger logger; | |||
TempCleaning(Logger logger) { | |||
this(new File(System.getProperty("java.io.tmpdir")), logger); | |||
} | |||
/** | |||
* For unit tests | |||
*/ | |||
TempCleaning(File tempDir, Logger logger) { | |||
this.logger = logger; | |||
this.tempDir = tempDir; | |||
} | |||
void clean() { | |||
logger.debug("Start temp cleaning..."); | |||
long cutoff = System.currentTimeMillis() - ONE_DAY_IN_MILLISECONDS; | |||
Collection<File> files = FileUtils.listFiles(tempDir, new AndFileFilter( | |||
new PrefixFileFilter("sonar-runner-batch"), | |||
new AgeFileFilter(cutoff)), null); | |||
for (File file : files) { | |||
FileUtils.deleteQuietly(file); | |||
} | |||
logger.debug("Temp cleaning done"); | |||
} | |||
} |
@@ -1,41 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
public class VersionUtils { | |||
private VersionUtils() { | |||
// only util static methods | |||
} | |||
public static boolean isAtLeast52(String version) { | |||
// it can be snapshot (5.2-SNAPSHOT) | |||
if (version == null) { | |||
return false; | |||
} | |||
int endIndex = Math.min(3, version.length()); | |||
try { | |||
return Double.parseDouble(version.substring(0, endIndex)) >= 5.2; | |||
} catch (NumberFormatException e) { | |||
return false; | |||
} | |||
} | |||
} |
@@ -1,22 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 | |||
*/ | |||
@javax.annotation.ParametersAreNonnullByDefault | |||
package org.sonar.runner.impl; | |||
@@ -1 +0,0 @@ | |||
${project.version} |
@@ -1,95 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache.Logger; | |||
import java.io.File; | |||
import java.util.Properties; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class DirsTest { | |||
Properties p = new Properties(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void should_init_default_task_work_dir() throws Exception { | |||
p.setProperty("sonar.task", "views"); | |||
new Dirs(mock(Logger.class)).init(p); | |||
File workDir = new File(p.getProperty(RunnerProperties.WORK_DIR, null)); | |||
assertThat(workDir).isNotNull().isDirectory(); | |||
assertThat(workDir.getCanonicalPath()).isEqualTo(new File(".").getCanonicalPath()); | |||
} | |||
@Test | |||
public void should_use_parameterized_task_work_dir() throws Exception { | |||
p.setProperty("sonar.task", "views"); | |||
p.setProperty(RunnerProperties.WORK_DIR, "generated/reports"); | |||
new Dirs(mock(Logger.class)).init(p); | |||
File workDir = new File(p.getProperty(RunnerProperties.WORK_DIR, null)); | |||
assertThat(workDir).isNotNull(); | |||
// separators from windows to unix | |||
assertThat(workDir.getCanonicalPath().replace("\\", "/")).contains("generated/reports"); | |||
} | |||
@Test | |||
public void should_init_default_project_dirs() throws Exception { | |||
p.setProperty("sonar.task", "scan"); | |||
new Dirs(mock(Logger.class)).init(p); | |||
File projectDir = new File(p.getProperty(ScanProperties.PROJECT_BASEDIR, null)); | |||
File workDir = new File(p.getProperty(RunnerProperties.WORK_DIR, null)); | |||
assertThat(projectDir).isNotNull().isDirectory(); | |||
assertThat(workDir).isNotNull(); | |||
assertThat(projectDir.getCanonicalPath()).isEqualTo(new File(".").getCanonicalPath()); | |||
assertThat(workDir.getName()).isEqualTo(".sonar"); | |||
assertThat(workDir.getParentFile()).isEqualTo(projectDir); | |||
} | |||
@Test | |||
public void should_set_relative_path_to_project_work_dir() throws Exception { | |||
File initialProjectDir = temp.getRoot(); | |||
p.setProperty("sonar.task", "scan"); | |||
p.setProperty(RunnerProperties.WORK_DIR, "relative/path"); | |||
p.setProperty(ScanProperties.PROJECT_BASEDIR, initialProjectDir.getAbsolutePath()); | |||
new Dirs(mock(Logger.class)).init(p); | |||
File projectDir = new File(p.getProperty(ScanProperties.PROJECT_BASEDIR, null)); | |||
File workDir = new File(p.getProperty(RunnerProperties.WORK_DIR, null)); | |||
assertThat(projectDir).isNotNull().isDirectory(); | |||
assertThat(projectDir.getCanonicalPath()).isEqualTo(initialProjectDir.getCanonicalPath()); | |||
assertThat(workDir).isNotNull(); | |||
assertThat(workDir.getCanonicalPath()).isEqualTo(new File(projectDir, "relative/path").getCanonicalPath()); | |||
} | |||
} |
@@ -1,326 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.mockito.ArgumentMatcher; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
import org.sonar.runner.cache.Logger; | |||
import org.sonar.runner.impl.ClassloadRules; | |||
import org.sonar.runner.impl.IsolatedLauncherFactory; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.nio.charset.Charset; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import static org.mockito.Matchers.anyBoolean; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.argThat; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
public class EmbeddedRunnerTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Test | |||
public void should_create() { | |||
assertThat(EmbeddedRunner.create(mock(LogOutput.class))).isNotNull().isInstanceOf(EmbeddedRunner.class); | |||
} | |||
private IsolatedLauncherFactory batchLauncher; | |||
private IsolatedLauncher launcher; | |||
private EmbeddedRunner runner; | |||
@Before | |||
public void setUp() { | |||
batchLauncher = mock(IsolatedLauncherFactory.class); | |||
launcher = mock(IsolatedLauncher.class); | |||
when(launcher.getVersion()).thenReturn("5.2"); | |||
when(batchLauncher.createLauncher(any(Properties.class), any(ClassloadRules.class), anyBoolean())).thenReturn(launcher); | |||
runner = new EmbeddedRunner(batchLauncher, mock(Logger.class), mock(LogOutput.class)); | |||
} | |||
@Test | |||
public void test_sync_project() { | |||
String projectKey = "proj"; | |||
runner.start(); | |||
runner.syncProject(projectKey); | |||
verify(launcher).syncProject(projectKey); | |||
} | |||
@Test | |||
public void test_server_version() { | |||
runner.start(); | |||
assertThat(runner.serverVersion()).isEqualTo("5.2"); | |||
} | |||
@Test | |||
public void test_run_before_start() { | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("started"); | |||
runner.runAnalysis(new Properties()); | |||
} | |||
@Test | |||
public void test_fail_project_sync_old_sq() { | |||
when(launcher.getVersion()).thenReturn("5.0"); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("version"); | |||
runner.setGlobalProperty("sonar.projectKey", "foo"); | |||
runner.start(); | |||
runner.syncProject("foo"); | |||
} | |||
@Test | |||
public void test_app() { | |||
EmbeddedRunner runner = EmbeddedRunner.create(mock(LogOutput.class)).setApp("Eclipse", "3.1"); | |||
assertThat(runner.app()).isEqualTo("Eclipse"); | |||
assertThat(runner.appVersion()).isEqualTo("3.1"); | |||
} | |||
@Test | |||
public void test_back_compatibility() { | |||
when(launcher.getVersion()).thenReturn("4.5"); | |||
Properties analysisProps = new Properties(); | |||
analysisProps.put("sonar.dummy", "summy"); | |||
runner.setGlobalProperty("sonar.projectKey", "foo"); | |||
runner.start(); | |||
runner.runAnalysis(analysisProps); | |||
runner.stop(); | |||
verify(batchLauncher).createLauncher(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return "foo".equals(((Properties) o).getProperty("sonar.projectKey")); | |||
} | |||
}), any(ClassloadRules.class), anyBoolean()); | |||
// it should have added a few properties to analysisProperties, and have merged global props | |||
final String[] mustHaveKeys = {"sonar.working.directory", "sonar.sourceEncoding", "sonar.projectBaseDir", | |||
"sonar.projectKey", "sonar.dummy"}; | |||
verify(launcher).executeOldVersion(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
Properties m = (Properties) o; | |||
for (String s : mustHaveKeys) { | |||
if (!m.containsKey(s)) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
}), eq(new LinkedList<>())); | |||
} | |||
@Test | |||
public void should_set_properties() { | |||
EmbeddedRunner runner = EmbeddedRunner.create(mock(LogOutput.class)); | |||
runner.setGlobalProperty("sonar.projectKey", "foo"); | |||
runner.addGlobalProperties(new Properties() { | |||
{ | |||
setProperty("sonar.login", "admin"); | |||
setProperty("sonar.password", "gniark"); | |||
} | |||
}); | |||
assertThat(runner.globalProperty("sonar.projectKey", null)).isEqualTo("foo"); | |||
assertThat(runner.globalProperty("sonar.login", null)).isEqualTo("admin"); | |||
assertThat(runner.globalProperty("sonar.password", null)).isEqualTo("gniark"); | |||
assertThat(runner.globalProperty("not.set", "this_is_default")).isEqualTo("this_is_default"); | |||
} | |||
@Test | |||
public void should_launch_batch() { | |||
runner.setGlobalProperty("sonar.projectKey", "foo"); | |||
runner.start(); | |||
runner.runAnalysis(new Properties()); | |||
runner.stop(); | |||
verify(batchLauncher).createLauncher(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return "foo".equals(((Properties) o).getProperty("sonar.projectKey")); | |||
} | |||
}), any(ClassloadRules.class), anyBoolean()); | |||
// it should have added a few properties to analysisProperties | |||
final String[] mustHaveKeys = {"sonar.working.directory", "sonar.sourceEncoding", "sonar.projectBaseDir"}; | |||
verify(launcher).execute(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
Properties m = (Properties) o; | |||
for (String s : mustHaveKeys) { | |||
if (!m.containsKey(s)) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
})); | |||
} | |||
@Test | |||
public void test_issue_adapter() { | |||
final List<Issue> issuesRecorded = new LinkedList<>(); | |||
IssueListener apiIssueListener = new IssueListener() { | |||
@Override | |||
public void handle(Issue issue) { | |||
issuesRecorded.add(issue); | |||
} | |||
}; | |||
IssueListenerAdapter adapter = new IssueListenerAdapter(apiIssueListener); | |||
org.sonar.runner.batch.IssueListener.Issue batchIssue = new org.sonar.runner.batch.IssueListener.Issue(); | |||
batchIssue.setAssigneeName("assignee"); | |||
adapter.handle(batchIssue); | |||
assertThat(issuesRecorded).hasSize(1); | |||
assertThat(issuesRecorded.get(0).getAssigneeName()).isEqualTo("assignee"); | |||
} | |||
@Test(expected = IllegalArgumentException.class) | |||
public void reject_issue_listener_old_version() { | |||
when(launcher.getVersion()).thenReturn("4.5"); | |||
launch_with_issue_listener(); | |||
} | |||
@Test | |||
public void launch_with_issue_listener() { | |||
runner.start(); | |||
runner.runAnalysis(mock(Properties.class), mock(IssueListener.class)); | |||
runner.stop(); | |||
verify(launcher).execute(any(Properties.class), any(org.sonar.runner.batch.IssueListener.class)); | |||
} | |||
@Test | |||
public void should_launch_batch_analysisProperties() { | |||
runner.setGlobalProperty("sonar.projectKey", "foo"); | |||
runner.start(); | |||
Properties analysisProperties = new Properties(); | |||
analysisProperties.put("sonar.projectKey", "value1"); | |||
runner.runAnalysis(analysisProperties); | |||
runner.stop(); | |||
verify(batchLauncher).createLauncher(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return "foo".equals(((Properties) o).getProperty("sonar.projectKey")); | |||
} | |||
}), any(ClassloadRules.class), anyBoolean()); | |||
verify(launcher).execute(argThat(new ArgumentMatcher<Properties>() { | |||
@Override | |||
public boolean matches(Object o) { | |||
return "value1".equals(((Properties) o).getProperty("sonar.projectKey")); | |||
} | |||
})); | |||
} | |||
@Test | |||
public void should_launch_in_simulation_mode() throws IOException { | |||
batchLauncher = new IsolatedLauncherFactory(mock(Logger.class)); | |||
runner = new EmbeddedRunner(batchLauncher, mock(Logger.class), mock(LogOutput.class)); | |||
File dump = temp.newFile(); | |||
Properties p = new Properties(); | |||
p.setProperty("sonar.projectKey", "foo"); | |||
runner.setGlobalProperty("sonarRunner.dumpToFile", dump.getAbsolutePath()); | |||
runner.start(); | |||
runner.runAnalysis(p); | |||
runner.stop(); | |||
Properties props = new Properties(); | |||
props.load(new FileInputStream(dump)); | |||
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo"); | |||
} | |||
@Test | |||
public void should_set_default_platform_encoding() throws Exception { | |||
Properties p = new Properties(); | |||
p.setProperty("sonar.task", "scan"); | |||
runner.initSourceEncoding(p); | |||
assertThat(p.getProperty("sonar.sourceEncoding", null)).isEqualTo(Charset.defaultCharset().name()); | |||
} | |||
@Test | |||
public void invalidate_after_stop() { | |||
runner.start(); | |||
runner.stop(); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("started"); | |||
runner.runAnalysis(new Properties()); | |||
} | |||
@Test | |||
public void cannot_start_twice() { | |||
runner.start(); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("started"); | |||
runner.start(); | |||
} | |||
@Test | |||
public void should_use_parameterized_encoding() throws Exception { | |||
Properties p = new Properties(); | |||
p.setProperty("sonar.task", "scan"); | |||
p.setProperty("sonar.sourceEncoding", "THE_ISO_1234"); | |||
runner.initSourceEncoding(p); | |||
assertThat(p.getProperty("sonar.sourceEncoding", null)).isEqualTo("THE_ISO_1234"); | |||
} | |||
@Test | |||
public void should_not_init_encoding_if_not_project_task() throws Exception { | |||
Properties p = new Properties(); | |||
p.setProperty("sonar.task", "views"); | |||
runner.initSourceEncoding(p); | |||
assertThat(p.getProperty("sonar.sourceEncoding", null)).isNull(); | |||
} | |||
} |
@@ -1,57 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.verify; | |||
import org.mockito.ArgumentCaptor; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.mockito.Mockito.mock; | |||
public class IssueListenerAdapterTest { | |||
private IssueListenerAdapter adapter; | |||
private IssueListener issueListener; | |||
@Before | |||
public void setUp() { | |||
issueListener = mock(IssueListener.class); | |||
adapter = new IssueListenerAdapter(issueListener); | |||
} | |||
@Test | |||
public void test() { | |||
org.sonar.runner.batch.IssueListener.Issue issue = new org.sonar.runner.batch.IssueListener.Issue(); | |||
issue.setAssigneeName("dummy"); | |||
issue.setStartLineOffset(2); | |||
adapter.handle(issue); | |||
ArgumentCaptor<Issue> argument = ArgumentCaptor.forClass(Issue.class); | |||
verify(issueListener).handle(argument.capture()); | |||
Issue i = argument.getValue(); | |||
assertThat(i.getAssigneeName()).isEqualTo("dummy"); | |||
assertThat(i.getStartLineOffset()).isEqualTo(2); | |||
} | |||
} |
@@ -1,76 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Matchers.startsWith; | |||
import org.junit.Test; | |||
import org.junit.Before; | |||
public class LoggerAdapterTest { | |||
private LoggerAdapter adapter; | |||
private LogOutput logOutput; | |||
@Before | |||
public void setUp() { | |||
logOutput = mock(LogOutput.class); | |||
adapter = new LoggerAdapter(logOutput); | |||
} | |||
@Test | |||
public void testDebug() { | |||
adapter.debug("debug"); | |||
verify(logOutput).log("debug", LogOutput.Level.DEBUG); | |||
verifyNoMoreInteractions(logOutput); | |||
} | |||
@Test | |||
public void testInfo() { | |||
adapter.info("info"); | |||
verify(logOutput).log("info", LogOutput.Level.INFO); | |||
verifyNoMoreInteractions(logOutput); | |||
} | |||
@Test | |||
public void testWarn() { | |||
adapter.warn("warn"); | |||
verify(logOutput).log("warn", LogOutput.Level.WARN); | |||
verifyNoMoreInteractions(logOutput); | |||
} | |||
@Test | |||
public void testError() { | |||
adapter.error("error"); | |||
verify(logOutput).log("error", LogOutput.Level.ERROR); | |||
verifyNoMoreInteractions(logOutput); | |||
} | |||
@Test | |||
public void testErrorThrowable() { | |||
adapter.error("error", new IllegalStateException("error")); | |||
verify(logOutput).log(startsWith("error\njava.lang.IllegalStateException: error"), eq(LogOutput.Level.ERROR)); | |||
verifyNoMoreInteractions(logOutput); | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 RunnerVersionTest { | |||
@Test | |||
public void should_load_version() { | |||
String version = RunnerVersion.version(); | |||
assertThat(version).isNotEmpty().contains(".").doesNotContain("$"); | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this program; if not, write to the Free Software | |||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
*/ | |||
package org.sonar.runner.api; | |||
import org.junit.Test; | |||
import org.sonar.runner.api.LogOutput.Level; | |||
import java.io.PrintStream; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.mock; | |||
public class StdOutLogOutputTest { | |||
private PrintStream stdOut = mock(PrintStream.class); | |||
private StdOutLogOutput logOutput = new StdOutLogOutput(stdOut); | |||
@Test | |||
public void test() { | |||
logOutput.log("msg", Level.INFO); | |||
verify(stdOut).println("INFO: msg"); | |||
} | |||
} |
@@ -1,128 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.rules.TemporaryFolder; | |||
import java.io.Closeable; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.util.Properties; | |||
import org.junit.Test; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class UtilsTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@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"); | |||
} | |||
@Test | |||
public void task_should_require_project() { | |||
Properties props = new Properties(); | |||
assertThat(Utils.taskRequiresProject(props)).isTrue(); | |||
props.setProperty("sonar.task", "scan"); | |||
assertThat(Utils.taskRequiresProject(props)).isTrue(); | |||
} | |||
@Test | |||
public void write_properties() throws IOException { | |||
File f = temp.newFile(); | |||
Properties p = new Properties(); | |||
p.put("key", "value"); | |||
Utils.writeProperties(f, p); | |||
assertThat(Files.readAllLines(f.toPath(), StandardCharsets.UTF_8)).contains("key=value"); | |||
} | |||
@Test | |||
public void task_should_not_require_project() { | |||
Properties props = new Properties(); | |||
props.setProperty("sonar.task", "views"); | |||
assertThat(Utils.taskRequiresProject(props)).isFalse(); | |||
} | |||
@Test | |||
public void close_quietly_error() throws IOException { | |||
Closeable c = mock(Closeable.class); | |||
doThrow(IOException.class).when(c).close(); | |||
Utils.closeQuietly(c); | |||
verify(c).close(); | |||
} | |||
@Test | |||
public void close_quietly_null() throws IOException { | |||
Utils.closeQuietly(null); | |||
} | |||
@Test | |||
public void close_quietly() throws IOException { | |||
Closeable c = mock(Closeable.class); | |||
Utils.closeQuietly(c); | |||
verify(c).close(); | |||
} | |||
@Test | |||
public void delete_quietly() { | |||
File f = mock(File.class); | |||
doThrow(IOException.class).when(f).toPath(); | |||
Utils.deleteQuietly(f); | |||
} | |||
@Test | |||
public void delete_non_empty_directory() throws IOException { | |||
/*- | |||
* Create test structure: | |||
* tmp | |||
* |-folder1 | |||
* |- file1 | |||
* |- folder2 | |||
* |- file2 | |||
*/ | |||
Path tmpDir = Files.createTempDirectory("junit"); | |||
Path folder1 = tmpDir.resolve("folder1"); | |||
Files.createDirectories(folder1); | |||
Path file1 = folder1.resolve("file1"); | |||
Files.write(file1, "test1".getBytes()); | |||
Path folder2 = folder1.resolve("folder2"); | |||
Files.createDirectories(folder2); | |||
Path file2 = folder1.resolve("file2"); | |||
Files.write(file2, "test2".getBytes()); | |||
// delete it | |||
assertThat(tmpDir.toFile()).exists(); | |||
Utils.deleteQuietly(tmpDir.toFile()); | |||
assertThat(tmpDir.toFile()).doesNotExist(); | |||
} | |||
} |
@@ -1,94 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import static org.mockito.Mockito.mock; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import org.junit.rules.ExpectedException; | |||
import java.nio.channels.OverlappingFileLockException; | |||
import java.nio.file.Paths; | |||
import org.junit.Test; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.rules.TemporaryFolder; | |||
public class DirectoryLockTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException exception = ExpectedException.none(); | |||
private DirectoryLock lock; | |||
@Before | |||
public void setUp() { | |||
lock = new DirectoryLock(temp.getRoot().toPath(), mock(Logger.class)); | |||
} | |||
@Test | |||
public void lock() { | |||
assertThat(temp.getRoot().list()).isEmpty(); | |||
lock.lock(); | |||
assertThat(temp.getRoot().toPath().resolve(".sonar_lock")).exists(); | |||
lock.unlock(); | |||
assertThat(temp.getRoot().list()).isEmpty(); | |||
} | |||
@Test | |||
public void tryLock() { | |||
assertThat(temp.getRoot().list()).isEmpty(); | |||
lock.tryLock(); | |||
assertThat(temp.getRoot().toPath().resolve(".sonar_lock")).exists(); | |||
lock.unlock(); | |||
assertThat(temp.getRoot().list()).isEmpty(); | |||
} | |||
@Test(expected = OverlappingFileLockException.class) | |||
public void error_2locks() { | |||
assertThat(temp.getRoot().list()).isEmpty(); | |||
lock.lock(); | |||
lock.lock(); | |||
} | |||
@Test | |||
public void unlockWithoutLock() { | |||
lock.unlock(); | |||
} | |||
@Test | |||
public void errorCreatingLock() { | |||
lock = new DirectoryLock(Paths.get("non", "existing", "path"), mock(Logger.class)); | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage("Failed to create lock"); | |||
lock.lock(); | |||
} | |||
@Test | |||
public void errorTryLock() { | |||
lock = new DirectoryLock(Paths.get("non", "existing", "path"), mock(Logger.class)); | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage("Failed to create lock"); | |||
lock.tryLock(); | |||
} | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.File; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class FileCacheBuilderTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void setUserHome() throws Exception { | |||
File userHome = temp.newFolder(); | |||
FileCache cache = new FileCacheBuilder(mock(Logger.class)).setUserHome(userHome).build(); | |||
assertThat(cache.getDir()).isDirectory().exists(); | |||
assertThat(cache.getDir().getName()).isEqualTo("cache"); | |||
assertThat(cache.getDir().getParentFile()).isEqualTo(userHome); | |||
} | |||
@Test | |||
public void user_home_property_can_be_null() { | |||
FileCache cache = new FileCacheBuilder(mock(Logger.class)).setUserHome((String) null).build(); | |||
// does not fail. It uses default path or env variable | |||
assertThat(cache.getDir()).isDirectory().exists(); | |||
assertThat(cache.getDir().getName()).isEqualTo("cache"); | |||
} | |||
@Test | |||
public void use_default_path_or_env_variable() { | |||
FileCache cache = new FileCacheBuilder(mock(Logger.class)).build(); | |||
assertThat(cache.getDir()).isDirectory().exists(); | |||
assertThat(cache.getDir().getName()).isEqualTo("cache"); | |||
} | |||
} |
@@ -1,117 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class FileCacheTest { | |||
@Rule | |||
public TemporaryFolder tempFolder = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@Test | |||
public void not_in_cache() throws IOException { | |||
FileCache cache = FileCache.create(tempFolder.newFolder(), mock(Logger.class)); | |||
assertThat(cache.get("sonar-foo-plugin-1.5.jar", "ABCDE")).isNull(); | |||
} | |||
@Test | |||
public void found_in_cache() throws IOException { | |||
FileCache cache = FileCache.create(tempFolder.newFolder(), mock(Logger.class)); | |||
// populate the cache. Assume that hash is correct. | |||
File cachedFile = new File(new File(cache.getDir(), "ABCDE"), "sonar-foo-plugin-1.5.jar"); | |||
FileUtils.write(cachedFile, "body"); | |||
assertThat(cache.get("sonar-foo-plugin-1.5.jar", "ABCDE")).isNotNull().exists().isEqualTo(cachedFile); | |||
} | |||
@Test | |||
public void download_and_add_to_cache() throws IOException { | |||
FileHashes hashes = mock(FileHashes.class); | |||
FileCache cache = new FileCache(tempFolder.newFolder(), hashes, mock(Logger.class)); | |||
when(hashes.of(any(File.class))).thenReturn("ABCDE"); | |||
FileCache.Downloader downloader = new FileCache.Downloader() { | |||
public void download(String filename, File toFile) throws IOException { | |||
FileUtils.write(toFile, "body"); | |||
} | |||
}; | |||
File cachedFile = cache.get("sonar-foo-plugin-1.5.jar", "ABCDE", downloader); | |||
assertThat(cachedFile).isNotNull().exists().isFile(); | |||
assertThat(cachedFile.getName()).isEqualTo("sonar-foo-plugin-1.5.jar"); | |||
assertThat(cachedFile.getParentFile().getParentFile()).isEqualTo(cache.getDir()); | |||
assertThat(FileUtils.readFileToString(cachedFile)).isEqualTo("body"); | |||
} | |||
@Test | |||
public void download_corrupted_file() throws IOException { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("INVALID HASH"); | |||
FileHashes hashes = mock(FileHashes.class); | |||
FileCache cache = new FileCache(tempFolder.newFolder(), hashes, mock(Logger.class)); | |||
when(hashes.of(any(File.class))).thenReturn("VWXYZ"); | |||
FileCache.Downloader downloader = new FileCache.Downloader() { | |||
public void download(String filename, File toFile) throws IOException { | |||
FileUtils.write(toFile, "corrupted body"); | |||
} | |||
}; | |||
cache.get("sonar-foo-plugin-1.5.jar", "ABCDE", downloader); | |||
} | |||
@Test | |||
public void concurrent_download() throws IOException { | |||
FileHashes hashes = mock(FileHashes.class); | |||
when(hashes.of(any(File.class))).thenReturn("ABCDE"); | |||
final FileCache cache = new FileCache(tempFolder.newFolder(), hashes, mock(Logger.class)); | |||
FileCache.Downloader downloader = new FileCache.Downloader() { | |||
public void download(String filename, File toFile) throws IOException { | |||
// Emulate a concurrent download that adds file to cache before | |||
File cachedFile = new File(new File(cache.getDir(), "ABCDE"), "sonar-foo-plugin-1.5.jar"); | |||
FileUtils.write(cachedFile, "downloaded by other"); | |||
FileUtils.write(toFile, "downloaded by me"); | |||
} | |||
}; | |||
// do not fail | |||
File cachedFile = cache.get("sonar-foo-plugin-1.5.jar", "ABCDE", downloader); | |||
assertThat(cachedFile).isNotNull().exists().isFile(); | |||
assertThat(cachedFile.getName()).isEqualTo("sonar-foo-plugin-1.5.jar"); | |||
assertThat(cachedFile.getParentFile().getParentFile()).isEqualTo(cache.getDir()); | |||
assertThat(FileUtils.readFileToString(cachedFile)).contains("downloaded by"); | |||
} | |||
} |
@@ -1,127 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import org.apache.commons.codec.binary.Hex; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.IOUtils; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.math.BigInteger; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.security.SecureRandom; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class FileHashesTest { | |||
SecureRandom secureRandom = new SecureRandom(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void test_md5_hash() { | |||
assertThat(hash("sonar")).isEqualTo("d85e336d61f5344395c42126fac239bc"); | |||
// compare results with commons-codec | |||
for (int index = 0; index < 100; index++) { | |||
String random = randomString(); | |||
assertThat(hash(random)).as(random).isEqualTo( | |||
DigestUtils.md5Hex(random).toLowerCase() | |||
); | |||
} | |||
} | |||
@Test | |||
public void test_hash_file() throws IOException { | |||
File f = temp.newFile(); | |||
Files.write(f.toPath(), "sonar".getBytes(StandardCharsets.UTF_8)); | |||
assertThat(hashFile(f)).isEqualTo("d85e336d61f5344395c42126fac239bc"); | |||
} | |||
@Test | |||
public void test_toHex() { | |||
// lower-case | |||
assertThat(FileHashes.toHex("aloa_bi_bop_a_loula".getBytes())).isEqualTo("616c6f615f62695f626f705f615f6c6f756c61"); | |||
// compare results with commons-codec | |||
for (int index = 0; index < 100; index++) { | |||
String random = randomString(); | |||
assertThat(FileHashes.toHex(random.getBytes())).as(random).isEqualTo( | |||
Hex.encodeHexString(random.getBytes()).toLowerCase() | |||
); | |||
} | |||
} | |||
@Test | |||
public void fail_if_file_does_not_exist() throws IOException { | |||
File file = temp.newFile("does_not_exist"); | |||
FileUtils.forceDelete(file); | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Fail to compute hash of: " + file.getAbsolutePath()); | |||
new FileHashes().of(file); | |||
} | |||
@Test | |||
public void fail_if_stream_is_closed() throws Exception { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Fail to compute hash"); | |||
InputStream input = mock(InputStream.class); | |||
when(input.read(any(byte[].class), anyInt(), anyInt())).thenThrow(new IllegalThreadStateException()); | |||
new FileHashes().of(input); | |||
} | |||
private String randomString() { | |||
return new BigInteger(130, secureRandom).toString(32); | |||
} | |||
private String hash(String s) { | |||
InputStream in = new ByteArrayInputStream(s.getBytes()); | |||
try { | |||
return new FileHashes().of(in); | |||
} finally { | |||
IOUtils.closeQuietly(in); | |||
} | |||
} | |||
private String hashFile(File f) { | |||
return new FileHashes().of(f); | |||
} | |||
} |
@@ -1,78 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import java.nio.file.Files; | |||
import java.nio.file.Paths; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assume.assumeTrue; | |||
import static org.mockito.Mockito.mock; | |||
public class PersistentCacheBuilderTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void user_home_property_can_be_null() { | |||
PersistentCache cache = new PersistentCacheBuilder(mock(Logger.class)).setSonarHome(null).setAreaForGlobal("url").build(); | |||
assertTrue(Files.isDirectory(cache.getDirectory())); | |||
assertThat(cache.getDirectory()).endsWith(Paths.get("url", "global")); | |||
} | |||
@Test | |||
public void set_user_home() { | |||
PersistentCache cache = new PersistentCacheBuilder(mock(Logger.class)).setSonarHome(temp.getRoot().toPath()).setAreaForGlobal("url").build(); | |||
assertThat(cache.getDirectory()).isDirectory(); | |||
assertThat(cache.getDirectory()).startsWith(temp.getRoot().toPath()); | |||
assertTrue(Files.isDirectory(cache.getDirectory())); | |||
} | |||
@Test | |||
public void read_system_env() { | |||
assumeTrue(System.getenv("SONAR_USER_HOME") == null); | |||
System.setProperty("user.home", temp.getRoot().getAbsolutePath()); | |||
PersistentCache cache = new PersistentCacheBuilder(mock(Logger.class)).setAreaForGlobal("url").build(); | |||
assertTrue(Files.isDirectory(cache.getDirectory())); | |||
assertThat(cache.getDirectory()).startsWith(temp.getRoot().toPath()); | |||
} | |||
@Test | |||
public void directories() { | |||
System.setProperty("user.home", temp.getRoot().getAbsolutePath()); | |||
PersistentCache cache = new PersistentCacheBuilder(mock(Logger.class)).setAreaForProject("url", "0", "proj").build(); | |||
assertThat(cache.getDirectory()).endsWith(Paths.get(".sonar", "ws_cache", "url", "0", "projects", "proj")); | |||
cache = new PersistentCacheBuilder(mock(Logger.class)).setAreaForLocalProject("url", "0").build(); | |||
assertThat(cache.getDirectory()).endsWith(Paths.get(".sonar", "ws_cache", "url", "0", "local")); | |||
cache = new PersistentCacheBuilder(mock(Logger.class)).setAreaForGlobal("url").build(); | |||
assertThat(cache.getDirectory()).endsWith(Paths.get(".sonar", "ws_cache", "url", "global")); | |||
} | |||
} |
@@ -1,145 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import org.apache.commons.io.IOUtils; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.when; | |||
import static org.mockito.Mockito.atLeast; | |||
import static org.mockito.Mockito.verify; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class PersistentCacheTest { | |||
private final static String URI = "key1"; | |||
private final static String VALUE = "cache content"; | |||
private PersistentCache cache = null; | |||
private DirectoryLock lock = null; | |||
private PersistentCacheInvalidation invalidation = null; | |||
@Rule | |||
public TemporaryFolder tmp = new TemporaryFolder(); | |||
@Before | |||
public void setUp() throws IOException { | |||
invalidation = mock(PersistentCacheInvalidation.class); | |||
when(invalidation.test(any(Path.class))).thenReturn(false); | |||
lock = mock(DirectoryLock.class); | |||
when(lock.getFileLockName()).thenReturn("lock"); | |||
cache = new PersistentCache(tmp.getRoot().toPath(), invalidation, mock(Logger.class), lock); | |||
} | |||
@Test | |||
public void testCacheMiss() throws Exception { | |||
assertCacheHit(false); | |||
} | |||
@Test | |||
public void testClean() throws Exception { | |||
Path lockFile = cache.getDirectory().resolve("lock"); | |||
// puts entry | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
Files.write(lockFile, "test".getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(true); | |||
when(invalidation.test(any(Path.class))).thenReturn(true); | |||
cache.clean(); | |||
when(invalidation.test(any(Path.class))).thenReturn(false); | |||
assertCacheHit(false); | |||
// lock file should not get deleted | |||
assertThat(new String(Files.readAllBytes(lockFile), StandardCharsets.UTF_8)).isEqualTo("test"); | |||
} | |||
@Test | |||
public void testStream() throws IOException { | |||
cache.put("id", "test".getBytes()); | |||
InputStream stream = cache.getStream("id"); | |||
assertThat(IOUtils.toString(stream)).isEqualTo("test"); | |||
assertThat(cache.getStream("non existing")).isNull(); | |||
} | |||
@Test | |||
public void testClear() throws Exception { | |||
Path lockFile = cache.getDirectory().resolve("lock"); | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
Files.write(lockFile, "test".getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(true); | |||
cache.clear(); | |||
assertCacheHit(false); | |||
// lock file should not get deleted | |||
assertThat(new String(Files.readAllBytes(lockFile), StandardCharsets.UTF_8)).isEqualTo("test"); | |||
} | |||
@Test | |||
public void testCacheHit() throws Exception { | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(true); | |||
} | |||
@Test | |||
public void testReconfigure() throws Exception { | |||
assertCacheHit(false); | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(true); | |||
File root = tmp.getRoot(); | |||
FileUtils.deleteQuietly(root); | |||
// should re-create cache directory and start using the cache | |||
cache.reconfigure(); | |||
assertThat(root).exists(); | |||
assertCacheHit(false); | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(true); | |||
} | |||
@Test | |||
public void testExpiration() throws Exception { | |||
when(invalidation.test(any(Path.class))).thenReturn(true); | |||
cache.put(URI, VALUE.getBytes(StandardCharsets.UTF_8)); | |||
assertCacheHit(false); | |||
} | |||
private void assertCacheHit(boolean hit) throws Exception { | |||
assertCacheHit(cache, hit); | |||
} | |||
private void assertCacheHit(PersistentCache pCache, boolean hit) throws Exception { | |||
String expected = hit ? VALUE : null; | |||
assertThat(pCache.getString(URI)).isEqualTo(expected); | |||
verify(lock, atLeast(1)).unlock(); | |||
} | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.cache; | |||
import org.junit.Before; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import org.junit.Test; | |||
import org.junit.Rule; | |||
import org.junit.rules.TemporaryFolder; | |||
public class TTLCacheInvalidationTest { | |||
private Path testFile; | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Before | |||
public void setUp() throws IOException { | |||
testFile = temp.newFile().toPath(); | |||
} | |||
@Test | |||
public void testExpired() throws IOException { | |||
TTLCacheInvalidation invalidation = new TTLCacheInvalidation(-100); | |||
assertThat(invalidation.test(testFile)).isEqualTo(true); | |||
} | |||
@Test | |||
public void testValid() throws IOException { | |||
TTLCacheInvalidation invalidation = new TTLCacheInvalidation(100_000); | |||
assertThat(invalidation.test(testFile)).isEqualTo(false); | |||
} | |||
} |
@@ -1,106 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import org.junit.Test; | |||
import org.junit.Before; | |||
public class ClassloadRulesTest { | |||
private ClassloadRules rules; | |||
private Set<String> maskRules; | |||
private Set<String> unmaskRules; | |||
@Before | |||
public void setUp() { | |||
maskRules = new HashSet<>(); | |||
unmaskRules = new HashSet<>(); | |||
} | |||
@Test | |||
public void testUnmask() { | |||
unmaskRules.add("org.apache.ant."); | |||
rules = new ClassloadRules(maskRules, unmaskRules); | |||
assertThat(rules.canLoad("org.sonar.runner.Foo")).isFalse(); | |||
assertThat(rules.canLoad("org.objectweb.asm.ClassVisitor")).isFalse(); | |||
assertThat(rules.canLoad("org.apache")).isFalse(); | |||
assertThat(rules.canLoad("org.apache.ant.Foo")).isTrue(); | |||
assertThat(rules.canLoad("org.apache.ant.project.Project")).isTrue(); | |||
} | |||
@Test | |||
public void testUnmaskAll() { | |||
unmaskRules.add(""); | |||
rules = new ClassloadRules(maskRules, unmaskRules); | |||
assertThat(rules.canLoad("org.sonar.runner.Foo")).isTrue(); | |||
assertThat(rules.canLoad("org.objectweb.asm.ClassVisitor")).isTrue(); | |||
assertThat(rules.canLoad("org.apache")).isTrue(); | |||
assertThat(rules.canLoad("org.apache.ant.Foo")).isTrue(); | |||
assertThat(rules.canLoad("org.apache.ant.project.Project")).isTrue(); | |||
} | |||
@Test | |||
public void testDefault() { | |||
rules = new ClassloadRules(maskRules, unmaskRules); | |||
assertThat(rules.canLoad("org.sonar.runner.Foo")).isFalse(); | |||
} | |||
@Test | |||
public void testMaskAndUnmask() throws ClassNotFoundException { | |||
unmaskRules.add("org.apache.ant."); | |||
maskRules.add("org.apache.ant.foo."); | |||
rules = new ClassloadRules(maskRules, unmaskRules); | |||
assertThat(rules.canLoad("org.apache.ant.something")).isTrue(); | |||
assertThat(rules.canLoad("org.apache.ant.foo.something")).isFalse(); | |||
assertThat(rules.canLoad("org.apache")).isFalse(); | |||
} | |||
@Test | |||
public void testUsedByMaven() { | |||
maskRules.add( "org.slf4j.LoggerFactory" ); | |||
// Include slf4j Logger that is exposed by some Sonar components | |||
unmaskRules.add( "org.slf4j.Logger" ); | |||
unmaskRules.add( "org.slf4j.ILoggerFactory" ); | |||
// Exclude other slf4j classes | |||
// .unmask("org.slf4j.impl.") | |||
maskRules.add( "org.slf4j." ); | |||
// Exclude logback | |||
maskRules.add( "ch.qos.logback." ); | |||
maskRules.add( "org.sonar." ); | |||
unmaskRules.add("org.sonar.runner.batch."); | |||
// Guava is not the same version in SonarQube classloader | |||
maskRules.add( "com.google.common" ); | |||
// Include everything else | |||
unmaskRules.add( "" ); | |||
rules = new ClassloadRules(maskRules, unmaskRules); | |||
assertThat(rules.canLoad("org.sonar.runner.batch.IsolatedLauncher")).isTrue(); | |||
} | |||
} |
@@ -1,110 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Before; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.util.Arrays; | |||
import java.util.Enumeration; | |||
import java.util.HashSet; | |||
import static org.mockito.Mockito.when; | |||
import static org.mockito.Mockito.mock; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class IsolatedClassloaderTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private IsolatedClassloader classLoader; | |||
@Before | |||
public void setUp() { | |||
ClassLoader parent = getClass().getClassLoader(); | |||
classLoader = new IsolatedClassloader(parent, new ClassloadRules(new HashSet<String>(), new HashSet<String>())); | |||
} | |||
@Test | |||
public void should_use_isolated_system_classloader_when_parent_is_excluded() throws ClassNotFoundException, IOException { | |||
thrown.expect(ClassNotFoundException.class); | |||
thrown.expectMessage("org.junit.Test"); | |||
// JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM | |||
assertThat(classLoader.loadClass("java.lang.String", false)).isNotNull(); | |||
classLoader.loadClass("org.junit.Test", false); | |||
classLoader.close(); | |||
} | |||
@Test | |||
public void should_use_parent_to_load() throws ClassNotFoundException, IOException { | |||
ClassloadRules rules = mock(ClassloadRules.class); | |||
when(rules.canLoad("org.junit.Test")).thenReturn(true); | |||
classLoader = new IsolatedClassloader(getClass().getClassLoader(), rules); | |||
assertThat(classLoader.loadClass("org.junit.Test", false)).isNotNull(); | |||
} | |||
@Test | |||
public void add_jars() throws MalformedURLException { | |||
File f = new File("dummy"); | |||
File[] files = {f}; | |||
classLoader.addFiles(Arrays.asList(files)); | |||
assertThat(classLoader.getURLs()).contains(f.toURI().toURL()); | |||
} | |||
@Test | |||
public void error_add_jars() { | |||
File f = mock(File.class); | |||
when(f.toURI()).thenThrow(MalformedURLException.class); | |||
File[] files = {f}; | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Fail to create classloader"); | |||
classLoader.addFiles(Arrays.asList(files)); | |||
} | |||
@Test | |||
public void dont_get_resource_from_parent() { | |||
URL resource2 = classLoader.getParent().getResource("fake.jar"); | |||
assertThat(resource2).isNotNull(); | |||
// should not find resource through parent classloader | |||
URL resource = classLoader.getResource("fake.jar"); | |||
assertThat(resource).isNull(); | |||
} | |||
@Test | |||
public void dont_get_resources_from_parent() throws IOException { | |||
Enumeration<URL> resource2 = classLoader.getParent().getResources("fake.jar"); | |||
assertThat(resource2.hasMoreElements()).isTrue(); | |||
// should not find resource through parent classloader | |||
Enumeration<URL> resource = classLoader.getResources("fake.jar"); | |||
assertThat(resource.hasMoreElements()).isFalse(); | |||
} | |||
} |
@@ -1,96 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
import org.sonar.runner.batch.IssueListener; | |||
import org.sonar.runner.batch.LogOutput; | |||
import org.sonar.runner.cache.Logger; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Fail.fail; | |||
import static org.mockito.Mockito.mock; | |||
public class IsolatedLauncherFactoryTest { | |||
IsolatedLauncherFactory factory; | |||
Properties props; | |||
TempCleaning tempCleaning; | |||
JarDownloader jarDownloader; | |||
@Before | |||
public void setUp() { | |||
tempCleaning = mock(TempCleaning.class); | |||
factory = new IsolatedLauncherFactory(FakeIsolatedLauncher.class.getName(), tempCleaning, mock(Logger.class)); | |||
props = new Properties(); | |||
jarDownloader = mock(JarDownloader.class); | |||
} | |||
@Test | |||
public void should_use_isolated_classloader() { | |||
try { | |||
factory.createLauncher(jarDownloader, new ClassloadRules(new HashSet<String>(), new HashSet<String>())); | |||
fail(); | |||
} catch (RunnerException e) { | |||
// success | |||
} | |||
} | |||
public static class FakeIsolatedLauncher implements IsolatedLauncher { | |||
public static Properties props = null; | |||
public static IssueListener listener = null; | |||
@Override | |||
public void start(Properties properties, LogOutput logger, boolean preferCache) { | |||
} | |||
@Override | |||
public void stop() { | |||
} | |||
@Override | |||
public void execute(Properties properties) { | |||
FakeIsolatedLauncher.props = properties; | |||
} | |||
@Override | |||
public void executeOldVersion(Properties properties, List<Object> extensions) { | |||
} | |||
@Override | |||
public String getVersion() { | |||
return null; | |||
} | |||
@Override | |||
public void execute(Properties properties, IssueListener listener) { | |||
FakeIsolatedLauncher.props = properties; | |||
FakeIsolatedLauncher.listener = listener; | |||
} | |||
@Override | |||
public void syncProject(String projectKey) { | |||
} | |||
} | |||
} |
@@ -1,77 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.runner.batch.BatchIsolatedLauncher; | |||
import org.sonar.runner.cache.Logger; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.concurrent.Callable; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class IsolatedLauncherProxyTest { | |||
BatchIsolatedLauncher laucherProxy = null; | |||
ClassLoader cl = null; | |||
@Before | |||
public void setUp() { | |||
cl = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); | |||
} | |||
@Test | |||
public void delegate_proxied() throws ClassNotFoundException, InstantiationException, IllegalAccessException { | |||
String str = "test"; | |||
CharSequence s = IsolatedLauncherProxy.create(cl, str, CharSequence.class, mock(Logger.class)); | |||
assertThat(s).isEqualTo(str); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void exceptions_unwrapped() throws ReflectiveOperationException { | |||
Runnable r = IsolatedLauncherProxy.create(cl, Runnable.class, ExceptionThrower.class.getName(), mock(Logger.class)); | |||
r.run(); | |||
} | |||
@Test | |||
public void create_proxied() throws Exception { | |||
Callable<?> c = IsolatedLauncherProxy.create(cl, Callable.class, SimpleClass.class.getName(), mock(Logger.class)); | |||
assertThat(c.getClass().getClassLoader()).isEqualTo(cl); | |||
assertThat(c.getClass().getClassLoader()).isNotEqualTo(Thread.currentThread().getContextClassLoader()); | |||
assertThat(c.call()).isEqualTo(URLClassLoader.class.getSimpleName()); | |||
} | |||
public static class ExceptionThrower implements Runnable { | |||
@Override | |||
public void run() { | |||
throw new IllegalStateException("message"); | |||
} | |||
} | |||
public static class SimpleClass implements Callable<String> { | |||
@Override | |||
public String call() throws Exception { | |||
return Thread.currentThread().getContextClassLoader().getClass().getSimpleName(); | |||
} | |||
} | |||
} |
@@ -1,47 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Test; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.doReturn; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.spy; | |||
public class JarDownloaderTest { | |||
ServerConnection serverConnection = mock(ServerConnection.class); | |||
Properties props = new Properties(); | |||
JarDownloader downloader = spy(new JarDownloader(serverConnection, mock(Logger.class), props)); | |||
@Test | |||
public void should_download_jar_files() { | |||
doReturn(new ArrayList<File>()).when(downloader).download(); | |||
List<File> jarFiles = downloader.download(); | |||
assertThat(jarFiles).isNotNull(); | |||
} | |||
} |
@@ -1,48 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Test; | |||
import java.io.File; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
public class JarExtractorTest { | |||
@Test | |||
public void test_extract() throws Exception { | |||
File jarFile = new JarExtractor().extractToTemp("fake"); | |||
assertThat(jarFile).isFile().exists(); | |||
assertThat(FileUtils.readFileToString(jarFile, "UTF-8")).isEqualTo("Fake jar for unit tests"); | |||
assertThat(jarFile.toURI().toURL().toString()).doesNotContain("jar:file"); | |||
} | |||
@Test | |||
public void should_fail_to_extract() throws Exception { | |||
try { | |||
new JarExtractor().extractToTemp("unknown"); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Fail to extract unknown.jar"); | |||
} | |||
} | |||
} |
@@ -1,103 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.runner.cache.FileCache; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class JarsTest { | |||
ServerConnection connection = mock(ServerConnection.class); | |||
JarExtractor jarExtractor = mock(JarExtractor.class); | |||
FileCache fileCache = mock(FileCache.class); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void should_download_jar_files() throws Exception { | |||
File batchJar = temp.newFile("sonar-runner-batch.jar"); | |||
when(jarExtractor.extractToTemp("sonar-runner-batch")).thenReturn(batchJar); | |||
// index of the files to download | |||
when(connection.loadString("/batch_bootstrap/index")).thenReturn( | |||
"cpd.jar|CA124VADFSDS\n" + | |||
"squid.jar|34535FSFSDF\n"); | |||
Jars jars35 = new Jars(fileCache, connection, jarExtractor, mock(Logger.class)); | |||
List<File> files = jars35.download(); | |||
assertThat(files).isNotNull(); | |||
verify(connection, times(1)).loadString("/batch_bootstrap/index"); | |||
verifyNoMoreInteractions(connection); | |||
verify(fileCache, times(1)).get(eq("cpd.jar"), eq("CA124VADFSDS"), any(FileCache.Downloader.class)); | |||
verify(fileCache, times(1)).get(eq("squid.jar"), eq("34535FSFSDF"), any(FileCache.Downloader.class)); | |||
verifyNoMoreInteractions(fileCache); | |||
} | |||
@Test | |||
public void should_honor_sonarUserHome() throws IOException { | |||
Properties props = new Properties(); | |||
File f = temp.newFolder(); | |||
props.put("sonar.userHome", f.getAbsolutePath()); | |||
Jars jars = new Jars(connection, jarExtractor, mock(Logger.class), props); | |||
assertThat(jars.getFileCache().getDir()).isEqualTo(new File(f, "cache")); | |||
} | |||
@Test | |||
public void should_fail_to_download_files() throws Exception { | |||
File batchJar = temp.newFile("sonar-runner-batch.jar"); | |||
when(jarExtractor.extractToTemp("sonar-runner-batch")).thenReturn(batchJar); | |||
// index of the files to download | |||
when(connection.loadString("/batch_bootstrap/index")).thenThrow(new IllegalStateException()); | |||
Jars jars35 = new Jars(fileCache, connection, jarExtractor, mock(Logger.class)); | |||
try { | |||
jars35.download(); | |||
fail(); | |||
} catch (RuntimeException e) { | |||
assertThat(e).hasMessage("Fail to download libraries from server"); | |||
} | |||
} | |||
@Test | |||
public void test_jar_downloader() throws Exception { | |||
Jars.BatchFileDownloader downloader = new Jars.BatchFileDownloader(connection); | |||
File toFile = temp.newFile(); | |||
downloader.download("squid.jar", toFile); | |||
verify(connection).download("/batch/squid.jar", toFile); | |||
} | |||
} |
@@ -1,123 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.apache.commons.io.IOUtils; | |||
import org.mortbay.jetty.Handler; | |||
import org.mortbay.jetty.HttpConnection; | |||
import org.mortbay.jetty.HttpException; | |||
import org.mortbay.jetty.Request; | |||
import org.mortbay.jetty.Server; | |||
import org.mortbay.jetty.handler.AbstractHandler; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
import static javax.servlet.http.HttpServletResponse.SC_OK; | |||
import static org.apache.commons.io.IOUtils.write; | |||
class MockHttpServer { | |||
private Server server; | |||
private String responseBody; | |||
private String requestBody; | |||
private String mockResponseData; | |||
private int mockResponseStatus = SC_OK; | |||
public void start() throws Exception { | |||
// 0 is random available port | |||
server = new Server(0); | |||
server.setHandler(getMockHandler()); | |||
server.start(); | |||
} | |||
/** | |||
* Creates an {@link AbstractHandler handler} returning an arbitrary String as a response. | |||
* | |||
* @return never <code>null</code>. | |||
*/ | |||
public Handler getMockHandler() { | |||
Handler handler = new AbstractHandler() { | |||
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { | |||
// SONARPLUGINS-3061 | |||
if (target.startsWith("//")) { | |||
throw new HttpException(404, "Not found"); | |||
} | |||
Request baseRequest = request instanceof Request ? (Request) request : HttpConnection.getCurrentConnection().getRequest(); | |||
setResponseBody(getMockResponseData()); | |||
setRequestBody(IOUtils.toString(baseRequest.getInputStream())); | |||
response.setStatus(mockResponseStatus); | |||
response.setContentType("text/xml;charset=utf-8"); | |||
write(getResponseBody(), response.getOutputStream()); | |||
baseRequest.setHandled(true); | |||
} | |||
}; | |||
return handler; | |||
} | |||
public void stop() { | |||
try { | |||
if (server != null) { | |||
server.stop(); | |||
} | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to stop HTTP server", e); | |||
} | |||
} | |||
public void setResponseBody(String responseBody) { | |||
this.responseBody = responseBody; | |||
} | |||
public String getResponseBody() { | |||
return responseBody; | |||
} | |||
public void setRequestBody(String requestBody) { | |||
this.requestBody = requestBody; | |||
} | |||
public String getRequestBody() { | |||
return requestBody; | |||
} | |||
public void setMockResponseData(String mockResponseData) { | |||
this.mockResponseData = mockResponseData; | |||
} | |||
public void setMockResponseStatus(int status) { | |||
this.mockResponseStatus = status; | |||
} | |||
public String getMockResponseData() { | |||
return mockResponseData; | |||
} | |||
public int getPort() { | |||
return server.getConnectors()[0].getLocalPort(); | |||
} | |||
public String getUrl() { | |||
return "http://" + server.getConnectors()[0].getName(); | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.rules.ExternalResource; | |||
public class MockHttpServerInterceptor extends ExternalResource { | |||
private MockHttpServer server; | |||
@Override | |||
protected final void before() throws Throwable { | |||
server = new MockHttpServer(); | |||
server.start(); | |||
} | |||
@Override | |||
protected void after() { | |||
server.stop(); | |||
} | |||
public void setMockResponseData(String data) { | |||
server.setMockResponseData(data); | |||
} | |||
public void setMockResponseStatus(int status) { | |||
server.setMockResponseStatus(status); | |||
} | |||
public String url() { | |||
return server.getUrl(); | |||
} | |||
public int getPort() { | |||
return server.getPort(); | |||
} | |||
} |
@@ -1,52 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import java.io.IOException; | |||
import java.net.ServerSocket; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
class NetworkUtil { | |||
static int getNextAvailablePort() { | |||
for (int index = 0; index < 5; index++) { | |||
try { | |||
ServerSocket socket = new ServerSocket(0); | |||
int unusedPort = socket.getLocalPort(); | |||
socket.close(); | |||
if (isValidPort(unusedPort)) { | |||
return unusedPort; | |||
} | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Can not find an open network port", e); | |||
} | |||
} | |||
throw new IllegalStateException("Can not find an open network port"); | |||
} | |||
// Firefox blocks some reserverd ports : http://www-archive.mozilla.org/projects/netlib/PortBanning.html | |||
private static final List<Integer> BLOCKED_PORTS = Arrays.asList(2049, 4045, 6000); | |||
static boolean isValidPort(int port) { | |||
return port > 1023 && !BLOCKED_PORTS.contains(port); | |||
} | |||
} |
@@ -1,261 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import com.github.kevinsawicki.http.HttpRequest; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.runner.cache.Logger; | |||
import org.sonar.runner.cache.PersistentCache; | |||
import org.sonar.runner.cache.PersistentCacheBuilder; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.ConnectException; | |||
import java.net.SocketTimeoutException; | |||
import java.util.Properties; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Matchers.startsWith; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class ServerConnectionTest { | |||
@Rule | |||
public MockHttpServerInterceptor httpServer = new MockHttpServerInterceptor(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private PersistentCache cache; | |||
private Logger logger; | |||
@Before | |||
public void setUp() { | |||
cache = new PersistentCacheBuilder(mock(Logger.class)) | |||
.setAreaForGlobal("server") | |||
.setSonarHome(temp.getRoot().toPath()) | |||
.build(); | |||
logger = mock(Logger.class); | |||
} | |||
@Test | |||
public void continue_if_cache_put_fails() throws Exception { | |||
cache = mock(PersistentCache.class); | |||
doThrow(IOException.class).when(cache).put(anyString(), any(byte[].class)); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url(), null, true, false); | |||
String response = connection.loadString("/batch/index.txt"); | |||
assertThat(response).isEqualTo("abcde"); | |||
verify(logger).warn(startsWith("Failed to cache WS call:")); | |||
} | |||
@Test | |||
public void should_download_to_string() throws Exception { | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url(), null); | |||
String response = connection.loadString("/batch/index.txt"); | |||
assertThat(response).isEqualTo("abcde"); | |||
} | |||
@Test | |||
public void test_prefer_cache() throws IOException { | |||
File cacheDir = cache.getDirectory().toFile(); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, true); | |||
//not cached | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
String str = connection.loadString("/batch/index.txt"); | |||
//cached | |||
assertThat(str).isEqualTo("abcde"); | |||
assertThat(cacheDir.list().length).isEqualTo(1); | |||
httpServer.setMockResponseData("request2"); | |||
//got response in cached | |||
str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("abcde"); | |||
} | |||
@Test | |||
public void test_dont_prefer_cache() throws IOException { | |||
File cacheDir = cache.getDirectory().toFile(); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, false); | |||
//not cached | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
String str = connection.loadString("/batch/index.txt"); | |||
//cached | |||
assertThat(str).isEqualTo("abcde"); | |||
assertThat(cacheDir.list().length).isEqualTo(1); | |||
httpServer.setMockResponseData("request2"); | |||
//got response in cached | |||
str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("request2"); | |||
httpServer.after(); | |||
str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("request2"); | |||
} | |||
@Test | |||
public void should_download_to_file() throws Exception { | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url(), null); | |||
File toFile = temp.newFile(); | |||
connection.download("/batch/index.txt", toFile); | |||
assertThat(FileUtils.readFileToString(toFile)).isEqualTo("abcde"); | |||
} | |||
@Test | |||
public void should_throw_connection_exception_fallback_to_cache() throws IOException { | |||
cache = mock(PersistentCache.class); | |||
ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort(), | |||
true, false); | |||
try { | |||
connection.loadString("/batch/index.txt"); | |||
fail(); | |||
} catch (HttpRequest.HttpRequestException e) { | |||
verify(cache).getString(anyString()); | |||
assertThat(e.getCause()).isInstanceOf(ConnectException.class); | |||
} | |||
} | |||
@Test | |||
public void should_cache_jar_list() throws Exception { | |||
File cacheDir = cache.getDirectory().toFile(); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, false); | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
String str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("abcde"); | |||
assertThat(cacheDir.list().length).isEqualTo(1); | |||
httpServer.after(); | |||
str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("abcde"); | |||
} | |||
@Test | |||
public void should_throw_connection_exception() throws IOException { | |||
File cacheDir = cache.getDirectory().toFile(); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null); | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
String str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("abcde"); | |||
httpServer.after(); | |||
try { | |||
connection.loadString("/batch/index.txt"); | |||
fail("exception expected"); | |||
} catch (HttpRequest.HttpRequestException e) { | |||
// expected | |||
assertTrue((e.getCause() instanceof ConnectException) || (e.getCause() instanceof SocketTimeoutException)); | |||
// cache never used | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
} | |||
} | |||
@Test | |||
public void should_not_cache_not_issues_mode() throws Exception { | |||
File cacheDir = cache.getDirectory().toFile(); | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null); | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
String str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("abcde"); | |||
assertThat(cacheDir.list().length).isEqualTo(0); | |||
httpServer.setMockResponseData("request2"); | |||
str = connection.loadString("/batch/index.txt"); | |||
assertThat(str).isEqualTo("request2"); | |||
} | |||
// SONARPLUGINS-3061 | |||
@Test | |||
public void should_support_trailing_slash() throws Exception { | |||
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null); | |||
File toFile = temp.newFile(); | |||
connection.download("/batch/index.txt", toFile); | |||
assertThat(FileUtils.readFileToString(toFile)).isEqualTo("abcde"); | |||
} | |||
@Test | |||
public void should_not_download_file_when_host_is_down() throws Exception { | |||
ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort()); | |||
File toFile = temp.newFile(); | |||
try { | |||
connection.download("/batch/index.txt", toFile); | |||
fail(); | |||
} catch (Exception e) { | |||
// success | |||
} | |||
} | |||
@Test | |||
public void should_not_download_string_when_host_is_down() throws Exception { | |||
ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort()); | |||
try { | |||
connection.loadString("/batch/index.txt"); | |||
fail(); | |||
} catch (Exception e) { | |||
// success | |||
} | |||
} | |||
private ServerConnection createSimpleServerConnection(String url, Integer port) { | |||
return createSimpleServerConnection(url, port, false, false); | |||
} | |||
private ServerConnection createSimpleServerConnection(String url, Integer port, boolean issuesMode, boolean preferCache) { | |||
httpServer.setMockResponseData("abcde"); | |||
String fullUrl = port == null ? url : url + ":" + port; | |||
Properties props = new Properties(); | |||
props.setProperty("sonar.host.url", fullUrl); | |||
if (issuesMode) { | |||
props.setProperty("sonar.analysis.mode", "issues"); | |||
} | |||
return ServerConnection.create(props, cache, logger, preferCache); | |||
} | |||
} |
@@ -1,142 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.sonar.runner.batch.IssueListener; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.Properties; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class SimulatedLauncherTest { | |||
private static final String VERSION = "5.2"; | |||
private SimulatedLauncher launcher; | |||
private Logger logger; | |||
private String filename; | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Before | |||
public void setUp() { | |||
logger = mock(Logger.class); | |||
launcher = new SimulatedLauncher(VERSION, logger); | |||
filename = new File(temp.getRoot(), "props").getAbsolutePath(); | |||
} | |||
@Test | |||
public void testDump() throws IOException { | |||
Properties global = new Properties(); | |||
global.putAll(createProperties(true)); | |||
Properties analysis = new Properties(); | |||
analysis.putAll(createProperties(false)); | |||
launcher.start(global, null, false); | |||
launcher.execute(analysis); | |||
assertDump(global, analysis); | |||
} | |||
@Test | |||
public void testDump_with_issue_listener() throws IOException { | |||
Properties global = new Properties(); | |||
global.putAll(createProperties(true)); | |||
Properties analysis = new Properties(); | |||
analysis.putAll(createProperties(false)); | |||
launcher.start(global, null, false); | |||
launcher.execute(analysis, mock(IssueListener.class)); | |||
assertDump(global, analysis); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void error_if_no_dump_file() { | |||
launcher.execute(new Properties()); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void error_dump() throws IOException { | |||
Properties p = mock(Properties.class); | |||
doThrow(IOException.class).when(p).store(any(OutputStream.class), anyString()); | |||
launcher.execute(p); | |||
} | |||
@Test | |||
public void no_ops() { | |||
launcher.syncProject(null); | |||
} | |||
@Test | |||
public void testOldExecute() { | |||
Properties global = new Properties(); | |||
global.putAll(createProperties(true)); | |||
Properties analysis = new Properties(); | |||
analysis.putAll(createProperties(false)); | |||
launcher.start(global, null, false); | |||
launcher.executeOldVersion(analysis, null); | |||
} | |||
private Properties createProperties(boolean global) { | |||
Properties prop = new Properties(); | |||
prop.put("key1_" + global, "value1"); | |||
prop.put("key2_" + global, "value2"); | |||
prop.put(InternalProperties.RUNNER_DUMP_TO_FILE, filename); | |||
return prop; | |||
} | |||
@Test | |||
public void version() { | |||
assertThat(launcher.getVersion()).isEqualTo(VERSION); | |||
} | |||
private void assertDump(Properties global, Properties analysis) throws IOException { | |||
if (analysis != null) { | |||
Properties p = new Properties(); | |||
p.load(new FileInputStream(new File(filename))); | |||
assertThat(p).isEqualTo(analysis); | |||
} else { | |||
assertThat(new File(filename)).doesNotExist(); | |||
} | |||
if (global != null) { | |||
Properties p = new Properties(); | |||
p.load(new FileInputStream(new File(filename + ".global"))); | |||
assertThat(p).isEqualTo(global); | |||
} else { | |||
assertThat(new File(filename + ".global")).doesNotExist(); | |||
} | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.runner.cache.Logger; | |||
import java.io.File; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class TempCleaningTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void should_clean_jvm_tmp_dir() { | |||
TempCleaning cleaning = new TempCleaning(mock(Logger.class)); | |||
assertThat(cleaning.tempDir).isDirectory().exists(); | |||
} | |||
@Test | |||
public void should_clean() throws Exception { | |||
File dir = temp.newFolder(); | |||
File oldBatch = new File(dir, "sonar-runner-batch656.jar"); | |||
FileUtils.write(oldBatch, "foo"); | |||
oldBatch.setLastModified(System.currentTimeMillis() - 3 * TempCleaning.ONE_DAY_IN_MILLISECONDS); | |||
File youngBatch = new File(dir, "sonar-runner-batch123.jar"); | |||
FileUtils.write(youngBatch, "foo"); | |||
File doNotDelete = new File(dir, "jacoco.txt"); | |||
FileUtils.write(doNotDelete, "foo"); | |||
assertThat(oldBatch).exists(); | |||
assertThat(youngBatch).exists(); | |||
assertThat(doNotDelete).exists(); | |||
new TempCleaning(dir, mock(Logger.class)).clean(); | |||
assertThat(oldBatch).doesNotExist(); | |||
assertThat(youngBatch).exists(); | |||
assertThat(doNotDelete).exists(); | |||
} | |||
} |
@@ -1,39 +0,0 @@ | |||
/* | |||
* SonarQube Runner - API | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.impl; | |||
import org.junit.Test; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class VersionUtilsTest { | |||
@Test | |||
public void parse_version() { | |||
assertThat(VersionUtils.isAtLeast52("5.2")).isTrue(); | |||
assertThat(VersionUtils.isAtLeast52(null)).isFalse(); | |||
assertThat(VersionUtils.isAtLeast52("52")).isTrue(); | |||
assertThat(VersionUtils.isAtLeast52("5.0")).isFalse(); | |||
assertThat(VersionUtils.isAtLeast52("")).isFalse(); | |||
assertThat(VersionUtils.isAtLeast52("trash")).isFalse(); | |||
assertThat(VersionUtils.isAtLeast52("6.0.0")).isTrue(); | |||
assertThat(VersionUtils.isAtLeast52("5.2-SNAPSHOT")).isTrue(); | |||
} | |||
} |
@@ -1 +0,0 @@ | |||
Fake jar for unit tests |
@@ -1,2 +0,0 @@ | |||
sonar.login=foo | |||
sonar.password=bar |
@@ -1,4 +0,0 @@ | |||
@ECHO OFF | |||
@ECHO %CD% > echo.log | |||
@ECHO Parameter: %1 >> echo.log | |||
@ECHO Environment variable: %ENVVAR% >> echo.log |
@@ -1,6 +0,0 @@ | |||
#!/bin/sh | |||
WORKING_DIR=`pwd` | |||
echo $WORKING_DIR > echo.log | |||
echo "Parameter: $1" >> echo.log | |||
echo "Environment variable: $ENVVAR" >> echo.log |
@@ -1,6 +0,0 @@ | |||
@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 |
@@ -1,6 +0,0 @@ | |||
#!/bin/sh | |||
while test "notempty" | |||
do | |||
sleep 1 | |||
done |
@@ -1,5 +0,0 @@ | |||
@ECHO OFF | |||
@ECHO stdOut: first line | |||
@ECHO stdOut: second line | |||
@ECHO stdErr: first line 1>&2 | |||
@ECHO stdErr: second line 1>&2 |
@@ -1,6 +0,0 @@ | |||
#!/bin/sh | |||
echo stdOut: first line | |||
echo stdOut: second line | |||
echo stdErr: first line 1>&2 | |||
echo stdErr: second line 1>&2 |
@@ -1 +0,0 @@ | |||
/target/ |
@@ -1,12 +0,0 @@ | |||
<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.sonarsource.sonar-runner</groupId> | |||
<artifactId>sonar-runner</artifactId> | |||
<version>2.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>sonar-runner-batch-interface</artifactId> | |||
<name>SonarQube Runner - Batch Interface</name> | |||
</project> |
@@ -1,41 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch Interface | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 java.util.List; | |||
import java.util.Properties; | |||
public interface IsolatedLauncher { | |||
void start(Properties properties, LogOutput logOutput, boolean preferCache); | |||
void stop(); | |||
void execute(Properties properties); | |||
void execute(Properties properties, IssueListener listener); | |||
void syncProject(String projectKey); | |||
void executeOldVersion(Properties properties, List<Object> extensions); | |||
String getVersion(); | |||
} |
@@ -1,163 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch Interface | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
public interface IssueListener { | |||
void handle(Issue issue); | |||
class Issue { | |||
private Integer startLine; | |||
private Integer startLineOffset; | |||
private Integer endLine; | |||
private Integer endLineOffset; | |||
private String key; | |||
private String componentKey; | |||
private String message; | |||
private String ruleKey; | |||
private String ruleName; | |||
private String status; | |||
private String resolution; | |||
private boolean isNew; | |||
private String assigneeLogin; | |||
private String assigneeName; | |||
private String severity; | |||
public String getSeverity() { | |||
return severity; | |||
} | |||
public void setSeverity(String severity) { | |||
this.severity = severity; | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
public void setKey(String key) { | |||
this.key = key; | |||
} | |||
public String getComponentKey() { | |||
return componentKey; | |||
} | |||
public void setComponentKey(String componentKey) { | |||
this.componentKey = componentKey; | |||
} | |||
public Integer getStartLine() { | |||
return startLine; | |||
} | |||
public void setStartLine(Integer startLine) { | |||
this.startLine = startLine; | |||
} | |||
public Integer getStartLineOffset() { | |||
return startLineOffset; | |||
} | |||
public void setStartLineOffset(Integer startLineOffset) { | |||
this.startLineOffset = startLineOffset; | |||
} | |||
public Integer getEndLine() { | |||
return endLine; | |||
} | |||
public void setEndLine(Integer endLine) { | |||
this.endLine = endLine; | |||
} | |||
public Integer getEndLineOffset() { | |||
return endLineOffset; | |||
} | |||
public void setEndLineOffset(Integer endLineOffset) { | |||
this.endLineOffset = endLineOffset; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
public void setMessage(String message) { | |||
this.message = message; | |||
} | |||
public String getRuleKey() { | |||
return ruleKey; | |||
} | |||
public void setRuleKey(String ruleKey) { | |||
this.ruleKey = ruleKey; | |||
} | |||
public String getRuleName() { | |||
return ruleName; | |||
} | |||
public void setRuleName(String ruleName) { | |||
this.ruleName = ruleName; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public void setStatus(String status) { | |||
this.status = status; | |||
} | |||
public String getResolution() { | |||
return resolution; | |||
} | |||
public void setResolution(String resolution) { | |||
this.resolution = resolution; | |||
} | |||
public boolean isNew() { | |||
return isNew; | |||
} | |||
public void setNew(boolean isNew) { | |||
this.isNew = isNew; | |||
} | |||
public String getAssigneeLogin() { | |||
return assigneeLogin; | |||
} | |||
public void setAssigneeLogin(String assigneeLogin) { | |||
this.assigneeLogin = assigneeLogin; | |||
} | |||
public String getAssigneeName() { | |||
return assigneeName; | |||
} | |||
public void setAssigneeName(String assigneeName) { | |||
this.assigneeName = assigneeName; | |||
} | |||
} | |||
} |
@@ -1,33 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch Interface | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; | |||
/** | |||
* Allow to redirect batch logs to a custom output. By defaults logs are written to System.out | |||
* @since 5.2 | |||
*/ | |||
public interface LogOutput { | |||
void log(String formattedMessage, Level level); | |||
enum Level { | |||
ERROR, WARN, INFO, DEBUG, TRACE; | |||
} | |||
} |
@@ -1,20 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch Interface | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; |
@@ -1,5 +0,0 @@ | |||
sonar.projectKey=sample | |||
sonar.projectName=Sample | |||
sonar.projectVersion=1.0-SNAPSHOT | |||
sonar.projectDescription=Sample description | |||
sonar.sources=src |
@@ -1,91 +0,0 @@ | |||
<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.sonarsource.sonar-runner</groupId> | |||
<artifactId>sonar-runner</artifactId> | |||
<version>2.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>sonar-runner-batch</artifactId> | |||
<name>SonarQube Runner - Batch</name> | |||
<properties> | |||
<sonarBatchVersion>4.5.1</sonarBatchVersion> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>sonar-runner-batch-interface</artifactId> | |||
<version>${project.version}</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> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.assertj</groupId> | |||
<artifactId>assertj-core</artifactId> | |||
<version>2.1.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<excludes> | |||
<exclude>**/org/sonar/batch/bootstrapper/*</exclude> | |||
</excludes> | |||
</configuration> | |||
</plugin> | |||
<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> |
@@ -1,143 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.batch.bootstrapper; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.picocontainer.annotations.Nullable; | |||
/** | |||
* Entry point for sonar-runner 2.1. | |||
* | |||
* @since 2.14 | |||
*/ | |||
public class Batch { | |||
private Batch(Builder builder) { | |||
} | |||
public LoggingConfiguration getLoggingConfiguration() { | |||
return null; | |||
} | |||
/** | |||
* @deprecated since 4.4 use {@link #start()}, {@link #executeTask(Map)} and then {@link #stop()} | |||
*/ | |||
@Deprecated | |||
public synchronized Batch execute() { | |||
return this; | |||
} | |||
/** | |||
* @since 4.4 | |||
*/ | |||
public synchronized Batch start() { | |||
return start(false); | |||
} | |||
public synchronized Batch start(boolean preferCache) { | |||
return this; | |||
} | |||
/** | |||
* @since 4.4 | |||
*/ | |||
public Batch executeTask(Map<String, String> analysisProperties, Object... components) { | |||
return this; | |||
} | |||
/** | |||
* @since 5.2 | |||
*/ | |||
public Batch executeTask(Map<String, String> analysisProperties, IssueListener issueListener) { | |||
return this; | |||
} | |||
/** | |||
* @since 5.2 | |||
*/ | |||
public Batch syncProject(String projectKey) { | |||
return this; | |||
} | |||
/** | |||
* @since 4.4 | |||
*/ | |||
public synchronized void stop() { | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
public static final class Builder { | |||
private Builder() { | |||
} | |||
public Builder setEnvironment(EnvironmentInformation env) { | |||
return this; | |||
} | |||
public Builder setComponents(List<Object> l) { | |||
return this; | |||
} | |||
public Builder setLogOutput(@Nullable LogOutput logOutput) { | |||
return this; | |||
} | |||
/** | |||
* @deprecated since 3.7 use {@link #setBootstrapProperties(Map)} | |||
*/ | |||
@Deprecated | |||
public Builder setGlobalProperties(Map<String, String> globalProperties) { | |||
return this; | |||
} | |||
public Builder setBootstrapProperties(Map<String, String> bootstrapProperties) { | |||
return this; | |||
} | |||
public Builder addComponents(Object... components) { | |||
return this; | |||
} | |||
public Builder addComponent(Object component) { | |||
return this; | |||
} | |||
public boolean isEnableLoggingConfiguration() { | |||
return false; | |||
} | |||
/** | |||
* Logback is configured by default. It can be disabled, but n this case the batch bootstrapper must provide its | |||
* own implementation of SLF4J. | |||
*/ | |||
public Builder setEnableLoggingConfiguration(boolean b) { | |||
return this; | |||
} | |||
public Batch build() { | |||
return new Batch(this); | |||
} | |||
} | |||
} |
@@ -1,178 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.batch.bootstrapper; | |||
public interface IssueListener { | |||
void handle(Issue issue); | |||
class Issue { | |||
/** @since 5.3 */ | |||
private Integer startLine; | |||
/** @since 5.3 */ | |||
private Integer startLineOffset; | |||
/** @since 5.3 */ | |||
private Integer endLine; | |||
/** @since 5.3 */ | |||
private Integer endLineOffset; | |||
/** no longer exists since 5.3 */ | |||
private Integer line; | |||
private String key; | |||
private String componentKey; | |||
private String message; | |||
private String ruleKey; | |||
private String ruleName; | |||
private String status; | |||
private String resolution; | |||
private boolean isNew; | |||
private String assigneeLogin; | |||
private String assigneeName; | |||
private String severity; | |||
public String getSeverity() { | |||
return severity; | |||
} | |||
public void setSeverity(String severity) { | |||
this.severity = severity; | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
public void setKey(String key) { | |||
this.key = key; | |||
} | |||
public String getComponentKey() { | |||
return componentKey; | |||
} | |||
public void setComponentKey(String componentKey) { | |||
this.componentKey = componentKey; | |||
} | |||
public Integer getStartLine() { | |||
return startLine; | |||
} | |||
public void setStartLine(Integer startLine) { | |||
this.startLine = startLine; | |||
} | |||
public Integer getStartLineOffset() { | |||
return startLineOffset; | |||
} | |||
public void setStartLineOffset(Integer startLineOffset) { | |||
this.startLineOffset = startLineOffset; | |||
} | |||
public Integer getEndLine() { | |||
return endLine; | |||
} | |||
public void setEndLine(Integer endLine) { | |||
this.endLine = endLine; | |||
} | |||
public Integer getEndLineOffset() { | |||
return endLineOffset; | |||
} | |||
public void setEndLineOffset(Integer endLineOffset) { | |||
this.endLineOffset = endLineOffset; | |||
} | |||
public Integer getLine() { | |||
return line; | |||
} | |||
public void setLine(Integer line) { | |||
this.line = line; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
public void setMessage(String message) { | |||
this.message = message; | |||
} | |||
public String getRuleKey() { | |||
return ruleKey; | |||
} | |||
public void setRuleKey(String ruleKey) { | |||
this.ruleKey = ruleKey; | |||
} | |||
public String getRuleName() { | |||
return ruleName; | |||
} | |||
public void setRuleName(String ruleName) { | |||
this.ruleName = ruleName; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public void setStatus(String status) { | |||
this.status = status; | |||
} | |||
public String getResolution() { | |||
return resolution; | |||
} | |||
public void setResolution(String resolution) { | |||
this.resolution = resolution; | |||
} | |||
public boolean isNew() { | |||
return isNew; | |||
} | |||
public void setNew(boolean isNew) { | |||
this.isNew = isNew; | |||
} | |||
public String getAssigneeLogin() { | |||
return assigneeLogin; | |||
} | |||
public void setAssigneeLogin(String assigneeLogin) { | |||
this.assigneeLogin = assigneeLogin; | |||
} | |||
public String getAssigneeName() { | |||
return assigneeName; | |||
} | |||
public void setAssigneeName(String assigneeName) { | |||
this.assigneeName = assigneeName; | |||
} | |||
} | |||
} |
@@ -1,33 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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.batch.bootstrapper; | |||
/** | |||
* Allow to redirect batch logs to a custom output. By defaults logs are written to System.out | |||
* @since 5.2 | |||
*/ | |||
public interface LogOutput { | |||
void log(String formattedMessage, Level level); | |||
enum Level { | |||
ERROR, WARN, INFO, DEBUG, TRACE; | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 | |||
*/ | |||
/** | |||
* This package contains duplicated classes to avoid a dependency on SQ batch | |||
*/ | |||
package org.sonar.batch.bootstrapper; |
@@ -1,30 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 org.picocontainer.annotations.Nullable; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import java.util.List; | |||
import java.util.Properties; | |||
interface BatchFactory { | |||
Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput, @Nullable List<Object> extensions); | |||
} |
@@ -1,117 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 java.io.BufferedReader; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
/** | |||
* 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 BatchIsolatedLauncher implements IsolatedLauncher { | |||
private static final String VERSION_FORMAT = "^(\\d+)\\.(\\d+)"; | |||
private Batch batch = null; | |||
private final BatchFactory factory; | |||
private final Pattern versionPattern; | |||
public BatchIsolatedLauncher() { | |||
this(new DefaultBatchFactory()); | |||
} | |||
public BatchIsolatedLauncher(BatchFactory factory) { | |||
this.factory = factory; | |||
this.versionPattern = Pattern.compile(VERSION_FORMAT); | |||
} | |||
@Override | |||
public void start(Properties globalProperties, org.sonar.runner.batch.LogOutput logOutput, boolean preferCache) { | |||
batch = factory.createBatch(globalProperties, logOutput, null); | |||
batch.start(preferCache); | |||
} | |||
@Override | |||
public void stop() { | |||
batch.stop(); | |||
} | |||
@Override | |||
public void execute(Properties properties) { | |||
batch.executeTask((Map) properties); | |||
} | |||
@Override | |||
public void execute(Properties properties, IssueListener listener) { | |||
org.sonar.batch.bootstrapper.IssueListener batchIssueListener = Compatibility.getBatchIssueListener(listener, hasPreciseIssueLocation(getVersion())); | |||
batch.executeTask((Map) properties, batchIssueListener); | |||
} | |||
@Override | |||
public void syncProject(String projectKey) { | |||
batch.syncProject(projectKey); | |||
} | |||
/** | |||
* This method exists for backward compatibility with SonarQube < 5.2. | |||
*/ | |||
@Override | |||
public void executeOldVersion(Properties properties, List<Object> extensions) { | |||
factory.createBatch(properties, null, extensions).execute(); | |||
} | |||
boolean hasPreciseIssueLocation(String version) { | |||
if (version == null) { | |||
return false; | |||
} | |||
Matcher matcher = versionPattern.matcher(version); | |||
if (!matcher.find() || matcher.groupCount() < 2) { | |||
return false; | |||
} | |||
Integer major = Integer.parseInt(matcher.group(1)); | |||
Integer minor = Integer.parseInt(matcher.group(2)); | |||
return major > 5 || (major == 5 && minor >= 3); | |||
} | |||
@Override | |||
public String getVersion() { | |||
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); | |||
if (is == null) { | |||
return null; | |||
} | |||
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { | |||
return br.readLine(); | |||
} catch (IOException e) { | |||
return null; | |||
} | |||
} | |||
} |
@@ -1,87 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 org.sonar.batch.bootstrapper.Batch; | |||
import org.sonar.batch.bootstrapper.LogOutput; | |||
public class Compatibility { | |||
private Compatibility() { | |||
// Utility class | |||
} | |||
static void setLogOutputFor5dot2(Batch.Builder builder, final org.sonar.runner.batch.LogOutput logOutput) { | |||
builder.setLogOutput(new LogOutput() { | |||
@Override | |||
public void log(String formattedMessage, Level level) { | |||
logOutput.log(formattedMessage, org.sonar.runner.batch.LogOutput.Level.valueOf(level.name())); | |||
} | |||
}); | |||
} | |||
static org.sonar.batch.bootstrapper.IssueListener getBatchIssueListener(IssueListener listener, boolean hasPreciseLocation) { | |||
return new IssueListenerAdapter(listener, hasPreciseLocation); | |||
} | |||
static class IssueListenerAdapter implements org.sonar.batch.bootstrapper.IssueListener { | |||
private IssueListener listener; | |||
private boolean hasPreciseLocation; | |||
public IssueListenerAdapter(IssueListener listener, boolean hasPreciseLocation) { | |||
this.listener = listener; | |||
this.hasPreciseLocation = hasPreciseLocation; | |||
} | |||
@Override | |||
public void handle(Issue issue) { | |||
listener.handle(transformIssue(issue)); | |||
} | |||
private IssueListener.Issue transformIssue(Issue batchIssue) { | |||
IssueListener.Issue newIssue = new IssueListener.Issue(); | |||
newIssue.setAssigneeLogin(batchIssue.getAssigneeLogin()); | |||
newIssue.setAssigneeName(batchIssue.getAssigneeName()); | |||
newIssue.setComponentKey(batchIssue.getComponentKey()); | |||
newIssue.setKey(batchIssue.getKey()); | |||
newIssue.setResolution(batchIssue.getResolution()); | |||
newIssue.setRuleKey(batchIssue.getRuleKey()); | |||
newIssue.setRuleName(batchIssue.getRuleName()); | |||
newIssue.setMessage(batchIssue.getMessage()); | |||
newIssue.setNew(batchIssue.isNew()); | |||
newIssue.setSeverity(batchIssue.getSeverity()); | |||
newIssue.setStatus(batchIssue.getStatus()); | |||
if (hasPreciseLocation) { | |||
newIssue.setStartLine(batchIssue.getStartLine()); | |||
newIssue.setStartLineOffset(batchIssue.getStartLineOffset()); | |||
newIssue.setEndLine(batchIssue.getEndLine()); | |||
newIssue.setEndLineOffset(batchIssue.getEndLineOffset()); | |||
} else { | |||
newIssue.setStartLine(batchIssue.getLine()); | |||
newIssue.setEndLine(batchIssue.getLine()); | |||
} | |||
return newIssue; | |||
} | |||
} | |||
} |
@@ -1,49 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 org.picocontainer.annotations.Nullable; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
class DefaultBatchFactory implements BatchFactory { | |||
@Override | |||
public Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput, @Nullable List<Object> extensions) { | |||
EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.app"), properties.getProperty("sonarRunner.appVersion")); | |||
Batch.Builder builder = Batch.builder() | |||
.setEnvironment(env) | |||
.setBootstrapProperties((Map) properties); | |||
if (extensions != null) { | |||
builder.addComponents(extensions); | |||
} | |||
if (logOutput != null) { | |||
// Do that is a separate class to avoid NoClassDefFoundError for org/sonar/batch/bootstrapper/LogOutput | |||
Compatibility.setLogOutputFor5dot2(builder, logOutput); | |||
} | |||
return builder.build(); | |||
} | |||
} |
@@ -1,20 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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; |
@@ -1,128 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyListOf; | |||
import static org.mockito.Mockito.mock; | |||
public class BatchIsolatedLauncherTest { | |||
private Batch batch; | |||
private BatchFactory factory; | |||
private BatchIsolatedLauncher launcher; | |||
@Before | |||
public void setUp() { | |||
factory = mock(BatchFactory.class); | |||
batch = mock(Batch.class); | |||
when(factory.createBatch(any(Properties.class), any(LogOutput.class), anyListOf(Object.class))).thenReturn(batch); | |||
launcher = new BatchIsolatedLauncher(factory); | |||
} | |||
@Test | |||
public void supportsPreciseLocation() { | |||
assertThat(launcher.hasPreciseIssueLocation("5.2.9")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.2")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation(null)).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("4.5")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("4.5-SNAPSHOT")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("4-RC1")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("6")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("e6.0")).isFalse(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.3")).isTrue(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.10.1")).isTrue(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.3.1")).isTrue(); | |||
assertThat(launcher.hasPreciseIssueLocation("6.0")).isTrue(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.3-SNAPSHOT")).isTrue(); | |||
assertThat(launcher.hasPreciseIssueLocation("5.3-RC1")).isTrue(); | |||
} | |||
@Test | |||
public void executeOld() { | |||
Properties prop = new Properties(); | |||
List<Object> list = new LinkedList<>(); | |||
launcher.executeOldVersion(prop, list); | |||
verify(factory).createBatch(prop, null, list); | |||
verify(batch).execute(); | |||
verifyNoMoreInteractions(batch); | |||
verifyNoMoreInteractions(factory); | |||
} | |||
@Test(expected = NullPointerException.class) | |||
public void executeWithoutStart() { | |||
IssueListener issueListener = mock(IssueListener.class); | |||
Properties prop = new Properties(); | |||
launcher.execute(prop, issueListener); | |||
} | |||
@Test | |||
public void executeWithListener() { | |||
IssueListener issueListener = mock(IssueListener.class); | |||
Properties prop = new Properties(); | |||
launcher.start(null, null, true); | |||
launcher.execute(prop, issueListener); | |||
verify(batch).start(true); | |||
verify(batch).executeTask(eq((Map) prop), any(org.sonar.batch.bootstrapper.IssueListener.class)); | |||
verifyNoMoreInteractions(batch); | |||
} | |||
@Test | |||
public void proxy() { | |||
Properties prop = new Properties(); | |||
launcher.start(prop, null, true); | |||
launcher.syncProject("proj"); | |||
launcher.execute(prop); | |||
launcher.stop(); | |||
verify(factory).createBatch(any(Properties.class), any(LogOutput.class), anyListOf(Object.class)); | |||
verify(batch).start(true); | |||
verify(batch).syncProject("proj"); | |||
verify(batch).executeTask((Map) prop); | |||
verify(batch).stop(); | |||
verifyNoMoreInteractions(batch); | |||
verifyNoMoreInteractions(factory); | |||
} | |||
} |
@@ -1,120 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 static org.mockito.Mockito.times; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class CompatibilityTest { | |||
private IssueListener issueListener; | |||
@Before | |||
public void setUp() { | |||
issueListener = mock(IssueListener.class); | |||
} | |||
@Test | |||
public void test() { | |||
org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = new org.sonar.batch.bootstrapper.IssueListener.Issue(); | |||
setIssue(batchIssue); | |||
org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, false); | |||
adaptedIssueListener.handle(batchIssue); | |||
ArgumentCaptor<IssueListener.Issue> arg = ArgumentCaptor.forClass(IssueListener.Issue.class); | |||
verify(issueListener).handle(arg.capture()); | |||
assertIssue(arg.getValue(), false); | |||
} | |||
@Test | |||
public void testPrecise() { | |||
org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = new org.sonar.batch.bootstrapper.IssueListener.Issue(); | |||
setIssue(batchIssue); | |||
org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, true); | |||
adaptedIssueListener.handle(batchIssue); | |||
ArgumentCaptor<IssueListener.Issue> arg = ArgumentCaptor.forClass(IssueListener.Issue.class); | |||
verify(issueListener).handle(arg.capture()); | |||
assertIssue(arg.getValue(), true); | |||
} | |||
@Test | |||
public void preciseIssueLocationCompatibility() { | |||
org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = mock(org.sonar.batch.bootstrapper.IssueListener.Issue.class); | |||
org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, false); | |||
adaptedIssueListener.handle(batchIssue); | |||
verify(batchIssue, times(0)).getEndLine(); | |||
verify(batchIssue, times(0)).getStartLine(); | |||
verify(batchIssue, times(0)).getStartLineOffset(); | |||
verify(batchIssue, times(0)).getEndLineOffset(); | |||
} | |||
private static void setIssue(org.sonar.batch.bootstrapper.IssueListener.Issue issue) { | |||
issue.setAssigneeName("name"); | |||
issue.setRuleName("rule"); | |||
issue.setRuleKey("key"); | |||
issue.setMessage("msg"); | |||
issue.setAssigneeLogin("login"); | |||
issue.setLine(10); | |||
issue.setStartLine(5); | |||
issue.setEndLine(6); | |||
issue.setStartLineOffset(1); | |||
issue.setEndLineOffset(2); | |||
issue.setComponentKey("component"); | |||
issue.setSeverity("severity"); | |||
issue.setNew(true); | |||
issue.setStatus("status"); | |||
} | |||
private static void assertIssue(IssueListener.Issue issue, boolean precise) { | |||
assertThat(issue.getAssigneeName()).isEqualTo("name"); | |||
assertThat(issue.getRuleName()).isEqualTo("rule"); | |||
assertThat(issue.getRuleKey()).isEqualTo("key"); | |||
assertThat(issue.getMessage()).isEqualTo("msg"); | |||
assertThat(issue.getAssigneeLogin()).isEqualTo("login"); | |||
assertThat(issue.getComponentKey()).isEqualTo("component"); | |||
assertThat(issue.getSeverity()).isEqualTo("severity"); | |||
assertThat(issue.isNew()).isEqualTo(true); | |||
assertThat(issue.getStatus()).isEqualTo("status"); | |||
if (precise) { | |||
assertThat(issue.getStartLine()).isEqualTo(5); | |||
assertThat(issue.getEndLine()).isEqualTo(6); | |||
assertThat(issue.getStartLineOffset()).isEqualTo(1); | |||
assertThat(issue.getEndLineOffset()).isEqualTo(2); | |||
} else { | |||
assertThat(issue.getStartLine()).isEqualTo(10); | |||
assertThat(issue.getEndLine()).isEqualTo(10); | |||
assertThat(issue.getStartLineOffset()).isEqualTo(null); | |||
assertThat(issue.getEndLineOffset()).isEqualTo(null); | |||
} | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
/* | |||
* SonarQube Runner - Batch | |||
* Copyright (C) 2011 SonarSource | |||
* sonarqube@googlegroups.com | |||
* | |||
* 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 java.util.Properties; | |||
import org.junit.Test; | |||
import org.sonar.batch.bootstrapper.Batch; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class DefaultBatchFactoryTest { | |||
private Properties props = new Properties(); | |||
private BatchFactory factory = new DefaultBatchFactory(); | |||
@Test | |||
public void should_create_batch() { | |||
props.setProperty("sonar.projectBaseDir", "src/test/java_sample"); | |||
props.setProperty("sonar.projectKey", "sample"); | |||
props.setProperty("sonar.projectName", "Sample"); | |||
props.setProperty("sonar.projectVersion", "1.0"); | |||
props.setProperty("sonar.sources", "src"); | |||
Batch batch = factory.createBatch(props, null, null); | |||
assertThat(batch).isNotNull(); | |||
} | |||
} |