Browse Source

SONAR-7023 Restore task API

tags/5.3-RC1
Julien HENRY 8 years ago
parent
commit
01570a4518
20 changed files with 644 additions and 114 deletions
  1. 17
    8
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
  2. 3
    77
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
  3. 2
    2
      sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
  4. 2
    8
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  5. 56
    0
      sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java
  6. 72
    0
      sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java
  7. 91
    0
      sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java
  8. 36
    0
      sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java
  9. 75
    0
      sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java
  10. 85
    0
      sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java
  11. 23
    0
      sonar-batch/src/main/java/org/sonar/batch/task/package-info.java
  12. 2
    2
      sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java
  13. 63
    0
      sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java
  14. 90
    0
      sonar-batch/src/test/java/org/sonar/batch/task/TasksTest.java
  15. 0
    4
      sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
  16. 6
    1
      sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java
  17. 6
    3
      sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java
  18. 2
    2
      sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java
  19. 7
    4
      sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java
  20. 6
    3
      sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java

+ 17
- 8
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java View File

@@ -19,16 +19,12 @@
*/
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());

+ 3
- 77
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java View File

@@ -19,22 +19,11 @@
*/
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();
}

}

+ 2
- 2
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java View 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;
}


+ 2
- 8
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java View 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();

+ 56
- 0
sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java View File

@@ -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());
}

}

+ 72
- 0
sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java View File

@@ -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();
}

}

+ 91
- 0
sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java View File

@@ -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");
}
}
}

+ 36
- 0
sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java View File

@@ -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);
}

}

+ 75
- 0
sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java View File

@@ -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() + "'");
}
}
}
}

+ 85
- 0
sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java View File

@@ -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()));
}
}

}

+ 23
- 0
sonar-batch/src/main/java/org/sonar/batch/task/package-info.java View File

@@ -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;

+ 2
- 2
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java View 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)

+ 63
- 0
sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java View File

@@ -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() {
}
}
}

+ 90
- 0
sonar-batch/src/test/java/org/sonar/batch/task/TasksTest.java View File

@@ -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() {
}

}

}

+ 0
- 4
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View 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";

/**

+ 6
- 1
sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java View 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";


+ 6
- 3
sonar-plugin-api/src/main/java/org/sonar/api/task/Task.java View File

@@ -19,13 +19,16 @@
*/
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();


+ 2
- 2
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskComponent.java View 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 {
}

+ 7
- 4
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskDefinition.java View 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;

+ 6
- 3
sonar-plugin-api/src/main/java/org/sonar/api/task/TaskExtension.java View File

@@ -19,14 +19,17 @@
*/
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 {
}

Loading…
Cancel
Save