aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2013-03-14 15:49:22 +0100
committerSimon Brandhof <simon.brandhof@gmail.com>2013-03-14 15:49:22 +0100
commit8e7ffed1b130c47fe5ac394b4f5c78dd901bff1b (patch)
treeaee2f7cf51e711f85493df84ddf2d65bed6cd7f3
parent6f752d47dbf6438e73e86acdf8d481102b04ce73 (diff)
downloadsonarqube-8e7ffed1b130c47fe5ac394b4f5c78dd901bff1b.tar.gz
sonarqube-8e7ffed1b130c47fe5ac394b4f5c78dd901bff1b.zip
SONAR-4069 fix task extension point
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java27
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java48
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java12
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/tasks/ListTask.java (renamed from sonar-batch/src/main/java/org/sonar/batch/tasks/ListTasksTask.java)24
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java101
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java33
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/tasks/ListTaskTest.java (renamed from sonar-batch/src/test/java/org/sonar/batch/tasks/ListTasksTaskTest.java)25
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/tasks/TasksTest.java126
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java7
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java112
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java4
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/task/TaskDefinitionTest.java100
15 files changed, 348 insertions, 285 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index 07f2c9fe38d..188bf6d0ee2 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -117,6 +117,13 @@ import java.util.List;
@Properties({
@Property(
+ key = CoreProperties.TASK,
+ name = "Task to be executed",
+ defaultValue = CoreProperties.SCAN_TASK,
+ module = false,
+ project = false,
+ global = false),
+ @Property(
key = CoreProperties.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
index 7afac65e0ed..bc50ede3ea2 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
@@ -19,6 +19,8 @@
*/
package org.sonar.batch.bootstrap;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.task.Task;
@@ -27,8 +29,6 @@ import org.sonar.api.task.TaskExtension;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.tasks.Tasks;
-import javax.annotation.Nullable;
-
public class TaskContainer extends ComponentContainer {
public TaskContainer(ComponentContainer parent) {
@@ -41,23 +41,24 @@ public class TaskContainer extends ComponentContainer {
installComponentsUsingTaskExtensions();
}
- public void executeTask(@Nullable String key) {
+ public void execute() {
startComponents();
- TaskDefinition taskDef = get(Tasks.class).getTaskDefinition(key);
- if (taskDef != null) {
- Task task = get(taskDef.getTask());
- if (task != null) {
- task.execute();
- } else {
- throw new IllegalStateException("Command " + key + " is badly defined.");
- }
+ // default value is declared in CorePlugin
+ String taskKey = get(Settings.class).getString(CoreProperties.TASK);
+
+ TaskDefinition def = get(Tasks.class).definition(taskKey);
+ if (def == null) {
+ throw new SonarException("Task " + taskKey + " does not exist.");
+ }
+ Task task = get(def.taskClass());
+ if (task != null) {
+ task.execute();
} else {
- throw new SonarException("Command " + key + " does not exist.");
+ throw new IllegalStateException("Task " + taskKey + " is badly defined.");
}
}
-
private void installTaskExtensions() {
get(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() {
public boolean accept(Object extension) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
index 0ea009fd64e..c4d03ea8845 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
@@ -27,7 +27,7 @@ import org.sonar.batch.bootstrap.BootstrapContainer;
import org.sonar.batch.bootstrap.BootstrapProperties;
import org.sonar.batch.bootstrap.TaskContainer;
import org.sonar.batch.scan.ScanTask;
-import org.sonar.batch.tasks.ListTasksTask;
+import org.sonar.batch.tasks.ListTask;
import org.sonar.core.PicoUtils;
import java.util.Collections;
@@ -35,7 +35,7 @@ import java.util.List;
import java.util.Map;
/**
- * Entry point for batch bootstrappers.
+ * Entry point for sonar-runner.
*
* @since 2.14
*/
@@ -43,8 +43,7 @@ public final class Batch {
private LoggingConfiguration logging;
private List<Object> components;
- private Map<String, String> globalProperties = Maps.newHashMap();
- private String taskCommand;
+ private Map<String, String> bootstrapProperties = Maps.newHashMap();
private ProjectReactor projectReactor;
private Batch(Builder builder) {
@@ -53,16 +52,15 @@ public final class Batch {
if (builder.environment != null) {
components.add(builder.environment);
}
- if (builder.globalProperties != null) {
- globalProperties.putAll(builder.globalProperties);
+ if (builder.bootstrapProperties != null) {
+ bootstrapProperties.putAll(builder.bootstrapProperties);
} else {
// For backward compatibility, previously all properties were set in root project
- globalProperties.putAll(Maps.fromProperties(builder.projectReactor.getRoot().getProperties()));
+ bootstrapProperties.putAll(Maps.fromProperties(builder.projectReactor.getRoot().getProperties()));
}
- this.taskCommand = builder.taskCommand;
projectReactor = builder.projectReactor;
if (builder.isEnableLoggingConfiguration()) {
- logging = LoggingConfiguration.create().setProperties(globalProperties);
+ logging = LoggingConfiguration.create().setProperties(bootstrapProperties);
}
}
@@ -83,29 +81,31 @@ public final class Batch {
}
private void startBatch() {
- BootstrapContainer bootstrapModule = null;
+ BootstrapContainer bootstrapContainer = null;
try {
List all = Lists.newArrayList(components);
- all.add(new BootstrapProperties(globalProperties));
- all.add(projectReactor);
+ all.add(new BootstrapProperties(bootstrapProperties));
+ if (projectReactor != null) {
+ all.add(projectReactor);
+ }
- bootstrapModule = BootstrapContainer.create(all);
- bootstrapModule.startComponents();
+ bootstrapContainer = BootstrapContainer.create(all);
+ bootstrapContainer.startComponents();
- TaskContainer taskContainer = new TaskContainer(bootstrapModule);
+ TaskContainer taskContainer = new TaskContainer(bootstrapContainer);
taskContainer.add(
ScanTask.DEFINITION, ScanTask.class,
- ListTasksTask.DEFINITION, ListTasksTask.class
+ ListTask.DEFINITION, ListTask.class
);
- taskContainer.executeTask(taskCommand);
+ taskContainer.execute();
} catch (RuntimeException e) {
PicoUtils.propagateStartupException(e);
} finally {
try {
- if (bootstrapModule != null) {
- bootstrapModule.stopComponents();
+ if (bootstrapContainer != null) {
+ bootstrapContainer.stopComponents();
}
} catch (Exception e) {
// never throw exceptions in a finally block
@@ -119,8 +119,7 @@ public final class Batch {
}
public static final class Builder {
- private Map<String, String> globalProperties;
- private String taskCommand;
+ private Map<String, String> bootstrapProperties;
private ProjectReactor projectReactor;
private EnvironmentInformation environment;
private List<Object> components = Lists.newArrayList();
@@ -145,12 +144,7 @@ public final class Batch {
}
public Builder setGlobalProperties(Map<String, String> globalProperties) {
- this.globalProperties = globalProperties;
- return this;
- }
-
- public Builder setTaskCommand(String taskCommand) {
- this.taskCommand = taskCommand;
+ this.bootstrapProperties = globalProperties;
return this;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java
index 8ce1266e456..49e25856afa 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java
@@ -19,19 +19,19 @@
*/
package org.sonar.batch.scan;
+import org.sonar.api.CoreProperties;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.task.Task;
import org.sonar.api.task.TaskDefinition;
import org.sonar.batch.bootstrap.TaskContainer;
public class ScanTask implements Task {
- public static final String COMMAND = "inspect";
- public static final TaskDefinition DEFINITION = TaskDefinition.create()
- .setDescription("Scan project and upload report to server")
- .setName("Project Scan")
- .setCommand(COMMAND)
- .setTask(ScanTask.class);
+ public static final TaskDefinition DEFINITION = TaskDefinition.builder()
+ .description("Scan project")
+ .key(CoreProperties.SCAN_TASK)
+ .taskClass(ScanTask.class)
+ .build();
private final ComponentContainer taskContainer;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/tasks/ListTasksTask.java b/sonar-batch/src/main/java/org/sonar/batch/tasks/ListTask.java
index dd08729b2cf..93002714b91 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/tasks/ListTasksTask.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/tasks/ListTask.java
@@ -22,28 +22,28 @@ package org.sonar.batch.tasks;
import org.sonar.api.task.Task;
import org.sonar.api.task.TaskDefinition;
-public class ListTasksTask implements Task {
+public class ListTask implements Task {
- public static final String COMMAND = "list-tasks";
+ public static final String KEY = "list";
- public static final TaskDefinition DEFINITION = TaskDefinition.create()
- .setDescription("List available tasks")
- .setName("List Tasks")
- .setCommand(COMMAND)
- .setTask(ListTasksTask.class);
+ public static final TaskDefinition DEFINITION = TaskDefinition.builder()
+ .key(KEY)
+ .description("List available tasks")
+ .taskClass(ListTask.class)
+ .build();
- private final Tasks taskManager;
+ private final Tasks tasks;
- public ListTasksTask(Tasks taskManager) {
- this.taskManager = taskManager;
+ public ListTask(Tasks tasks) {
+ this.tasks = tasks;
}
public void execute() {
logBlankLine();
log("Available tasks:");
logBlankLine();
- for (TaskDefinition taskDef : taskManager.getTaskDefinitions()) {
- log(" - " + taskDef.getCommand() + ": " + taskDef.getDescription());
+ for (TaskDefinition def : tasks.definitions()) {
+ log(" - " + def.key() + ": " + def.description());
}
logBlankLine();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java b/sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java
index 3c7de6d9117..6a2fa892720 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java
@@ -19,103 +19,56 @@
*/
package org.sonar.batch.tasks;
+import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
import org.sonar.api.task.Task;
import org.sonar.api.task.TaskComponent;
import org.sonar.api.task.TaskDefinition;
import org.sonar.api.utils.SonarException;
-import org.sonar.batch.scan.ScanTask;
-
-import javax.annotation.Nullable;
+import java.util.Collection;
import java.util.Map;
-import java.util.regex.Pattern;
+import java.util.SortedMap;
public class Tasks implements TaskComponent {
- private static final Logger LOG = LoggerFactory.getLogger(Tasks.class);
- private static final String COMMAND_PATTERN = "[a-zA-Z0-9\\-\\_]+";
-
- private final TaskDefinition[] taskDefinitions;
- private final Settings settings;
-
- private final Map<String, TaskDefinition> taskDefByCommand = Maps.newHashMap();
- private final Map<Class<? extends Task>, TaskDefinition> taskDefByTask = Maps.newHashMap();
+ private final SortedMap<String, TaskDefinition> byKey;
- public Tasks(Settings settings, TaskDefinition[] taskDefinitions) {
- this.settings = settings;
- this.taskDefinitions = taskDefinitions;
+ public Tasks(TaskDefinition[] definitions) {
+ SortedMap<String, TaskDefinition> map = Maps.newTreeMap();
+ for (TaskDefinition definition : definitions) {
+ if (map.containsKey(definition.key())) {
+ throw new SonarException("Task '" + definition.key() + "' is declared twice");
+ }
+ map.put(definition.key(), definition);
+ }
+ this.byKey = ImmutableSortedMap.copyOf(map);
}
- public TaskDefinition getTaskDefinition(@Nullable String command) {
- String finalCommand = StringUtils.defaultIfBlank(command, settings.getString(CoreProperties.TASK));
- finalCommand = StringUtils.defaultIfBlank(finalCommand, ScanTask.COMMAND);
-
- if (taskDefByCommand.containsKey(finalCommand)) {
- return taskDefByCommand.get(finalCommand);
- }
- throw new SonarException("No task found for command: " + finalCommand);
+ public TaskDefinition definition(String taskKey) {
+ return byKey.get(taskKey);
}
- public TaskDefinition[] getTaskDefinitions() {
- return taskDefinitions;
+ public Collection<TaskDefinition> definitions() {
+ return byKey.values();
}
/**
* Perform validation of task definitions
*/
public void start() {
- for (TaskDefinition def : taskDefinitions) {
- validateTask(def);
- validateName(def);
- validateCommand(def);
- validateDescription(def);
- }
+ checkDuplicatedClasses();
}
- private void validateName(TaskDefinition def) {
- if (StringUtils.isBlank(def.getName())) {
- throw new SonarException("Task definition for task '" + def.getTask().getName() + "' doesn't define task name");
+ private void checkDuplicatedClasses() {
+ Map<Class<? extends Task>, TaskDefinition> byClass = Maps.newHashMap();
+ for (TaskDefinition def : definitions()) {
+ TaskDefinition other = byClass.get(def.taskClass());
+ if (other == null) {
+ byClass.put(def.taskClass(), def);
+ } else {
+ throw new SonarException("Task '" + def.taskClass().getName() + "' is defined twice: first by '" + other.key() + "' and then by '" + def.key() + "'");
+ }
}
-
}
-
- private void validateCommand(TaskDefinition def) {
- String command = def.getCommand();
- if (StringUtils.isBlank(command)) {
- throw new SonarException("Task definition '" + def.getName() + "' doesn't define task command");
- }
- if (!Pattern.matches(COMMAND_PATTERN, command)) {
- throw new SonarException("Command '" + command + "' for task definition '" + def.getName() + "' is not valid and should match " + COMMAND_PATTERN);
- }
- if (taskDefByCommand.containsKey(command)) {
- throw new SonarException("Task '" + def.getName() + "' uses the same command than task '" + taskDefByCommand.get(command).getName() + "'");
- }
- taskDefByCommand.put(command, def);
- }
-
- private void validateDescription(TaskDefinition def) {
- if (StringUtils.isBlank(def.getDescription())) {
- LOG.warn("Task definition {} doesn't define a description. Using name as description.", def.getName());
- def.setDescription(def.getName());
- }
- }
-
- private void validateTask(TaskDefinition def) {
- Class<? extends Task> taskClass = def.getTask();
- if (taskClass == null) {
- throw new SonarException("Task definition '" + def.getName() + "' doesn't define the associated task class");
- }
- if (taskDefByTask.containsKey(taskClass)) {
- throw new SonarException("Task '" + def.getTask().getName() + "' is defined twice: first by '" + taskDefByTask.get(taskClass).getName() + "' and then by '" + def.getName()
- + "'");
- }
- taskDefByTask.put(taskClass, def);
- }
-
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java
new file mode 100644
index 00000000000..fcf4c52244a
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java
@@ -0,0 +1,33 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan;
+
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ScanTaskTest {
+ @Test
+ public void test_definition() {
+ assertThat(ScanTask.DEFINITION).isNotNull();
+ assertThat(ScanTask.DEFINITION.key()).isEqualTo(CoreProperties.SCAN_TASK);
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/tasks/ListTasksTaskTest.java b/sonar-batch/src/test/java/org/sonar/batch/tasks/ListTaskTest.java
index d66c0c688ed..0848ffc6894 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/tasks/ListTasksTaskTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/tasks/ListTaskTest.java
@@ -20,25 +20,42 @@
package org.sonar.batch.tasks;
import org.junit.Test;
+import org.sonar.api.task.Task;
import org.sonar.api.task.TaskDefinition;
+import java.util.Arrays;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-public class ListTasksTaskTest {
+public class ListTaskTest {
@Test
public void should_list_available_tasks() {
- TaskDefinition def = TaskDefinition.create().setCommand("purge").setName("Purge").setDescription("Purge database");
Tasks tasks = mock(Tasks.class);
- when(tasks.getTaskDefinitions()).thenReturn(new TaskDefinition[]{def});
- ListTasksTask task = spy(new ListTasksTask(tasks));
+ when(tasks.definitions()).thenReturn(Arrays.asList(
+ TaskDefinition.builder().key("foo").description("Foo").taskClass(FooTask.class).build(),
+ TaskDefinition.builder().key("purge").description("Purge database").taskClass(FakePurgeTask.class).build()
+ ));
+
+ ListTask task = spy(new ListTask(tasks));
task.execute();
verify(task, times(1)).log("Available tasks:");
+ verify(task, times(1)).log(" - foo: Foo");
verify(task, times(1)).log(" - purge: Purge database");
}
+
+ private static class FakePurgeTask implements Task {
+ public void execute() {
+ }
+ }
+
+ private static class FooTask implements Task {
+ public void execute() {
+ }
+ }
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/tasks/TasksTest.java b/sonar-batch/src/test/java/org/sonar/batch/tasks/TasksTest.java
index 102576ec196..a2e394d9b1b 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/tasks/TasksTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/tasks/TasksTest.java
@@ -19,12 +19,9 @@
*/
package org.sonar.batch.tasks;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
import org.sonar.api.task.Task;
import org.sonar.api.task.TaskDefinition;
import org.sonar.api.utils.SonarException;
@@ -37,115 +34,43 @@ public class TasksTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
- private Settings settings;
-
- @Before
- public void prepare() {
- settings = new Settings();
- }
-
- @Test
- public void shouldReturnTaskDefinitions() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {ScanTask.DEFINITION, ListTasksTask.DEFINITION});
- assertThat(tasks.getTaskDefinitions().length).isEqualTo(2);
- }
-
- @Test
- public void shouldReturnInspectionTask() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {ScanTask.DEFINITION, ListTasksTask.DEFINITION});
- tasks.start();
- assertThat(tasks.getTaskDefinition(ScanTask.COMMAND)).isEqualTo(ScanTask.DEFINITION);
- }
-
- @Test
- public void shouldReturnInspectionTaskByDefault() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {ScanTask.DEFINITION, ListTasksTask.DEFINITION});
- tasks.start();
- assertThat(tasks.getTaskDefinition(null)).isEqualTo(ScanTask.DEFINITION);
- }
-
- @Test
- public void shouldReturnUsePropertyWhenNoCommand() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {ScanTask.DEFINITION, ListTasksTask.DEFINITION});
- tasks.start();
- assertThat(tasks.getTaskDefinition(ListTasksTask.COMMAND)).isEqualTo(ListTasksTask.DEFINITION);
- assertThat(tasks.getTaskDefinition(null)).isEqualTo(ScanTask.DEFINITION);
-
- settings.setProperty(CoreProperties.TASK, ListTasksTask.COMMAND);
- assertThat(tasks.getTaskDefinition(null)).isEqualTo(ListTasksTask.DEFINITION);
- assertThat(tasks.getTaskDefinition(ScanTask.COMMAND)).isEqualTo(ScanTask.DEFINITION);
- }
-
- @Test
- public void shouldThrowWhenCommandNotFound() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {ScanTask.DEFINITION, ListTasksTask.DEFINITION});
-
- thrown.expect(SonarException.class);
- thrown.expectMessage("No task found for command: not-exists");
-
- tasks.getTaskDefinition("not-exists");
- }
-
- @Test
- public void shouldThrowWhenCommandMissing() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setName("foo").setTask(FakeTask1.class)});
-
- thrown.expect(SonarException.class);
- thrown.expectMessage("Task definition 'foo' doesn't define task command");
-
- tasks.start();
- }
-
@Test
- public void shouldThrowWhenCommandInvalid() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setName("foo").setTask(FakeTask1.class).setCommand("Azc-12_bbC")});
- tasks.start();
-
- tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setName("foo").setTask(FakeTask1.class).setCommand("with space")});
-
- thrown.expect(SonarException.class);
- thrown.expectMessage("Command 'with space' for task definition 'foo' is not valid and should match [a-zA-Z0-9\\-\\_]+");
-
- tasks.start();
+ public void should_get_definitions() {
+ Tasks tasks = new Tasks(new TaskDefinition[]{ScanTask.DEFINITION, ListTask.DEFINITION});
+ assertThat(tasks.definitions()).hasSize(2);
}
@Test
- public void shouldThrowWhenDuplicateCommand() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {
- TaskDefinition.create().setName("foo1").setTask(FakeTask1.class).setCommand("cmd"),
- TaskDefinition.create().setName("foo2").setTask(FakeTask2.class).setCommand("cmd")});
-
- thrown.expect(SonarException.class);
- thrown.expectMessage("Task 'foo2' uses the same command than task 'foo1'");
-
+ public void should_get_definition_by_key() {
+ Tasks tasks = new Tasks(new TaskDefinition[]{ScanTask.DEFINITION, ListTask.DEFINITION});
tasks.start();
+ assertThat(tasks.definition(ListTask.DEFINITION.key())).isEqualTo(ListTask.DEFINITION);
}
@Test
- public void shouldThrowWhenNameMissing() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setCommand("foo").setTask(FakeTask1.class)});
+ public void should_return_null_if_task_not_found() {
+ Tasks tasks = new Tasks(new TaskDefinition[]{ScanTask.DEFINITION, ListTask.DEFINITION});
- thrown.expect(SonarException.class);
- thrown.expectMessage("Task definition for task 'org.sonar.batch.tasks.TasksTest$FakeTask1' doesn't define task name");
-
- tasks.start();
+ assertThat(tasks.definition("not-exists")).isNull();
}
@Test
- public void shouldThrowWhenTaskMissing() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setCommand("foo").setName("bar")});
-
+ public void should_fail_on_duplicated_keys() {
thrown.expect(SonarException.class);
- thrown.expectMessage("Task definition 'bar' doesn't define the associated task class");
+ thrown.expectMessage("Task 'foo' is declared twice");
- tasks.start();
+ new Tasks(new TaskDefinition[]{
+ TaskDefinition.builder().key("foo").taskClass(FakeTask1.class).description("foo1").build(),
+ TaskDefinition.builder().key("foo").taskClass(FakeTask2.class).description("foo2").build()
+ });
}
@Test
- public void shouldThrowWhenDuplicateTask() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {
- TaskDefinition.create().setName("foo1").setTask(FakeTask1.class).setCommand("cmd1"),
- TaskDefinition.create().setName("foo2").setTask(FakeTask1.class).setCommand("cmd2")});
+ public void should_fail_on_duplicated_class() {
+ Tasks tasks = new Tasks(new TaskDefinition[]{
+ TaskDefinition.builder().key("foo1").taskClass(FakeTask1.class).description("foo1").build(),
+ TaskDefinition.builder().key("foo2").taskClass(FakeTask1.class).description("foo1").build()
+ });
thrown.expect(SonarException.class);
thrown.expectMessage("Task 'org.sonar.batch.tasks.TasksTest$FakeTask1' is defined twice: first by 'foo1' and then by 'foo2'");
@@ -153,23 +78,12 @@ public class TasksTest {
tasks.start();
}
- @Test
- public void shouldUseNameWhenDescriptionIsMissing() {
- Tasks tasks = new Tasks(settings, new TaskDefinition[] {TaskDefinition.create().setName("foo").setCommand("cmd").setTask(FakeTask1.class)});
- tasks.start();
-
- assertThat(tasks.getTaskDefinition("cmd").getDescription()).isEqualTo("foo");
- }
-
private static class FakeTask1 implements Task {
-
public void execute() {
}
-
}
private static class FakeTask2 implements Task {
-
public void execute() {
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index fdacc52dfbd..6d51b51a2a8 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -344,8 +344,13 @@ public interface CoreProperties {
String TASK = "sonar.task";
/**
+ * @since 3.6
+ */
+ String SCAN_TASK = "scan";
+
+ /**
* @deprecated replaced in v3.4 by properties specific to languages, for example sonar.java.coveragePlugin
- * See http://jira.codehaus.org/browse/SONARJAVA-39 for more details.
+ * See http://jira.codehaus.org/browse/SONARJAVA-39 for more details.
*/
@Deprecated
String CORE_COVERAGE_PLUGIN_PROPERTY = "sonar.core.codeCoveragePlugin";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java b/sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java
index 42bfdddcaac..e705c2725b6 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java
@@ -22,7 +22,7 @@ package org.sonar.api.task;
/**
* Implement this interface to provide the behavior of a task.
- * @since 3.5
+ * @since 3.6
*/
public interface Task extends TaskExtension {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java
index 346b8bcf351..7cf26d1b132 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java
@@ -20,10 +20,9 @@
package org.sonar.api.task;
/**
- * Dependency Injection : all the classes implementing this interface are available in the task IoC container.
- * Just add a parameter to the constructor of your component.
+ * All the classes implementing this interface can be injected in public constructors of {@link TaskExtension}.
*
- * @since 3.5
+ * @since 3.6
*/
public interface TaskComponent {
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java
index 2d7d595bd78..1fba731dd49 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java
@@ -19,64 +19,104 @@
*/
package org.sonar.api.task;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import java.util.regex.Pattern;
+
/**
- * Implement this interface to provide a new task.
- * @since 3.5
+ * Register and describe a {@link TaskExtension}.
+ *
+ * @since 3.6
*/
-public class TaskDefinition implements TaskComponent {
+public class TaskDefinition implements TaskExtension, Comparable<TaskDefinition> {
+ static final String KEY_PATTERN = "[a-zA-Z0-9\\-\\_]+";
- private String name;
- private String description;
- private String command;
- private Class<? extends Task> task;
-
- private TaskDefinition() {
+ private final String key;
+ private final String description;
+ private final Class<? extends Task> taskClass;
+ private TaskDefinition(Builder builder) {
+ this.key = builder.key;
+ this.description = builder.description;
+ this.taskClass = builder.taskClass;
}
- public static TaskDefinition create() {
- return new TaskDefinition();
+ public String description() {
+ return description;
}
- public String getName() {
- return name;
+ public String key() {
+ return key;
}
- public TaskDefinition setName(String name) {
- this.name = name;
- return this;
+ public Class<? extends Task> taskClass() {
+ return taskClass;
}
- public String getDescription() {
- return description;
+ @Override
+ public String toString() {
+ return "Task " + key + "[class=" + taskClass.getName() + ", desc=" + description + "]";
}
- public TaskDefinition setDescription(String description) {
- this.description = description;
- return this;
+ public static Builder builder() {
+ return new Builder();
}
- public String getCommand() {
- return command;
- }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
- public TaskDefinition setCommand(String command) {
- this.command = command;
- return this;
+ TaskDefinition that = (TaskDefinition) o;
+ if (!key.equals(that.key)) {
+ return false;
+ }
+ return true;
}
- public Class<? extends Task> getTask() {
- return task;
+ @Override
+ public int hashCode() {
+ return key.hashCode();
}
- public TaskDefinition setTask(Class<? extends Task> task) {
- this.task = task;
- return this;
+ public int compareTo(TaskDefinition o) {
+ return key.compareTo(o.key);
}
- @Override
- public String toString() {
- return "Definition of task " + task + " with command " + command;
- }
+ public static class Builder {
+ private String key;
+ private String description;
+ private Class<? extends Task> taskClass;
+
+ private Builder() {
+ }
+ public Builder key(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public Builder description(String s) {
+ this.description = s;
+ return this;
+ }
+
+ public Builder taskClass(Class<? extends Task> taskClass) {
+ this.taskClass = taskClass;
+ return this;
+ }
+
+ public TaskDefinition build() {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "Task key must be set");
+ Preconditions.checkArgument(Pattern.matches(KEY_PATTERN, key), "Task key '" + key + "' must match " + KEY_PATTERN);
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(description), "Description must be set for task '" + key + "'");
+ Preconditions.checkArgument(taskClass != null, "Class must be set for task '" + key + "'");
+ return new TaskDefinition(this);
+ }
+ }
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java
index 92c3c7230de..8406650ef79 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java
@@ -22,9 +22,9 @@ package org.sonar.api.task;
import org.sonar.api.Extension;
/**
- * Task extension point.
+ * Task extension point
*
- * @since 3.5
+ * @since 3.6
*/
public interface TaskExtension extends Extension, TaskComponent {
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/task/TaskDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/task/TaskDefinitionTest.java
new file mode 100644
index 00000000000..dcd581946aa
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/task/TaskDefinitionTest.java
@@ -0,0 +1,100 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.task;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class TaskDefinitionTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void should_build() {
+ TaskDefinition def = TaskDefinition.builder().key("foo").taskClass(FooTask.class).description("Foo").build();
+ assertThat(def.key()).isEqualTo("foo");
+ assertThat(def.description()).isEqualTo("Foo");
+ assertThat(def.taskClass()).isEqualTo(FooTask.class);
+ assertThat(def.toString()).isEqualTo("Task foo[class=org.sonar.api.task.TaskDefinitionTest$FooTask, desc=Foo]");
+ }
+
+ @Test
+ public void test_equals_and_hashcode() {
+ TaskDefinition def1 = TaskDefinition.builder().key("one").taskClass(FooTask.class).description("Foo").build();
+ TaskDefinition def1bis = TaskDefinition.builder().key("one").taskClass(FooTask.class).description("Foo").build();
+ TaskDefinition def2 = TaskDefinition.builder().key("two").taskClass(FooTask.class).description("Foo").build();
+
+ assertThat(def1).isEqualTo(def1);
+ assertThat(def1).isEqualTo(def1bis);
+ assertThat(def2).isNotEqualTo(def1);
+ assertThat(def2).isNotEqualTo("one");
+ assertThat(def2).isNotEqualTo(null);
+
+ assertThat(def1.hashCode()).isEqualTo(def1.hashCode());
+ assertThat(def1.hashCode()).isEqualTo(def1bis.hashCode());
+ }
+
+ @Test
+ public void test_compare() {
+ TaskDefinition foo = TaskDefinition.builder().key("foo").taskClass(FooTask.class).description("Foo").build();
+ TaskDefinition bar = TaskDefinition.builder().key("bar").taskClass(FooTask.class).description("Bar").build();
+
+ assertThat(foo.compareTo(bar)).isGreaterThan(0);
+ assertThat(foo.compareTo(foo)).isEqualTo(0);
+ assertThat(bar.compareTo(foo)).isLessThan(0);
+ }
+
+ @Test
+ public void description_should_be_required() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Description must be set for task 'foo'");
+ TaskDefinition.builder().key("foo").taskClass(FooTask.class).build();
+ }
+
+ @Test
+ public void key_should_be_required() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Task key must be set");
+ TaskDefinition.builder().description("Foo").taskClass(FooTask.class).build();
+ }
+
+ @Test
+ public void key_should_not_contain_spaces() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Task key 'fo o' must match " + TaskDefinition.KEY_PATTERN);
+ TaskDefinition.builder().key("fo o").description("foo").taskClass(FooTask.class).build();
+ }
+
+ @Test
+ public void class_should_be_required() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Class must be set for task 'foo'");
+ TaskDefinition.builder().key("foo").description("Foo").build();
+ }
+
+ private static class FooTask implements Task {
+ public void execute() {
+ }
+ }
+}