diff options
20 files changed, 644 insertions, 114 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index c0dccbeb412..526d2aada6c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -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()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 9df2d26f876..035c7f489a9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -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(); - } - } 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 116272c6a20..a640c0e27fb 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 @@ -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; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 55a10b6fd25..b694beb38cf 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -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 index 00000000000..2b5e63ad2ed --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/ListTask.java @@ -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 index 00000000000..87e8647ef42 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/ScanTask.java @@ -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 index 00000000000..d4518f2a03d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/TaskContainer.java @@ -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 index 00000000000..abfdcc51c04 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/TaskProperties.java @@ -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 index 00000000000..55f9cdd20c2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/Tasks.java @@ -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 index 00000000000..c9d8bd173a2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/ViewsTask.java @@ -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 index 00000000000..72ec1c3766c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/task/package-info.java @@ -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; diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java index 7c9358509c3..9d422c1b1ba 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java @@ -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 index 00000000000..ccc22ddb00f --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/task/ListTaskTest.java @@ -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 index 00000000000..4857b185fb4 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/task/TasksTest.java @@ -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() { + } + + } + +} 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 9e4a625b626..c1bbad7271b 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 @@ -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"; /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java index 46b2e7abd26..e1efd17a1ea 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/InstantiationStrategy.java @@ -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"; 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 dd280ef2ab1..be7677f17a7 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 @@ -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(); 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 3c863a01937..0f3a1ba0074 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 @@ -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 { } 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 858c37b725d..8271252a3ca 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 @@ -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; 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 f389d069dcf..93a4e841ca1 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 @@ -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 { } |