]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7023 Restore task API 634/head
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 16 Nov 2015 17:10:44 +0000 (18:10 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 17 Nov 2015 17:18:21 +0000 (18:18 +0100)
20 files changed:
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/task/package-info.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java
sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/task/TasksTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java
sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java

index c0dccbeb4120cb8a5e3560bb82f1cf74ea9259a3..526d2aada6c95a33b089062e9d2215ceb9f8b5cd 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
-import org.sonar.batch.issue.tracking.TrackedIssue;
-
-import org.sonar.batch.issue.tracking.ServerIssueFromWs;
-import org.sonar.core.issue.tracking.Tracker;
 import com.google.common.collect.Lists;
-
 import java.util.Collection;
 import java.util.List;
-
 import org.sonar.batch.cpd.CpdComponents;
+import org.sonar.batch.issue.tracking.ServerIssueFromWs;
+import org.sonar.batch.issue.tracking.TrackedIssue;
 import org.sonar.batch.scan.report.ConsoleReport;
 import org.sonar.batch.scan.report.HtmlReport;
 import org.sonar.batch.scan.report.IssuesReportBuilder;
@@ -39,8 +35,13 @@ import org.sonar.batch.scm.ScmConfiguration;
 import org.sonar.batch.scm.ScmSensor;
 import org.sonar.batch.source.CodeColorizerSensor;
 import org.sonar.batch.source.LinesSensor;
+import org.sonar.batch.task.ListTask;
+import org.sonar.batch.task.ScanTask;
+import org.sonar.batch.task.Tasks;
+import org.sonar.batch.task.ViewsTask;
 import org.sonar.core.component.DefaultResourceTypes;
 import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.core.issue.tracking.Tracker;
 
 public class BatchComponents {
   private BatchComponents() {
@@ -66,8 +67,16 @@ public class BatchComponents {
       HtmlReport.class,
       IssuesReportBuilder.class,
       SourceProvider.class,
-      RuleNameProvider.class
-      );
+      RuleNameProvider.class,
+
+      // Tasks
+      Tasks.class,
+      ListTask.DEFINITION,
+      ListTask.class,
+      ScanTask.DEFINITION,
+      ScanTask.class,
+      ViewsTask.DEFINITION,
+      ViewsTask.class);
     components.addAll(CorePropertyDefinitions.all());
     // CPD
     components.addAll(CpdComponents.all());
index 9df2d26f876490afa88e773785d130e97f455fc7..035c7f489a9b8bcb82d493e32d68ce484478cda2 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
-import com.github.kevinsawicki.http.HttpRequest;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.List;
 import java.util.Map;
-import javax.annotation.CheckForNull;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.SonarPlugin;
-import org.sonar.api.platform.Server;
-import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.UriReader;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.batch.analysis.AnalysisProperties;
-import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.cache.GlobalPersistentCacheProvider;
 import org.sonar.batch.cache.ProjectSyncContainer;
 import org.sonar.batch.cache.StrategyWSLoaderProvider;
@@ -44,7 +33,7 @@ import org.sonar.batch.platform.DefaultServer;
 import org.sonar.batch.repository.DefaultGlobalRepositoriesLoader;
 import org.sonar.batch.repository.GlobalRepositoriesLoader;
 import org.sonar.batch.repository.GlobalRepositoriesProvider;
-import org.sonar.batch.scan.ProjectScanContainer;
+import org.sonar.batch.task.TaskContainer;
 import org.sonar.core.platform.ComponentContainer;
 import org.sonar.core.platform.PluginClassloaderFactory;
 import org.sonar.core.platform.PluginInfo;
@@ -53,12 +42,8 @@ import org.sonar.core.platform.PluginRepository;
 import org.sonar.core.util.DefaultHttpDownloader;
 import org.sonar.core.util.UuidFactoryImpl;
 
-import static java.lang.String.format;
-
 public class GlobalContainer extends ComponentContainer {
 
-  private static final org.sonar.api.utils.log.Logger LOG = Loggers.get(GlobalContainer.class);
-
   private final Map<String, String> bootstrapProperties;
   private boolean preferCache;
 
@@ -133,71 +118,12 @@ public class GlobalContainer extends ComponentContainer {
     }
   }
 
-  public void executeAnalysis(Map<String, String> analysisProperties, Object... components) {
-    GlobalProperties globalProperties = this.getComponentByType(GlobalProperties.class);
-    // SONAR-6888
-    String task = analysisProperties.get(CoreProperties.TASK);
-    if ("views".equals(task)) {
-      triggerViews(this.getComponentByType(ServerClient.class), this.getComponentByType(Server.class));
-      return;
-    }
-    if (StringUtils.isNotBlank(task) && !CoreProperties.SCAN_TASK.equals(task)) {
-      throw MessageException.of("Tasks are no more supported on batch side since SonarQube 5.2");
-    }
-
-    AnalysisProperties props = new AnalysisProperties(analysisProperties, globalProperties.property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
-    if (isIssuesMode(props)) {
-      String projectKey = getProjectKeyWithBranch(props);
-      new ProjectSyncContainer(this, projectKey, false).execute();
-    }
-    new ProjectScanContainer(this, props, components).execute();
-  }
-
-  private static void triggerViews(ServerClient serverClient, Server server) {
-    LOG.info("Trigger Views update");
-    URL url;
-    try {
-      url = new URL(serverClient.getURL() + "/api/views/run");
-    } catch (MalformedURLException e) {
-      throw new IllegalArgumentException("Invalid URL", e);
-    }
-    HttpRequest request = HttpRequest.post(url);
-    request.trustAllCerts();
-    request.trustAllHosts();
-    request.header("User-Agent", format("SonarQube %s", server.getVersion()));
-    request.basic(serverClient.getLogin(), serverClient.getPassword());
-    if (!request.ok()) {
-      int responseCode = request.code();
-      if (responseCode == 401) {
-        throw new IllegalStateException(format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
-      }
-      if (responseCode == 409) {
-        throw new IllegalStateException("A full refresh of Views is already queued or running");
-      }
-      if (responseCode == 403) {
-        // SONAR-4397 Details are in response content
-        throw new IllegalStateException(request.body());
-      }
-      throw new IllegalStateException(format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
-    }
-  }
-
-  @CheckForNull
-  private static String getProjectKeyWithBranch(AnalysisProperties props) {
-    String projectKey = props.property(CoreProperties.PROJECT_KEY_PROPERTY);
-    if (projectKey != null && props.property(CoreProperties.PROJECT_BRANCH_PROPERTY) != null) {
-      projectKey = projectKey + ":" + props.property(CoreProperties.PROJECT_BRANCH_PROPERTY);
-    }
-    return projectKey;
+  public void executeTask(Map<String, String> taskProperties, Object... components) {
+    new TaskContainer(this, taskProperties, components).execute();
   }
 
   public void syncProject(String projectKey, boolean force) {
     new ProjectSyncContainer(this, projectKey, force).execute();
   }
 
-  private boolean isIssuesMode(AnalysisProperties props) {
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(this.getComponentByType(GlobalProperties.class), props);
-    return mode.isIssues();
-  }
-
 }
index 116272c6a20fe070c6b02c667d0baf6af4534040..a640c0e27fb0abce6141551e412c6bf7adf61f1e 100644 (file)
@@ -107,7 +107,7 @@ public final class Batch {
    */
   public Batch executeTask(Map<String, String> analysisProperties, Object... components) {
     checkStarted();
-    bootstrapContainer.executeAnalysis(analysisProperties, components);
+    bootstrapContainer.executeTask(analysisProperties, components);
     return this;
   }
 
@@ -116,7 +116,7 @@ public final class Batch {
    */
   public Batch executeTask(Map<String, String> analysisProperties, IssueListener issueListener) {
     checkStarted();
-    bootstrapContainer.executeAnalysis(analysisProperties, components, issueListener);
+    bootstrapContainer.executeTask(analysisProperties, components, issueListener);
     return this;
   }
 
index 55a10b6fd25538d96e727e6351bd4907d50dde9a..b694beb38cf0fea059641f30f2d94e999d4dae3c 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.batch.scan;
 
-import org.sonar.batch.issue.DefaultProjectIssues;
-
 import com.google.common.annotations.VisibleForTesting;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.InstantiationStrategy;
@@ -49,6 +47,7 @@ import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.index.Caches;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.issue.DefaultIssueCallback;
+import org.sonar.batch.issue.DefaultProjectIssues;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader;
 import org.sonar.batch.issue.tracking.IssueTransition;
@@ -99,20 +98,15 @@ public class ProjectScanContainer extends ComponentContainer {
 
   private static final Logger LOG = Loggers.get(ProjectScanContainer.class);
 
-  private final Object[] components;
   private final AnalysisProperties props;
 
-  public ProjectScanContainer(ComponentContainer globalContainer, AnalysisProperties props, Object... components) {
+  public ProjectScanContainer(ComponentContainer globalContainer, AnalysisProperties props) {
     super(globalContainer);
     this.props = props;
-    this.components = components;
   }
 
   @Override
   protected void doBeforeStart() {
-    for (Object component : components) {
-      add(component);
-    }
     addBatchComponents();
     getComponentByType(ProjectLock.class).tryLock();
     addBatchExtensions();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java b/sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java
new file mode 100644 (file)
index 0000000..2b5e63a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+public class ListTask implements Task {
+
+  private static final Logger LOG = Loggers.get(ListTask.class);
+
+  public static final String KEY = "list";
+
+  public static final TaskDefinition DEFINITION = TaskDefinition.builder()
+    .key(KEY)
+    .description("List available tasks")
+    .taskClass(ListTask.class)
+    .build();
+
+  private final Tasks tasks;
+
+  public ListTask(Tasks tasks) {
+    this.tasks = tasks;
+  }
+
+  @Override
+  public void execute() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("\nAvailable tasks:\n");
+    for (TaskDefinition def : tasks.definitions()) {
+      sb.append("  - " + def.key() + ": " + def.description() + "\n");
+    }
+    sb.append("\n");
+    LOG.info(sb.toString());
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java b/sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java
new file mode 100644 (file)
index 0000000..87e8647
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import javax.annotation.CheckForNull;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.cache.ProjectSyncContainer;
+import org.sonar.batch.scan.ProjectScanContainer;
+import org.sonar.core.platform.ComponentContainer;
+
+public class ScanTask implements Task {
+  public static final TaskDefinition DEFINITION = TaskDefinition.builder()
+    .description("Scan project")
+    .key(CoreProperties.SCAN_TASK)
+    .taskClass(ScanTask.class)
+    .build();
+
+  private final ComponentContainer taskContainer;
+  private final TaskProperties taskProps;
+
+  public ScanTask(TaskContainer taskContainer, TaskProperties taskProps) {
+    this.taskContainer = taskContainer;
+    this.taskProps = taskProps;
+  }
+
+  @Override
+  public void execute() {
+    AnalysisProperties props = new AnalysisProperties(taskProps.properties(), taskProps.property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
+    if (isIssuesMode(props)) {
+      String projectKey = getProjectKeyWithBranch(props);
+      new ProjectSyncContainer(taskContainer, projectKey, false).execute();
+    }
+    new ProjectScanContainer(taskContainer, props).execute();
+  }
+
+  @CheckForNull
+  private static String getProjectKeyWithBranch(AnalysisProperties props) {
+    String projectKey = props.property(CoreProperties.PROJECT_KEY_PROPERTY);
+    if (projectKey != null && props.property(CoreProperties.PROJECT_BRANCH_PROPERTY) != null) {
+      projectKey = projectKey + ":" + props.property(CoreProperties.PROJECT_BRANCH_PROPERTY);
+    }
+    return projectKey;
+  }
+
+  private boolean isIssuesMode(AnalysisProperties props) {
+    DefaultAnalysisMode mode = new DefaultAnalysisMode(taskContainer.getComponentByType(GlobalProperties.class), props);
+    return mode.isIssues();
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java
new file mode 100644 (file)
index 0000000..d4518f2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.config.EmailSettings;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.bootstrap.ExtensionInstaller;
+import org.sonar.batch.bootstrap.ExtensionMatcher;
+import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.core.platform.ComponentContainer;
+
+public class TaskContainer extends ComponentContainer {
+
+  private final Map<String, String> taskProperties;
+  private final Object[] components;
+
+  public TaskContainer(ComponentContainer parent, Map<String, String> taskProperties, Object... components) {
+    super(parent);
+    this.taskProperties = taskProperties;
+    this.components = components;
+  }
+
+  @Override
+  protected void doBeforeStart() {
+    addTaskExtensions();
+    addCoreComponents();
+    for (Object component : components) {
+      add(component);
+    }
+  }
+
+  private void addCoreComponents() {
+    add(new TaskProperties(taskProperties, getParent().getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)));
+    add(EmailSettings.class);
+  }
+
+  private void addTaskExtensions() {
+    getComponentByType(ExtensionInstaller.class).install(this, new TaskExtensionFilter());
+  }
+
+  static class TaskExtensionFilter implements ExtensionMatcher {
+    @Override
+    public boolean accept(Object extension) {
+      return ExtensionUtils.isBatchSide(extension)
+        && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_TASK);
+    }
+  }
+
+  @Override
+  public void doAfterStart() {
+    // default value is declared in CorePlugin
+    String taskKey = StringUtils.defaultIfEmpty(taskProperties.get(CoreProperties.TASK), CoreProperties.SCAN_TASK);
+    // Release memory
+    taskProperties.clear();
+
+    TaskDefinition def = getComponentByType(Tasks.class).definition(taskKey);
+    if (def == null) {
+      throw MessageException.of("Task '" + taskKey + "' does not exist. Please use '" + ListTask.KEY + "' task to see all available tasks.");
+    }
+    Task task = getComponentByType(def.taskClass());
+    if (task != null) {
+      task.execute();
+    } else {
+      throw new IllegalStateException("Task " + taskKey + " is badly defined");
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java b/sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java
new file mode 100644 (file)
index 0000000..abfdcc5
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.sonar.batch.bootstrap.UserProperties;
+
+/**
+ * Batch properties that are specific to a task (for example
+ * coming from sonar-project.properties).
+ */
+public class TaskProperties extends UserProperties {
+
+  public TaskProperties(Map<String, String> properties, @Nullable String pathToSecretKey) {
+    super(properties, pathToSecretKey);
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java b/sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java
new file mode 100644 (file)
index 0000000..55f9cdd
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.Map;
+import java.util.SortedMap;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+
+@BatchSide
+@InstantiationStrategy(InstantiationStrategy.PER_TASK)
+public class Tasks {
+
+  private final SortedMap<String, TaskDefinition> byKey;
+
+  public Tasks(TaskDefinition[] definitions) {
+    SortedMap<String, TaskDefinition> map = Maps.newTreeMap();
+    for (TaskDefinition definition : definitions) {
+      if (map.containsKey(definition.key())) {
+        throw new IllegalStateException("Task '" + definition.key() + "' is declared twice");
+      }
+      map.put(definition.key(), definition);
+    }
+    this.byKey = ImmutableSortedMap.copyOf(map);
+  }
+
+  public TaskDefinition definition(String taskKey) {
+    return byKey.get(taskKey);
+  }
+
+  public Collection<TaskDefinition> definitions() {
+    return byKey.values();
+  }
+
+  /**
+   * Perform validation of task definitions
+   */
+  public void start() {
+    checkDuplicatedClasses();
+  }
+
+  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 IllegalStateException("Task '" + def.taskClass().getName() + "' is defined twice: first by '" + other.key() + "' and then by '" + def.key() + "'");
+      }
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java b/sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java
new file mode 100644 (file)
index 0000000..c9d8bd1
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import com.github.kevinsawicki.http.HttpRequest;
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.platform.Server;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.batch.bootstrap.ServerClient;
+
+import static java.lang.String.format;
+
+public class ViewsTask implements Task {
+
+  private static final Logger LOG = Loggers.get(ViewsTask.class);
+
+  public static final String KEY = "views";
+
+  public static final TaskDefinition DEFINITION = TaskDefinition.builder()
+    .key(KEY)
+    .description("Trigger Views update")
+    .taskClass(ViewsTask.class)
+    .build();
+
+  private final ServerClient serverClient;
+  private final Server server;
+
+  public ViewsTask(ServerClient serverClient, Server server) {
+    this.serverClient = serverClient;
+    this.server = server;
+  }
+
+  @Override
+  public void execute() {
+    LOG.info("Trigger Views update");
+    URL url;
+    try {
+      url = new URL(serverClient.getURL() + "/api/views/run");
+    } catch (MalformedURLException e) {
+      throw new IllegalArgumentException("Invalid URL", e);
+    }
+    HttpRequest request = HttpRequest.post(url);
+    request.trustAllCerts();
+    request.trustAllHosts();
+    request.header("User-Agent", format("SonarQube %s", server.getVersion()));
+    request.basic(serverClient.getLogin(), serverClient.getPassword());
+    if (!request.ok()) {
+      int responseCode = request.code();
+      if (responseCode == 401) {
+        throw new IllegalStateException(format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD));
+      }
+      if (responseCode == 409) {
+        throw new IllegalStateException("A full refresh of Views is already queued or running");
+      }
+      if (responseCode == 403) {
+        // SONAR-4397 Details are in response content
+        throw new IllegalStateException(request.body());
+      }
+      throw new IllegalStateException(format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/task/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/task/package-info.java
new file mode 100644 (file)
index 0000000..72ec1c3
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.task;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index 7c9358509c3f2c6a04c44d54a53686465229b08d..9d422c1b1bab81f179d67d748645864f33b68e65 100644 (file)
@@ -40,8 +40,8 @@ public class ProjectScanContainerTest {
     assertThat(filter.accept(MyProjectExtension.class)).isFalse();
     assertThat(filter.accept(new MyServerExtension())).isFalse();
     assertThat(filter.accept(MyServerExtension.class)).isFalse();
-    assertThat(filter.accept(new MyTaskExtension())).isTrue();
-    assertThat(filter.accept(MyTaskExtension.class)).isTrue();
+    assertThat(filter.accept(new MyTaskExtension())).isFalse();
+    assertThat(filter.accept(MyTaskExtension.class)).isFalse();
   }
 
   @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
diff --git a/sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java b/sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java
new file mode 100644 (file)
index 0000000..ccc22dd
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ListTaskTest {
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  @Test
+  public void should_list_available_tasks() {
+    Tasks tasks = mock(Tasks.class);
+    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 = new ListTask(tasks);
+
+    task.execute();
+
+    assertThat(logTester.logs(LoggerLevel.INFO)).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.INFO).get(0)).contains("Available tasks:", "  - foo: Foo", "  - 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/task/TasksTest.java b/sonar-batch/src/test/java/org/sonar/batch/task/TasksTest.java
new file mode 100644 (file)
index 0000000..4857b18
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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  02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TasksTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void should_get_definitions() {
+    Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION});
+    assertThat(tasks.definitions()).hasSize(2);
+  }
+
+  @Test
+  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 should_return_null_if_task_not_found() {
+    Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION});
+
+    assertThat(tasks.definition("not-exists")).isNull();
+  }
+
+  @Test
+  public void should_fail_on_duplicated_keys() {
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Task 'foo' is declared twice");
+
+    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 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(IllegalStateException.class);
+    thrown.expectMessage("Task 'org.sonar.batch.task.TasksTest$FakeTask1' is defined twice: first by 'foo1' and then by 'foo2'");
+
+    tasks.start();
+  }
+
+  private static class FakeTask1 implements Task {
+    public void execute() {
+    }
+  }
+
+  private static class FakeTask2 implements Task {
+    public void execute() {
+    }
+
+  }
+
+}
index 9e4a625b626eac1a531bef2247dca1b2d20f2150..c1bbad7271b8ebc9dc4ad6cb2eac6f1b01ec77c5 100644 (file)
@@ -362,16 +362,12 @@ public interface CoreProperties {
 
   /**
    * @since 3.5
-   * @deprecated since 5.2 no more task concept on batch side
    */
-  @Deprecated
   String TASK = "sonar.task";
 
   /**
    * @since 3.6
-   * @deprecated since 5.2 no more task concept on batch side
    */
-  @Deprecated
   String SCAN_TASK = "scan";
 
   /**
index 46b2e7abd26d7e34b6958efbb1c13575e3099c27..e1efd17a1eaf16c123b6a18265a284941ea3b5c4 100644 (file)
@@ -34,7 +34,12 @@ import java.lang.annotation.Target;
 public @interface InstantiationStrategy {
 
   /**
-   * Shared extension. Lifecycle is the full analysis.
+   * Shared task extension. Available in task container.
+   */
+  String PER_TASK = "PER_TASK";
+
+  /**
+   * Shared extension. Available in top level project container.
    */
   String PER_BATCH = "PER_BATCH";
 
index dd280ef2ab1eb969488ab46b8117e1de1be0bf10..be7677f17a77dfac99e4e963d3ba4abad7916759 100644 (file)
  */
 package org.sonar.api.task;
 
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
+
 /**
  * Implement this interface to provide the behavior of a task.
  * @since 3.6
- * @deprecated since 5.1 all tasks (devcockpit, views) will be moved to server side
  */
-@Deprecated
-public interface Task extends TaskExtension {
+@BatchSide
+@InstantiationStrategy(InstantiationStrategy.PER_TASK)
+public interface Task {
 
   void execute();
 
index 3c863a01937e019b0086008cf4943bdeb52226db..0f3a1ba0074fbc999ed2326a9c04664914687164 100644 (file)
@@ -26,10 +26,10 @@ import org.sonar.api.batch.InstantiationStrategy;
  * All the classes implementing this interface can be injected in public constructors of {@link TaskExtension}.
  *
  * @since 3.6
- * @deprecated since 5.1 all tasks (devcockpit, views) will be moved to server side
+ * @deprecated since 5.1. Use {@link BatchSide} and {@link InstantiationStrategy#PER_TASK} 
  */
 @Deprecated
 @BatchSide
-@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+@InstantiationStrategy(InstantiationStrategy.PER_TASK)
 public interface TaskComponent {
 }
index 858c37b725d2d84d5c342a002ecbefab996a9486..8271252a3ca2a1137ca6dae3058e68d379562966 100644 (file)
@@ -21,17 +21,20 @@ package org.sonar.api.task;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
-
 import java.util.regex.Pattern;
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
 
 /**
  * Register and describe a {@link TaskExtension}.
  *
  * @since 3.6
- * @deprecated since 5.1 all tasks (devcockpit, views) will be moved to server side
  */
-@Deprecated
-public class TaskDefinition implements TaskExtension, Comparable<TaskDefinition> {
+@ExtensionPoint
+@BatchSide
+@InstantiationStrategy(InstantiationStrategy.PER_TASK)
+public class TaskDefinition implements Comparable<TaskDefinition> {
   static final String KEY_PATTERN = "[a-zA-Z0-9\\-\\_]+";
 
   private final String key;
index f389d069dcf3b0e3a78897905b43af733001a8e2..93a4e841ca19238954a64ae14027c7bf91afc816 100644 (file)
  */
 package org.sonar.api.task;
 
-import org.sonar.api.Extension;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
 
 /**
  * Task extension point
  *
  * @since 3.6
- * @deprecated since 5.1 all tasks (devcockpit, views) will be moved to server side
+ * @deprecated since 5.1. Not used.
  */
 @Deprecated
-public interface TaskExtension extends Extension, TaskComponent {
+@BatchSide
+@InstantiationStrategy(InstantiationStrategy.PER_TASK)
+public interface TaskExtension {
 }