aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-runner-api/src/main/java/org/sonar/runner/api
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-runner-api/src/main/java/org/sonar/runner/api')
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java123
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java30
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java195
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java56
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java118
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java40
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java102
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java42
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java41
-rw-r--r--sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java23
10 files changed, 770 insertions, 0 deletions
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java
new file mode 100644
index 0000000..89f9fbe
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java
@@ -0,0 +1,123 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import java.io.File;
+import java.util.*;
+
+class Command {
+ private final String executable;
+ private final List<String> arguments;
+ private final Map<String, String> env;
+ private final File directory;
+
+ private Command(Builder builder) {
+ this.executable = builder.executable;
+ this.arguments = Collections.unmodifiableList(builder.arguments);
+ this.env = Collections.unmodifiableMap(builder.env);
+ this.directory = builder.directory;
+ }
+
+ File directory() {
+ return directory;
+ }
+
+ String executable() {
+ return executable;
+ }
+
+ List<String> arguments() {
+ return arguments;
+ }
+
+ /**
+ * Environment variables that are propagated during command execution.
+ *
+ * @return a non-null and immutable map of variables
+ */
+ Map<String, String> envVariables() {
+ return env;
+ }
+
+ String[] toStrings() {
+ String[] strings = new String[1 + arguments.size()];
+ strings[0] = executable;
+ for (int index = 0; index < arguments.size(); index++) {
+ strings[index + 1] = arguments.get(index);
+ }
+ return strings;
+ }
+
+ @Override
+ public String toString() {
+ return Utils.join(toStrings(), " ");
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+ private String executable;
+ private final List<String> arguments = new ArrayList<String>();
+ private final Map<String, String> env = new HashMap<String, String>();
+ private File directory;
+
+ private Builder() {
+ }
+
+ Builder setExecutable(String s) {
+ this.executable = s;
+ return this;
+ }
+
+ Builder addArguments(String... args) {
+ arguments.addAll(Arrays.asList(args));
+ return this;
+ }
+
+ Builder addArguments(List<String> args) {
+ arguments.addAll(args);
+ return this;
+ }
+
+ Builder setEnvVariable(String key, String value) {
+ env.put(key, value);
+ return this;
+ }
+
+ Builder addEnvVariables(Map<String, String> map) {
+ env.putAll(map);
+ return this;
+ }
+
+ Builder setDirectory(File d) {
+ this.directory = d;
+ return this;
+ }
+
+ Command build() {
+ if (executable == null) {
+ throw new IllegalArgumentException("Command executable is not defined");
+ }
+ return new Command(this);
+ }
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java
new file mode 100644
index 0000000..ba068e3
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java
@@ -0,0 +1,30 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import javax.annotation.Nullable;
+
+class CommandException extends RuntimeException {
+
+ CommandException(String message, Command command, @Nullable Throwable throwable) {
+ super(message + " [command: " + command + "]", throwable);
+ }
+
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java
new file mode 100644
index 0000000..e3ac470
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java
@@ -0,0 +1,195 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Synchronously execute a native command line. It's much more limited than the Apache Commons Exec library.
+ * For example it does not allow to run asynchronously or to automatically quote command-line arguments.
+ *
+ * @since 2.7
+ */
+class CommandExecutor {
+
+ private static final CommandExecutor INSTANCE = new CommandExecutor();
+
+ private CommandExecutor() {
+ }
+
+ static CommandExecutor create() {
+ // stateless object, so a single singleton can be shared
+ return INSTANCE;
+ }
+
+ int execute(Command command, StreamConsumer stdOut, StreamConsumer stdErr, long timeoutMilliseconds) {
+ ExecutorService executorService = null;
+ Process process = null;
+ StreamGobbler outputGobbler = null;
+ StreamGobbler errorGobbler = null;
+ try {
+ ProcessBuilder builder = new ProcessBuilder(command.toStrings());
+ if (command.directory() != null) {
+ builder.directory(command.directory());
+ }
+ builder.environment().putAll(command.envVariables());
+ process = builder.start();
+
+ outputGobbler = new StreamGobbler(process.getInputStream(), stdOut);
+ errorGobbler = new StreamGobbler(process.getErrorStream(), stdErr);
+ outputGobbler.start();
+ errorGobbler.start();
+
+ final Process finalProcess = process;
+ executorService = Executors.newSingleThreadExecutor();
+ Future<Integer> ft = executorService.submit(new Callable<Integer>() {
+ public Integer call() throws Exception {
+ return finalProcess.waitFor();
+ }
+ });
+ int exitCode = ft.get(timeoutMilliseconds, TimeUnit.MILLISECONDS);
+ waitUntilFinish(outputGobbler);
+ waitUntilFinish(errorGobbler);
+ verifyGobbler(command, outputGobbler, "stdOut");
+ verifyGobbler(command, errorGobbler, "stdErr");
+ return exitCode;
+
+ } catch (TimeoutException te) {
+ process.destroy();
+ throw new CommandException("Timeout exceeded: " + timeoutMilliseconds + " ms", command, te);
+
+ } catch (CommandException e) {
+ throw e;
+
+ } catch (Exception e) {
+ throw new CommandException("Fail to execute command", command, e);
+
+ } finally {
+ waitUntilFinish(outputGobbler);
+ waitUntilFinish(errorGobbler);
+ closeStreams(process);
+
+ if (executorService != null) {
+ executorService.shutdown();
+ }
+ }
+ }
+
+ private void verifyGobbler(Command command, StreamGobbler gobbler, String type) {
+ if (gobbler.getException() != null) {
+ throw new CommandException("Error inside " + type + " stream", command, gobbler.getException());
+ }
+ }
+
+ /**
+ * Execute command and display error and output streams in log.
+ * Method {@link #execute(Command, StreamConsumer, StreamConsumer, long)} is preferable,
+ * when fine-grained control of output of command required.
+ */
+ int execute(Command command, long timeoutMilliseconds) {
+ return execute(command, new DefaultConsumer(), new DefaultConsumer(), timeoutMilliseconds);
+ }
+
+ private void closeStreams(Process process) {
+ if (process != null) {
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getOutputStream());
+ IOUtils.closeQuietly(process.getErrorStream());
+ }
+ }
+
+ private void waitUntilFinish(StreamGobbler thread) {
+ if (thread != null) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ System.err.println("InterruptedException while waiting finish of " + thread.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static class StreamGobbler extends Thread {
+ private final InputStream is;
+ private final StreamConsumer consumer;
+ private volatile Exception exception;
+
+ StreamGobbler(InputStream is, StreamConsumer consumer) {
+ super("ProcessStreamGobbler");
+ this.is = is;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void run() {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ consumeLine(line);
+ }
+ } catch (IOException ioe) {
+ exception = ioe;
+
+ } finally {
+ IOUtils.closeQuietly(br);
+ IOUtils.closeQuietly(isr);
+ }
+ }
+
+ private void consumeLine(String line) {
+ if (exception == null) {
+ try {
+ consumer.consumeLine(line);
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+ }
+
+
+ interface StreamConsumer {
+ void consumeLine(String line);
+ }
+
+ private static class DefaultConsumer implements StreamConsumer {
+ public void consumeLine(String line) {
+ System.out.println(line);
+ }
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
new file mode 100644
index 0000000..3357e9a
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
@@ -0,0 +1,56 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import org.sonar.runner.impl.BatchLauncher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class EmbeddedRunner extends Runner<EmbeddedRunner> {
+
+ private final List<Object> extensions = new ArrayList<Object>();
+
+ private EmbeddedRunner() {
+ }
+
+ public static EmbeddedRunner create() {
+ return new EmbeddedRunner();
+ }
+
+ public EmbeddedRunner setUnmaskedPackages(String... packages) {
+ return setProperty("sonarRunner.unmaskedPackages", Utils.join(packages, ","));
+ }
+
+ public EmbeddedRunner addExtensions(Object... objects) {
+ extensions.addAll(Arrays.asList(objects));
+ return this;
+ }
+
+ List<Object> extensions() {
+ return extensions;
+ }
+
+ @Override
+ public void doExecute() {
+ new BatchLauncher().execute(properties(), extensions);
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
new file mode 100644
index 0000000..7a6c3cc
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
@@ -0,0 +1,118 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import org.apache.commons.io.IOUtils;
+import org.sonar.runner.impl.BatchLauncherMain;
+import org.sonar.runner.impl.JarExtractor;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class ForkedRunner extends Runner<ForkedRunner> {
+
+ private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
+
+ private final Command.Builder commandBuilder;
+ private final JarExtractor jarExtractor;
+
+ ForkedRunner(Command.Builder commandBuilder, JarExtractor jarExtractor) {
+ this.commandBuilder = commandBuilder;
+ this.jarExtractor = jarExtractor;
+ }
+
+ public static ForkedRunner create() {
+ Os os = new Os();
+ Command.Builder builder = Command.builder().setExecutable(os.usedJavaExe().getAbsolutePath());
+ return new ForkedRunner(builder, new JarExtractor());
+ }
+
+ @Override
+ public void doExecute() {
+ File propertiesFile = writeProperties();
+ File jarFile = extractJar();
+ fork(jarFile, propertiesFile);
+ }
+
+ private File extractJar() {
+ return jarExtractor.extract("sonar-runner-impl");
+ }
+
+ private void fork(File jarFile, File propertiesFile) {
+ // java -jar sonar-runner-impl.jar path/to/propertiesFile
+ Command command = commandBuilder
+ .addArguments("-cp", jarFile.getAbsolutePath())
+ .addArguments(BatchLauncherMain.class.getName())
+ .addArguments(propertiesFile.getAbsolutePath())
+ .build();
+ System.out.println("---------- execute: " + command);
+ int status = CommandExecutor.create().execute(command, ONE_DAY_IN_MILLISECONDS);
+ if (status != 0) {
+ throw new IllegalStateException("TODO");
+ }
+ }
+
+ private File writeProperties() {
+ OutputStream output = null;
+ try {
+ File file = File.createTempFile("sonar-project", ".properties");
+ output = new FileOutputStream(file);
+ properties().store(output, "Generated by sonar-runner");
+ return file;
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to export sonar-runner properties", e);
+
+ } finally {
+ IOUtils.closeQuietly(output);
+ }
+ }
+
+ public ForkedRunner setJavaCommand(String s) {
+ commandBuilder.setExecutable(s);
+ return this;
+ }
+
+ public ForkedRunner addJvmArgument(String... s) {
+ commandBuilder.addArguments(Arrays.asList(s));
+ return this;
+ }
+
+ public ForkedRunner addJvmArguments(List<String> args) {
+ commandBuilder.addArguments(args);
+ return this;
+ }
+
+ public ForkedRunner setJvmEnvVariable(String key, String value) {
+ commandBuilder.setEnvVariable(key, value);
+ return this;
+ }
+
+ public ForkedRunner addJvmEnvVariables(Map<String, String> map) {
+ commandBuilder.addEnvVariables(map);
+ return this;
+ }
+
+
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
new file mode 100644
index 0000000..da51361
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import java.io.File;
+
+class Os {
+ boolean isWindows() {
+ return System.getProperty("os.name").contains("Windows");
+ }
+
+ File usedJavaHome() {
+ return new File(System.getProperty("java.home"));
+ }
+
+ /**
+ * Path to the java executable used by this VM
+ */
+ File usedJavaExe() {
+ File bin = new File(usedJavaHome(), "bin");
+ return new File(bin, isWindows() ? "java.exe" : "java");
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
new file mode 100644
index 0000000..079cdb9
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
@@ -0,0 +1,102 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import org.sonar.runner.impl.Constants;
+import org.sonar.runner.impl.Logs;
+
+import javax.annotation.Nullable;
+
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Properties;
+
+/**
+ * @since 2.2
+ */
+public abstract class Runner<T extends Runner> {
+
+ private final Properties properties = new Properties();
+
+ protected Runner() {
+ initProperties();
+ }
+
+ private void initProperties() {
+ // default values
+ properties.put(Constants.HOST_URL, "http://localhost:9000");
+ properties.put(Constants.TASK, "scan");
+ properties.put(Constants.PROP_APP, "SonarRunner");
+ properties.put(Constants.PROP_APP_VERSION, RunnerVersion.version());
+ }
+
+ public Properties properties() {
+ Properties clone = new Properties();
+ clone.putAll(properties);
+ return clone;
+ }
+
+ public T addProperties(Properties p) {
+ properties.putAll(p);
+ return (T) this;
+ }
+
+ public T setProperty(String key, String value) {
+ properties.setProperty(key, value);
+ return (T) this;
+ }
+
+ public String property(String key, @Nullable String defaultValue) {
+ return properties.getProperty(key, defaultValue);
+ }
+
+ public T setApp(String app, String version) {
+ setProperty(Constants.PROP_APP, app);
+ setProperty(Constants.PROP_APP_VERSION, version);
+ return (T) this;
+ }
+
+ public String app() {
+ return property(Constants.PROP_APP, null);
+ }
+
+ public String appVersion() {
+ return property(Constants.PROP_APP_VERSION, null);
+ }
+
+ public void execute() {
+ initSourceEncoding();
+ doExecute();
+ }
+
+ private void initSourceEncoding() {
+ String sourceEncoding = property(Constants.SOURCE_ENCODING, null);
+ boolean platformDependent = false;
+ if (sourceEncoding == null || sourceEncoding.equals("")) {
+ sourceEncoding = Charset.defaultCharset().name();
+ platformDependent = true;
+ setProperty(Constants.SOURCE_ENCODING, sourceEncoding);
+ }
+ Logs.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + sourceEncoding + "\""
+ + (platformDependent ? " (analysis is platform dependent)" : ""));
+ }
+
+ protected abstract void doExecute();
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
new file mode 100644
index 0000000..d15f72f
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import java.util.Scanner;
+
+public enum RunnerVersion {
+
+ INSTANCE;
+
+ private String version;
+
+ public static String version() {
+ return INSTANCE.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();
+ }
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java
new file mode 100644
index 0000000..9be34b8
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java
@@ -0,0 +1,41 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.runner.api;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+class Utils {
+ /**
+ * Similar to org.apache.commons.lang.StringUtils#join()
+ */
+ static String join(String[] array, String delimiter) {
+ StringBuilder sb = new StringBuilder();
+ Iterator it = Arrays.asList(array).iterator();
+ while (it.hasNext()) {
+ sb.append(it.next());
+ if (!it.hasNext()) {
+ break;
+ }
+ sb.append(delimiter);
+ }
+ return sb.toString();
+ }
+}
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java
new file mode 100644
index 0000000..a362ee1
--- /dev/null
+++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Sonar Runner - API
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.runner.api;
+
+import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file