aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pom.xml6
-rwxr-xr-xsrc/main/assembly/bin/sonar-runner2
-rw-r--r--src/main/assembly/bin/sonar-runner.bat2
-rw-r--r--src/main/java/org/sonar/runner/Launcher.java28
-rw-r--r--src/main/java/org/sonar/runner/Main.java4
-rw-r--r--src/main/java/org/sonar/runner/Runner.java28
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/BootstrapClassLoader.java121
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/BootstrapException.java36
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/Bootstrapper.java167
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/BootstrapperIOUtils.java98
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/BootstrapperVersion.java52
-rw-r--r--src/main/java/org/sonar/runner/bootstrapper/package-info.java23
-rw-r--r--src/test/java/org/sonar/runner/LauncherTest.java19
-rw-r--r--src/test/java/org/sonar/runner/RunnerTest.java21
-rw-r--r--src/test/java/org/sonar/runner/bootstrapper/BootstrapClassLoaderTest.java61
-rw-r--r--src/test/java/org/sonar/runner/bootstrapper/BootstrapperTest.java65
-rw-r--r--src/test/java/org/sonar/runner/bootstrapper/BootstrapperVersionTest.java36
17 files changed, 715 insertions, 54 deletions
diff --git a/pom.xml b/pom.xml
index e6a9adb..f274e25 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,12 +60,6 @@
</properties>
<dependencies>
- <!-- Would be embedded in final JAR -->
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-batch-bootstrapper</artifactId>
- <version>${sonar.buildVersion}</version>
- </dependency>
<!-- Would be available after bootstrapping -->
<dependency>
<groupId>ch.qos.logback</groupId>
diff --git a/src/main/assembly/bin/sonar-runner b/src/main/assembly/bin/sonar-runner
index a9c4a75..0aaf46b 100755
--- a/src/main/assembly/bin/sonar-runner
+++ b/src/main/assembly/bin/sonar-runner
@@ -22,7 +22,7 @@ else
fi
JAVA_CMD="`which java`"
-JAVA_CLASSPATH="${SONAR_RUNNER_HOME}"/lib/sonar-runner.jar:"${SONAR_RUNNER_HOME}"/lib/sonar-batch-bootstrapper.jar
+JAVA_CLASSPATH="${SONAR_RUNNER_HOME}"/lib/sonar-runner.jar
PROJECT_HOME=`pwd`
#echo "Info: Using sonar-runner at $SONAR_RUNNER_HOME"
diff --git a/src/main/assembly/bin/sonar-runner.bat b/src/main/assembly/bin/sonar-runner.bat
index 4896fdd..fd97e3e 100644
--- a/src/main/assembly/bin/sonar-runner.bat
+++ b/src/main/assembly/bin/sonar-runner.bat
@@ -70,7 +70,7 @@ echo %SONAR_RUNNER_HOME%
set PROJECT_HOME=%CD%
-%JAVA_EXEC% %SONAR_RUNNER_OPTS% -classpath "%SONAR_RUNNER_HOME%\lib\sonar-runner.jar";"%SONAR_RUNNER_HOME%\lib\sonar-batch-bootstrapper.jar" "-Drunner.home=%SONAR_RUNNER_HOME%" "-Dproject.home=%PROJECT_HOME%" org.sonar.runner.Main %*
+%JAVA_EXEC% %SONAR_RUNNER_OPTS% -classpath "%SONAR_RUNNER_HOME%\lib\sonar-runner.jar" "-Drunner.home=%SONAR_RUNNER_HOME%" "-Dproject.home=%PROJECT_HOME%" org.sonar.runner.Main %*
diff --git a/src/main/java/org/sonar/runner/Launcher.java b/src/main/java/org/sonar/runner/Launcher.java
index 614662d..2549300 100644
--- a/src/main/java/org/sonar/runner/Launcher.java
+++ b/src/main/java/org/sonar/runner/Launcher.java
@@ -23,6 +23,7 @@ package org.sonar.runner;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.EnvironmentConfiguration;
@@ -37,29 +38,28 @@ import org.sonar.batch.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.runner.model.SonarProjectBuilder;
+import java.io.File;
import java.io.InputStream;
+import java.util.Properties;
/**
* Contrary to {@link org.sonar.runner.Runner}, 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.
- *
- * TODO : instead of depending on Runner, that is not in the same classloader, we could simply use Properties :
- * sonar.runner.version, sonar.runner.debug, sonar.runner.projectDir, sonar.runner.workDir and all the
- * other properties.
*/
public class Launcher {
- private Runner runner;
+ private Properties propertiesFromRunner;
- public Launcher(Runner runner) {
- this.runner = runner;
+ public Launcher(Properties properties) {
+ this.propertiesFromRunner = properties;
}
/**
* This method invoked from {@link Main}. Do not rename it.
*/
public void execute() {
- ProjectDefinition project = SonarProjectBuilder.create(runner.getProjectDir(), runner.getProperties()).generateProjectDefinition();
+ File baseDir = new File(propertiesFromRunner.getProperty(Runner.PROPERTY_PROJECT_DIR));
+ ProjectDefinition project = SonarProjectBuilder.create(baseDir, propertiesFromRunner).generateProjectDefinition();
Configuration initialConfiguration = getInitialConfiguration(project);
initLogging(initialConfiguration);
executeBatch(project, initialConfiguration);
@@ -67,7 +67,8 @@ public class Launcher {
private void executeBatch(ProjectDefinition project, Configuration initialConfiguration) {
ProjectReactor reactor = new ProjectReactor(project);
- Batch batch = Batch.create(reactor, initialConfiguration, new EnvironmentInformation("Runner", runner.getRunnerVersion()));
+ String runnerVersion = propertiesFromRunner.getProperty(Runner.PROPERTY_RUNNER_VERSION);
+ Batch batch = Batch.create(reactor, initialConfiguration, new EnvironmentInformation("Runner", runnerVersion));
batch.execute();
}
@@ -77,7 +78,7 @@ public class Launcher {
jc.setContext(context);
context.reset();
InputStream input = Batch.class.getResourceAsStream("/org/sonar/batch/logback.xml");
- System.setProperty("ROOT_LOGGER_LEVEL", runner.isDebug() ? "DEBUG" : "INFO");
+ System.setProperty("ROOT_LOGGER_LEVEL", isDebug() ? "DEBUG" : "INFO");
context.putProperty("SQL_LOGGER_LEVEL", getSqlLevel(initialConfiguration));// since 2.14. Ignored on previous versions.
context.putProperty("SQL_RESULTS_LOGGER_LEVEL", getSqlResultsLevel(initialConfiguration));// since 2.14. Ignored on previous versions.
try {
@@ -91,11 +92,18 @@ public class Launcher {
}
}
+ @VisibleForTesting
+ protected boolean isDebug() {
+ return Boolean.parseBoolean(propertiesFromRunner.getProperty(Runner.PROPERTY_VERBOSE, propertiesFromRunner.getProperty(Runner.PROPERTY_OLD_DEBUG_MODE, "false")));
+ }
+
+ @VisibleForTesting
protected static String getSqlLevel(Configuration config) {
boolean showSql = config.getBoolean("sonar.showSql", false);
return showSql ? "DEBUG" : "WARN";
}
+ @VisibleForTesting
protected static String getSqlResultsLevel(Configuration config) {
boolean showSql = config.getBoolean("sonar.showSqlResults", false);
return showSql ? "DEBUG" : "WARN";
diff --git a/src/main/java/org/sonar/runner/Main.java b/src/main/java/org/sonar/runner/Main.java
index aedfc8a..d133b5f 100644
--- a/src/main/java/org/sonar/runner/Main.java
+++ b/src/main/java/org/sonar/runner/Main.java
@@ -20,8 +20,8 @@
package org.sonar.runner;
-import org.sonar.batch.bootstrapper.BootstrapException;
-import org.sonar.batch.bootstrapper.BootstrapperIOUtils;
+import org.sonar.runner.bootstrapper.BootstrapException;
+import org.sonar.runner.bootstrapper.BootstrapperIOUtils;
import java.io.File;
import java.io.FileInputStream;
diff --git a/src/main/java/org/sonar/runner/Runner.java b/src/main/java/org/sonar/runner/Runner.java
index 4224689..130bc8c 100644
--- a/src/main/java/org/sonar/runner/Runner.java
+++ b/src/main/java/org/sonar/runner/Runner.java
@@ -19,10 +19,10 @@
*/
package org.sonar.runner;
-import org.sonar.batch.bootstrapper.BootstrapClassLoader;
-import org.sonar.batch.bootstrapper.BootstrapException;
-import org.sonar.batch.bootstrapper.Bootstrapper;
-import org.sonar.batch.bootstrapper.BootstrapperIOUtils;
+import org.sonar.runner.bootstrapper.BootstrapClassLoader;
+import org.sonar.runner.bootstrapper.BootstrapException;
+import org.sonar.runner.bootstrapper.Bootstrapper;
+import org.sonar.runner.bootstrapper.BootstrapperIOUtils;
import java.io.File;
import java.io.IOException;
@@ -37,11 +37,15 @@ import java.util.Properties;
* @since 1.1
*/
public final class Runner {
+
+ public static final String PROPERTY_PROJECT_DIR = "sonar.runner.projectDir";
+ public static final String PROPERTY_RUNNER_VERSION = "sonar.runner.version";
+
/**
* @deprecated Replaced by sonar.verbose since 1.2
*/
@Deprecated
- public static final String DEBUG_MODE = "runner.debug";
+ public static final String PROPERTY_OLD_DEBUG_MODE = "runner.debug";
/**
* @since 1.2
@@ -109,6 +113,8 @@ public final class Runner {
if (!projectDir.isDirectory() || !projectDir.exists()) {
throw new IllegalArgumentException("Project home must be an existing directory: " + path);
}
+ // project home exist, add its absolute path as "sonar.runner.projectDir" property
+ properties.put(PROPERTY_PROJECT_DIR, projectDir.getAbsolutePath());
workDir = initWorkDir();
}
@@ -145,10 +151,6 @@ public final class Runner {
return properties;
}
- public boolean isDebug() {
- return Boolean.parseBoolean(properties.getProperty(PROPERTY_VERBOSE, properties.getProperty(DEBUG_MODE, "false")));
- }
-
public String getRunnerVersion() {
InputStream in = null;
try {
@@ -201,12 +203,8 @@ public final class Runner {
try {
Thread.currentThread().setContextClassLoader(sonarClassLoader);
Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.runner.Launcher");
- // TODO: hack to instantiate SonarProjectBuilder in this classloader otherwise it will be found in the parent one, where no deps are
- // available
- sonarClassLoader.findClass("org.sonar.runner.model.SonarProjectBuilder");
- // END of hack
- Constructor<?> constructor = launcherClass.getConstructor(Runner.class);
- Object launcher = constructor.newInstance(this);
+ Constructor<?> constructor = launcherClass.getConstructor(Properties.class);
+ Object launcher = constructor.newInstance(getProperties());
Method method = launcherClass.getMethod("execute");
method.invoke(launcher);
} catch (InvocationTargetException e) {
diff --git a/src/main/java/org/sonar/runner/bootstrapper/BootstrapClassLoader.java b/src/main/java/org/sonar/runner/bootstrapper/BootstrapClassLoader.java
new file mode 100644
index 0000000..694a8b9
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/BootstrapClassLoader.java
@@ -0,0 +1,121 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+
+/**
+ * Special {@link URLClassLoader} to execute Sonar, which restricts loading from parent.
+ */
+public class BootstrapClassLoader extends URLClassLoader {
+
+ private final String[] unmaskedPackages;
+
+ public BootstrapClassLoader(ClassLoader parent, String... unmaskedPackages) {
+ super(new URL[0], parent);
+ this.unmaskedPackages = unmaskedPackages;
+ }
+
+ /**
+ * {@inheritDoc} Visibility of a method has been relaxed to public.
+ */
+ @Override
+ public void addURL(URL url) {
+ super.addURL(url);
+ }
+
+ /**
+ * {@inheritDoc} Visibility of a method has been relaxed to public.
+ */
+ @Override
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ return super.findClass(name);
+ }
+
+ /**
+ * @return true, if class can be loaded from parent ClassLoader
+ */
+ boolean canLoadFromParent(String name) {
+ for (String pkg : unmaskedPackages) {
+ if (name.startsWith(pkg + ".")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Same behavior as in {@link URLClassLoader#loadClass(String, boolean)}, except loading from parent.
+ */
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ // First, check if the class has already been loaded
+ Class<?> c = findLoadedClass(name);
+ if (c == null) {
+ try {
+ // Load from parent
+ if (getParent() != null && canLoadFromParent(name)) {
+ c = getParent().loadClass(name);
+ } else {
+ // Load from system
+
+ // I don't know for other vendors, but for Oracle JVM :
+ // - ClassLoader.getSystemClassLoader() is sun.misc.Launcher$AppClassLoader. It contains app classpath.
+ // - ClassLoader.getSystemClassLoader().getParent() is sun.misc.Launcher$ExtClassLoader. It contains core JVM
+ ClassLoader systemClassLoader = getSystemClassLoader();
+ if (systemClassLoader.getParent() != null) {
+ systemClassLoader = systemClassLoader.getParent();
+ }
+ c = systemClassLoader.loadClass(name);
+ }
+ } catch (ClassNotFoundException e) {
+ // If still not found, then invoke findClass in order
+ // to find the class.
+ c = findClass(name);
+ }
+ }
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+
+ /**
+ * Unlike {@link URLClassLoader#getResource(String)} don't return resource from parent.
+ * See http://jira.codehaus.org/browse/SONAR-2276
+ */
+ @Override
+ public URL getResource(String name) {
+ return findResource(name);
+ }
+
+ /**
+ * Unlike {@link URLClassLoader#getResources(String)} don't return resources from parent.
+ * See http://jira.codehaus.org/browse/SONAR-2276
+ */
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return findResources(name);
+ }
+
+}
diff --git a/src/main/java/org/sonar/runner/bootstrapper/BootstrapException.java b/src/main/java/org/sonar/runner/bootstrapper/BootstrapException.java
new file mode 100644
index 0000000..7bad396
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/BootstrapException.java
@@ -0,0 +1,36 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+public class BootstrapException extends RuntimeException {
+
+ public BootstrapException(String message) {
+ super(message);
+ }
+
+ public BootstrapException(Throwable cause) {
+ super(cause);
+ }
+
+ public BootstrapException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/org/sonar/runner/bootstrapper/Bootstrapper.java b/src/main/java/org/sonar/runner/bootstrapper/Bootstrapper.java
new file mode 100644
index 0000000..d8e2187
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/Bootstrapper.java
@@ -0,0 +1,167 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Bootstrapper {
+
+ private static final String VERSION_PATH = "/api/server/version";
+ private static final String BATCH_PATH = "/batch/";
+
+ public static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
+ public static final int READ_TIMEOUT_MILLISECONDS = 60000;
+
+ private File bootDir;
+ private String serverUrl;
+ private String productToken;
+ private String serverVersion;
+
+ /**
+ * @param productToken part of User-Agent request-header field - see http://tools.ietf.org/html/rfc1945#section-10.15
+ */
+ public Bootstrapper(String productToken, String serverUrl, File workDir) {
+ this.productToken = productToken;
+ bootDir = new File(workDir, "batch");
+ bootDir.mkdirs();
+ if (serverUrl.endsWith("/")) {
+ this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1);
+ } else {
+ this.serverUrl = serverUrl;
+ }
+ }
+
+ /**
+ * @return server url
+ */
+ public String getServerUrl() {
+ return serverUrl;
+ }
+
+ /**
+ * @return server version
+ */
+ public String getServerVersion() {
+ if (serverVersion == null) {
+ try {
+ serverVersion = remoteContent(VERSION_PATH);
+ } catch (IOException e) {
+ throw new BootstrapException(e.getMessage(), e);
+ }
+ }
+ return serverVersion;
+ }
+
+ /**
+ * Download batch files from server and creates {@link BootstrapClassLoader}.
+ * To use this method version of Sonar should be at least 2.6.
+ *
+ * @param urls additional URLs for loading classes and resources
+ * @param parent parent ClassLoader
+ * @param unmaskedPackages only classes and resources from those packages would be available for loading from parent
+ */
+ public BootstrapClassLoader createClassLoader(URL[] urls, ClassLoader parent, String... unmaskedPackages) {
+ BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, unmaskedPackages);
+ List<File> files = downloadBatchFiles();
+ for (URL url : urls) {
+ classLoader.addURL(url);
+ }
+ for (File file : files) {
+ try {
+ classLoader.addURL(file.toURI().toURL());
+ } catch (MalformedURLException e) {
+ throw new BootstrapException(e);
+ }
+ }
+ return classLoader;
+ }
+
+ private void remoteContentToFile(String path, File toFile) {
+ InputStream input = null;
+ FileOutputStream output = null;
+ String fullUrl = serverUrl + path;
+ try {
+ HttpURLConnection connection = newHttpConnection(new URL(fullUrl));
+ output = new FileOutputStream(toFile, false);
+ input = connection.getInputStream();
+ BootstrapperIOUtils.copyLarge(input, output);
+ } catch (IOException e) {
+ BootstrapperIOUtils.closeQuietly(output);
+ BootstrapperIOUtils.deleteFileQuietly(toFile);
+ throw new BootstrapException("Fail to download the file: " + fullUrl, e);
+ } finally {
+ BootstrapperIOUtils.closeQuietly(input);
+ BootstrapperIOUtils.closeQuietly(output);
+ }
+ }
+
+ String remoteContent(String path) throws IOException {
+ String fullUrl = serverUrl + path;
+ HttpURLConnection conn = newHttpConnection(new URL(fullUrl));
+ Reader reader = new InputStreamReader((InputStream) conn.getContent());
+ try {
+ int statusCode = conn.getResponseCode();
+ if (statusCode != HttpURLConnection.HTTP_OK) {
+ throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode);
+ }
+ return BootstrapperIOUtils.toString(reader);
+ } finally {
+ BootstrapperIOUtils.closeQuietly(reader);
+ conn.disconnect();
+ }
+ }
+
+ /**
+ * By convention, the product tokens are listed in order of their significance for identifying the application.
+ */
+ String getUserAgent() {
+ return "sonar-bootstrapper/" + BootstrapperVersion.getVersion() + " " + productToken;
+ }
+
+ HttpURLConnection newHttpConnection(URL url) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS);
+ connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS);
+ connection.setInstanceFollowRedirects(true);
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("User-Agent", getUserAgent());
+ return connection;
+ }
+
+ private List<File> downloadBatchFiles() {
+ try {
+ List<File> files = new ArrayList<File>();
+ String libs = remoteContent(BATCH_PATH);
+ for (String lib : libs.split(",")) {
+ File file = new File(bootDir, lib);
+ remoteContentToFile(BATCH_PATH + lib, file);
+ files.add(file);
+ }
+ return files;
+ } catch (Exception e) {
+ throw new BootstrapException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/sonar/runner/bootstrapper/BootstrapperIOUtils.java b/src/main/java/org/sonar/runner/bootstrapper/BootstrapperIOUtils.java
new file mode 100644
index 0000000..c47186a
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/BootstrapperIOUtils.java
@@ -0,0 +1,98 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import java.io.*;
+
+public final class BootstrapperIOUtils {
+
+ private BootstrapperIOUtils() {
+ // only static methods
+ }
+
+ /**
+ * The default buffer size to use.
+ */
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+ /**
+ * Unconditionally close a <code>Closeable</code>.
+ */
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ if (closeable != null) {
+ closeable.close();
+ }
+ } catch (IOException ioe) { // NOSONAR
+ }
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a String.
+ */
+ public static String toString(Reader input) throws IOException {
+ StringWriter sw = new StringWriter();
+ copyLarge(input, sw);
+ return sw.toString();
+ }
+
+ /**
+ * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
+ */
+ public static long copyLarge(InputStream input, OutputStream output) throws IOException {
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ /**
+ * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+ */
+ public static long copyLarge(Reader input, Writer output) throws IOException {
+ char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ /**
+ * Deletes a file (not a directory).
+ */
+ public static boolean deleteFileQuietly(File file) {
+ if (file == null) {
+ return false;
+ }
+ try {
+ return file.delete();
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/org/sonar/runner/bootstrapper/BootstrapperVersion.java b/src/main/java/org/sonar/runner/bootstrapper/BootstrapperVersion.java
new file mode 100644
index 0000000..b50c03b
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/BootstrapperVersion.java
@@ -0,0 +1,52 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public enum BootstrapperVersion {
+
+ INSTANCE;
+
+ private static final String PROPERTIES_PATH = "/org/sonar/runner/version.txt";
+ private String version;
+
+ public static String getVersion() {
+ return INSTANCE.version;
+ }
+
+ private BootstrapperVersion() {
+ InputStream input = getClass().getResourceAsStream(PROPERTIES_PATH);
+ try {
+ Properties properties = new Properties();
+ properties.load(input);
+ this.version = properties.getProperty("version");
+
+ } catch (IOException e) {
+ // Can not load the version
+ this.version = "";
+
+ } finally {
+ BootstrapperIOUtils.closeQuietly(input);
+ }
+ }
+}
diff --git a/src/main/java/org/sonar/runner/bootstrapper/package-info.java b/src/main/java/org/sonar/runner/bootstrapper/package-info.java
new file mode 100644
index 0000000..b685e7d
--- /dev/null
+++ b/src/main/java/org/sonar/runner/bootstrapper/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+/**
+ * Provides API to bootstrap Sonar Batch.
+ */
+package org.sonar.runner.bootstrapper; \ No newline at end of file
diff --git a/src/test/java/org/sonar/runner/LauncherTest.java b/src/test/java/org/sonar/runner/LauncherTest.java
index 0cec5f5..ea78d82 100644
--- a/src/test/java/org/sonar/runner/LauncherTest.java
+++ b/src/test/java/org/sonar/runner/LauncherTest.java
@@ -23,6 +23,8 @@ import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.junit.Test;
+import java.util.Properties;
+
import static org.fest.assertions.Assertions.assertThat;
public class LauncherTest {
@@ -53,4 +55,21 @@ public class LauncherTest {
assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("WARN");
}
+ @Test
+ public void shouldDetermineVerboseMode() {
+ Properties properties = new Properties();
+ Launcher launcher = new Launcher(properties);
+ assertThat(launcher.isDebug()).isFalse();
+ properties.setProperty(Runner.PROPERTY_VERBOSE, "true");
+ assertThat(launcher.isDebug()).isTrue();
+ }
+
+ @Test
+ public void shouldSupportDeprecatedDebugProperty() {
+ Properties properties = new Properties();
+ Launcher launcher = new Launcher(properties);
+ properties.setProperty(Runner.PROPERTY_OLD_DEBUG_MODE, "true");
+ assertThat(launcher.isDebug()).isTrue();
+ }
+
}
diff --git a/src/test/java/org/sonar/runner/RunnerTest.java b/src/test/java/org/sonar/runner/RunnerTest.java
index 1099d1d..f1681ee 100644
--- a/src/test/java/org/sonar/runner/RunnerTest.java
+++ b/src/test/java/org/sonar/runner/RunnerTest.java
@@ -22,8 +22,8 @@ package org.sonar.runner;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.batch.bootstrapper.BootstrapException;
-import org.sonar.batch.bootstrapper.Bootstrapper;
+import org.sonar.runner.bootstrapper.BootstrapException;
+import org.sonar.runner.bootstrapper.Bootstrapper;
import java.io.File;
import java.util.Properties;
@@ -96,23 +96,6 @@ public class RunnerTest {
}
@Test
- public void shouldDetermineVerboseMode() {
- Properties properties = new Properties();
- Runner runner = Runner.create(properties);
- assertThat(runner.isDebug()).isFalse();
- properties.setProperty(Runner.PROPERTY_VERBOSE, "true");
- assertThat(runner.isDebug()).isTrue();
- }
-
- @Test
- public void shouldSupportDeprecatedDebugProperty() {
- Properties properties = new Properties();
- Runner runner = Runner.create(properties);
- properties.setProperty(Runner.DEBUG_MODE, "true");
- assertThat(runner.isDebug()).isTrue();
- }
-
- @Test
public void shouldInitDirs() throws Exception {
Properties props = new Properties();
File home = new File(getClass().getResource("/org/sonar/runner/RunnerTest/shouldInitDirs/").toURI());
diff --git a/src/test/java/org/sonar/runner/bootstrapper/BootstrapClassLoaderTest.java b/src/test/java/org/sonar/runner/bootstrapper/BootstrapClassLoaderTest.java
new file mode 100644
index 0000000..87496ba
--- /dev/null
+++ b/src/test/java/org/sonar/runner/bootstrapper/BootstrapClassLoaderTest.java
@@ -0,0 +1,61 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.runner.bootstrapper.BootstrapClassLoader;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class BootstrapClassLoaderTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void shouldRestrictLoadingFromParent() throws Exception {
+ BootstrapClassLoader classLoader = new BootstrapClassLoader(getClass().getClassLoader(), "org.sonar.ant");
+ assertThat(classLoader.canLoadFromParent("org.sonar.ant.Launcher")).isTrue();
+ assertThat(classLoader.canLoadFromParent("org.objectweb.asm.ClassVisitor")).isFalse();
+ }
+
+ @Test
+ public void use_isolated_system_classloader_when_parent_is_excluded() throws ClassNotFoundException {
+ thrown.expect(ClassNotFoundException.class);
+ thrown.expectMessage("org.junit.Test");
+ ClassLoader parent = getClass().getClassLoader();
+ BootstrapClassLoader classLoader = new BootstrapClassLoader(parent);
+
+ // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM
+ assertThat(classLoader.loadClass("java.lang.String", false)).isNotNull();
+ classLoader.loadClass("org.junit.Test", false);
+ }
+
+ @Test
+ public void find_in_parent_when_matches_unmasked_packages() throws ClassNotFoundException {
+ ClassLoader parent = getClass().getClassLoader();
+ BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, "org.junit");
+
+ // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM
+ assertThat(classLoader.loadClass("org.junit.Test", false)).isNotNull();
+ }
+}
diff --git a/src/test/java/org/sonar/runner/bootstrapper/BootstrapperTest.java b/src/test/java/org/sonar/runner/bootstrapper/BootstrapperTest.java
new file mode 100644
index 0000000..0741701
--- /dev/null
+++ b/src/test/java/org/sonar/runner/bootstrapper/BootstrapperTest.java
@@ -0,0 +1,65 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import org.junit.Test;
+import org.sonar.runner.bootstrapper.Bootstrapper;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class BootstrapperTest {
+
+ @Test
+ public void shouldRemoveLastUrlSlash() {
+ Bootstrapper bootstrapper = new Bootstrapper("", "http://test/", new File("target"));
+ assertThat(bootstrapper.getServerUrl()).isEqualTo("http://test");
+ }
+
+ @Test(expected = Exception.class)
+ public void shouldFailIfCanNotConnectServer() {
+ Bootstrapper bootstrapper = new Bootstrapper("", "http://unknown.foo", new File("target"));
+ bootstrapper.getServerVersion();
+ }
+
+ @Test
+ public void shouldReturnUserAgent() {
+ Bootstrapper bootstrapper = new Bootstrapper("test/0.1", "http://unknown.foo", new File("target"));
+ String userAgent = bootstrapper.getUserAgent();
+
+ assertThat(userAgent.length()).isGreaterThan(0);
+ assertThat(userAgent).startsWith("sonar-bootstrapper/");
+ assertThat(userAgent).endsWith(" test/0.1");
+ }
+
+ @Test
+ public void shouldReturnValidVersion() {
+ Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target")) {
+ @Override
+ String remoteContent(String path) throws IOException {
+ return "2.6";
+ }
+ };
+ assertThat(bootstrapper.getServerVersion()).isEqualTo("2.6");
+ }
+
+}
diff --git a/src/test/java/org/sonar/runner/bootstrapper/BootstrapperVersionTest.java b/src/test/java/org/sonar/runner/bootstrapper/BootstrapperVersionTest.java
new file mode 100644
index 0000000..c42fd18
--- /dev/null
+++ b/src/test/java/org/sonar/runner/bootstrapper/BootstrapperVersionTest.java
@@ -0,0 +1,36 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.bootstrapper;
+
+import org.junit.Test;
+import org.sonar.runner.bootstrapper.BootstrapperVersion;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class BootstrapperVersionTest {
+
+ @Test
+ public void shouldLoadVersion() {
+ String version = BootstrapperVersion.getVersion();
+ assertThat(version).contains(".");
+ assertThat(version).doesNotContain("$");
+ }
+
+}