*/
package org.sonar.runner.api;
+import org.sonar.runner.impl.Logs;
+
import javax.annotation.Nullable;
import java.io.BufferedReader;
}
}
- private void monitorProcess(final ProcessMonitor processMonitor, final ExecutorService executor, final Process process) {
+ private static void monitorProcess(final ProcessMonitor processMonitor, final ExecutorService executor, final Process process) {
new Thread() {
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e) {
- System.err.println("InterruptedException while waiting finish of " + thread.toString());
- e.printStackTrace();
+ Logs.error("InterruptedException while waiting finish of " + thread.toString(), e);
}
}
}
*/
package org.sonar.runner.api;
+import org.sonar.home.log.LogListener;
+
+import org.sonar.runner.impl.Logs;
import org.sonar.runner.batch.IsolatedLauncher;
import org.sonar.runner.impl.IsolatedLauncherFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
/**
* @since 2.2
*/
public class EmbeddedRunner extends Runner<EmbeddedRunner> {
-
private final IsolatedLauncherFactory launcherFactory;
private IsolatedLauncher launcher;
+ private String sqVersion;
private final List<Object> extensions = new ArrayList<Object>();
private static final String MASK_RULES_PROP = "sonarRunner.maskRules";
return new EmbeddedRunner(new IsolatedLauncherFactory());
}
+ public static EmbeddedRunner create(LogListener logListener) {
+ Logs.setListener(logListener);
+ return new EmbeddedRunner(new IsolatedLauncherFactory());
+ }
+
/**
* Sonar is executed in an almost fully isolated classloader (mask everything by default). This method allows to unmask some classes based on
* a prefix of their fully qualified name. It is related to the extensions provided by {@link #addExtensions(Object...)}.
@Override
protected void doStart() {
launcher = launcherFactory.createLauncher(globalProperties());
- launcher.start(globalProperties(), extensions);
+ if (Utils.isAtLeast52(launcher.getVersion())) {
+ launcher.start(globalProperties(), extensions, Logs.getListener());
+ }
}
@Override
protected void doStop() {
- launcher.stop();
+ if (Utils.isAtLeast52(launcher.getVersion())) {
+ launcher.stop();
+ }
}
@Override
protected void doExecute(Properties analysisProperties) {
- launcher.execute(analysisProperties);
+ if (Utils.isAtLeast52(launcher.getVersion())) {
+ launcher.execute(analysisProperties);
+ } else {
+ Properties prop = new Properties();
+ prop.putAll(globalProperties());
+ prop.putAll(analysisProperties);
+ launcher.executeOldVersion(prop, extensions);
+ }
}
}
*/
package org.sonar.runner.api;
+import org.sonar.runner.impl.Logs;
import org.sonar.runner.impl.BatchLauncherMain;
import org.sonar.runner.impl.JarExtractor;
private final Map<String, String> jvmEnvVariables = new HashMap<String, String>();
private final List<String> jvmArguments = new ArrayList<String>();
private String javaExecutable;
- private StreamConsumer stdOut = null, stdErr = null;
+ private StreamConsumer stdOut = null;
+ private StreamConsumer stdErr = null;
private final JarExtractor jarExtractor;
private final CommandExecutor commandExecutor;
}
/**
- * Subscribe to the standard output. By default output is {@link System.out}
+ * @deprecated Since 2.5. Use {@link ForkedRunner#setLogListener} instead.
+ * Subscribe to the standard output from the forked process. By default, logs messages are sent with INFO level to the log listener set.
*/
public ForkedRunner setStdOut(@Nullable StreamConsumer stream) {
this.stdOut = stream;
}
/**
- * Subscribe to the error output. By default output is {@link System.err}
+ * @deprecated Since 2.5. Use {@link ForkedRunner#setLogListener}instead.
+ * Subscribe to the error output from the forked process. By default, logs messages are sent with ERROR level to the log listener set.
*/
public ForkedRunner setStdErr(@Nullable StreamConsumer stream) {
this.stdErr = stream;
@Override
protected void doExecute(Properties props) {
- //merge both global and analysis-specific properties because it will be used both to start and to execute.
+ // merge both global and analysis-specific properties because it will be used both to start and to execute.
Properties p = new Properties();
-
+
p.putAll(globalProperties());
p.putAll(props);
-
+
ForkCommand forkCommand = createCommand(p);
try {
fork(forkCommand);
deleteTempFiles(forkCommand);
}
}
-
+
@Override
protected void doStop() {
- //nothing to do
+ // nothing to do
}
-
+
@Override
protected void doStart() {
- //nothing to do
+ // nothing to do
}
ForkCommand createCommand(Properties p) {
private void fork(ForkCommand forkCommand) {
if (stdOut == null) {
- stdOut = new PrintStreamConsumer(System.out);
+ stdOut = new StreamConsumer() {
+ @Override
+ public void consumeLine(String line) {
+ Logs.info(line);
+ }
+ };
}
if (stdErr == null) {
- stdErr = new PrintStreamConsumer(System.err);
+ stdErr = new StreamConsumer() {
+ @Override
+ public void consumeLine(String line) {
+ Logs.error(line);
+ }
+ };
}
+
int status = commandExecutor.execute(forkCommand.command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS, processMonitor);
if (status != 0) {
if (processMonitor != null && processMonitor.stop()) {
*/
package org.sonar.runner.api;
+import org.sonar.home.log.LogListener;
+
+import org.sonar.runner.impl.Logs;
import org.sonar.runner.impl.InternalProperties;
import javax.annotation.Nullable;
* @since 2.2
*/
public abstract class Runner<T extends Runner> {
-
private final Properties globalProperties = new Properties();
protected Runner() {
clone.putAll(globalProperties);
return clone;
}
+
+ /**
+ * Set a log stream. All log events will be redirected to the listener.
+ * By default, all logs are sent to stdout, except for logs of ERROR level, which are sent to stderr.
+ * If null is given, the default is behavior is set.
+ */
+ public T setLogListener(LogListener stream) {
+ Logs.setListener(stream);
+ return (T) this;
+ }
/**
* Declare Sonar properties, for example sonar.projectKey=>foo.
if (dumpToFile != null) {
File dumpFile = new File(dumpToFile);
Utils.writeProperties(dumpFile, copy);
- System.out.println("Simulation mode. Configuration written to " + dumpFile.getAbsolutePath());
+ Logs.info("Simulation mode. Configuration written to " + dumpFile.getAbsolutePath());
} else {
doExecute(copy);
}
import java.util.Arrays;
import java.util.Iterator;
import java.util.Properties;
-import java.nio.file.attribute.*;
+import java.nio.file.attribute.BasicFileAttributes;
class Utils {
private Utils() {
// only util static methods
}
+
+ static boolean isAtLeast52(String version) {
+ //it can be snapshot (5.2-SNAPSHOT)
+ if(version == null) {
+ return false;
+ }
+
+ int endIndex = Math.min(3, version.length());
+ return Double.parseDouble(version.substring(0, endIndex)) >= 5.2;
+ }
/**
* Similar to org.apache.commons.lang.StringUtils#join()
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Properties;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.any;
import static org.fest.assertions.Assertions.assertThat;
public void setUp() {
batchLauncher = mock(IsolatedLauncherFactory.class);
launcher = mock(IsolatedLauncher.class);
+ when(launcher.getVersion()).thenReturn("5.2");
when(batchLauncher.createLauncher(any(Properties.class))).thenReturn(launcher);
runner = new EmbeddedRunner(batchLauncher);
}
assertThat(runner.app()).isEqualTo("Eclipse");
assertThat(runner.appVersion()).isEqualTo("3.1");
}
+
+ @Test
+ public void test_back_compatibility() {
+ when(launcher.getVersion()).thenReturn("4.5");
+
+ final FakeExtension fakeExtension = new FakeExtension();
+ List<Object> extensionList = new LinkedList<>();
+ extensionList.add(fakeExtension);
+
+ Properties analysisProps = new Properties();
+ analysisProps.put("sonar.dummy", "summy");
+
+ runner.addExtensions(fakeExtension);
+ 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"));
+ }
+ }));
+
+ // 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(extensionList));
+ }
@Test
public void should_set_unmasked_packages() {
*/
package org.sonar.runner.api;
+import org.sonar.home.log.LogListener.Level;
+import org.sonar.home.log.LogListener;
+import org.sonar.runner.impl.Logs;
import org.mockito.Mockito;
-
import org.mockito.ArgumentCaptor;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.ArgumentMatcher;
import org.sonar.runner.impl.JarExtractor;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Properties;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.mockito.Matchers.any;
}
@Test
- public void should_print_to_standard_outputs_by_default() throws IOException {
+ public void should_use_log_listener() throws IOException {
JarExtractor jarExtractor = createMockExtractor();
CommandExecutor commandExecutor = mock(CommandExecutor.class);
ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor);
runner.execute();
-
- verify(commandExecutor).execute(any(Command.class), argThat(new StdConsumerMatcher(System.out)), argThat(new StdConsumerMatcher(System.err)), anyLong(),
- any(ProcessMonitor.class));
+ LogListener listener = mock(LogListener.class);
+ Logs.setListener(listener);
+
+ ArgumentCaptor<StreamConsumer> arg1 = ArgumentCaptor.forClass(StreamConsumer.class);
+ ArgumentCaptor<StreamConsumer> arg2 = ArgumentCaptor.forClass(StreamConsumer.class);
+
+ verify(commandExecutor).execute(any(Command.class), arg1.capture(), arg2.capture(), anyLong(), any(ProcessMonitor.class));
+ arg1.getValue().consumeLine("test1");
+ arg2.getValue().consumeLine("test2");
+
+ verify(listener).log("test1", Level.INFO);
+ verify(listener).log("test2", Level.ERROR);
+ verifyNoMoreInteractions(listener);
}
+
+ @Test
+ public void should_print_to_consumers_by_default() throws IOException {
+ final List<String> printedLines = new LinkedList<>();
+ StreamConsumer consumer = new StreamConsumer() {
+ @Override
+ public void consumeLine(String line) {
+ printedLines.add(line);
+ }
+ };
+ JarExtractor jarExtractor = createMockExtractor();
- static class StdConsumerMatcher extends ArgumentMatcher<StreamConsumer> {
- PrintStream output;
-
- StdConsumerMatcher(PrintStream output) {
- this.output = output;
- }
+ CommandExecutor commandExecutor = mock(CommandExecutor.class);
+ ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor);
+ runner.setStdOut(consumer);
+ runner.setStdErr(consumer);
+ runner.execute();
- public boolean matches(Object o) {
- return ((PrintStreamConsumer) o).output == output;
- }
+ verify(commandExecutor).execute(any(Command.class), eq(consumer), eq(consumer), anyLong(),
+ any(ProcessMonitor.class));
}
@Test
@Test
public void testUsedJavaExe() throws Exception {
- System.out.println(System.getProperty("java.io.tmpdir"));
File javaExe = new Os().thisJavaExe();
assertThat(javaExe).isNotNull().isFile().exists();
assertThat(javaExe.getName()).contains("java");
assertThat(Utils.join(new String[] {"foo", "bar"}, ",")).isEqualTo("foo,bar");
}
+ @Test
+ public void parse_version() {
+ assertThat(Utils.isAtLeast52("5.2")).isTrue();
+ assertThat(Utils.isAtLeast52(null)).isFalse();
+ assertThat(Utils.isAtLeast52("52")).isTrue();
+ assertThat(Utils.isAtLeast52("5.0")).isFalse();
+ assertThat(Utils.isAtLeast52("6.0.0")).isTrue();
+ assertThat(Utils.isAtLeast52("5.2-SNAPSHOT")).isTrue();
+ }
+
@Test
public void task_should_require_project() {
Properties props = new Properties();
Utils.closeQuietly(c);
verify(c).close();
}
-
+
@Test
public void close_quietly_null() throws IOException {
Utils.closeQuietly(null);
</parent>
<artifactId>sonar-runner-batch-interface</artifactId>
- <name>SonarQube Runner - Batch API</name>
+ <name>SonarQube Runner - Batch Interface</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-home</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
</project>
/*
- * SonarQube Runner - Batch API
+ * SonarQube Runner - Batch Interface
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
*/
package org.sonar.runner.batch;
+import org.sonar.home.log.LogListener;
+
import java.util.List;
import java.util.Properties;
public interface IsolatedLauncher {
void start(Properties properties, List<Object> extensions);
+
+ void start(Properties properties, List<Object> extensions, LogListener logListener);
+
void stop();
+
void execute(Properties properties);
+
+ void executeOldVersion(Properties properties, List<Object> extensions);
+
+ String getVersion();
}
<name>SonarQube Runner - Batch</name>
<properties>
- <sonarBatchVersion>4.4</sonarBatchVersion>
+ <sonarBatchVersion>5.2-SNAPSHOT</sonarBatchVersion>
</properties>
<dependencies>
*/
package org.sonar.runner.batch;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.joran.JoranConfigurator;
-import ch.qos.logback.core.joran.spi.JoranException;
+import org.sonar.home.log.LogListener;
+import org.picocontainer.annotations.Nullable;
import com.google.common.annotations.VisibleForTesting;
+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 org.slf4j.LoggerFactory;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
@Override
public void start(Properties globalProperties, List<Object> extensions) {
- batch = createBatch(globalProperties, extensions);
+ start(globalProperties, extensions, null);
+ }
+
+ @Override
+ public void start(Properties globalProperties, List<Object> extensions, @Nullable LogListener logListener) {
+ batch = createBatch(globalProperties, extensions, logListener);
batch.start();
}
batch.executeTask((Map) properties);
}
- Batch createBatch(Properties properties, List<Object> extensions) {
- initLogging(properties);
+ Batch createBatch(Properties properties, List<Object> extensions, @Nullable LogListener logListener) {
EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.app"), properties.getProperty("sonarRunner.appVersion"));
- return Batch.builder()
+ Batch.Builder builder = Batch.builder()
.setEnvironment(env)
.addComponents(extensions)
- .setBootstrapProperties((Map) properties)
- .build();
- }
+ .setBootstrapProperties((Map) properties);
- private void initLogging(Properties props) {
- LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
- JoranConfigurator jc = new JoranConfigurator();
- jc.setContext(context);
- context.reset();
- try (InputStream input = Batch.class.getResourceAsStream("/org/sonar/batch/logback.xml")) {
- System.setProperty("ROOT_LOGGER_LEVEL", isDebug(props) ? DEBUG : "INFO");
- context.putProperty("SQL_LOGGER_LEVEL", getSqlLevel(props));
- context.putProperty("SQL_RESULTS_LOGGER_LEVEL", getSqlResultsLevel(props));
- jc.doConfigure(input);
- } catch (JoranException e) {
- throw new SonarException("can not initialize logging", e);
- } catch (IOException e1) {
- throw new SonarException("couldn't close resource", e1);
+ if (logListener != null) {
+ builder.setLogListener(logListener);
}
- }
- @VisibleForTesting
- protected boolean isDebug(Properties props) {
- return Boolean.parseBoolean(props.getProperty("sonar.verbose", FALSE));
+ return builder.build();
}
- @VisibleForTesting
- protected static String getSqlLevel(Properties props) {
- boolean showSql = "true".equals(props.getProperty("sonar.showSql", FALSE));
- return showSql ? DEBUG : WARN;
+ /**
+ * This method exists for backward compatibility with SonarQube < 5.2.
+ */
+ @Override
+ public void executeOldVersion(Properties properties, List<Object> extensions) {
+ createBatch(properties, extensions, null).execute();
}
- @VisibleForTesting
- protected static String getSqlResultsLevel(Properties props) {
- boolean showSql = "true".equals(props.getProperty("sonar.showSqlResults", FALSE));
- return showSql ? DEBUG : WARN;
+ @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;
+ }
}
}
+++ /dev/null
-/*
- * SonarQube Runner - Batch
- * Copyright (C) 2011 SonarSource
- * dev@sonar.codehaus.org
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-/**
- * Internal package that creates the project definition and launches the analyses based on it.
- * Should not be used by consumers.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.runner.batch;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
props.setProperty("sonar.projectName", "Sample");
props.setProperty("sonar.projectVersion", "1.0");
props.setProperty("sonar.sources", "src");
- Batch batch = launcher.createBatch(props, Collections.emptyList());
+ Batch batch = launcher.createBatch(props, Collections.emptyList(), null);
assertThat(batch).isNotNull();
}
-
- @Test
- public void testGetSqlLevel() throws Exception {
- assertThat(BatchIsolatedLauncher.getSqlLevel(props)).isEqualTo("WARN");
-
- props.setProperty("sonar.showSql", "true");
- assertThat(BatchIsolatedLauncher.getSqlLevel(props)).isEqualTo("DEBUG");
-
- props.setProperty("sonar.showSql", "false");
- assertThat(BatchIsolatedLauncher.getSqlLevel(props)).isEqualTo("WARN");
- }
-
- @Test
- public void testGetSqlResultsLevel() throws Exception {
- assertThat(BatchIsolatedLauncher.getSqlResultsLevel(props)).isEqualTo("WARN");
-
- props.setProperty("sonar.showSqlResults", "true");
- assertThat(BatchIsolatedLauncher.getSqlResultsLevel(props)).isEqualTo("DEBUG");
-
- props.setProperty("sonar.showSqlResults", "false");
- assertThat(BatchIsolatedLauncher.getSqlResultsLevel(props)).isEqualTo("WARN");
- }
-
- @Test
- public void shouldDetermineVerboseMode() {
- assertThat(launcher.isDebug(props)).isFalse();
-
- props.setProperty("sonar.verbose", "true");
- assertThat(launcher.isDebug(props)).isTrue();
- }
}
*/
package org.sonar.runner;
+import org.sonar.runner.impl.Logs;
+
class Stats {
private long startTime;
Stats stop() {
long stopTime = System.currentTimeMillis() - startTime;
- System.out.println("Total time: " + formatTime(stopTime));
+ Logs.info("Total time: " + formatTime(stopTime));
System.gc();
Runtime r = Runtime.getRuntime();
long mb = 1024L * 1024;
- System.out.println("Final Memory: " + (r.totalMemory() - r.freeMemory()) / mb + "M/" + r.totalMemory() / mb + "M");
+ Logs.info("Final Memory: " + (r.totalMemory() - r.freeMemory()) / mb + "M/" + r.totalMemory() / mb + "M");
return this;
}
*/
package org.sonar.runner;
+import org.sonar.runner.impl.Logs;
+
import org.sonar.runner.api.RunnerVersion;
class SystemInfo {
}
static void print() {
- System.out.println("SonarQube Runner " + RunnerVersion.version());
- System.out.println(java());
- System.out.println(os());
+ Logs.info("SonarQube Runner " + RunnerVersion.version());
+ Logs.info(java());
+ Logs.info(os());
String runnerOpts = System.getenv("SONAR_RUNNER_OPTS");
if (runnerOpts != null) {
- System.out.println("SONAR_RUNNER_OPTS=" + runnerOpts);
+ Logs.info("SONAR_RUNNER_OPTS=" + runnerOpts);
}
}
*/
package org.sonar.runner;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
+import org.sonar.home.log.LogListener;
+
+import org.sonar.runner.impl.Logs;
+
import java.io.UnsupportedEncodingException;
import org.junit.Test;
@Test
public void shouldPrintStats() throws UnsupportedEncodingException {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- System.setOut(new PrintStream(output));
+ final StringBuffer sb = new StringBuffer();
+ Logs.setListener(new LogListener() {
+ @Override
+ public void log(String msg, Level level) {
+ sb.append(msg + System.lineSeparator());
+ }
+ });
new Stats().start().stop();
- String out = output.toString("UTF-8");
- String[] lines = out.split("\n");
+ String out = sb.toString();
+ String[] lines = out.split(System.lineSeparator());
assertThat(lines).hasSize(2);
- assertThat(lines[0]).startsWith("Total time: ");
- assertThat(lines[1]).startsWith("Final Memory: ");
+ assertThat(lines[0]).contains("Total time: ");
+ assertThat(lines[1]).contains("Final Memory: ");
}
@Test
<relocation>
<pattern>org.sonar.home</pattern>
<shadedPattern>org.sonar.runner.home</shadedPattern>
+ <!-- shared between sonar-runner-api, sonar-runner-impl and sonar-batch -->
+ <excludes>
+ <exclude>org.sonar.home.log.LogListener</exclude>
+ <exclude>org.sonar.home.log.LogListener$Level</exclude>
+ </excludes>
</relocation>
</relocations>
</configuration>
Properties props = loadProperties(args[0]);
IsolatedLauncher launcher = launcherFactory.createLauncher(props);
launcher.start(props, Collections.emptyList());
- launcher.execute(props);
- launcher.stop();
+ try {
+ launcher.execute(props);
+ } finally {
+ //persistit has non-daemon threads that need to be stopped or the jvm w'ont quit
+ launcher.stop();
+ }
}
private static Properties loadProperties(String arg) throws IOException {
}
private static void addIsolatedLauncherMaskRule(Properties props) {
- String unmask = "UNMASK|org.sonar.runner.batch.IsolatedLauncher";
+ String unmask = "UNMASK|org.sonar.runner.batch.IsolatedLauncher,UNMASK|org.sonar.home.log.LogListener";
String currentRules = (String) props.get(InternalProperties.RUNNER_MASK_RULES);
if (currentRules == null) {
*/
package org.sonar.runner.impl;
+import org.sonar.home.log.LogListener.Level;
+
+import org.sonar.home.log.LogListener;
+
+import javax.annotation.Nullable;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
public class Logs {
+ private static LogListener listener = new PrintStreamLogListener(getDefaultFwdMap());
+ private static boolean debugEnabled = false;
+
private Logs() {
}
- private static boolean debugEnabled = false;
+ public static void setListener(@Nullable LogListener listener) {
+ if (listener == null) {
+ Logs.listener = new PrintStreamLogListener(getDefaultFwdMap());
+ } else {
+ Logs.listener = listener;
+ }
+ }
+
+ public static LogListener getListener() {
+ return Logs.listener;
+ }
public static void setDebugEnabled(boolean debugEnabled) {
Logs.debugEnabled = debugEnabled;
public static void debug(String message) {
if (isDebugEnabled()) {
- System.out.println("DEBUG: " + message);
+ log(message, Level.DEBUG);
}
}
public static void info(String message) {
- System.out.println("INFO: " + message);
+ log(message, Level.INFO);
}
public static void warn(String message) {
- System.out.println("WARN: " + message);
+ log(message, Level.WARN);
}
public static void error(String message) {
- System.err.println("ERROR: " + message);
+ log(message, Level.ERROR);
}
public static void error(String message, Throwable t) {
- System.err.println("ERROR: " + message);
+ log(message, Level.ERROR);
if (t != null) {
- t.printStackTrace(System.err);
+ StringWriter sw = new StringWriter();
+
+ t.printStackTrace(new PrintWriter(sw));
+ String[] lines = sw.toString().split(System.getProperty("line.separator"));
+ for (String l : lines) {
+ log(l, Level.ERROR);
+ }
+ }
+ }
+
+ private static void log(String msg, Level level) {
+ listener.log(msg, level);
+ }
+
+ private static Map<Level, PrintStream> getDefaultFwdMap() {
+ Map<Level, PrintStream> map = new EnumMap<>(Level.class);
+
+ map.put(Level.ERROR, System.err);
+ map.put(Level.WARN, System.out);
+ map.put(Level.INFO, System.out);
+ map.put(Level.DEBUG, System.out);
+ map.put(Level.TRACE, System.out);
+ return map;
+ }
+
+ private static class PrintStreamLogListener implements LogListener {
+ Map<Level, PrintStream> forwardMap;
+
+ PrintStreamLogListener(Map<Level, PrintStream> forwardMap) {
+ this.forwardMap = new HashMap<>(forwardMap);
+ }
+
+ @Override
+ public void log(String msg, Level level) {
+ PrintStream ps = forwardMap.get(level);
+ if (ps != null) {
+ ps.append(level.toString() + ": " + msg + System.lineSeparator());
+ }
}
}
}
*/
package org.sonar.runner.impl;
+import org.sonar.home.log.LogListener;
import org.junit.Before;
import org.sonar.runner.batch.IsolatedLauncher;
public void execute(Properties properties) {
FakeIsolatedLauncher.props = properties;
}
+
+ @Override
+ public void start(Properties properties, List<Object> extensions, LogListener logListener) {
+ }
+
+ @Override
+ public void executeOldVersion(Properties properties, List<Object> extensions) {
+ }
+
+ @Override
+ public String getVersion() {
+ return null;
+ }
}
}
--- /dev/null
+/*
+ * SonarQube Runner - Implementation
+ * 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.impl;
+
+import org.sonar.home.log.LogListener;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Before;
+
+public class LogsTest {
+ private static final String EXPECTED_DEBUG = "DEBUG: debug\n";
+ private static final String EXPECTED_INFO = "INFO: info\n";
+ private static final String EXPECTED_ERROR = "ERROR: error\n";
+
+ private ByteArrayOutputStream recordedSystemOut = new ByteArrayOutputStream();
+ private ByteArrayOutputStream recordedSystemErr = new ByteArrayOutputStream();
+
+ @Before
+ public void restoreDefault() {
+ recordedSystemOut = new ByteArrayOutputStream();
+ recordedSystemErr = new ByteArrayOutputStream();
+
+ System.setOut(new PrintStream(recordedSystemOut));
+ System.setErr(new PrintStream(recordedSystemErr));
+
+ Logs.setDebugEnabled(false);
+ Logs.setListener(null);
+ }
+
+ @Test
+ public void testNull() throws UnsupportedEncodingException {
+ Logs.setListener(null);
+ testDefault();
+ }
+
+ @Test
+ public void testDefault() throws UnsupportedEncodingException {
+ writeTest();
+
+ assertThat(recordedSystemOut.toString(StandardCharsets.UTF_8.name())).isEqualTo(EXPECTED_INFO);
+ assertThat(recordedSystemErr.toString(StandardCharsets.UTF_8.name())).isEqualTo(EXPECTED_ERROR);
+ }
+
+ @Test
+ public void testDebug() throws UnsupportedEncodingException {
+ Logs.setDebugEnabled(true);
+ writeTest();
+
+ assertThat(recordedSystemOut.toString(StandardCharsets.UTF_8.name())).isEqualTo(EXPECTED_DEBUG + EXPECTED_INFO);
+ assertThat(recordedSystemErr.toString(StandardCharsets.UTF_8.name())).isEqualTo(EXPECTED_ERROR);
+ }
+
+ @Test
+ public void testCustomListener() {
+ TestLogListener listener = new TestLogListener();
+
+ Logs.setListener(listener);
+ Logs.setDebugEnabled(true);
+
+ Logs.debug("debug");
+
+ assertThat(listener.msg).isEqualTo("debug");
+ assertThat(listener.level).isEqualTo(LogListener.Level.DEBUG);
+ }
+
+ private class TestLogListener implements LogListener {
+ String msg;
+ Level level;
+
+ @Override
+ public void log(String msg, Level level) {
+ this.msg = msg;
+ this.level = level;
+ }
+ }
+
+ private static void writeTest() {
+ Logs.debug("debug");
+ Logs.info("info");
+ Logs.error("error");
+ }
+}