diff options
Diffstat (limited to 'sonar-batch/src/main')
40 files changed, 960 insertions, 213 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java index c49b4f48327..a01928f3529 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java +++ b/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java @@ -38,6 +38,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class); private boolean mediumTestMode; + private boolean notAssociated; public DefaultAnalysisMode(GlobalProperties globalProps, AnalysisProperties props) { init(globalProps.properties(), props.properties()); @@ -47,6 +48,10 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi return mediumTestMode; } + public boolean isNotAssociated() { + return notAssociated; + } + private void init(Map<String, String> globalProps, Map<String, String> analysisProps) { // make sure analysis is consistent with global properties boolean globalPreview = isIssues(globalProps); @@ -64,6 +69,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi validate(mode); issues = CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode) || CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode); mediumTestMode = "true".equals(getPropertyWithFallback(analysisProps, globalProps, FakePluginInstaller.MEDIUM_TEST_ENABLED)); + notAssociated = issues && rootProjectKeyMissing(analysisProps); } public void printMode() { @@ -77,6 +83,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi if (mediumTestMode) { LOG.info("Medium test mode"); } + if (notAssociated) { + LOG.info("Project is not associated with the server"); + } } private static String getPropertyWithFallback(Map<String, String> props1, Map<String, String> props2, String key) { @@ -93,4 +102,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi return CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode); } + private static boolean rootProjectKeyMissing(Map<String, String> props) { + // ProjectReactorBuilder depends on this class, so it will only create this property later + return !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY); + } + } 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 67e5843c8ee..5469343c55d 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 @@ -123,14 +123,22 @@ public class GlobalContainer extends ComponentContainer { public void executeAnalysis(Map<String, String> analysisProperties, Object... components) { AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); if (isIssuesMode(props)) { - new ProjectSyncContainer(this, props, false).execute(); + String projectKey = getProjectKeyWithBranch(props); + new ProjectSyncContainer(this, projectKey, false).execute(); } new ProjectScanContainer(this, props, components).execute(); } - public void syncProject(Map<String, String> analysisProperties, boolean force) { - AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); - new ProjectSyncContainer(this, props, force).execute(); + 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 syncProject(String projectKey, boolean force) { + new ProjectSyncContainer(this, projectKey, force).execute(); } private boolean isIssuesMode(AnalysisProperties props) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java index 0faef082627..901e2d2cac2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.bootstrap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @@ -27,10 +30,13 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -55,8 +61,8 @@ import org.sonar.core.util.DefaultHttpDownloader; */ @BatchSide public class ServerClient { - private static final String GET = "GET"; + private static final Logger LOG = LoggerFactory.getLogger(ServerClient.class); private GlobalProperties props; private DefaultHttpDownloader.BaseHttpDownloader downloader; @@ -68,6 +74,20 @@ public class ServerClient { public String getURL() { return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/"); } + + public String getServerVersion() { + InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); + if (is == null) { + LOG.warn("Failed to get SQ version"); + return null; + } + try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + return br.readLine(); + } catch (IOException e) { + LOG.warn("Failed to get SQ version", e); + return null; + } + } public URI getURI(String pathStartingWithSlash) { Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /: " + pathStartingWithSlash); 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 14070c17d21..e98e6366f7a 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 @@ -19,9 +19,6 @@ */ package org.sonar.batch.bootstrapper; -import com.google.common.collect.ImmutableMap; - -import org.sonar.api.CoreProperties; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -134,8 +131,7 @@ public final class Batch { */ public Batch syncProject(String projectKey) { checkStarted(); - Map<String, String> props = ImmutableMap.of(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); - bootstrapContainer.syncProject(props, true); + bootstrapContainer.syncProject(projectKey, true); return this; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java b/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java index 3308098c6a0..248d2b8b350 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.cache; +import javax.annotation.Nullable; + import org.sonar.batch.bootstrap.ServerClient; import org.sonar.home.cache.PersistentCache; @@ -40,7 +42,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus { } @Override - public void save(String projectKey) { + public void save(@Nullable String projectKey) { Date now = new Date(); try { @@ -55,7 +57,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus { } @Override - public void delete(String projectKey) { + public void delete(@Nullable String projectKey) { try { cache.put(getKey(projectKey), new byte[0]); } catch (IOException e) { @@ -64,7 +66,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus { } @Override - public Date getSyncStatus(String projectKey) { + public Date getSyncStatus(@Nullable String projectKey) { try { byte[] status = cache.get(getKey(projectKey), null); if (status == null || status.length == 0) { @@ -79,7 +81,11 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus { } } - private String getKey(String projectKey) { - return STATUS_PREFIX + client.getURL() + "-" + projectKey; + private String getKey(@Nullable String projectKey) { + if (projectKey != null) { + return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion() + "-" + projectKey; + } else { + return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion(); + } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java new file mode 100644 index 00000000000..e21d1420694 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java @@ -0,0 +1,92 @@ +/* + * 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.cache; + +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.protocol.input.QProfile; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public class NonAssociatedCacheSynchronizer { + private static final Logger LOG = LoggerFactory.getLogger(NonAssociatedCacheSynchronizer.class); + + private ProjectCacheStatus cacheStatus; + private QualityProfileLoader qualityProfileLoader; + private ActiveRulesLoader activeRulesLoader; + + public NonAssociatedCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ActiveRulesLoader activeRulesLoader, ProjectCacheStatus cacheStatus) { + this.qualityProfileLoader = qualityProfileLoader; + this.activeRulesLoader = activeRulesLoader; + this.cacheStatus = cacheStatus; + } + + public void execute(boolean force) { + Date lastSync = cacheStatus.getSyncStatus(null); + + if (lastSync != null) { + if (!force) { + LOG.info("Found cache [{}]", lastSync); + return; + } else { + LOG.info("-- Found cache [{}], synchronizing data..", lastSync); + } + cacheStatus.delete(null); + } else { + LOG.info("-- Cache not found, synchronizing data.."); + } + + loadData(); + saveStatus(); + } + + private static Collection<String> getKeys(Collection<QProfile> qProfiles) { + List<String> list = new ArrayList<>(qProfiles.size()); + for (QProfile qp : qProfiles) { + list.add(qp.key()); + } + + return list; + } + + private void saveStatus() { + cacheStatus.save(null); + LOG.info("-- Succesfully synchronized cache"); + } + + private void loadData() { + Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class)); + + profiler.startInfo("Load default quality profiles"); + Collection<QProfile> qProfiles = qualityProfileLoader.load(null, null); + profiler.stopInfo(); + + profiler.startInfo("Load default active rules"); + activeRulesLoader.load(getKeys(qProfiles), null); + profiler.stopInfo(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java b/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java index 878554d2499..99040ba9927 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java @@ -49,14 +49,14 @@ public class PersistentCacheProvider extends ProviderAdapter { builder.setSonarHome(Paths.get(home)); } - builder.setVersion(getVersion()); + builder.setVersion(getServerVersion()); cache = builder.build(); } return cache; } - private String getVersion() { + private String getServerVersion() { InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); if (is == null) { LOG.warn("Failed to get SQ version"); diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java index 281cd60f344..a5f97349a4b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java @@ -19,12 +19,14 @@ */ package org.sonar.batch.cache; +import javax.annotation.Nullable; + import java.util.Date; public interface ProjectCacheStatus { - void save(String projectKey); + void save(@Nullable String projectKey); - void delete(String projectKey); + void delete(@Nullable String projectKey); - Date getSyncStatus(String projectKey); + Date getSyncStatus(@Nullable String projectKey); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java index 6e6bccc9572..d2d6cbcf2db 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java @@ -19,100 +19,98 @@ */ package org.sonar.batch.cache; -import org.sonar.batch.analysis.AnalysisProperties; +import com.google.common.base.Function; import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.log.Loggers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; -import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.batch.protocol.input.BatchInput.ServerIssue; -import com.google.common.base.Function; -import org.sonar.batch.protocol.input.FileData; +import org.sonar.batch.protocol.input.QProfile; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.batch.repository.user.UserRepositoryLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; +import java.util.List; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.sonar.batch.protocol.input.ProjectRepositories; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.batch.repository.user.UserRepositoryLoader; -import org.sonar.batch.issue.tracking.ServerLineHashesLoader; -import org.sonar.batch.repository.ServerIssuesLoader; -import org.sonar.batch.repository.ProjectRepositoriesLoader; public class ProjectCacheSynchronizer { private static final Logger LOG = LoggerFactory.getLogger(ProjectCacheSynchronizer.class); - private static final int NUM_THREAD = 2; - private final ProjectDefinition project; - private final AnalysisProperties properties; - private final ProjectRepositoriesLoader projectRepositoryLoader; private final ServerIssuesLoader issuesLoader; - private final ServerLineHashesLoader lineHashesLoader; private final UserRepositoryLoader userRepository; private final ProjectCacheStatus cacheStatus; - - public ProjectCacheSynchronizer(ProjectReactor project, ProjectRepositoriesLoader projectRepositoryLoader, AnalysisProperties properties, - ServerIssuesLoader issuesLoader, ServerLineHashesLoader lineHashesLoader, UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) { - this.project = project.getRoot(); - this.projectRepositoryLoader = projectRepositoryLoader; - this.properties = properties; + private final QualityProfileLoader qualityProfileLoader; + private final ProjectSettingsLoader projectSettingsLoader; + private final ActiveRulesLoader activeRulesLoader; + + public ProjectCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ProjectSettingsLoader projectSettingsLoader, + ActiveRulesLoader activeRulesLoader, ServerIssuesLoader issuesLoader, + UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) { + this.qualityProfileLoader = qualityProfileLoader; + this.projectSettingsLoader = projectSettingsLoader; + this.activeRulesLoader = activeRulesLoader; this.issuesLoader = issuesLoader; - this.lineHashesLoader = lineHashesLoader; this.userRepository = userRepository; this.cacheStatus = cacheStatus; } - public void load(boolean force) { - Date lastSync = cacheStatus.getSyncStatus(project.getKeyWithBranch()); + public void load(String projectKey, boolean force) { + Date lastSync = cacheStatus.getSyncStatus(projectKey); if (lastSync != null) { if (!force) { - LOG.info("Found project [{}] cache [{}]", project.getKeyWithBranch(), lastSync); + LOG.info("Found project [{}] cache [{}]", projectKey, lastSync); return; } else { - LOG.info("-- Found project [{}] cache [{}], synchronizing data..", project.getKeyWithBranch(), lastSync); + LOG.info("-- Found project [{}] cache [{}], synchronizing data..", projectKey, lastSync); } - cacheStatus.delete(project.getKeyWithBranch()); + cacheStatus.delete(projectKey); } else { - LOG.info("-- Cache for project [{}] not found, synchronizing data..", project.getKeyWithBranch()); + LOG.info("-- Cache for project [{}] not found, synchronizing data..", projectKey); } - loadData(); - saveStatus(); + loadData(projectKey); + saveStatus(projectKey); } - private void saveStatus() { - cacheStatus.save(project.getKeyWithBranch()); + private void saveStatus(String projectKey) { + cacheStatus.save(projectKey); LOG.info("-- Succesfully synchronized project cache"); } - private static String getComponentKey(String moduleKey, String filePath) { - return moduleKey + ":" + filePath; - } - - private void loadData() { + private void loadData(String projectKey) { Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class)); - profiler.startInfo("Load project repository"); - ProjectRepositories projectRepo = projectRepositoryLoader.load(project, properties, null); + profiler.startInfo("Load project settings"); + ProjectSettingsRepo settings = projectSettingsLoader.load(projectKey, null); profiler.stopInfo(); - if (projectRepo.lastAnalysisDate() == null) { + if (settings.lastAnalysisDate() == null) { LOG.debug("No previous analysis found"); return; } + profiler.startInfo("Load project quality profiles"); + Collection<QProfile> qProfiles = qualityProfileLoader.load(projectKey, null); + profiler.stopInfo(); + + Collection<String> profileKeys = getKeys(qProfiles); + + profiler.startInfo("Load project active rules"); + activeRulesLoader.load(profileKeys, projectKey); + profiler.stopInfo(); + profiler.startInfo("Load server issues"); UserLoginAccumulator consumer = new UserLoginAccumulator(); - issuesLoader.load(project.getKeyWithBranch(), consumer); + issuesLoader.load(projectKey, consumer); profiler.stopInfo(); profiler.startInfo("Load user information (" + consumer.loginSet.size() + " users)"); @@ -120,56 +118,15 @@ public class ProjectCacheSynchronizer { userRepository.load(login, null); } profiler.stopInfo("Load user information"); - - loadLineHashes(projectRepo.fileDataByModuleAndPath(), profiler); } - private void loadLineHashes(Map<String, Map<String, FileData>> fileDataByModuleAndPath, Profiler profiler) { - ExecutorService executor = Executors.newFixedThreadPool(NUM_THREAD); - int numFiles = 0; - - for (Map<String, FileData> fileDataByPath : fileDataByModuleAndPath.values()) { - numFiles += fileDataByPath.size(); - } - profiler.startInfo("Load line file hashes (" + numFiles + " files)"); - - for (Entry<String, Map<String, FileData>> e1 : fileDataByModuleAndPath.entrySet()) { - String moduleKey = e1.getKey(); - - for (Entry<String, FileData> e2 : e1.getValue().entrySet()) { - String filePath = e2.getKey(); - executor.submit(new LineHashLoadWorker(getComponentKey(moduleKey, filePath))); - } - } - - executor.shutdown(); - - try { - boolean done = executor.awaitTermination(30, TimeUnit.MINUTES); - if (!done) { - executor.shutdownNow(); - throw new IllegalStateException("Timeout while fetching line hashes"); - } - } catch (InterruptedException e) { - executor.shutdownNow(); - throw new IllegalStateException("Interrupted while fetching line hashes", e); + private static Collection<String> getKeys(Collection<QProfile> qProfiles) { + List<String> list = new ArrayList<>(qProfiles.size()); + for (QProfile qp : qProfiles) { + list.add(qp.key()); } - profiler.stopInfo("Load line file hashes (done)"); - } - - private class LineHashLoadWorker implements Callable<Void> { - private String fileKey; - - LineHashLoadWorker(String fileKey) { - this.fileKey = fileKey; - } - - @Override - public Void call() throws Exception { - lineHashesLoader.getLineHashes(fileKey, null); - return null; - } + return list; } private static class UserLoginAccumulator implements Function<ServerIssue, Void> { diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java index 0f625f72ca0..0bd67f66261 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java @@ -19,59 +19,73 @@ */ package org.sonar.batch.cache; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.batch.repository.ProjectRepositoriesFactoryProvider; + import org.sonar.batch.analysis.DefaultAnalysisMode; -import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.api.CoreProperties; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + import org.sonar.batch.analysis.AnalysisProperties; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.DefaultProjectSettingsLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.batch.repository.user.UserRepositoryLoader; -import org.sonar.batch.issue.tracking.ServerLineHashesLoader; import org.sonar.batch.repository.DefaultProjectRepositoriesLoader; import org.sonar.batch.repository.DefaultServerIssuesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; import org.sonar.batch.repository.ServerIssuesLoader; -import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader; import org.sonar.core.platform.ComponentContainer; public class ProjectSyncContainer extends ComponentContainer { private final boolean force; - private final AnalysisProperties properties; + private final String projectKey; - public ProjectSyncContainer(ComponentContainer globalContainer, AnalysisProperties analysisProperties, boolean force) { + public ProjectSyncContainer(ComponentContainer globalContainer, String projectKey, boolean force) { super(globalContainer); - this.properties = analysisProperties; + this.projectKey = projectKey; this.force = force; } @Override public void doBeforeStart() { - ProjectReactor projectReactor = createProjectReactor(); - add(projectReactor); addComponents(); } - private ProjectReactor createProjectReactor() { - ProjectDefinition rootProjectDefinition = ProjectDefinition.create(); - rootProjectDefinition.setProperties(properties.properties()); - return new ProjectReactor(rootProjectDefinition); - } - @Override public void doAfterStart() { - getComponentByType(ProjectCacheSynchronizer.class).load(force); + if (projectKey != null) { + getComponentByType(ProjectCacheSynchronizer.class).load(projectKey, force); + } else { + getComponentByType(NonAssociatedCacheSynchronizer.class).execute(force); + } + } + + private static DefaultAnalysisMode createIssuesAnalisysMode() { + Map<String, String> props = ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES); + GlobalProperties globalProps = new GlobalProperties(props); + AnalysisProperties analysisProps = new AnalysisProperties(props); + return new DefaultAnalysisMode(globalProps, analysisProps); } private void addComponents() { - add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_FIRST), - properties, - DefaultAnalysisMode.class, - ProjectCacheSynchronizer.class, - UserRepositoryLoader.class); + add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_ONLY), + projectKey != null ? ProjectCacheSynchronizer.class : NonAssociatedCacheSynchronizer.class, + UserRepositoryLoader.class, + new ProjectRepositoriesFactoryProvider(projectKey), + createIssuesAnalisysMode()); addIfMissing(DefaultProjectCacheStatus.class, ProjectCacheStatus.class); addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class); - addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class); + addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class); + addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class); + addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java index 092bab08b0b..d6966e97548 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java @@ -51,13 +51,13 @@ public class WSLoader { SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY; } - private final LoadStrategy loadStrategy; + private final LoadStrategy defautLoadStrategy; private ServerStatus serverStatus; private final ServerClient client; private final PersistentCache cache; public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient client) { - this.loadStrategy = strategy; + this.defautLoadStrategy = strategy; this.serverStatus = UNKNOWN; this.cache = cache; this.client = client; @@ -65,19 +65,28 @@ public class WSLoader { @Nonnull public WSLoaderResult<ByteSource> loadSource(String id) { - WSLoaderResult<byte[]> byteResult = load(id); + WSLoaderResult<byte[]> byteResult = load(id, defautLoadStrategy); return new WSLoaderResult<ByteSource>(ByteSource.wrap(byteResult.get()), byteResult.isFromCache()); } @Nonnull public WSLoaderResult<String> loadString(String id) { - WSLoaderResult<byte[]> byteResult = load(id); + return loadString(id, defautLoadStrategy); + } + @Nonnull + public WSLoaderResult<String> loadString(String id, WSLoader.LoadStrategy strategy) { + WSLoaderResult<byte[]> byteResult = load(id, strategy); return new WSLoaderResult<String>(new String(byteResult.get(), StandardCharsets.UTF_8), byteResult.isFromCache()); } @Nonnull public WSLoaderResult<byte[]> load(String id) { - switch (loadStrategy) { + return load(id, defautLoadStrategy); + } + + @Nonnull + public WSLoaderResult<byte[]> load(String id, WSLoader.LoadStrategy strategy) { + switch (strategy) { case CACHE_FIRST: return loadFromCacheFirst(id, true); case CACHE_ONLY: @@ -91,7 +100,7 @@ public class WSLoader { } public LoadStrategy getStrategy() { - return this.loadStrategy; + return this.defautLoadStrategy; } private void switchToOffline() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java index 6577835f1cb..e291a05f15a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.issue.tracking; -import org.sonar.batch.cache.WSLoaderResult; +import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.batch.cache.WSLoaderResult; import org.sonar.batch.cache.WSLoader; import org.apache.commons.lang.mutable.MutableBoolean; @@ -50,7 +51,7 @@ public class DefaultServerLineHashesLoader implements ServerLineHashesLoader { Profiler profiler = Profiler.createIfDebug(Loggers.get(getClass())) .addContext("file", fileKey) .startDebug("Load line hashes"); - WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey)); + WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey), LoadStrategy.CACHE_FIRST); try { if (fromCache != null) { fromCache.setValue(result.isFromCache()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java index f202e552613..e0d5cc547c1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java @@ -19,16 +19,22 @@ */ package org.sonar.batch.issue.tracking; +import org.sonar.batch.analysis.DefaultAnalysisMode; + +import org.sonar.batch.repository.ProjectSettingsRepo; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.common.collect.Sets; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; + import javax.annotation.CheckForNull; + import org.sonar.api.batch.BatchSide; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRule; @@ -41,7 +47,6 @@ import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.report.ReportPublisher; @@ -72,7 +77,7 @@ public class LocalIssueTracking { public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking, ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater, ActiveRules activeRules, ServerIssueRepository serverIssueRepository, - ProjectRepositories projectRepositories, ReportPublisher reportPublisher) { + ProjectSettingsRepo projectRepositories, ReportPublisher reportPublisher, DefaultAnalysisMode mode) { this.componentCache = resourceCache; this.issueCache = issueCache; this.tracking = tracking; @@ -84,7 +89,7 @@ public class LocalIssueTracking { this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate(); this.changeContext = IssueChangeContext.createScan(analysisDate); this.activeRules = activeRules; - this.hasServerAnalysis = projectRepositories.lastAnalysisDate() != null; + this.hasServerAnalysis = !mode.isNotAssociated() && projectRepositories.lastAnalysisDate() != null; } public void execute() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java index b2c86b9e2c0..3861684da2e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java @@ -19,29 +19,42 @@ */ package org.sonar.batch.repository; -import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.rule.ModuleQProfiles; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.apache.commons.lang.mutable.MutableBoolean; -import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.AnalysisMode; -import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.batch.protocol.input.ProjectRepositories; -public class ProjectRepositoriesProvider extends ProviderAdapter { - +public class DefaultProjectRepositoriesFactory implements ProjectRepositoriesFactory { private static final String LOG_MSG = "Load project repositories"; - private static final Logger LOG = Loggers.get(ProjectRepositoriesProvider.class); + private static final Logger LOG = Loggers.get(DefaultProjectRepositoriesFactory.class); + private static final String NON_EXISTING = "non1-existing2-project3-key"; + + private final DefaultAnalysisMode analysisMode; + private final ProjectRepositoriesLoader loader; + private final AnalysisProperties props; + private final ProjectReactor projectReactor; private ProjectRepositories projectReferentials; - public ProjectRepositories provide(ProjectRepositoriesLoader loader, ProjectReactor reactor, AnalysisProperties taskProps, AnalysisMode analysisMode) { + public DefaultProjectRepositoriesFactory(ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, ProjectRepositoriesLoader loader, AnalysisProperties props) { + this.projectReactor = projectReactor; + this.analysisMode = analysisMode; + this.loader = loader; + this.props = props; + } + + @Override + public ProjectRepositories create() { if (projectReferentials == null) { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); MutableBoolean fromCache = new MutableBoolean(); - projectReferentials = loader.load(reactor.getRoot(), taskProps, fromCache); + projectReferentials = loader.load(getProjectKey(), getSonarProfile(), fromCache); profiler.stopInfo(fromCache.booleanValue()); if (analysisMode.isIssues() && projectReferentials.lastAnalysisDate() == null) { @@ -50,4 +63,19 @@ public class ProjectRepositoriesProvider extends ProviderAdapter { } return projectReferentials; } + + private String getProjectKey() { + if (analysisMode.isNotAssociated()) { + return NON_EXISTING; + } + return projectReactor.getRoot().getKeyWithBranch(); + } + + private String getSonarProfile() { + String profile = null; + if (!analysisMode.isIssues()) { + profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP); + } + return profile; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java index 58383811367..a3b38ee4ec5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java @@ -23,12 +23,10 @@ import org.sonar.batch.cache.WSLoaderResult; import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.cache.WSLoader; -import org.sonar.batch.analysis.AnalysisProperties; import javax.annotation.Nullable; import org.apache.commons.lang.mutable.MutableBoolean; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.utils.MessageException; @@ -50,13 +48,12 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad } @Override - public ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache) { - String projectKey = projectDefinition.getKeyWithBranch(); - String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKey); - if (taskProperties.properties().containsKey(ModuleQProfiles.SONAR_PROFILE_PROP)) { + public ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache) { + String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKeyWithBranch); + if (sonarProfile != null) { LOG.warn("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); - url += "&profile=" + BatchUtils.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP)); + url += "&profile=" + BatchUtils.encodeForUrl(sonarProfile); } url += "&preview=" + analysisMode.isIssues(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java new file mode 100644 index 00000000000..acb23823686 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.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.repository; + +import org.apache.commons.lang.mutable.MutableBoolean; + +import javax.annotation.Nullable; + +import java.util.Map; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class DefaultProjectSettingsLoader implements ProjectSettingsLoader { + private ProjectRepositoriesFactory projectRepositoryFactory; + + public DefaultProjectSettingsLoader(ProjectRepositoriesFactory projectRepositoryFactory) { + this.projectRepositoryFactory = projectRepositoryFactory; + } + + @Override + public ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache) { + ProjectRepositories pr = projectRepositoryFactory.create(); + return new ProjectSettingsRepo(toTable(pr.settings()), toTable(pr.fileDataByModuleAndPath()), pr.lastAnalysisDate()); + } + + private static <T, U, V> Table<T, U, V> toTable(Map<T, Map<U, V>> map) { + Table<T, U, V> table = HashBasedTable.create(); + + for (Map.Entry<T, Map<U, V>> e1 : map.entrySet()) { + for (Map.Entry<U, V> e2 : e1.getValue().entrySet()) { + table.put(e1.getKey(), e2.getKey(), e2.getValue()); + } + } + + return table; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java new file mode 100644 index 00000000000..ab86309f3d5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java @@ -0,0 +1,50 @@ +/* + * 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.repository; + +import javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.ProjectRepositories; +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; + +public class DefaultQualityProfileLoader implements QualityProfileLoader { + + private ProjectRepositoriesFactory projectRepositoriesFactory; + + public DefaultQualityProfileLoader(ProjectRepositoriesFactory projectRepositoriesFactory) { + this.projectRepositoriesFactory = projectRepositoriesFactory; + } + + @Override + public Collection<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile) { + ProjectRepositories pr = projectRepositoriesFactory.create(); + validate(pr.qProfiles()); + return pr.qProfiles(); + } + + private static void validate(Collection<QProfile> profiles) { + if (profiles == null || profiles.isEmpty()) { + throw new IllegalStateException("No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis."); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java new file mode 100644 index 00000000000..13ce299d8f9 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java @@ -0,0 +1,28 @@ +/* + * 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.repository; + +import org.sonar.batch.protocol.input.ProjectRepositories; + +public interface ProjectRepositoriesFactory { + + ProjectRepositories create(); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java new file mode 100644 index 00000000000..609d7a73f5d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java @@ -0,0 +1,41 @@ +/* + * 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.repository; + +import org.picocontainer.injectors.ProviderAdapter; + +public class ProjectRepositoriesFactoryProvider extends ProviderAdapter { + + private final String projectKey; + private SyncProjectRepositoriesFactory factory; + + public ProjectRepositoriesFactoryProvider(String projectKey) { + this.projectKey = projectKey; + this.factory = null; + } + + public ProjectRepositoriesFactory provide(ProjectRepositoriesLoader loader) { + if (factory == null) { + factory = new SyncProjectRepositoriesFactory(projectKey, loader); + } + + return factory; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java index 444af1a3b62..d066b488940 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java @@ -19,16 +19,14 @@ */ package org.sonar.batch.repository; -import org.sonar.batch.analysis.AnalysisProperties; import javax.annotation.Nullable; import org.apache.commons.lang.mutable.MutableBoolean; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.batch.protocol.input.ProjectRepositories; public interface ProjectRepositoriesLoader { - ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache); + ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java new file mode 100644 index 00000000000..dc0efbcc4e4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java @@ -0,0 +1,28 @@ +/* + * 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.repository; + +import org.apache.commons.lang.mutable.MutableBoolean; + +import javax.annotation.Nullable; + +public interface ProjectSettingsLoader { + ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java new file mode 100644 index 00000000000..5fa0db433b5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java @@ -0,0 +1,64 @@ +/* + * 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.repository; + +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import javax.annotation.Nullable; + +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.protocol.input.FileData; +import com.google.common.collect.Table; +import com.google.common.collect.ImmutableTable; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.picocontainer.injectors.ProviderAdapter; + +public class ProjectSettingsProvider extends ProviderAdapter { + private static final Logger LOG = Loggers.get(ProjectSettingsProvider.class); + private ProjectSettingsRepo settings = null; + + public ProjectSettingsRepo provide(@Nullable ProjectSettingsLoader loader, ProjectReactor projectReactor, DefaultAnalysisMode mode) { + if (settings == null) { + if (mode.isNotAssociated()) { + settings = createNonAssociatedProjectSettings(); + } else { + MutableBoolean fromCache = new MutableBoolean(); + settings = loader.load(projectReactor.getRoot().getKeyWithBranch(), fromCache); + checkProject(mode); + } + } + + return settings; + } + + private void checkProject(DefaultAnalysisMode mode) { + if (mode.isIssues() && settings.lastAnalysisDate() == null) { + LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'."); + } + } + + private static ProjectSettingsRepo createNonAssociatedProjectSettings() { + Table<String, String, String> emptySettings = ImmutableTable.of(); + Table<String, String, FileData> emptyFileData = ImmutableTable.of(); + return new ProjectSettingsRepo(emptySettings, emptyFileData, null); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java new file mode 100644 index 00000000000..1e53de437bc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java @@ -0,0 +1,66 @@ +/* + * 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.repository; + +import com.google.common.collect.Table; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.FileData; + +import java.util.Date; +import java.util.Map; + +public class ProjectSettingsRepo { + private Table<String, String, String> settingsByModule = null; + private Table<String, String, FileData> fileDataByModuleAndPath = null; + private Date lastAnalysisDate; + + public ProjectSettingsRepo(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath, + @Nullable Date lastAnalysisDate) { + super(); + this.settingsByModule = settingsByModule; + this.fileDataByModuleAndPath = fileDataByModuleAndPath; + this.lastAnalysisDate = lastAnalysisDate; + } + + public Map<String, FileData> fileDataByPath(String moduleKey) { + return fileDataByModuleAndPath.row(moduleKey); + } + + public Table<String, String, FileData> fileDataByModuleAndPath() { + return fileDataByModuleAndPath; + } + + public Map<String, String> settings(String moduleKey) { + return settingsByModule.row(moduleKey); + } + + @CheckForNull + public FileData fileData(String projectKey, String path) { + return fileDataByModuleAndPath.get(projectKey, path); + } + + @CheckForNull + public Date lastAnalysisDate() { + return lastAnalysisDate; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java new file mode 100644 index 00000000000..1d41398bff4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java @@ -0,0 +1,30 @@ +/* + * 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.repository; + +import javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; + +public interface QualityProfileLoader { + Collection<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java new file mode 100644 index 00000000000..6f40ef0c7a8 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java @@ -0,0 +1,48 @@ +/* + * 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.repository; + +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; + +import org.sonar.api.batch.AnalysisMode; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.rule.ModuleQProfiles; +import org.picocontainer.injectors.ProviderAdapter; + +public class QualityProfileProvider extends ProviderAdapter { + private ModuleQProfiles profiles = null; + + public ModuleQProfiles provide(ProjectReactor projectReactor, QualityProfileLoader loader, AnalysisProperties props, AnalysisMode mode) { + if (this.profiles == null) { + String profile = null; + if (!mode.isIssues()) { + profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP); + } + Collection<QProfile> qps = loader.load(projectReactor.getRoot().getKeyWithBranch(), profile); + profiles = new ModuleQProfiles(qps); + } + + return profiles; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java new file mode 100644 index 00000000000..f01cce9102b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java @@ -0,0 +1,74 @@ +/* + * 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.repository; + +import javax.annotation.Nullable; + +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class SyncProjectRepositoriesFactory implements ProjectRepositoriesFactory { + private static final String LOG_MSG = "Load project repositories"; + private static final Logger LOG = Loggers.get(SyncProjectRepositoriesFactory.class); + private static final String NON_EXISTING = "non1-existing2-project3-key"; + + private final ProjectRepositoriesLoader loader; + private final String projectKey; + + private ProjectRepositories projectRepositories; + + public SyncProjectRepositoriesFactory(@Nullable String projectKey, ProjectRepositoriesLoader loader) { + this.projectKey = projectKey; + this.loader = loader; + } + + @Override + public ProjectRepositories create() { + if (projectRepositories == null) { + projectRepositories = newInstance(); + } + + return projectRepositories; + } + + public ProjectRepositories newInstance() { + if (projectRepositories == null) { + Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); + MutableBoolean fromCache = new MutableBoolean(); + projectRepositories = loader.load(getProjectKey(projectKey), null, fromCache); + profiler.stopInfo(fromCache.booleanValue()); + + if (projectRepositories.lastAnalysisDate() == null) { + LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'."); + } + } + return projectRepositories; + } + + private static String getProjectKey(@Nullable String projectKey) { + if (projectKey == null) { + return NON_EXISTING; + } + return projectKey; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java new file mode 100644 index 00000000000..bf2ff6d406a --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java @@ -0,0 +1,28 @@ +/* + * 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.rule; + +import org.sonar.batch.protocol.input.ActiveRule; + +import java.util.Collection; + +public interface ActiveRulesLoader { + Collection<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java index 5bb0a4a86bd..ee2529cadff 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java @@ -19,14 +19,17 @@ */ package org.sonar.batch.rule; +import org.sonar.api.batch.bootstrap.ProjectReactor; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.NewActiveRule; import org.sonar.api.rule.RuleKey; import org.sonar.batch.protocol.input.ActiveRule; -import org.sonar.batch.protocol.input.ProjectRepositories; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map.Entry; /** @@ -37,16 +40,16 @@ public class ActiveRulesProvider extends ProviderAdapter { private ActiveRules singleton = null; - public ActiveRules provide(ProjectRepositories ref) { + public ActiveRules provide(ActiveRulesLoader ref, ModuleQProfiles qProfiles, ProjectReactor projectReactor) { if (singleton == null) { - singleton = load(ref); + singleton = load(ref, qProfiles, projectReactor); } return singleton; } - private static ActiveRules load(ProjectRepositories ref) { + private static ActiveRules load(ActiveRulesLoader loader, ModuleQProfiles qProfiles, ProjectReactor projectReactor) { ActiveRulesBuilder builder = new ActiveRulesBuilder(); - for (ActiveRule activeRule : ref.activeRules()) { + for (ActiveRule activeRule : loader.load(getKeys(qProfiles), projectReactor.getRoot().getKeyWithBranch())) { NewActiveRule newActiveRule = builder.create(RuleKey.of(activeRule.repositoryKey(), activeRule.ruleKey())); newActiveRule.setName(activeRule.name()); newActiveRule.setSeverity(activeRule.severity()); @@ -63,4 +66,14 @@ public class ActiveRulesProvider extends ProviderAdapter { } return builder.build(); } + + private static Collection<String> getKeys(ModuleQProfiles qProfiles) { + List<String> keys = new ArrayList<>(qProfiles.findAll().size()); + + for (QProfile qp : qProfiles.findAll()) { + keys.add(qp.getKey()); + } + + return keys; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java new file mode 100644 index 00000000000..05e58846648 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java @@ -0,0 +1,43 @@ +/* + * 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.rule; + +import org.sonar.batch.repository.ProjectRepositoriesFactory; + +import org.sonar.batch.protocol.input.ActiveRule; + +import java.util.Collection; + +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class DefaultActiveRulesLoader implements ActiveRulesLoader { + private final ProjectRepositoriesFactory projectRepositoriesFactory; + + public DefaultActiveRulesLoader(ProjectRepositoriesFactory projectRepositoriesFactory) { + this.projectRepositoriesFactory = projectRepositoriesFactory; + } + + @Override + public Collection<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey) { + ProjectRepositories pr = projectRepositoriesFactory.create(); + return pr.activeRules(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java index f7474cf181d..3ac70d83150 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java @@ -21,7 +21,6 @@ package org.sonar.batch.rule; import com.google.common.collect.ImmutableMap; import org.sonar.api.batch.BatchSide; -import org.sonar.batch.protocol.input.ProjectRepositories; import javax.annotation.CheckForNull; @@ -37,10 +36,10 @@ public class ModuleQProfiles { public static final String SONAR_PROFILE_PROP = "sonar.profile"; private final Map<String, QProfile> byLanguage; - public ModuleQProfiles(ProjectRepositories ref) { + public ModuleQProfiles(Collection<org.sonar.batch.protocol.input.QProfile> profiles) { ImmutableMap.Builder<String, QProfile> builder = ImmutableMap.builder(); - for (org.sonar.batch.protocol.input.QProfile qProfile : ref.qProfiles()) { + for (org.sonar.batch.protocol.input.QProfile qProfile : profiles) { builder.put(qProfile.language(), new QProfile().setKey(qProfile.key()).setName(qProfile.name()).setLanguage(qProfile.language()).setRulesUpdatedAt(qProfile.rulesUpdatedAt())); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 039da7d57e0..89c5e8bcc17 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -148,7 +148,6 @@ public class ModuleScanContainer extends ComponentContainer { CoverageExclusions.class, // rules - ModuleQProfiles.class, new RulesProfileProvider(), QProfileSensor.class, CheckFactory.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java index f8b770f59dd..00692d53cfa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.collect.Lists; import java.util.List; @@ -30,20 +31,19 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; import org.sonar.batch.bootstrap.GlobalSettings; -import org.sonar.batch.protocol.input.ProjectRepositories; /** * @since 2.12 */ public class ModuleSettings extends Settings { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettingsRepo; private DefaultAnalysisMode analysisMode; - public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectReferentials, + public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectSettingsRepo projectSettingsRepo, DefaultAnalysisMode analysisMode) { super(batchSettings.getDefinitions()); - this.projectReferentials = projectReferentials; + this.projectSettingsRepo = projectSettingsRepo; this.analysisMode = analysisMode; getEncryption().setPathToSecretKey(batchSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); @@ -58,13 +58,13 @@ public class ModuleSettings extends Settings { private void addProjectProperties(ProjectDefinition moduleDefinition, GlobalSettings batchSettings) { addProperties(batchSettings.getProperties()); - addProperties(projectReferentials.settings(moduleDefinition.getKeyWithBranch())); + addProperties(projectSettingsRepo.settings(moduleDefinition.getKeyWithBranch())); } private void addBuildProperties(ProjectDefinition project) { List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project); for (ProjectDefinition p : orderedProjects) { - addProperties(p.getProperties()); + addProperties(p.properties()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java index 8f42e7bc366..6266bf44e6c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java @@ -55,7 +55,6 @@ public class ProjectLock implements Startable { if (lockFile == null) { failAlreadyInProgress(null); } - } catch (OverlappingFileLockException e) { failAlreadyInProgress(e); } catch (IOException e) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java index 96d7bad1c5c..81b54609351 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.apache.commons.lang.ArrayUtils; +import org.sonar.api.batch.AnalysisMode; +import org.apache.commons.lang.ArrayUtils; import org.sonar.batch.analysis.AnalysisProperties; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; @@ -108,12 +109,16 @@ public class ProjectReactorBuilder { */ private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, CoreProperties.WORKING_DIRECTORY, PROPERTY_MODULES, CoreProperties.PROJECT_DESCRIPTION_PROPERTY); + + private static final String NON_ASSOCIATED_PROJECT_KEY = "project"; - private AnalysisProperties taskProps; + private final AnalysisProperties taskProps; + private final AnalysisMode analysisMode; private File rootProjectWorkDir; - public ProjectReactorBuilder(AnalysisProperties props) { + public ProjectReactorBuilder(AnalysisProperties props, AnalysisMode analysisMode) { this.taskProps = props; + this.analysisMode = analysisMode; } public ProjectReactor execute() { @@ -160,8 +165,16 @@ public class ProjectReactorBuilder { extractPropertiesByModule(propertiesByModuleId, moduleId, currentModuleProperties); } } - + + private static void prepareNonAssociatedProject(Map<String, String> props, AnalysisMode mode) { + if(mode.isIssues() && !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY)) { + props.put(CoreProperties.PROJECT_KEY_PROPERTY, NON_ASSOCIATED_PROJECT_KEY); + } + } + protected ProjectDefinition defineRootProject(Map<String, String> rootProperties, @Nullable ProjectDefinition parent) { + prepareNonAssociatedProject(rootProperties, analysisMode); + if (rootProperties.containsKey(PROPERTY_MODULES)) { checkMandatoryProperties(rootProperties, MANDATORY_PROPERTIES_FOR_MULTIMODULE_PROJECT); } else { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java index b53e457ab7c..a856b44b894 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java @@ -28,7 +28,6 @@ import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; -import org.sonar.api.utils.SonarException; import org.sonar.core.component.ComponentKeys; /** @@ -58,7 +57,7 @@ public class ProjectReactorValidator { validateBranch(validationMessages, branch); if (!validationMessages.isEmpty()) { - throw new SonarException("Validation of project reactor failed:\n o " + Joiner.on("\n o ").join(validationMessages)); + throw new IllegalStateException("Validation of project reactor failed:\n o " + Joiner.on("\n o ").join(validationMessages)); } } 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 b00888779cf..d6f58f0852a 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,6 +19,16 @@ */ package org.sonar.batch.scan; +import org.sonar.batch.repository.DefaultProjectRepositoriesFactory; + +import org.sonar.batch.repository.QualityProfileProvider; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.DefaultProjectSettingsLoader; +import org.sonar.batch.repository.ProjectSettingsProvider; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.rule.ActiveRulesLoader; import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.analysis.AnalysisWSLoaderProvider; import org.sonar.batch.analysis.AnalysisTempFolderProvider; @@ -69,7 +79,6 @@ import org.sonar.batch.report.MetadataPublisher; import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.report.SourcePublisher; import org.sonar.batch.report.TestExecutionAndCoveragePublisher; -import org.sonar.batch.repository.ProjectRepositoriesProvider; import org.sonar.batch.repository.language.DefaultLanguagesRepository; import org.sonar.batch.rule.ActiveRulesProvider; import org.sonar.batch.scan.filesystem.InputPathCache; @@ -116,6 +125,7 @@ public class ProjectScanContainer extends ComponentContainer { props, DefaultAnalysisMode.class, ProjectReactorBuilder.class, + DefaultProjectRepositoriesFactory.class, new MutableProjectReactorProvider(), new ImmutableProjectReactorProvider(), ProjectBuildersExecutor.class, @@ -126,7 +136,6 @@ public class ProjectScanContainer extends ComponentContainer { DefaultProjectTree.class, ProjectExclusions.class, ProjectReactorValidator.class, - new ProjectRepositoriesProvider(), new AnalysisWSLoaderProvider(), CodeColorizers.class, MetricProvider.class, @@ -136,6 +145,7 @@ public class ProjectScanContainer extends ComponentContainer { Caches.class, BatchComponentCache.class, DefaultIssueCallback.class, + new ProjectSettingsProvider(), // temp new AnalysisTempFolderProvider(), @@ -146,6 +156,7 @@ public class ProjectScanContainer extends ComponentContainer { // rules new ActiveRulesProvider(), + new QualityProfileProvider(), // issues IssueUpdater.class, @@ -153,8 +164,8 @@ public class ProjectScanContainer extends ComponentContainer { IssueWorkflow.class, IssueCache.class, DefaultProjectIssues.class, - LocalIssueTracking.class, ServerIssueRepository.class, + LocalIssueTracking.class, // metrics DefaultMetricFinder.class, @@ -190,9 +201,16 @@ public class ProjectScanContainer extends ComponentContainer { ScanTaskObservers.class, UserRepositoryLoader.class); - addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class); addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class); + addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class); + addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class); + addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); + addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); + } + + private boolean isProjectAssociated() { + return !getComponentByType(DefaultAnalysisMode.class).isNotAssociated(); } private void addBatchExtensions() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java index 7cd20223d20..df9b5135100 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.collect.ImmutableMap; import java.util.Map; @@ -32,7 +33,6 @@ import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; import org.sonar.batch.bootstrap.DroppedPropertyChecker; import org.sonar.batch.bootstrap.GlobalSettings; -import org.sonar.batch.protocol.input.ProjectRepositories; public class ProjectSettings extends Settings { @@ -45,11 +45,11 @@ public class ProjectSettings extends Settings { ); private final GlobalSettings globalSettings; - private final ProjectRepositories projectRepositories; + private final ProjectSettingsRepo projectRepositories; private final DefaultAnalysisMode mode; public ProjectSettings(ProjectReactor reactor, GlobalSettings globalSettings, PropertyDefinitions propertyDefinitions, - ProjectRepositories projectRepositories, DefaultAnalysisMode mode) { + ProjectSettingsRepo projectRepositories, DefaultAnalysisMode mode) { super(propertyDefinitions); this.mode = mode; getEncryption().setPathToSecretKey(globalSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java index 36131c29331..51232ec5701 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java @@ -19,21 +19,22 @@ */ package org.sonar.batch.scan.filesystem; +import org.sonar.batch.repository.ProjectSettingsRepo; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; import org.sonar.batch.protocol.input.FileData; -import org.sonar.batch.protocol.input.ProjectRepositories; class StatusDetection { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettings; - StatusDetection(ProjectRepositories projectReferentials) { - this.projectReferentials = projectReferentials; + StatusDetection(ProjectSettingsRepo projectSettings) { + this.projectSettings = projectSettings; } InputFile.Status status(String projectKey, String relativePath, String hash) { - FileData fileDataPerPath = projectReferentials.fileData(projectKey, relativePath); + FileData fileDataPerPath = projectSettings.fileData(projectKey, relativePath); if (fileDataPerPath == null) { return InputFile.Status.ADDED; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java index 643f915cafc..f372b171df2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java @@ -19,15 +19,16 @@ */ package org.sonar.batch.scan.filesystem; +import org.sonar.batch.repository.ProjectSettingsRepo; + import org.sonar.api.batch.BatchSide; -import org.sonar.batch.protocol.input.ProjectRepositories; @BatchSide public class StatusDetectionFactory { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectReferentials; - public StatusDetectionFactory(ProjectRepositories projectReferentials) { + public StatusDetectionFactory(ProjectSettingsRepo projectReferentials) { this.projectReferentials = projectReferentials; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java index 62144aa09d0..7d24836072d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java @@ -19,8 +19,11 @@ */ package org.sonar.batch.scm; +import org.sonar.batch.repository.ProjectSettingsRepo; + import java.util.LinkedList; import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -33,7 +36,6 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.protocol.input.FileData; -import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.filesystem.InputPathCache; @@ -44,16 +46,16 @@ public final class ScmSensor implements Sensor { private final ProjectDefinition projectDefinition; private final ScmConfiguration configuration; private final FileSystem fs; - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettings; private final BatchComponentCache resourceCache; private final ReportPublisher publishReportJob; public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration, - ProjectRepositories projectReferentials, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache, + ProjectSettingsRepo projectSettings, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache, ReportPublisher publishReportJob) { this.projectDefinition = projectDefinition; this.configuration = configuration; - this.projectReferentials = projectReferentials; + this.projectSettings = projectSettings; this.fs = fs; this.resourceCache = resourceCache; this.publishReportJob = publishReportJob; @@ -95,7 +97,7 @@ public final class ScmSensor implements Sensor { if (configuration.forceReloadAll()) { addIfNotEmpty(filesToBlame, f); } else { - FileData fileData = projectReferentials.fileData(projectDefinition.getKeyWithBranch(), f.relativePath()); + FileData fileData = projectSettings.fileData(projectDefinition.getKeyWithBranch(), f.relativePath()); if (f.status() != Status.SAME || fileData == null || fileData.needBlame()) { addIfNotEmpty(filesToBlame, f); } |