diff options
Diffstat (limited to 'sonar-runner-api/src/main/java/org/sonar/runner/api')
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 |