From 6ae87caef4147e53cb58866c99ce978edd4d3b9c Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Thu, 30 Jul 2015 10:24:11 +0200 Subject: [PATCH] SONARUNNER-143 New API to retrieve issues produced by the analysis --- .../org/sonar/runner/api/EmbeddedRunner.java | 114 ++++++++--- .../main/java/org/sonar/runner/api/Issue.java | 188 ++++++++++++++++++ .../org/sonar/runner/api/IssueListener.java | 24 +++ .../sonar/runner/api/EmbeddedRunnerTest.java | 41 +++- .../impl/IsolatedLauncherFactoryTest.java | 11 +- sonar-runner-batch-interface/pom.xml | 7 - .../sonar/runner/batch/IsolatedLauncher.java | 5 +- .../org/sonar/runner/batch/IssueListener.java | 111 +++++++++++ .../runner/batch/BatchIsolatedLauncher.java | 6 + .../org/sonar/runner/batch/Compatibility.java | 32 ++- 10 files changed, 496 insertions(+), 43 deletions(-) create mode 100644 sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java create mode 100644 sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListener.java create mode 100644 sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java 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 index 6b2ae23..c24bbb6 100644 --- 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 @@ -23,9 +23,12 @@ import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; +import java.security.InvalidParameterException; import java.util.Locale; import java.util.Properties; + import javax.annotation.Nullable; + import org.sonar.home.cache.Logger; import org.sonar.runner.batch.IsolatedLauncher; import org.sonar.runner.impl.InternalProperties; @@ -50,35 +53,7 @@ public class EmbeddedRunner { } public static EmbeddedRunner create(final LogOutput logOutput) { - Logger logger = new Logger() { - - @Override - public void warn(String msg) { - logOutput.log(msg, LogOutput.Level.WARN); - } - - @Override - public void info(String msg) { - logOutput.log(msg, LogOutput.Level.INFO); - } - - @Override - public void error(String msg, Throwable t) { - StringWriter errors = new StringWriter(); - t.printStackTrace(new PrintWriter(errors)); - logOutput.log(msg + "\n" + errors.toString(), LogOutput.Level.ERROR); - } - - @Override - public void error(String msg) { - logOutput.log(msg, LogOutput.Level.ERROR); - } - - @Override - public void debug(String msg) { - logOutput.log(msg, LogOutput.Level.DEBUG); - } - }; + Logger logger = new LoggerAdapter(logOutput); return new EmbeddedRunner(new IsolatedLauncherFactory(logger), logger, logOutput); } @@ -131,6 +106,10 @@ public class EmbeddedRunner { } public void runAnalysis(Properties analysisProperties) { + runAnalysis(analysisProperties, null); + } + + public void runAnalysis(Properties analysisProperties, @Nullable IssueListener issueListener) { Properties copy = new Properties(); copy.putAll(analysisProperties); initAnalysisProperties(copy); @@ -141,7 +120,7 @@ public class EmbeddedRunner { Utils.writeProperties(dumpFile, copy); logger.info("Simulation mode. Configuration written to " + dumpFile.getAbsolutePath()); } else { - doExecute(copy); + doExecute(copy, issueListener); } } @@ -216,14 +195,85 @@ public class EmbeddedRunner { } } - protected void doExecute(Properties analysisProperties) { + protected void doExecute(Properties analysisProperties, @Nullable IssueListener issueListener) { if (VersionUtils.isAtLeast52(launcher.getVersion())) { - launcher.execute(analysisProperties); + if (issueListener != null) { + launcher.execute(analysisProperties, new IssueListenerAdapter(issueListener)); + } else { + launcher.execute(analysisProperties); + } } else { + if (issueListener != null) { + throw new InvalidParameterException("Issue listeners not supported in current version: " + launcher.getVersion()); + } Properties prop = new Properties(); prop.putAll(globalProperties()); prop.putAll(analysisProperties); launcher.executeOldVersion(prop); } } + + static class IssueListenerAdapter implements org.sonar.runner.batch.IssueListener { + private IssueListener apiIssueListener; + + public IssueListenerAdapter(IssueListener apiIssueListener) { + this.apiIssueListener = apiIssueListener; + } + + @Override + public void handle(org.sonar.runner.batch.IssueListener.Issue issue) { + apiIssueListener.handle(transformIssue(issue)); + } + + private static org.sonar.runner.api.Issue transformIssue(org.sonar.runner.batch.IssueListener.Issue batchIssue) { + org.sonar.runner.api.Issue.Builder issueBuilder = org.sonar.runner.api.Issue.builder(); + + issueBuilder.setAssignee(batchIssue.getAssignee()); + issueBuilder.setComponentKey(batchIssue.getComponentKey()); + issueBuilder.setKey(batchIssue.getKey()); + issueBuilder.setLine(batchIssue.getLine()); + issueBuilder.setMessage(batchIssue.getMessage()); + issueBuilder.setNew(batchIssue.isNew()); + issueBuilder.setResolution(batchIssue.getResolution()); + issueBuilder.setRule(batchIssue.getRule()); + issueBuilder.setStatus(batchIssue.getStatus()); + + return issueBuilder.build(); + } + } + + private static class LoggerAdapter implements Logger { + private LogOutput logOutput; + + LoggerAdapter(LogOutput logOutput) { + this.logOutput = logOutput; + } + + @Override + public void warn(String msg) { + logOutput.log(msg, LogOutput.Level.WARN); + } + + @Override + public void info(String msg) { + logOutput.log(msg, LogOutput.Level.INFO); + } + + @Override + public void error(String msg, Throwable t) { + StringWriter errors = new StringWriter(); + t.printStackTrace(new PrintWriter(errors)); + logOutput.log(msg + "\n" + errors.toString(), LogOutput.Level.ERROR); + } + + @Override + public void error(String msg) { + logOutput.log(msg, LogOutput.Level.ERROR); + } + + @Override + public void debug(String msg) { + logOutput.log(msg, LogOutput.Level.DEBUG); + } + }; } diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java new file mode 100644 index 0000000..184a0b5 --- /dev/null +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java @@ -0,0 +1,188 @@ +/* + * SonarQube Runner - API + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * 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.concurrent.Immutable; + +import java.util.Date; + +@Immutable +public final class Issue { + private final String key; + private final String componentKey; + private final Integer line; + private final String message; + private final String rule; + private final String status; + private final String resolution; + private final boolean isNew; + private final String assignee; + + private Issue(String key, String componentKey, Integer line, String message, String rule, String status, String resolution, boolean isNew, String assignee) { + super(); + this.key = key; + this.componentKey = componentKey; + this.line = line; + this.message = message; + this.rule = rule; + this.status = status; + this.resolution = resolution; + this.isNew = isNew; + this.assignee = assignee; + } + + public static class Builder { + private String key; + private String componentKey; + private Integer line; + private String message; + private String rule; + private String status; + private String resolution; + private boolean isNew; + private String assignee; + + public String getKey() { + return key; + } + + public Builder setKey(String key) { + this.key = key; + return this; + } + + public String getComponentKey() { + return componentKey; + } + + public Builder setComponentKey(String componentKey) { + this.componentKey = componentKey; + return this; + } + + public Integer getLine() { + return line; + } + + public Builder setLine(Integer line) { + this.line = line; + return this; + } + + public String getMessage() { + return message; + } + + public Builder setMessage(String message) { + this.message = message; + return this; + } + + public String getRule() { + return rule; + } + + public Builder setRule(String rule) { + this.rule = rule; + return this; + } + + public String getStatus() { + return status; + } + + public Builder setStatus(String status) { + this.status = status; + return this; + } + + public String getResolution() { + return resolution; + } + + public Builder setResolution(String resolution) { + this.resolution = resolution; + return this; + } + + public boolean isNew() { + return isNew; + } + + public Builder setNew(boolean isNew) { + this.isNew = isNew; + return this; + } + + public String getAssignee() { + return assignee; + } + + public Builder setAssignee(String assignee) { + this.assignee = assignee; + return this; + } + + public Issue build() { + return new Issue(key, componentKey, line, message, rule, status, resolution, isNew, assignee); + } + } + + public static Builder builder() { + return new Builder(); + } + + public String getKey() { + return key; + } + + public String getComponentKey() { + return componentKey; + } + + public Integer getLine() { + return line; + } + + public String getMessage() { + return message; + } + + public String getRule() { + return rule; + } + + public String getStatus() { + return status; + } + + public String getResolution() { + return resolution; + } + + public boolean isNew() { + return isNew; + } + + public String getAssignee() { + return assignee; + } + +} diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListener.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListener.java new file mode 100644 index 0000000..a804a93 --- /dev/null +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListener.java @@ -0,0 +1,24 @@ +/* + * SonarQube Runner - API + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * 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; + +public interface IssueListener { + void handle(Issue issue); +} diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java index e838381..05df62c 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java @@ -19,11 +19,17 @@ */ package org.sonar.runner.api; +import org.sonar.runner.api.EmbeddedRunner.IssueListenerAdapter; + +import java.awt.geom.IllegalPathStateException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.Charset; +import java.util.LinkedList; +import java.util.List; import java.util.Properties; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,7 +38,6 @@ import org.mockito.ArgumentMatcher; import org.sonar.home.cache.Logger; import org.sonar.runner.batch.IsolatedLauncher; import org.sonar.runner.impl.IsolatedLauncherFactory; - import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; @@ -155,6 +160,40 @@ public class EmbeddedRunnerTest { })); } + @Test + public void test_issue_adapter() { + final List issuesRecorded = new LinkedList<>(); + IssueListener apiIssueListener = new IssueListener() { + @Override + public void handle(Issue issue) { + issuesRecorded.add(issue); + } + }; + IssueListenerAdapter adapter = new IssueListenerAdapter(apiIssueListener); + + org.sonar.runner.batch.IssueListener.Issue batchIssue = new org.sonar.runner.batch.IssueListener.Issue(); + batchIssue.setAssignee("assignee"); + adapter.handle(batchIssue); + + assertThat(issuesRecorded).hasSize(1); + assertThat(issuesRecorded.get(0).getAssignee()).isEqualTo("assignee"); + } + + @Test(expected = IllegalArgumentException.class) + public void reject_issue_listener_old_version() { + when(launcher.getVersion()).thenReturn("4.5"); + launch_with_issue_listener(); + } + + @Test + public void launch_with_issue_listener() { + runner.start(); + runner.runAnalysis(mock(Properties.class), mock(IssueListener.class)); + runner.stop(); + + verify(launcher).execute(any(Properties.class), any(org.sonar.runner.batch.IssueListener.class)); + } + @Test public void should_launch_batch_analysisProperties() { runner.setGlobalProperty("sonar.projectKey", "foo"); diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedLauncherFactoryTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedLauncherFactoryTest.java index a186d72..957d840 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedLauncherFactoryTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedLauncherFactoryTest.java @@ -19,13 +19,15 @@ */ package org.sonar.runner.impl; +import org.sonar.runner.batch.IssueListener; + import java.util.Properties; + import org.junit.Before; import org.junit.Test; import org.sonar.home.cache.Logger; import org.sonar.runner.batch.IsolatedLauncher; import org.sonar.runner.batch.LogOutput; - import static org.fest.assertions.Fail.fail; import static org.mockito.Mockito.mock; @@ -55,6 +57,7 @@ public class IsolatedLauncherFactoryTest { public static class FakeIsolatedLauncher implements IsolatedLauncher { public static Properties props = null; + public static IssueListener listener = null; @Override public void start(Properties properties, LogOutput logger) { @@ -77,5 +80,11 @@ public class IsolatedLauncherFactoryTest { public String getVersion() { return null; } + + @Override + public void execute(Properties properties, IssueListener listener) { + FakeIsolatedLauncher.props = properties; + FakeIsolatedLauncher.listener = listener; + } } } diff --git a/sonar-runner-batch-interface/pom.xml b/sonar-runner-batch-interface/pom.xml index 6555e66..cf19ca9 100644 --- a/sonar-runner-batch-interface/pom.xml +++ b/sonar-runner-batch-interface/pom.xml @@ -9,11 +9,4 @@ sonar-runner-batch-interface SonarQube Runner - Batch Interface - - - org.codehaus.sonar - sonar-home - provided - - diff --git a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java index 834c11b..cb09445 100644 --- a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java +++ b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java @@ -28,8 +28,11 @@ public interface IsolatedLauncher { void stop(); void execute(Properties properties); - + + void execute(Properties properties, IssueListener listener); + void executeOldVersion(Properties properties); String getVersion(); + } diff --git a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java new file mode 100644 index 0000000..72a94e4 --- /dev/null +++ b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java @@ -0,0 +1,111 @@ +/* + * SonarQube Runner - Batch Interface + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * 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.batch; + +import java.util.Date; + +public interface IssueListener { + void handle(Issue issue); + + class Issue { + private String key; + private String componentKey; + private Integer line; + private String message; + private String rule; + private String status; + private String resolution; + private boolean isNew; + private String assignee; + + public void setKey(String key) { + this.key = key; + } + + public void setComponentKey(String componentKey) { + this.componentKey = componentKey; + } + + public void setLine(Integer line) { + this.line = line; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setRule(String rule) { + this.rule = rule; + } + + public void setStatus(String status) { + this.status = status; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + + public void setNew(boolean isNew) { + this.isNew = isNew; + } + + public void setAssignee(String assignee) { + this.assignee = assignee; + } + + public String getKey() { + return key; + } + + public String getComponentKey() { + return componentKey; + } + + public Integer getLine() { + return line; + } + + public String getMessage() { + return message; + } + + public String getRule() { + return rule; + } + + public String getStatus() { + return status; + } + + public String getResolution() { + return resolution; + } + + public boolean isNew() { + return isNew; + } + + public String getAssignee() { + return assignee; + } + + } +} diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java index 6b6a710..098e929 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java @@ -54,6 +54,12 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { batch.executeTask((Map) properties); } + @Override + public void execute(Properties properties, IssueListener listener) { + org.sonar.batch.bootstrapper.IssueListener batchIssueListener = Compatibility.getBatchIssueListener(listener); + batch.executeTask((Map) properties, batchIssueListener); + } + Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput) { EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.app"), properties.getProperty("sonarRunner.appVersion")); Batch.Builder builder = Batch.builder() diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java index a2d16d0..99eb70c 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java @@ -19,11 +19,11 @@ */ package org.sonar.runner.batch; +import org.sonar.api.issue.Issue; import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.LogOutput; public class Compatibility { - private Compatibility() { // Utility class } @@ -39,4 +39,34 @@ public class Compatibility { }); } + static org.sonar.batch.bootstrapper.IssueListener getBatchIssueListener(IssueListener listener) { + return new IssueListenerAdapter(listener); + } + + static class IssueListenerAdapter implements org.sonar.batch.bootstrapper.IssueListener { + private IssueListener listener; + + public IssueListenerAdapter(IssueListener listener) { + this.listener = listener; + } + + @Override + public void handle(Issue issue) { + listener.handle(transformIssue(issue)); + } + + private static IssueListener.Issue transformIssue(Issue batchIssue) { + IssueListener.Issue newIssue = new IssueListener.Issue(); + newIssue.setAssignee(batchIssue.assignee()); + newIssue.setComponentKey(batchIssue.componentKey()); + newIssue.setKey(batchIssue.key()); + newIssue.setResolution(batchIssue.resolution()); + newIssue.setRule(batchIssue.ruleKey().toString()); + newIssue.setMessage(batchIssue.message()); + newIssue.setNew(batchIssue.isNew()); + newIssue.setLine(batchIssue.line()); + + return newIssue; + } + } } -- 2.39.5