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