@@ -19,6 +19,8 @@ | |||
*/ | |||
package org.sonar.runner.api; | |||
import org.sonar.runner.impl.Logs; | |||
import javax.annotation.Nullable; | |||
import java.io.BufferedReader; | |||
@@ -101,7 +103,7 @@ class CommandExecutor { | |||
} | |||
} | |||
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() { | |||
@@ -149,8 +151,7 @@ class CommandExecutor { | |||
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); | |||
} | |||
} | |||
} |
@@ -19,13 +19,15 @@ | |||
*/ | |||
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; | |||
/** | |||
@@ -36,9 +38,9 @@ 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"; | |||
@@ -53,6 +55,11 @@ public class EmbeddedRunner extends Runner<EmbeddedRunner> { | |||
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...)}. | |||
@@ -104,16 +111,27 @@ public class EmbeddedRunner extends Runner<EmbeddedRunner> { | |||
@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); | |||
} | |||
} | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.runner.api; | |||
import org.sonar.runner.impl.Logs; | |||
import org.sonar.runner.impl.BatchLauncherMain; | |||
import org.sonar.runner.impl.JarExtractor; | |||
@@ -46,7 +47,8 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
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; | |||
@@ -121,7 +123,8 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
} | |||
/** | |||
* 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; | |||
@@ -129,7 +132,8 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
} | |||
/** | |||
* 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; | |||
@@ -138,12 +142,12 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
@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); | |||
@@ -151,15 +155,15 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
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) { | |||
@@ -196,11 +200,22 @@ public class ForkedRunner extends Runner<ForkedRunner> { | |||
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()) { |
@@ -19,6 +19,9 @@ | |||
*/ | |||
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; | |||
@@ -30,7 +33,6 @@ import java.util.Properties; | |||
* @since 2.2 | |||
*/ | |||
public abstract class Runner<T extends Runner> { | |||
private final Properties globalProperties = new Properties(); | |||
protected Runner() { | |||
@@ -41,6 +43,16 @@ public abstract class Runner<T extends 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. | |||
@@ -93,7 +105,7 @@ public abstract class Runner<T extends Runner> { | |||
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); | |||
} |
@@ -33,12 +33,22 @@ import java.nio.file.SimpleFileVisitor; | |||
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() |
@@ -31,8 +31,11 @@ import org.sonar.runner.impl.InternalProperties; | |||
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; | |||
@@ -58,6 +61,7 @@ public class EmbeddedRunnerTest { | |||
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); | |||
} | |||
@@ -68,6 +72,48 @@ public class EmbeddedRunnerTest { | |||
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() { |
@@ -19,8 +19,10 @@ | |||
*/ | |||
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; | |||
@@ -29,12 +31,16 @@ import org.junit.rules.TemporaryFolder; | |||
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; | |||
@@ -74,27 +80,46 @@ public class ForkedRunnerTest { | |||
} | |||
@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 |
@@ -40,7 +40,6 @@ public class OsTest { | |||
@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"); |
@@ -41,6 +41,16 @@ public class UtilsTest { | |||
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(); | |||
@@ -64,7 +74,7 @@ public class UtilsTest { | |||
Utils.closeQuietly(c); | |||
verify(c).close(); | |||
} | |||
@Test | |||
public void close_quietly_null() throws IOException { | |||
Utils.closeQuietly(null); |
@@ -7,5 +7,13 @@ | |||
</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> |
@@ -1,5 +1,5 @@ | |||
/* | |||
* SonarQube Runner - Batch API | |||
* SonarQube Runner - Batch Interface | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
@@ -19,11 +19,21 @@ | |||
*/ | |||
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(); | |||
} |
@@ -10,7 +10,7 @@ | |||
<name>SonarQube Runner - Batch</name> | |||
<properties> | |||
<sonarBatchVersion>4.4</sonarBatchVersion> | |||
<sonarBatchVersion>5.2-SNAPSHOT</sonarBatchVersion> | |||
</properties> | |||
<dependencies> |
@@ -19,18 +19,19 @@ | |||
*/ | |||
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; | |||
@@ -49,7 +50,12 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { | |||
@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(); | |||
} | |||
@@ -63,47 +69,38 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { | |||
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; | |||
} | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/* | |||
* 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; | |||
@@ -38,38 +38,8 @@ public class IsolatedLauncherTest { | |||
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(); | |||
} | |||
} |
@@ -19,6 +19,8 @@ | |||
*/ | |||
package org.sonar.runner; | |||
import org.sonar.runner.impl.Logs; | |||
class Stats { | |||
private long startTime; | |||
@@ -32,12 +34,12 @@ class Stats { | |||
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; | |||
} |
@@ -19,6 +19,8 @@ | |||
*/ | |||
package org.sonar.runner; | |||
import org.sonar.runner.impl.Logs; | |||
import org.sonar.runner.api.RunnerVersion; | |||
class SystemInfo { | |||
@@ -28,12 +30,12 @@ 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); | |||
} | |||
} | |||
@@ -19,8 +19,10 @@ | |||
*/ | |||
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; | |||
@@ -31,17 +33,22 @@ public class StatsTest { | |||
@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 |
@@ -123,6 +123,11 @@ | |||
<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> |
@@ -40,8 +40,12 @@ public class BatchLauncherMain { | |||
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 { |
@@ -69,7 +69,7 @@ public class IsolatedLauncherFactory { | |||
} | |||
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) { |
@@ -19,11 +19,37 @@ | |||
*/ | |||
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; | |||
@@ -35,26 +61,63 @@ public class Logs { | |||
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()); | |||
} | |||
} | |||
} | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.runner.impl; | |||
import org.sonar.home.log.LogListener; | |||
import org.junit.Before; | |||
import org.sonar.runner.batch.IsolatedLauncher; | |||
@@ -102,5 +103,18 @@ public class IsolatedLauncherFactoryTest { | |||
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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
/* | |||
* 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"); | |||
} | |||
} |