diff options
232 files changed, 3876 insertions, 2366 deletions
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/AlertsEmailTemplate.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/AlertsEmailTemplate.java index a66444ad838..2ea0faf34f2 100644 --- a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/AlertsEmailTemplate.java +++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/AlertsEmailTemplate.java @@ -65,7 +65,7 @@ public class AlertsEmailTemplate extends EmailTemplate { .setMessage(messageBody); } - private String generateSubject(String projectName, String alertLevel, boolean isNewAlert) { + private static String generateSubject(String projectName, String alertLevel, boolean isNewAlert) { StringBuilder subjectBuilder = new StringBuilder(); if (Metric.Level.OK.toString().equals(alertLevel)) { subjectBuilder.append("\"").append(projectName).append("\" is back to green"); @@ -1159,8 +1159,8 @@ </scm> <ciManagement> - <system>bamboo</system> - <url>http://bamboo.ci.codehaus.org/browse/SONAR-DEF</url> + <system>travis-ci</system> + <url>https://travis-ci.org/SonarSource/sonarqube</url> </ciManagement> <licenses> diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java index 4919aa890d4..d4bad3f9b50 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java @@ -33,8 +33,9 @@ import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.step.PersistFileSourcesStep; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; @@ -49,12 +50,15 @@ public class PersistFileSourcesStepTest { public static final int NUMBER_OF_LINES = 1000; public static final String PROJECT_UUID = Uuids.create(); - DbComponentsRefCache dbComponentsRefCache = new DbComponentsRefCache(); - @Rule public DbTester dbTester = new DbTester(); + @Rule public Benchmark benchmark = new Benchmark(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); @@ -70,7 +74,7 @@ public class PersistFileSourcesStepTest { long start = System.currentTimeMillis(); - PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, dbComponentsRefCache, reportReader); + PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, treeRootHolder, reportReader); step.execute(); long end = System.currentTimeMillis(); @@ -92,17 +96,17 @@ public class PersistFileSourcesStepTest { .setRef(1) .setType(Constants.ComponentType.PROJECT); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, "PROJECT", PROJECT_UUID)); - + List<Component> components = new ArrayList<>(); for (int fileRef = 2; fileRef <= NUMBER_OF_FILES + 1; fileRef++) { - generateFileReport(fileRef); + components.add(generateFileReport(fileRef)); project.addChildRef(fileRef); } + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, "PROJECT", components.toArray(new Component[components.size()]))); reportReader.putComponent(project.build()); } - private void generateFileReport(int fileRef) throws IOException { + private Component generateFileReport(int fileRef) throws IOException { LineData lineData = new LineData(); for (int line = 1; line <= NUMBER_OF_LINES; line++) { lineData.generateLineData(line); @@ -113,14 +117,14 @@ public class PersistFileSourcesStepTest { .setLines(NUMBER_OF_LINES) .build()); - dbComponentsRefCache.addComponent(fileRef, new DbComponent((long) fileRef, "PROJECT:" + fileRef, Uuids.create())); - reportReader.putFileSourceLines(fileRef, lineData.lines); reportReader.putCoverage(fileRef, lineData.coverages); reportReader.putChangesets(lineData.changesetsBuilder.setComponentRef(fileRef).build()); reportReader.putSyntaxHighlighting(fileRef, lineData.highlightings); reportReader.putSymbols(fileRef, lineData.symbols); reportReader.putDuplications(fileRef, lineData.duplications); + + return new DumbComponent(Component.Type.FILE, fileRef, Uuids.create(), "PROJECT:" + fileRef); } private static class LineData { diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalAction.java index 23956d9bbba..c87da9be3a7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalAction.java @@ -80,7 +80,6 @@ public class GlobalAction implements BatchWsAction { private void addMetrics(GlobalRepositories ref, DbSession session) { for (MetricDto metric : dbClient.metricDao().selectEnabled(session)) { - Boolean optimizedBestValue = metric.isOptimizedBestValue(); ref.addMetric( new org.sonar.batch.protocol.input.Metric(metric.getId(), metric.getKey(), metric.getValueType(), @@ -91,7 +90,7 @@ public class GlobalAction implements BatchWsAction { metric.isUserManaged(), metric.getWorstValue(), metric.getBestValue(), - optimizedBestValue != null ? optimizedBestValue : false)); + metric.isOptimizedBestValue())); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java index bd699806a7e..5906dd51515 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java @@ -24,6 +24,12 @@ import com.google.common.base.Function; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; @@ -54,14 +60,6 @@ import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import org.sonar.server.user.UserSession; -import javax.annotation.Nullable; - -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; @@ -188,7 +186,7 @@ public class ProjectRepositoryLoader { } } - private Map<String, String> getPropertiesMap(List<PropertyDto> propertyDtos, boolean hasScanPerm) { + private static Map<String, String> getPropertiesMap(List<PropertyDto> propertyDtos, boolean hasScanPerm) { Map<String, String> properties = newHashMap(); for (PropertyDto propertyDto : propertyDtos) { String key = propertyDto.getKey(); @@ -292,7 +290,7 @@ public class ProjectRepositoryLoader { } } - private void addFileData(DbSession session, ProjectRepositories ref, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) { + private static void addFileData(DbSession session, ProjectRepositories ref, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) { Map<String, String> moduleKeysByUuid = newHashMap(); for (ComponentDto module : moduleChildren) { moduleKeysByUuid.put(module.uuid(), module.key()); @@ -320,7 +318,7 @@ public class ProjectRepositoryLoader { } } - private Map<String, String> moduleUuidsByKey(ComponentDto module, List<ComponentDto> moduleChildren) { + private static Map<String, String> moduleUuidsByKey(ComponentDto module, List<ComponentDto> moduleChildren) { Map<String, String> moduleUuidsByKey = newHashMap(); for (ComponentDto componentDto : moduleChildren) { moduleUuidsByKey.put(componentDto.key(), componentDto.uuid()); @@ -328,7 +326,7 @@ public class ProjectRepositoryLoader { return moduleUuidsByKey; } - private Map<String, Long> moduleIdsByKey(ComponentDto module, List<ComponentDto> moduleChildren) { + private static Map<String, Long> moduleIdsByKey(ComponentDto module, List<ComponentDto> moduleChildren) { Map<String, Long> moduleIdsByKey = newHashMap(); for (ComponentDto componentDto : moduleChildren) { moduleIdsByKey.put(componentDto.key(), componentDto.getId()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultComponentFinder.java index 409cbc84208..698c46a57f7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultComponentFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultComponentFinder.java @@ -22,15 +22,14 @@ package org.sonar.server.component; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.List; +import java.util.Set; import org.sonar.api.component.Component; import org.sonar.api.utils.Paging; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import java.util.Collection; -import java.util.List; -import java.util.Set; - import static com.google.common.collect.Lists.newArrayList; /** @@ -98,7 +97,7 @@ public class DefaultComponentFinder { } } - private Collection<? extends Component> pagedComponents(Collection<? extends Component> components, Paging paging) { + private static Collection<? extends Component> pagedComponents(Collection<? extends Component> components, Paging paging) { Set<Component> pagedComponents = Sets.newLinkedHashSet(); int index = 0; for (Component component : components) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java index 1609cfd1282..95d920b3a04 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java @@ -20,6 +20,10 @@ package org.sonar.server.component.ws; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.BooleanUtils; import org.sonar.api.i18n.I18n; import org.sonar.api.measures.CoreMetrics; @@ -41,12 +45,6 @@ import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; @@ -149,7 +147,7 @@ public class AppAction implements RequestHandler { json.prop("fav", isFavourite); } - private void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) { + private static void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) { boolean hasBrowsePermission = userSession.hasComponentPermission(UserRole.USER, component.key()); json.prop("canMarkAsFavourite", userSession.isLoggedIn() && hasBrowsePermission); json.prop("canCreateManualIssue", userSession.isLoggedIn() && hasBrowsePermission); @@ -168,7 +166,7 @@ public class AppAction implements RequestHandler { json.endObject(); } - private MeasureDto coverageMeasure(Map<String, MeasureDto> measuresByMetricKey) { + private static MeasureDto coverageMeasure(Map<String, MeasureDto> measuresByMetricKey) { MeasureDto overallCoverage = measuresByMetricKey.get(CoreMetrics.OVERALL_COVERAGE_KEY); MeasureDto itCoverage = measuresByMetricKey.get(CoreMetrics.IT_COVERAGE_KEY); MeasureDto utCoverage = measuresByMetricKey.get(CoreMetrics.COVERAGE_KEY); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/EventsWs.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/EventsWs.java index 8f566aff6d2..f6e2501c5a1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/EventsWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/EventsWs.java @@ -36,7 +36,7 @@ public class EventsWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java index 1d1256f9af3..3b398223e33 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java @@ -120,7 +120,7 @@ public class SearchAction implements RequestHandler { json.close(); } - private Set<Long> pagedProjectIds(Collection<Long> projectIds, SearchOptions options) { + private static Set<Long> pagedProjectIds(Collection<Long> projectIds, SearchOptions options) { Set<Long> results = Sets.newLinkedHashSet(); int index = 0; for (Long projectId : projectIds) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java index 6477b7aa6a2..e6e3556393b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java @@ -22,7 +22,7 @@ package org.sonar.server.computation.batch; import java.io.File; import java.util.Objects; -public class BatchReportDirectoryHolderImpl implements BatchReportDirectoryHolder, MutableBatchReportDirectoryHolder { +public class BatchReportDirectoryHolderImpl implements MutableBatchReportDirectoryHolder { private File directory; @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java index 13cb2d6f960..c48e0289b1a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java @@ -125,38 +125,45 @@ public class BatchReportReaderImpl implements BatchReportReader { public CloseableIterator<String> readFileSource(int fileRef) { File file = delegate.readFileSource(fileRef); if (file == null) { - throw new IllegalStateException("Unable to find source for file #" + fileRef + ". File does not exist: " + file); + throw new IllegalStateException("Unable to find source for file #" + fileRef); } try { - final LineIterator lineIterator = IOUtils.lineIterator(FileUtils.openInputStream(file), StandardCharsets.UTF_8); - return new CloseableIterator<String>() { - @Override - public boolean hasNext() { - return lineIterator.hasNext(); - } - - @Override - public String next() { - return lineIterator.next(); - } - - @Override - protected String doNext() { - // never called anyway - throw new NoSuchElementException("Empty closeable Iterator has no element"); - } - - @Override - protected void doClose() throws Exception { - lineIterator.close(); - } - }; + return new CloseableLineIterator(IOUtils.lineIterator(FileUtils.openInputStream(file), StandardCharsets.UTF_8)); } catch (IOException e) { throw new IllegalStateException("Fail to traverse file: " + file, e); } } + private static class CloseableLineIterator extends CloseableIterator<String> { + private final LineIterator lineIterator; + + public CloseableLineIterator(LineIterator lineIterator) { + this.lineIterator = lineIterator; + } + + @Override + public boolean hasNext() { + return lineIterator.hasNext(); + } + + @Override + public String next() { + return lineIterator.next(); + } + + @Override + protected String doNext() { + // never called anyway + throw new NoSuchElementException("Empty closeable Iterator has no element"); + } + + @Override + protected void doClose() throws Exception { + lineIterator.close(); + } + } + @Override public CloseableIterator<BatchReport.Test> readTests(int testFileRef) { File file = delegate.readTests(testFileRef); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java index cefa2dfa615..ac25a24b155 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java @@ -53,7 +53,9 @@ public interface Component { */ String getKey(); - // FIXME we should not expose a batch specific information + /** + * The component ref in the batch report. + */ int getRef(); List<Component> getChildren(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java index f5f1d5ba0bb..3e2e8f6a484 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java @@ -31,7 +31,7 @@ import static com.google.common.collect.Iterables.filter; public class ComponentImpl implements Component { private final Type type; - private final BatchReport.Component component; + private final int ref; private final List<Component> children; // Mutable values @@ -39,7 +39,7 @@ public class ComponentImpl implements Component { private String uuid; public ComponentImpl(BatchReport.Component component, @Nullable Iterable<Component> children) { - this.component = component; + this.ref = component.getRef(); this.type = convertType(component.getType()); this.children = children == null ? Collections.<Component>emptyList() : copyOf(filter(children, notNull())); } @@ -66,9 +66,10 @@ public class ComponentImpl implements Component { @Override public int getRef() { - return component.getRef(); + return ref; } + @Override public String getUuid() { if (uuid == null) { throw new UnsupportedOperationException(String.format("Component uuid of ref '%s' has not be fed yet", getRef())); @@ -81,6 +82,7 @@ public class ComponentImpl implements Component { return this; } + @Override public String getKey() { if (key == null) { throw new UnsupportedOperationException(String.format("Component key of ref '%s' has not be fed yet", getRef())); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DbComponentsRefCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DbIdsRepository.java index 3c9dda687f4..a07059b1e43 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DbComponentsRefCache.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DbIdsRepository.java @@ -24,53 +24,33 @@ import java.util.HashMap; import java.util.Map; /** - * Cache of components (id, uuid and key) that can be used in the persistence steps - * Snapshot id will also be added in this cache + * Cache of persisted component (component id) that can be used in the persistence steps */ -public class DbComponentsRefCache { +public class DbIdsRepository { - private final Map<Integer, DbComponent> componentsByRef; + private final Map<Integer, Long> componentIdsByRef; - public DbComponentsRefCache() { - componentsByRef = new HashMap<>(); + public DbIdsRepository() { + componentIdsByRef = new HashMap<>(); } - public DbComponentsRefCache addComponent(Integer ref, DbComponent component) { - componentsByRef.put(ref, component); + public DbIdsRepository setComponentId(Component component, long componentId) { + int ref = component.getRef(); + Long existingComponentId = componentIdsByRef.get(ref); + if (existingComponentId != null) { + throw new IllegalArgumentException(String.format("Component ref '%s' has already a component id", ref)); + } + componentIdsByRef.put(ref, componentId); return this; } - public DbComponent getByRef(Integer ref) { - DbComponent component = componentsByRef.get(ref); - if (component == null) { - throw new IllegalArgumentException(String.format("Component ref '%s' does not exists", ref)); + public long getComponentId(Component component) { + int ref = component.getRef(); + Long componentId = componentIdsByRef.get(ref); + if (componentId == null) { + throw new IllegalArgumentException(String.format("Component ref '%s' has no component id", ref)); } - return componentsByRef.get(ref); + return componentId; } - public static class DbComponent { - - private Long id; - private String uuid; - private String key; - - public DbComponent(Long id, String key, String uuid) { - this.id = id; - this.key = key; - this.uuid = uuid; - } - - public Long getId() { - return id; - } - - public String getKey() { - return key; - } - - public String getUuid() { - return uuid; - } - - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java index c368b15bccc..4f4dc144870 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareVisitor.java @@ -43,17 +43,17 @@ public abstract class DepthTraversalTypeAwareVisitor implements TypeAwareVisitor } if (order == Order.PRE_ORDER) { - visitNode(component); + visitAny(component); } visitChildren(component); if (order == Order.POST_ORDER) { - visitNode(component); + visitAny(component); } } - private void visitNode(Component component) { + protected void visitAny(Component component) { switch (component.getType()) { case PROJECT: visitProject(component); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java index 4b990fc9845..6d6d03348e6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java @@ -26,4 +26,12 @@ public interface TreeRootHolder { * @throws IllegalStateException if the holder is empty (ie. there is no root yet) */ Component getRoot(); + + /** + * Return a component by its batch reference + * + * @throws IllegalStateException if the holder is empty (ie. there is no root yet) + * @throws IllegalArgumentException if there's no component for the reference + */ + Component getComponentByRef(int ref); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java index 1be5c91b1b0..fc0aee4129c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java @@ -19,24 +19,54 @@ */ package org.sonar.server.computation.component; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; + /** * Holds the reference to the root of the {@link Component} tree for the current CE run. */ public class TreeRootHolderImpl implements MutableTreeRootHolder { + private Component root; + private Map<Integer, Component> componentsByRef = new HashMap<>(); @Override public void setRoot(Component newRoot) { this.root = Objects.requireNonNull(newRoot); + feedComponentsByRef(); } @Override public Component getRoot() { + checkRoot(); + return this.root; + } + + @Override + public Component getComponentByRef(int ref) { + checkRoot(); + Component component = componentsByRef.get(ref); + if (component == null) { + throw new IllegalArgumentException(String.format("Component '%s' hasn't been found", ref)); + } + return component; + } + + private void checkRoot() { if (this.root == null) { throw new IllegalStateException("Root has not been created yet"); } - return this.root; + } + + private void feedComponentsByRef() { + new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) { + @Override + public void visitAny(Component component) { + componentsByRef.put(component.getRef(), component); + } + }.visit(root); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java index 2c96179620d..897160138c1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java @@ -36,7 +36,7 @@ import org.sonar.server.computation.ReportQueue; import org.sonar.server.computation.activity.ActivityManager; import org.sonar.server.computation.batch.BatchReportDirectoryHolderImpl; import org.sonar.server.computation.batch.BatchReportReaderImpl; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.ProjectSettingsRepository; import org.sonar.server.computation.component.TreeRootHolderImpl; import org.sonar.server.computation.event.EventRepositoryImpl; @@ -126,7 +126,7 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co ProjectSettingsRepository.class, // component caches - DbComponentsRefCache.class, + DbIdsRepository.class, // issues ScmAccountCacheLoader.class, @@ -147,20 +147,18 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co ); } + @Override public void process() { // calls the first getComponentByType(ComputationService.class).process(); } + @Override public void cleanup() { - ReportQueue.Item item = null; try { - item = getComponentByType(ReportQueue.Item.class); stopComponents(); } catch (Throwable t) { - Loggers.get(ComputeEngineContainerImpl.class).error( - String.format("Cleanup of container for item '%s' failed", item == null ? null : item.dto), - t); + Loggers.get(ComputeEngineContainerImpl.class).error(String.format("Cleanup of container for item '%s' failed", item), t); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java index 61718b66689..ed9f648bab0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java @@ -44,8 +44,6 @@ public class MeasureRepositoryImpl implements MeasureRepository { public Optional<MeasureDto> findPrevious(Component component, Metric<?> metric) { try (DbSession dbSession = dbClient.openSession(false)) { return Optional.fromNullable( - // TODO replace component.getKey() by ${link #getKey} as component.getKey() is only for project/module and does not take into - // account usage of the branch dbClient.measureDao().findByComponentKeyAndMetricKey(dbSession, component.getKey(), metric.getKey()) ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/source/SymbolsLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SymbolsLineReader.java index db57bcb52c8..ce8bdf2c609 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/source/SymbolsLineReader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SymbolsLineReader.java @@ -20,9 +20,6 @@ package org.sonar.server.computation.source; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.server.source.db.FileSourceDb; - import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -32,6 +29,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.source.db.FileSourceDb; import static com.google.common.collect.Lists.newArrayList; @@ -68,7 +67,7 @@ public class SymbolsLineReader implements LineReader { } } - private void appendSymbol(StringBuilder lineSymbol, BatchReport.Range range, int line, int symbolId, String sourceLine) { + private static void appendSymbol(StringBuilder lineSymbol, BatchReport.Range range, int line, int symbolId, String sourceLine) { if (matchLine(range, line)) { String offsets = RangeOffsetHelper.offsetToString(range, line, sourceLine.length()); if (!offsets.isEmpty()) { @@ -105,7 +104,7 @@ public class SymbolsLineReader implements LineReader { return range.getStartLine() <= line && range.getEndLine() >= line; } - private Map<BatchReport.Symbols.Symbol, Integer> createIdsBySymbolMap(List<BatchReport.Symbols.Symbol> symbols) { + private static Map<BatchReport.Symbols.Symbol, Integer> createIdsBySymbolMap(List<BatchReport.Symbols.Symbol> symbols) { Map<BatchReport.Symbols.Symbol, Integer> map = new HashMap<>(); int symbolId = 1; for (BatchReport.Symbols.Symbol symbol : symbols) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ApplyPermissionsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ApplyPermissionsStep.java index e67e6b15809..0b30dbf19e5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ApplyPermissionsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ApplyPermissionsStep.java @@ -24,7 +24,7 @@ import org.sonar.api.resources.Qualifiers; import org.sonar.core.permission.PermissionFacade; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; import org.sonar.server.issue.index.IssueAuthorizationIndexer; @@ -35,15 +35,15 @@ import org.sonar.server.issue.index.IssueAuthorizationIndexer; public class ApplyPermissionsStep implements ComputationStep { private final DbClient dbClient; - private final DbComponentsRefCache dbComponentsRefCache; + private final DbIdsRepository dbIdsRepository; private final IssueAuthorizationIndexer indexer; private final PermissionFacade permissionFacade; private final TreeRootHolder treeRootHolder; - public ApplyPermissionsStep(DbClient dbClient, DbComponentsRefCache dbComponentsRefCache, IssueAuthorizationIndexer indexer, + public ApplyPermissionsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, IssueAuthorizationIndexer indexer, PermissionFacade permissionFacade, TreeRootHolder treeRootHolder) { this.dbClient = dbClient; - this.dbComponentsRefCache = dbComponentsRefCache; + this.dbIdsRepository = dbIdsRepository; this.indexer = indexer; this.permissionFacade = permissionFacade; this.treeRootHolder = treeRootHolder; @@ -53,7 +53,7 @@ public class ApplyPermissionsStep implements ComputationStep { public void execute() { DbSession session = dbClient.openSession(false); try { - long projectId = dbComponentsRefCache.getByRef(treeRootHolder.getRoot().getRef()).getId(); + long projectId = dbIdsRepository.getComponentId(treeRootHolder.getRoot()); if (permissionFacade.countComponentPermissions(session, projectId) == 0) { permissionFacade.grantDefaultRoles(session, projectId, Qualifiers.PROJECT); session.commit(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java index a967fd10526..25190675f4c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java @@ -52,7 +52,7 @@ public class ComputationSteps { QualityProfileEventsStep.class, // Persist data - PersistComponentsStep.class, + PersistComponentsAndSnapshotsStep.class, PersistNumberOfDaysSinceLastCommitStep.class, PersistMeasuresStep.class, PersistIssuesStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexComponentsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexComponentsStep.java index 75b8d6acb2b..53285259121 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexComponentsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexComponentsStep.java @@ -21,26 +21,27 @@ package org.sonar.server.computation.step; import org.sonar.core.resource.ResourceIndexerDao; -import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.TreeRootHolder; /** * Components are currently indexed in db table RESOURCE_INDEX, not in Elasticsearch */ public class IndexComponentsStep implements ComputationStep { + private final ResourceIndexerDao resourceIndexerDao; - private final DbComponentsRefCache dbComponentsRefCache; - private final BatchReportReader reportReader; + private final DbIdsRepository dbIdsRepository; + private final TreeRootHolder treeRootHolder; - public IndexComponentsStep(ResourceIndexerDao resourceIndexerDao, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public IndexComponentsStep(ResourceIndexerDao resourceIndexerDao, DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder) { this.resourceIndexerDao = resourceIndexerDao; - this.dbComponentsRefCache = dbComponentsRefCache; - this.reportReader = reportReader; + this.dbIdsRepository = dbIdsRepository; + this.treeRootHolder = treeRootHolder; } @Override public void execute() { - resourceIndexerDao.indexProject(dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()).getId()); + resourceIndexerDao.indexProject(dbIdsRepository.getComponentId(treeRootHolder.getRoot())); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexSourceLinesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexSourceLinesStep.java index ee709b8e154..237871065bf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexSourceLinesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexSourceLinesStep.java @@ -19,25 +19,22 @@ */ package org.sonar.server.computation.step; -import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.source.index.SourceLineIndexer; public class IndexSourceLinesStep implements ComputationStep { private final SourceLineIndexer indexer; - private final DbComponentsRefCache dbComponentsRefCache; - private final BatchReportReader reportReader; + private final TreeRootHolder treeRootHolder; - public IndexSourceLinesStep(SourceLineIndexer indexer, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public IndexSourceLinesStep(SourceLineIndexer indexer, TreeRootHolder treeRootHolder) { this.indexer = indexer; - this.dbComponentsRefCache = dbComponentsRefCache; - this.reportReader = reportReader; + this.treeRootHolder = treeRootHolder; } @Override public void execute() { - indexer.index(dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()).getUuid()); + indexer.index(treeRootHolder.getRoot().getUuid()); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexTestsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexTestsStep.java index bd5666e539a..663b0c2030e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexTestsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IndexTestsStep.java @@ -20,25 +20,22 @@ package org.sonar.server.computation.step; -import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.test.index.TestIndexer; public class IndexTestsStep implements ComputationStep { private final TestIndexer indexer; - private final DbComponentsRefCache dbComponentsRefCache; - private final BatchReportReader reportReader; + private final TreeRootHolder treeRootHolder; - public IndexTestsStep(TestIndexer indexer, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public IndexTestsStep(TestIndexer indexer, TreeRootHolder treeRootHolder) { this.indexer = indexer; - this.dbComponentsRefCache = dbComponentsRefCache; - this.reportReader = reportReader; + this.treeRootHolder = treeRootHolder; } @Override public void execute() { - indexer.index(dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()).getUuid()); + indexer.index(treeRootHolder.getRoot().getUuid()); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStep.java new file mode 100644 index 00000000000..844f00e3e2f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStep.java @@ -0,0 +1,346 @@ +/* + * 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.server.computation.step; + +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Scopes; +import org.sonar.api.utils.System2; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.component.SnapshotDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.util.NonNullInputFunction; +import org.sonar.server.computation.batch.BatchReportReader; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.TreeRootHolder; +import org.sonar.server.db.DbClient; + +/** + * Persist components and snapshots + * Also feed the components cache {@link DbIdsRepository} + */ +public class PersistComponentsAndSnapshotsStep implements ComputationStep { + + private final System2 system2; + private final DbClient dbClient; + private final TreeRootHolder treeRootHolder; + private final BatchReportReader reportReader; + + private final DbIdsRepository dbIdsRepositor; + + public PersistComponentsAndSnapshotsStep(System2 system2, DbClient dbClient, TreeRootHolder treeRootHolder, BatchReportReader reportReader, DbIdsRepository dbIdsRepositor) { + this.system2 = system2; + this.dbClient = dbClient; + this.treeRootHolder = treeRootHolder; + this.reportReader = reportReader; + this.dbIdsRepositor = dbIdsRepositor; + } + + @Override + public void execute() { + DbSession session = dbClient.openSession(false); + try { + org.sonar.server.computation.component.Component root = treeRootHolder.getRoot(); + List<ComponentDto> components = dbClient.componentDao().selectComponentsFromProjectKey(session, root.getKey()); + Map<String, ComponentDto> componentDtosByKey = componentDtosByKey(components); + PersisComponentExecutor componentContext = new PersisComponentExecutor(session, componentDtosByKey, reportReader, reportReader.readMetadata().getAnalysisDate()); + + componentContext.recursivelyProcessComponent(root, null, null); + session.commit(); + } finally { + session.close(); + } + } + + private class PersisComponentExecutor { + + private final BatchReportReader reportReader; + private final Map<String, ComponentDto> componentDtosByKey; + private final DbSession dbSession; + private final long analysisDate; + + private ComponentDto project; + private SnapshotDto projectSnapshot; + + public PersisComponentExecutor(DbSession dbSession, Map<String, ComponentDto> componentDtosByKey, BatchReportReader reportReader, long analysisDate) { + this.reportReader = reportReader; + this.componentDtosByKey = componentDtosByKey; + this.dbSession = dbSession; + this.analysisDate = analysisDate; + } + + private void recursivelyProcessComponent(Component component, @Nullable ComponentDto lastModule, @Nullable SnapshotDto parentSnapshot) { + BatchReport.Component reportComponent = reportReader.readComponent(component.getRef()); + + switch (component.getType()) { + case PROJECT: + PersistedComponent persistedProject = processProject(component, reportComponent); + this.project = persistedProject.componentDto; + this.projectSnapshot = persistedProject.parentSnapshot; + processChildren(component, project, persistedProject.parentSnapshot); + break; + case MODULE: + PersistedComponent persistedModule = processModule(component, reportComponent, nonNullLastModule(lastModule), nonNullParentSnapshot(parentSnapshot)); + processChildren(component, persistedModule.componentDto, persistedModule.parentSnapshot); + break; + case DIRECTORY: + PersistedComponent persistedDirectory = processDirectory(component, reportComponent, nonNullLastModule(lastModule), nonNullParentSnapshot(parentSnapshot)); + processChildren(component, nonNullLastModule(lastModule), persistedDirectory.parentSnapshot); + break; + case FILE: + processFile(component, reportComponent, nonNullLastModule(lastModule), nonNullParentSnapshot(parentSnapshot)); + break; + default: + throw new IllegalStateException(String.format("Unsupported component type '%s'", component.getType())); + } + } + + private void processChildren(Component component, ComponentDto lastModule, SnapshotDto parentSnapshot) { + for (Component child : component.getChildren()) { + recursivelyProcessComponent(child, lastModule, parentSnapshot); + } + } + + private ComponentDto nonNullLastModule(@Nullable ComponentDto lastModule) { + return lastModule == null ? project : lastModule; + } + + private SnapshotDto nonNullParentSnapshot(@Nullable SnapshotDto parentSnapshot) { + return parentSnapshot == null ? projectSnapshot : parentSnapshot; + } + + public PersistedComponent processProject(Component project, BatchReport.Component reportComponent) { + ComponentDto componentDto = createComponentDto(reportComponent, project); + + componentDto.setScope(Scopes.PROJECT); + componentDto.setQualifier(Qualifiers.PROJECT); + componentDto.setName(reportComponent.getName()); + componentDto.setLongName(componentDto.name()); + if (reportComponent.hasDescription()) { + componentDto.setDescription(reportComponent.getDescription()); + } + componentDto.setProjectUuid(componentDto.uuid()); + componentDto.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP); + + ComponentDto projectDto = persistComponent(project.getRef(), componentDto); + SnapshotDto snapshotDto = persistSnapshot(projectDto, reportComponent.getVersion(), null); + + addToCache(project, projectDto, snapshotDto); + + return new PersistedComponent(projectDto, snapshotDto); + } + + public PersistedComponent processModule(Component module, BatchReport.Component reportComponent, ComponentDto lastModule, SnapshotDto parentSnapshot) { + ComponentDto componentDto = createComponentDto(reportComponent, module); + + componentDto.setScope(Scopes.PROJECT); + componentDto.setQualifier(Qualifiers.MODULE); + componentDto.setName(reportComponent.getName()); + componentDto.setLongName(componentDto.name()); + if (reportComponent.hasPath()) { + componentDto.setPath(reportComponent.getPath()); + } + if (reportComponent.hasDescription()) { + componentDto.setDescription(reportComponent.getDescription()); + } + componentDto.setParentProjectId(project.getId()); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath(lastModule.moduleUuidPath() + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP); + + ComponentDto moduleDto = persistComponent(module.getRef(), componentDto); + SnapshotDto snapshotDto = persistSnapshot(moduleDto, reportComponent.getVersion(), parentSnapshot); + + addToCache(module, moduleDto, snapshotDto); + return new PersistedComponent(moduleDto, snapshotDto); + } + + public PersistedComponent processDirectory(org.sonar.server.computation.component.Component directory, BatchReport.Component reportComponent, + ComponentDto lastModule, SnapshotDto parentSnapshot) { + ComponentDto componentDto = createComponentDto(reportComponent, directory); + + componentDto.setScope(Scopes.DIRECTORY); + componentDto.setQualifier(Qualifiers.DIRECTORY); + componentDto.setName(reportComponent.getPath()); + componentDto.setLongName(reportComponent.getPath()); + if (reportComponent.hasPath()) { + componentDto.setPath(reportComponent.getPath()); + } + + componentDto.setParentProjectId(lastModule.getId()); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); + + ComponentDto directoryDto = persistComponent(directory.getRef(), componentDto); + SnapshotDto snapshotDto = persistSnapshot(directoryDto, null, parentSnapshot); + + addToCache(directory, directoryDto, snapshotDto); + return new PersistedComponent(directoryDto, snapshotDto); + } + + public void processFile(org.sonar.server.computation.component.Component file, BatchReport.Component reportComponent, + ComponentDto lastModule, SnapshotDto parentSnapshot) { + ComponentDto componentDto = createComponentDto(reportComponent, file); + + componentDto.setScope(Scopes.FILE); + componentDto.setQualifier(getFileQualifier(reportComponent)); + componentDto.setName(FilenameUtils.getName(reportComponent.getPath())); + componentDto.setLongName(reportComponent.getPath()); + if (reportComponent.hasPath()) { + componentDto.setPath(reportComponent.getPath()); + } + if (reportComponent.hasLanguage()) { + componentDto.setLanguage(reportComponent.getLanguage()); + } + + componentDto.setParentProjectId(lastModule.getId()); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); + + ComponentDto fileDto = persistComponent(file.getRef(), componentDto); + SnapshotDto snapshotDto = persistSnapshot(fileDto, null, parentSnapshot); + + addToCache(file, fileDto, snapshotDto); + } + + private ComponentDto createComponentDto(BatchReport.Component reportComponent, org.sonar.server.computation.component.Component component) { + String componentKey = component.getKey(); + String componentUuid = component.getUuid(); + + ComponentDto componentDto = new ComponentDto(); + componentDto.setUuid(componentUuid); + componentDto.setKey(componentKey); + componentDto.setDeprecatedKey(componentKey); + componentDto.setEnabled(true); + return componentDto; + } + + private ComponentDto persistComponent(int componentRef, ComponentDto componentDto) { + ComponentDto existingComponent = componentDtosByKey.get(componentDto.getKey()); + if (existingComponent == null) { + dbClient.componentDao().insert(dbSession, componentDto); + return componentDto; + } else { + if (updateComponent(existingComponent, componentDto)) { + dbClient.componentDao().update(dbSession, existingComponent); + } + return existingComponent; + } + } + + private SnapshotDto persistSnapshot(ComponentDto componentDto, @Nullable String version, @Nullable SnapshotDto parentSnapshot){ + SnapshotDto snapshotDto = new SnapshotDto(); +// .setRootProjectId(project.getId()) +// .setVersion(version) +// .setComponentId(componentDto.getId()) +// .setQualifier(componentDto.qualifier()) +// .setScope(componentDto.scope()) +// .setCreatedAt(analysisDate) +// .setBuildDate(system2.now()); +// +// if (parentSnapshot != null) { +// snapshotDto +// .setParentId(parentSnapshot.getId()) +// .setRootId(parentSnapshot.getRootId() == null ? parentSnapshot.getId() : parentSnapshot.getRootId()) +// .setDepth(parentSnapshot.getDepth() + 1) +// .setPath(parentSnapshot.getPath() + parentSnapshot.getId() + "."); +// } else { +// snapshotDto +// .setPath("") +// .setDepth(0); +// } +// dbClient.snapshotDao().insert(dbSession, snapshotDto); + return snapshotDto; + } + + private void addToCache(Component component, ComponentDto componentDto, SnapshotDto snapshotDto) { + dbIdsRepositor.setComponentId(component, componentDto.getId()); + } + + private boolean updateComponent(ComponentDto existingComponent, ComponentDto newComponent) { + boolean isUpdated = false; + if (!StringUtils.equals(existingComponent.name(), newComponent.name())) { + existingComponent.setName(newComponent.name()); + isUpdated = true; + } + if (!StringUtils.equals(existingComponent.description(), newComponent.description())) { + existingComponent.setDescription(newComponent.description()); + isUpdated = true; + } + if (!StringUtils.equals(existingComponent.path(), newComponent.path())) { + existingComponent.setPath(newComponent.path()); + isUpdated = true; + } + if (!StringUtils.equals(existingComponent.moduleUuid(), newComponent.moduleUuid())) { + existingComponent.setModuleUuid(newComponent.moduleUuid()); + isUpdated = true; + } + if (!existingComponent.moduleUuidPath().equals(newComponent.moduleUuidPath())) { + existingComponent.setModuleUuidPath(newComponent.moduleUuidPath()); + isUpdated = true; + } + if (!ObjectUtils.equals(existingComponent.parentProjectId(), newComponent.parentProjectId())) { + existingComponent.setParentProjectId(newComponent.parentProjectId()); + isUpdated = true; + } + return isUpdated; + } + + private String getFileQualifier(BatchReport.Component reportComponent) { + return reportComponent.getIsTest() ? Qualifiers.UNIT_TEST_FILE : Qualifiers.FILE; + } + + private class PersistedComponent { + private ComponentDto componentDto; + private SnapshotDto parentSnapshot; + + public PersistedComponent(ComponentDto componentDto, SnapshotDto parentSnapshot) { + this.componentDto = componentDto; + this.parentSnapshot = parentSnapshot; + } + } + + } + + private static Map<String, ComponentDto> componentDtosByKey(List<ComponentDto> components) { + return Maps.uniqueIndex(components, new NonNullInputFunction<ComponentDto, String>() { + @Override + public String doApply(ComponentDto input) { + return input.key(); + } + }); + } + + @Override + public String getDescription() { + return "Feed components and snapshots"; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java deleted file mode 100644 index 629b883697b..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * 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.server.computation.step; - -import com.google.common.collect.Maps; -import java.util.List; -import java.util.Map; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.util.NonNullInputFunction; -import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.TreeRootHolder; -import org.sonar.server.db.DbClient; - -public class PersistComponentsStep implements ComputationStep { - - private final DbClient dbClient; - private final DbComponentsRefCache dbComponentsRefCache; - private final BatchReportReader reportReader; - private final TreeRootHolder treeRootHolder; - - public PersistComponentsStep(DbClient dbClient, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader, TreeRootHolder treeRootHolder) { - this.dbClient = dbClient; - this.dbComponentsRefCache = dbComponentsRefCache; - this.reportReader = reportReader; - this.treeRootHolder = treeRootHolder; - } - - @Override - public void execute() { - DbSession session = dbClient.openSession(false); - try { - Component root = treeRootHolder.getRoot(); - List<ComponentDto> components = dbClient.componentDao().selectComponentsFromProjectKey(session, root.getKey()); - Map<String, ComponentDto> componentDtosByKey = componentDtosByKey(components); - ComponentContext componentContext = new ComponentContext(session, componentDtosByKey); - - ComponentDto projectDto = processProject(root, reportReader.readComponent(root.getRef()), componentContext); - processChildren(componentContext, root, projectDto, projectDto); - session.commit(); - } finally { - session.close(); - } - } - - private void recursivelyProcessComponent(ComponentContext componentContext, Component component, ComponentDto parentModule, ComponentDto project) { - BatchReport.Component reportComponent = reportReader.readComponent(component.getRef()); - - switch (component.getType()) { - case MODULE: - ComponentDto moduleDto = processModule(component, reportComponent, componentContext, parentModule, project.getId()); - processChildren(componentContext, component, moduleDto, project); - break; - case DIRECTORY: - processDirectory(component, reportComponent, componentContext, parentModule, project.getId()); - processChildren(componentContext, component, parentModule, project); - break; - case FILE: - processFile(component, reportComponent, componentContext, parentModule, project.getId()); - processChildren(componentContext, component, parentModule, project); - break; - default: - throw new IllegalStateException(String.format("Unsupported component type '%s'", component.getType())); - } - } - - private void processChildren(ComponentContext componentContext, Component component, ComponentDto parentModule, ComponentDto project) { - for (Component child : component.getChildren()) { - recursivelyProcessComponent(componentContext, child, parentModule, project); - } - } - - public ComponentDto processProject(Component project, BatchReport.Component reportComponent, ComponentContext componentContext) { - ComponentDto componentDto = createComponentDto(project); - - componentDto.setScope(Scopes.PROJECT); - componentDto.setQualifier(Qualifiers.PROJECT); - componentDto.setName(reportComponent.getName()); - componentDto.setLongName(componentDto.name()); - if (reportComponent.hasDescription()) { - componentDto.setDescription(reportComponent.getDescription()); - } - componentDto.setProjectUuid(componentDto.uuid()); - componentDto.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP); - - return persistComponent(project.getRef(), componentDto, componentContext); - } - - public ComponentDto processModule(Component module, BatchReport.Component reportComponent, ComponentContext componentContext, ComponentDto lastModule, long projectId) { - ComponentDto componentDto = createComponentDto(module); - - componentDto.setScope(Scopes.PROJECT); - componentDto.setQualifier(Qualifiers.MODULE); - componentDto.setName(reportComponent.getName()); - componentDto.setLongName(componentDto.name()); - if (reportComponent.hasPath()) { - componentDto.setPath(reportComponent.getPath()); - } - if (reportComponent.hasDescription()) { - componentDto.setDescription(reportComponent.getDescription()); - } - componentDto.setParentProjectId(projectId); - componentDto.setProjectUuid(lastModule.projectUuid()); - componentDto.setModuleUuid(lastModule.uuid()); - componentDto.setModuleUuidPath((lastModule.moduleUuidPath() + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP)); - - return persistComponent(module.getRef(), componentDto, componentContext); - } - - public void processDirectory(Component directory, BatchReport.Component reportComponent, ComponentContext componentContext, ComponentDto lastModule, long projectId) { - ComponentDto componentDto = createComponentDto(directory); - - componentDto.setScope(Scopes.DIRECTORY); - componentDto.setQualifier(Qualifiers.DIRECTORY); - componentDto.setName(reportComponent.getPath()); - componentDto.setLongName(reportComponent.getPath()); - if (reportComponent.hasPath()) { - componentDto.setPath(reportComponent.getPath()); - } - - componentDto.setParentProjectId(lastModule.getId()); - componentDto.setProjectUuid(lastModule.projectUuid()); - componentDto.setModuleUuid(lastModule.uuid()); - componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); - - persistComponent(directory.getRef(), componentDto, componentContext); - } - - public void processFile(Component file, BatchReport.Component reportComponent, ComponentContext componentContext, ComponentDto lastModule, long projectId) { - ComponentDto componentDto = createComponentDto(file); - - componentDto.setScope(Scopes.FILE); - componentDto.setQualifier(getFileQualifier(reportComponent)); - componentDto.setName(FilenameUtils.getName(reportComponent.getPath())); - componentDto.setLongName(reportComponent.getPath()); - if (reportComponent.hasPath()) { - componentDto.setPath(reportComponent.getPath()); - } - if (reportComponent.hasLanguage()) { - componentDto.setLanguage(reportComponent.getLanguage()); - } - - componentDto.setParentProjectId(lastModule.getId()); - componentDto.setProjectUuid(lastModule.projectUuid()); - componentDto.setModuleUuid(lastModule.uuid()); - componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); - - persistComponent(file.getRef(), componentDto, componentContext); - } - - private ComponentDto createComponentDto(Component component) { - String componentKey = component.getKey(); - String componentUuid = component.getUuid(); - - ComponentDto componentDto = new ComponentDto(); - componentDto.setUuid(componentUuid); - componentDto.setKey(componentKey); - componentDto.setDeprecatedKey(componentKey); - componentDto.setEnabled(true); - return componentDto; - } - - private ComponentDto persistComponent(int componentRef, ComponentDto componentDto, ComponentContext componentContext) { - ComponentDto existingComponent = componentContext.componentDtosByKey.get(componentDto.getKey()); - if (existingComponent == null) { - dbClient.componentDao().insert(componentContext.dbSession, componentDto); - dbComponentsRefCache.addComponent(componentRef, new DbComponentsRefCache.DbComponent(componentDto.getId(), componentDto.getKey(), componentDto.uuid())); - return componentDto; - } else { - if (updateComponent(existingComponent, componentDto)) { - dbClient.componentDao().update(componentContext.dbSession, existingComponent); - } - dbComponentsRefCache.addComponent(componentRef, new DbComponentsRefCache.DbComponent(existingComponent.getId(), existingComponent.getKey(), existingComponent.uuid())); - return existingComponent; - } - } - - private static boolean updateComponent(ComponentDto existingComponent, ComponentDto newComponent) { - boolean isUpdated = false; - if (!StringUtils.equals(existingComponent.name(), newComponent.name())) { - existingComponent.setName(newComponent.name()); - isUpdated = true; - } - if (!StringUtils.equals(existingComponent.description(), newComponent.description())) { - existingComponent.setDescription(newComponent.description()); - isUpdated = true; - } - if (!StringUtils.equals(existingComponent.path(), newComponent.path())) { - existingComponent.setPath(newComponent.path()); - isUpdated = true; - } - if (!StringUtils.equals(existingComponent.moduleUuid(), newComponent.moduleUuid())) { - existingComponent.setModuleUuid(newComponent.moduleUuid()); - isUpdated = true; - } - if (!existingComponent.moduleUuidPath().equals(newComponent.moduleUuidPath())) { - existingComponent.setModuleUuidPath(newComponent.moduleUuidPath()); - isUpdated = true; - } - if (!ObjectUtils.equals(existingComponent.parentProjectId(), newComponent.parentProjectId())) { - existingComponent.setParentProjectId(newComponent.parentProjectId()); - isUpdated = true; - } - return isUpdated; - } - - private static String getFileQualifier(BatchReport.Component reportComponent) { - return reportComponent.getIsTest() ? Qualifiers.UNIT_TEST_FILE : Qualifiers.FILE; - } - - private Map<String, ComponentDto> componentDtosByKey(List<ComponentDto> components) { - return Maps.uniqueIndex(components, new NonNullInputFunction<ComponentDto, String>() { - @Override - public String doApply(ComponentDto input) { - return input.key(); - } - }); - } - - private static class ComponentContext { - private final Map<String, ComponentDto> componentDtosByKey; - private final DbSession dbSession; - - public ComponentContext(DbSession dbSession, Map<String, ComponentDto> componentDtosByKey) { - this.componentDtosByKey = componentDtosByKey; - this.dbSession = dbSession; - } - } - - @Override - public String getDescription() { - return "Feed components cache"; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java index 5cd1ae314cc..f635806afd1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java @@ -30,21 +30,28 @@ import org.sonar.core.metric.db.MetricDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; + /** * Persist duplications into */ public class PersistDuplicationsStep implements ComputationStep { private final DbClient dbClient; - private final DbComponentsRefCache dbComponentsRefCache; + private final DbIdsRepository dbIdsRepository; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; - public PersistDuplicationsStep(DbClient dbClient, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; - this.dbComponentsRefCache = dbComponentsRefCache; + this.dbIdsRepository = dbIdsRepository; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @@ -52,102 +59,89 @@ public class PersistDuplicationsStep implements ComputationStep { public void execute() { DbSession session = dbClient.openSession(true); try { - MetricDto duplicationMetric = dbClient.metricDao().selectNullableByKey(session, CoreMetrics.DUPLICATIONS_DATA_KEY); - DuplicationContext duplicationContext = new DuplicationContext(duplicationMetric, session); - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); - recursivelyProcessComponent(duplicationContext, rootComponentRef); + MetricDto duplicationMetric = dbClient.metricDao().selectByKey(session, CoreMetrics.DUPLICATIONS_DATA_KEY); + new DuplicationVisitor(session, duplicationMetric).visit(treeRootHolder.getRoot()); session.commit(); } finally { MyBatis.closeQuietly(session); } } - private void recursivelyProcessComponent(DuplicationContext duplicationContext, int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - List<BatchReport.Duplication> duplications = reportReader.readComponentDuplications(componentRef); - if (!duplications.isEmpty()) { - saveDuplications(duplicationContext, component, duplications); - } + private class DuplicationVisitor extends DepthTraversalTypeAwareVisitor { + + private final DbSession session; + private final MetricDto duplicationMetric; - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(duplicationContext, childRef); + private DuplicationVisitor(DbSession session, MetricDto duplicationMetric) { + super(Component.Type.FILE, PRE_ORDER); + this.session = session; + this.duplicationMetric = duplicationMetric; } - } - private void saveDuplications(DuplicationContext duplicationContext, BatchReport.Component component, List<BatchReport.Duplication> duplications) { + @Override + public void visitFile(Component file) { + visitComponent(file); + } - DbComponentsRefCache.DbComponent dbComponent = dbComponentsRefCache.getByRef(component.getRef()); - String duplicationXml = createXmlDuplications(duplicationContext, dbComponent.getKey(), duplications); - MeasureDto measureDto = new MeasureDto() - .setMetricId(duplicationContext.metric().getId()) - .setData(duplicationXml) - .setComponentId(dbComponent.getId()) - .setSnapshotId(component.getSnapshotId()); - dbClient.measureDao().insert(duplicationContext.session(), measureDto); - } + private void visitComponent(Component component) { + List<BatchReport.Duplication> duplications = reportReader.readComponentDuplications(component.getRef()); + if (!duplications.isEmpty()) { + BatchReport.Component batchComponent = reportReader.readComponent(component.getRef()); + saveDuplications(batchComponent, component, duplications); + } + } - private String createXmlDuplications(DuplicationContext duplicationContext, String componentKey, Iterable<BatchReport.Duplication> duplications) { + private void saveDuplications(BatchReport.Component batchComponent, Component component, List<BatchReport.Duplication> duplications) { + String duplicationXml = createXmlDuplications(component.getKey(), duplications); + MeasureDto measureDto = new MeasureDto() + .setMetricId(duplicationMetric.getId()) + .setData(duplicationXml) + .setComponentId(dbIdsRepository.getComponentId(component)) + .setSnapshotId(batchComponent.getSnapshotId()); + dbClient.measureDao().insert(session, measureDto); + } - StringBuilder xml = new StringBuilder(); - xml.append("<duplications>"); - for (BatchReport.Duplication duplication : duplications) { - xml.append("<g>"); - appendDuplication(xml, componentKey, duplication.getOriginPosition()); - for (BatchReport.Duplicate duplicationBlock : duplication.getDuplicateList()) { - processDuplicationBlock(duplicationContext, xml, duplicationBlock, componentKey); + private String createXmlDuplications(String componentKey, Iterable<BatchReport.Duplication> duplications) { + StringBuilder xml = new StringBuilder(); + xml.append("<duplications>"); + for (BatchReport.Duplication duplication : duplications) { + xml.append("<g>"); + appendDuplication(xml, componentKey, duplication.getOriginPosition()); + for (BatchReport.Duplicate duplicationBlock : duplication.getDuplicateList()) { + processDuplicationBlock(xml, duplicationBlock, componentKey); + } + xml.append("</g>"); } - xml.append("</g>"); + xml.append("</duplications>"); + return xml.toString(); } - xml.append("</duplications>"); - return xml.toString(); - } - private void processDuplicationBlock(DuplicationContext duplicationContext, StringBuilder xml, BatchReport.Duplicate duplicate, String componentKey) { - - if (duplicate.hasOtherFileKey()) { - // componentKey is only set for cross project duplications - String crossProjectComponentKey = duplicate.getOtherFileKey(); - appendDuplication(xml, crossProjectComponentKey, duplicate); - } else { - if (duplicate.hasOtherFileRef()) { - // Duplication is on a different file - BatchReport.Component duplicationComponent = reportReader.readComponent(duplicate.getOtherFileRef()); - DbComponentsRefCache.DbComponent dbComponent = dbComponentsRefCache.getByRef(duplicationComponent.getRef()); - appendDuplication(xml, dbComponent.getKey(), duplicate); + private void processDuplicationBlock(StringBuilder xml, BatchReport.Duplicate duplicate, String componentKey) { + if (duplicate.hasOtherFileKey()) { + // componentKey is only set for cross project duplications + String crossProjectComponentKey = duplicate.getOtherFileKey(); + appendDuplication(xml, crossProjectComponentKey, duplicate); } else { - // Duplication is on a the same file - appendDuplication(xml, componentKey, duplicate); + if (duplicate.hasOtherFileRef()) { + // Duplication is on a different file + appendDuplication(xml, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()).getKey(), duplicate); + } else { + // Duplication is on a the same file + appendDuplication(xml, componentKey, duplicate); + } } } - } - - private static void appendDuplication(StringBuilder xml, String componentKey, BatchReport.Duplicate duplicate) { - appendDuplication(xml, componentKey, duplicate.getRange()); - } - - private static void appendDuplication(StringBuilder xml, String componentKey, Range range) { - int length = range.getEndLine() - range.getStartLine() + 1; - xml.append("<b s=\"").append(range.getStartLine()) - .append("\" l=\"").append(length) - .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey)) - .append("\"/>"); - } - - private static class DuplicationContext { - private DbSession session; - private MetricDto duplicationMetric; - - DuplicationContext(MetricDto duplicationMetric, DbSession session) { - this.duplicationMetric = duplicationMetric; - this.session = session; - } - public MetricDto metric() { - return duplicationMetric; + private void appendDuplication(StringBuilder xml, String componentKey, BatchReport.Duplicate duplicate) { + appendDuplication(xml, componentKey, duplicate.getRange()); } - public DbSession session() { - return session; + private void appendDuplication(StringBuilder xml, String componentKey, Range range) { + int length = range.getEndLine() - range.getStartLine() + 1; + xml.append("<b s=\"").append(range.getStartLine()) + .append("\" l=\"").append(length) + .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey)) + .append("\"/>"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistEventsStep.java index 45c9aef8f3b..e1a6af7af2e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistEventsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistEventsStep.java @@ -28,20 +28,24 @@ import org.sonar.core.event.EventDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; + public class PersistEventsStep implements ComputationStep { private final DbClient dbClient; private final System2 system2; - private final DbComponentsRefCache dbComponentsRefCache; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; - public PersistEventsStep(DbClient dbClient, System2 system2, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public PersistEventsStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; this.system2 = system2; - this.dbComponentsRefCache = dbComponentsRefCache; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @@ -49,73 +53,89 @@ public class PersistEventsStep implements ComputationStep { public void execute() { DbSession session = dbClient.openSession(false); try { - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); - recursivelyProcessComponent(session, rootComponentRef); + new EventVisitor(session, reportReader.readMetadata().getAnalysisDate()).visit(treeRootHolder.getRoot()); session.commit(); } finally { MyBatis.closeQuietly(session); } } - private void recursivelyProcessComponent(DbSession session, int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - long analysisDate = reportReader.readMetadata().getAnalysisDate(); - processEvents(session, component, analysisDate); - saveVersionEvent(session, component, analysisDate); + private class EventVisitor extends DepthTraversalTypeAwareVisitor { + + private final DbSession session; + private final long analysisDate; - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(session, childRef); + private EventVisitor(DbSession session, long analysisDate) { + super(Component.Type.FILE, PRE_ORDER); + this.session = session; + this.analysisDate = analysisDate; } - } - private void processEvents(DbSession session, BatchReport.Component component, Long analysisDate) { - List<BatchReport.Event> events = component.getEventList(); - if (!events.isEmpty()) { - for (BatchReport.Event event : component.getEventList()) { - dbClient.eventDao().insert(session, newBaseEvent(component, analysisDate) - .setName(event.getName()) - .setCategory(convertCategory(event.getCategory())) - .setDescription(event.hasDescription() ? event.getDescription() : null) - .setData(event.hasEventData() ? event.getEventData() : null) - ); + @Override + public void visitModule(Component module) { + visitProjectOrModule(module); + } + + @Override + public void visitProject(Component project) { + visitProjectOrModule(project); + } + + private void visitProjectOrModule(Component component) { + BatchReport.Component batchComponent = reportReader.readComponent(component.getRef()); + processEvents(batchComponent, component); + saveVersionEvent(batchComponent, component); + } + + private void processEvents(BatchReport.Component batchComponent, Component component) { + List<BatchReport.Event> events = batchComponent.getEventList(); + if (!events.isEmpty()) { + for (BatchReport.Event event : events) { + dbClient.eventDao().insert(session, newBaseEvent(component, batchComponent.getSnapshotId()) + .setName(event.getName()) + .setCategory(convertCategory(event.getCategory())) + .setDescription(event.hasDescription() ? event.getDescription() : null) + .setData(event.hasEventData() ? event.getEventData() : null) + ); + } } } - } - private void saveVersionEvent(DbSession session, BatchReport.Component component, Long analysisDate) { - if (component.hasVersion()) { - deletePreviousEventsHavingSameVersion(session, component); - dbClient.eventDao().insert(session, newBaseEvent(component, analysisDate) - .setName(component.getVersion()) - .setCategory(EventDto.CATEGORY_VERSION) - ); + private void saveVersionEvent(BatchReport.Component batchComponent, Component component) { + if (batchComponent.hasVersion()) { + deletePreviousEventsHavingSameVersion(batchComponent, component); + dbClient.eventDao().insert(session, newBaseEvent(component, batchComponent.getSnapshotId()) + .setName(batchComponent.getVersion()) + .setCategory(EventDto.CATEGORY_VERSION) + ); + } } - } - private void deletePreviousEventsHavingSameVersion(DbSession session, BatchReport.Component component) { - for (EventDto dto : dbClient.eventDao().selectByComponentUuid(session, dbComponentsRefCache.getByRef(component.getRef()).getUuid())) { - if (dto.getCategory().equals(EventDto.CATEGORY_VERSION) && dto.getName().equals(component.getVersion())) { - dbClient.eventDao().delete(session, dto.getId()); + private void deletePreviousEventsHavingSameVersion(BatchReport.Component batchComponent, Component component) { + for (EventDto dto : dbClient.eventDao().selectByComponentUuid(session, component.getUuid())) { + if (dto.getCategory().equals(EventDto.CATEGORY_VERSION) && dto.getName().equals(batchComponent.getVersion())) { + dbClient.eventDao().delete(session, dto.getId()); + } } } - } - private EventDto newBaseEvent(BatchReport.Component component, Long analysisDate) { - return new EventDto() - .setComponentUuid(dbComponentsRefCache.getByRef(component.getRef()).getUuid()) - .setSnapshotId(component.getSnapshotId()) - .setCreatedAt(system2.now()) - .setDate(analysisDate); - } + private EventDto newBaseEvent(Component component, long snapshotId) { + return new EventDto() + .setComponentUuid(component.getUuid()) + .setSnapshotId(snapshotId) + .setCreatedAt(system2.now()) + .setDate(analysisDate); + } - private static String convertCategory(Constants.EventCategory category) { - switch (category) { - case ALERT: - return EventDto.CATEGORY_ALERT; - case PROFILE: - return EventDto.CATEGORY_PROFILE; - default: - throw new IllegalArgumentException(String.format("Unsupported category %s", category.name())); + private String convertCategory(Constants.EventCategory category) { + switch (category) { + case ALERT: + return EventDto.CATEGORY_ALERT; + case PROFILE: + return EventDto.CATEGORY_PROFILE; + default: + throw new IllegalArgumentException(String.format("Unsupported category %s", category.name())); + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java index e988b39d41b..523f4a3d608 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java @@ -29,14 +29,15 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; import org.sonar.api.utils.System2; -import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceDto.Type; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.source.ComputeFileSourceData; import org.sonar.server.computation.source.CoverageLineReader; import org.sonar.server.computation.source.DuplicationLineReader; @@ -48,28 +49,48 @@ import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDb; import org.sonar.server.util.CloseableIterator; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; + public class PersistFileSourcesStep implements ComputationStep { private final DbClient dbClient; private final System2 system2; - private final DbComponentsRefCache dbComponentsRefCache; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; - public PersistFileSourcesStep(DbClient dbClient, System2 system2, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; this.system2 = system2; - this.dbComponentsRefCache = dbComponentsRefCache; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @Override public void execute() { - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); // Don't use batch insert for file_sources since keeping all data in memory can produce OOM for big files DbSession session = dbClient.openSession(false); try { - final Map<String, FileSourceDto> previousFileSourcesByUuid = new HashMap<>(); - String projectUuid = dbComponentsRefCache.getByRef(rootComponentRef).getUuid(); + new FileSourceVisitor(session).visit(treeRootHolder.getRoot()); + } finally { + MyBatis.closeQuietly(session); + } + } + + private class FileSourceVisitor extends DepthTraversalTypeAwareVisitor { + + private final DbSession session; + + private Map<String, FileSourceDto> previousFileSourcesByUuid = new HashMap<>(); + private String projectUuid; + + private FileSourceVisitor(DbSession session) { + super(Component.Type.FILE, PRE_ORDER); + this.session = session; + } + + @Override + public void visitProject(Component project) { + this.projectUuid = project.getUuid(); session.select("org.sonar.core.source.db.FileSourceMapper.selectHashesForProject", ImmutableMap.of("projectUuid", projectUuid, "dataType", Type.SOURCE), new ResultHandler() { @Override @@ -78,90 +99,69 @@ public class PersistFileSourcesStep implements ComputationStep { previousFileSourcesByUuid.put(dto.getFileUuid(), dto); } }); - - recursivelyProcessComponent(new FileSourcesContext(session, previousFileSourcesByUuid, projectUuid), rootComponentRef); - } finally { - MyBatis.closeQuietly(session); } - } - private void recursivelyProcessComponent(FileSourcesContext fileSourcesContext, int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - if (component.getType().equals(Constants.ComponentType.FILE)) { - CloseableIterator<String> linesIterator = reportReader.readFileSource(componentRef); - LineReaders lineReaders = new LineReaders(reportReader, componentRef); + @Override + public void visitFile(Component file) { + int fileRef = file.getRef(); + BatchReport.Component component = reportReader.readComponent(fileRef); + CloseableIterator<String> linesIterator = reportReader.readFileSource(fileRef); + LineReaders lineReaders = new LineReaders(reportReader, fileRef); try { ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(linesIterator, lineReaders.readers(), component.getLines()); ComputeFileSourceData.Data fileSourceData = computeFileSourceData.compute(); - persistSource(fileSourcesContext, fileSourceData, component); + persistSource(fileSourceData, file.getUuid()); } catch (Exception e) { - throw new IllegalStateException(String.format("Cannot persist sources of %s", component.getPath()), e); + throw new IllegalStateException(String.format("Cannot persist sources of %s", file.getKey()), e); } finally { linesIterator.close(); lineReaders.close(); } } - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(fileSourcesContext, childRef); - } - } + private void persistSource(ComputeFileSourceData.Data fileSourceData, String componentUuid) { + FileSourceDb.Data fileData = fileSourceData.getFileSourceData(); - private void persistSource(FileSourcesContext fileSourcesContext, ComputeFileSourceData.Data fileSourceData, BatchReport.Component component) { - FileSourceDb.Data fileData = fileSourceData.getFileSourceData(); - - byte[] data = FileSourceDto.encodeSourceData(fileData); - String dataHash = DigestUtils.md5Hex(data); - String srcHash = fileSourceData.getSrcHash(); - String lineHashes = fileSourceData.getLineHashes(); - String componentUuid = dbComponentsRefCache.getByRef(component.getRef()).getUuid(); - FileSourceDto previousDto = fileSourcesContext.previousFileSourcesByUuid.get(componentUuid); - - if (previousDto == null) { - FileSourceDto dto = new FileSourceDto() - .setProjectUuid(fileSourcesContext.projectUuid) - .setFileUuid(componentUuid) - .setDataType(Type.SOURCE) - .setBinaryData(data) - .setSrcHash(srcHash) - .setDataHash(dataHash) - .setLineHashes(lineHashes) - .setCreatedAt(system2.now()) - .setUpdatedAt(system2.now()); - dbClient.fileSourceDao().insert(fileSourcesContext.session, dto); - fileSourcesContext.session.commit(); - } else { - // Update only if data_hash has changed or if src_hash is missing (progressive migration) - boolean binaryDataUpdated = !dataHash.equals(previousDto.getDataHash()); - boolean srcHashUpdated = !srcHash.equals(previousDto.getSrcHash()); - if (binaryDataUpdated || srcHashUpdated) { - previousDto + byte[] data = FileSourceDto.encodeSourceData(fileData); + String dataHash = DigestUtils.md5Hex(data); + String srcHash = fileSourceData.getSrcHash(); + String lineHashes = fileSourceData.getLineHashes(); + FileSourceDto previousDto = previousFileSourcesByUuid.get(componentUuid); + + if (previousDto == null) { + FileSourceDto dto = new FileSourceDto() + .setProjectUuid(projectUuid) + .setFileUuid(componentUuid) + .setDataType(Type.SOURCE) .setBinaryData(data) - .setDataHash(dataHash) .setSrcHash(srcHash) - .setLineHashes(lineHashes); - // Optimization only change updated at when updating binary data to avoid unnecessary indexation by E/S - if (binaryDataUpdated) { - previousDto.setUpdatedAt(system2.now()); + .setDataHash(dataHash) + .setLineHashes(lineHashes) + .setCreatedAt(system2.now()) + .setUpdatedAt(system2.now()); + dbClient.fileSourceDao().insert(session, dto); + session.commit(); + } else { + // Update only if data_hash has changed or if src_hash is missing (progressive migration) + boolean binaryDataUpdated = !dataHash.equals(previousDto.getDataHash()); + boolean srcHashUpdated = !srcHash.equals(previousDto.getSrcHash()); + if (binaryDataUpdated || srcHashUpdated) { + previousDto + .setBinaryData(data) + .setDataHash(dataHash) + .setSrcHash(srcHash) + .setLineHashes(lineHashes); + // Optimization only change updated at when updating binary data to avoid unnecessary indexation by E/S + if (binaryDataUpdated) { + previousDto.setUpdatedAt(system2.now()); + } + dbClient.fileSourceDao().update(previousDto); + session.commit(); } - dbClient.fileSourceDao().update(previousDto); - fileSourcesContext.session.commit(); } } } - private static class FileSourcesContext { - DbSession session; - Map<String, FileSourceDto> previousFileSourcesByUuid; - String projectUuid; - - public FileSourcesContext(DbSession session, Map<String, FileSourceDto> previousFileSourcesByUuid, String projectUuid) { - this.previousFileSourcesByUuid = previousFileSourcesByUuid; - this.session = session; - this.projectUuid = projectUuid; - } - } - private static class LineReaders { private final List<LineReader> readers = new ArrayList<>(); private final List<CloseableIterator<?>> iterators = new ArrayList<>(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java index fb9c8287772..df107b009c2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java @@ -96,7 +96,7 @@ public class PersistIssuesStep implements ComputationStep { } } - private void insertChanges(IssueChangeMapper mapper, DefaultIssue issue) { + private static void insertChanges(IssueChangeMapper mapper, DefaultIssue issue) { for (IssueComment comment : issue.comments()) { DefaultIssueComment c = (DefaultIssueComment) comment; if (c.isNew()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java index b9e4129ff7a..f6f51d04271 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java @@ -29,12 +29,16 @@ import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.measure.db.MeasureDto; import org.sonar.core.persistence.DbSession; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.issue.RuleCache; import org.sonar.server.computation.measure.MetricCache; import org.sonar.server.db.DbClient; import static com.google.common.collect.Lists.newArrayList; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; public class PersistMeasuresStep implements ComputationStep { @@ -46,15 +50,17 @@ public class PersistMeasuresStep implements ComputationStep { private final DbClient dbClient; private final RuleCache ruleCache; private final MetricCache metricCache; - private final DbComponentsRefCache dbComponentsRefCache; + private final DbIdsRepository dbIdsRepository; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; public PersistMeasuresStep(DbClient dbClient, RuleCache ruleCache, MetricCache metricCache, - DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; this.ruleCache = ruleCache; this.metricCache = metricCache; - this.dbComponentsRefCache = dbComponentsRefCache; + this.dbIdsRepository = dbIdsRepository; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @@ -65,33 +71,45 @@ public class PersistMeasuresStep implements ComputationStep { @Override public void execute() { - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); - try (DbSession dbSession = dbClient.openSession(true)) { - recursivelyProcessComponent(dbSession, rootComponentRef); + DbSession dbSession = dbClient.openSession(true); + try { + new MeasureVisitor(dbSession).visit(treeRootHolder.getRoot()); dbSession.commit(); + } finally { + dbSession.close(); } } - private void recursivelyProcessComponent(DbSession dbSession, int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - List<BatchReport.Measure> measures = reportReader.readComponentMeasures(componentRef); - persistMeasures(dbSession, measures, component); - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(dbSession, childRef); + private class MeasureVisitor extends DepthTraversalTypeAwareVisitor { + + private final DbSession session; + + private MeasureVisitor(DbSession session) { + super(Component.Type.FILE, PRE_ORDER); + this.session = session; + } + + @Override + protected void visitAny(Component component) { + int componentRef = component.getRef(); + BatchReport.Component batchComponent = reportReader.readComponent(componentRef); + List<BatchReport.Measure> measures = reportReader.readComponentMeasures(componentRef); + persistMeasures(measures, dbIdsRepository.getComponentId(component), batchComponent.getSnapshotId()); + } - } - private void persistMeasures(DbSession dbSession, List<BatchReport.Measure> batchReportMeasures, final BatchReport.Component component) { - for (BatchReport.Measure measure : batchReportMeasures) { - if (FORBIDDEN_METRIC_KEYS.contains(measure.getMetricKey())) { - throw new IllegalStateException(String.format("Measures on metric '%s' cannot be send in the report", measure.getMetricKey())); + private void persistMeasures(List<BatchReport.Measure> batchReportMeasures, long componentId, long snapshotId) { + for (BatchReport.Measure measure : batchReportMeasures) { + if (FORBIDDEN_METRIC_KEYS.contains(measure.getMetricKey())) { + throw new IllegalStateException(String.format("Measures on metric '%s' cannot be send in the report", measure.getMetricKey())); + } + dbClient.measureDao().insert(session, toMeasureDto(measure, componentId, snapshotId)); } - dbClient.measureDao().insert(dbSession, toMeasureDto(measure, component)); } } @VisibleForTesting - MeasureDto toMeasureDto(BatchReport.Measure in, BatchReport.Component component) { + MeasureDto toMeasureDto(BatchReport.Measure in, long componentId, long snapshotId) { if (!in.hasValueType()) { throw new IllegalStateException(String.format("Measure %s does not have value type", in)); } @@ -109,8 +127,8 @@ public class PersistMeasuresStep implements ComputationStep { out.setAlertText(in.hasAlertText() ? in.getAlertText() : null); out.setDescription(in.hasDescription() ? in.getDescription() : null); out.setSeverity(in.hasSeverity() ? in.getSeverity().name() : null); - out.setComponentId(dbComponentsRefCache.getByRef(component.getRef()).getId()); - out.setSnapshotId(component.getSnapshotId()); + out.setComponentId(componentId); + out.setSnapshotId(snapshotId); out.setMetricId(metricCache.get(in.getMetricKey()).getId()); out.setRuleId(in.hasRuleKey() ? ruleCache.get(RuleKey.parse(in.getRuleKey())).getId() : null); out.setCharacteristicId(in.hasCharactericId() ? in.getCharactericId() : null); @@ -120,6 +138,13 @@ public class PersistMeasuresStep implements ComputationStep { return out; } + private MeasureDto setData(BatchReport.Measure in, MeasureDto out) { + if (in.hasStringValue()) { + out.setData(in.getStringValue()); + } + return out; + } + /** * return the numerical value as a double. It's the type used in db. * Returns null if no numerical value found @@ -139,12 +164,4 @@ public class PersistMeasuresStep implements ComputationStep { return null; } } - - private MeasureDto setData(BatchReport.Measure in, MeasureDto out) { - if (in.hasStringValue()) { - out.setData(in.getStringValue()); - } - - return out; - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java index 8b1262c3af0..74f9bd8258e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java @@ -30,13 +30,15 @@ import org.sonar.core.measure.db.MeasureDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.measure.MetricCache; import org.sonar.server.db.DbClient; import org.sonar.server.source.index.SourceLineIndex; import static com.google.common.base.Objects.firstNonNull; -import static com.google.common.base.Preconditions.checkState; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { @@ -46,18 +48,16 @@ public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { private final SourceLineIndex sourceLineIndex; private final MetricCache metricCache; private final System2 system; - private final DbComponentsRefCache dbComponentsRefCache; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; - private long lastCommitTimestamp = 0L; - public PersistNumberOfDaysSinceLastCommitStep(System2 system, DbClient dbClient, SourceLineIndex sourceLineIndex, MetricCache metricCache, - DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; this.sourceLineIndex = sourceLineIndex; this.metricCache = metricCache; this.system = system; - this.dbComponentsRefCache = dbComponentsRefCache; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @@ -68,38 +68,17 @@ public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { @Override public void execute() { - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); - recursivelyProcessComponent(rootComponentRef); + NumberOfDaysSinceLastCommitVisitor visitor = new NumberOfDaysSinceLastCommitVisitor(); + visitor.visit(treeRootHolder.getRoot()); - if (!commitFound()) { - Long lastCommitFromIndex = lastCommitFromIndex(dbComponentsRefCache.getByRef(rootComponentRef).getUuid()); + long lastCommitTimestamp = visitor.lastCommitTimestampFromReport; + if (lastCommitTimestamp == 0L) { + Long lastCommitFromIndex = lastCommitFromIndex(treeRootHolder.getRoot().getUuid()); lastCommitTimestamp = firstNonNull(lastCommitFromIndex, lastCommitTimestamp); } - if (commitFound()) { - persistNumberOfDaysSinceLastCommit(); - } - } - - private void recursivelyProcessComponent(int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - BatchReport.Changesets scm = reportReader.readChangesets(componentRef); - processScm(scm); - - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(childRef); - } - } - - private void processScm(@Nullable BatchReport.Changesets scm) { - if (scm == null) { - return; - } - - for (BatchReport.Changesets.Changeset changeset : scm.getChangesetList()) { - if (changeset.hasDate() && changeset.getDate() > lastCommitTimestamp) { - lastCommitTimestamp = changeset.getDate(); - } + if (lastCommitTimestamp != 0L) { + persistNumberOfDaysSinceLastCommit(lastCommitTimestamp); } } @@ -109,9 +88,7 @@ public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { return lastCommitDate == null ? null : lastCommitDate.getTime(); } - private void persistNumberOfDaysSinceLastCommit() { - checkState(commitFound(), "The last commit time should exist"); - + private void persistNumberOfDaysSinceLastCommit(long lastCommitTimestamp) { long numberOfDaysSinceLastCommit = (system.now() - lastCommitTimestamp) / MILLISECONDS_PER_DAY; DbSession dbSession = dbClient.openSession(true); try { @@ -125,7 +102,30 @@ public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { } } - private boolean commitFound() { - return lastCommitTimestamp != 0L; + private class NumberOfDaysSinceLastCommitVisitor extends DepthTraversalTypeAwareVisitor { + + private long lastCommitTimestampFromReport = 0L; + + private NumberOfDaysSinceLastCommitVisitor() { + super(Component.Type.FILE, PRE_ORDER); + } + + @Override + public void visitFile(Component component) { + BatchReport.Changesets scm = reportReader.readChangesets(component.getRef()); + processScm(scm); + } + + private void processScm(@Nullable BatchReport.Changesets scm) { + if (scm == null) { + return; + } + + for (BatchReport.Changesets.Changeset changeset : scm.getChangesetList()) { + if (changeset.hasDate() && changeset.getDate() > lastCommitTimestampFromReport) { + lastCommitTimestampFromReport = changeset.getDate(); + } + } + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java index fa446ef3ea4..949c73698de 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistProjectLinksStep.java @@ -35,10 +35,13 @@ import org.sonar.core.component.ComponentLinkDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; import static com.google.common.collect.Sets.newHashSet; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.PRE_ORDER; /** * Persist project and module links @@ -47,7 +50,7 @@ public class PersistProjectLinksStep implements ComputationStep { private final DbClient dbClient; private final I18n i18n; - private final DbComponentsRefCache dbComponentsRefCache; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; private static final Map<Constants.ComponentLinkType, String> typesConverter = ImmutableMap.of( @@ -58,10 +61,10 @@ public class PersistProjectLinksStep implements ComputationStep { Constants.ComponentLinkType.ISSUE, ComponentLinkDto.TYPE_ISSUE_TRACKER ); - public PersistProjectLinksStep(DbClient dbClient, I18n i18n, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader) { + public PersistProjectLinksStep(DbClient dbClient, I18n i18n, TreeRootHolder treeRootHolder, BatchReportReader reportReader) { this.dbClient = dbClient; this.i18n = i18n; - this.dbComponentsRefCache = dbComponentsRefCache; + this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; } @@ -69,65 +72,76 @@ public class PersistProjectLinksStep implements ComputationStep { public void execute() { DbSession session = dbClient.openSession(false); try { - int rootComponentRef = reportReader.readMetadata().getRootComponentRef(); - recursivelyProcessComponent(session, rootComponentRef); + new PorjectLinkVisitor(session).visit(treeRootHolder.getRoot()); session.commit(); } finally { MyBatis.closeQuietly(session); } } - private void recursivelyProcessComponent(DbSession session, int componentRef) { - BatchReport.Component component = reportReader.readComponent(componentRef); - processLinks(session, component); + private class PorjectLinkVisitor extends DepthTraversalTypeAwareVisitor { - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(session, childRef); + private final DbSession session; + + private PorjectLinkVisitor(DbSession session) { + super(Component.Type.FILE, PRE_ORDER); + this.session = session; + } + + @Override + public void visitProject(Component project) { + processComponent(project); } - } - private void processLinks(DbSession session, BatchReport.Component component) { - if (component.getType().equals(Constants.ComponentType.PROJECT) || component.getType().equals(Constants.ComponentType.MODULE)) { - List<BatchReport.ComponentLink> links = component.getLinkList(); - String componentUuid = dbComponentsRefCache.getByRef(component.getRef()).getUuid(); + @Override + public void visitModule(Component module) { + processComponent(module); + } + + private void processComponent(Component component) { + BatchReport.Component batchComponent = reportReader.readComponent(component.getRef()); + processLinks(component.getUuid(), batchComponent.getLinkList()); + } + + private void processLinks(String componentUuid, List<BatchReport.ComponentLink> links) { List<ComponentLinkDto> previousLinks = dbClient.componentLinkDao().selectByComponentUuid(session, componentUuid); mergeLinks(session, componentUuid, links, previousLinks); } - } - private void mergeLinks(DbSession session, String componentUuid, List<BatchReport.ComponentLink> links, List<ComponentLinkDto> previousLinks) { - Set<String> linkType = newHashSet(); - for (final BatchReport.ComponentLink link : links) { - String type = convertType(link.getType()); - if (!linkType.contains(type)) { - linkType.add(type); - } else { - throw new IllegalArgumentException(String.format("Link of type '%s' has already been declared on component '%s'", type, componentUuid)); - } + private void mergeLinks(DbSession session, String componentUuid, List<BatchReport.ComponentLink> links, List<ComponentLinkDto> previousLinks) { + Set<String> linkType = newHashSet(); + for (final BatchReport.ComponentLink link : links) { + String type = convertType(link.getType()); + if (!linkType.contains(type)) { + linkType.add(type); + } else { + throw new IllegalArgumentException(String.format("Link of type '%s' has already been declared on component '%s'", type, componentUuid)); + } - ComponentLinkDto previousLink = Iterables.find(previousLinks, new Predicate<ComponentLinkDto>() { - @Override - public boolean apply(@Nullable ComponentLinkDto input) { - return input != null && input.getType().equals(convertType(link.getType())); + ComponentLinkDto previousLink = Iterables.find(previousLinks, new Predicate<ComponentLinkDto>() { + @Override + public boolean apply(@Nullable ComponentLinkDto input) { + return input != null && input.getType().equals(convertType(link.getType())); + } + }, null); + if (previousLink == null) { + dbClient.componentLinkDao().insert(session, + new ComponentLinkDto() + .setComponentUuid(componentUuid) + .setType(type) + .setName(i18n.message(Locale.ENGLISH, "project_links." + type, null)) + .setHref(link.getHref()) + ); + } else { + previousLink.setHref(link.getHref()); + dbClient.componentLinkDao().update(session, previousLink); } - }, null); - if (previousLink == null) { - dbClient.componentLinkDao().insert(session, - new ComponentLinkDto() - .setComponentUuid(componentUuid) - .setType(type) - .setName(i18n.message(Locale.ENGLISH, "project_links." + type, null)) - .setHref(link.getHref()) - ); - } else { - previousLink.setHref(link.getHref()); - dbClient.componentLinkDao().update(session, previousLink); } - } - for (ComponentLinkDto dto : previousLinks) { - if (!linkType.contains(dto.getType()) && ComponentLinkDto.PROVIDED_TYPES.contains(dto.getType())) { - dbClient.componentLinkDao().delete(session, dto.getId()); + for (ComponentLinkDto dto : previousLinks) { + if (!linkType.contains(dto.getType()) && ComponentLinkDto.PROVIDED_TYPES.contains(dto.getType())) { + dbClient.componentLinkDao().delete(session, dto.getId()); + } } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java index aee6d513529..6f7a41f3467 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java @@ -46,7 +46,6 @@ import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceDto.Type; import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; @@ -60,14 +59,12 @@ public class PersistTestsStep implements ComputationStep { private final DbClient dbClient; private final System2 system; - private final DbComponentsRefCache dbComponentsRefCache; private final BatchReportReader reportReader; private final TreeRootHolder treeRootHolder; - public PersistTestsStep(DbClient dbClient, System2 system, DbComponentsRefCache dbComponentsRefCache, BatchReportReader reportReader, TreeRootHolder treeRootHolder) { + public PersistTestsStep(DbClient dbClient, System2 system, BatchReportReader reportReader, TreeRootHolder treeRootHolder) { this.dbClient = dbClient; this.system = system; - this.dbComponentsRefCache = dbComponentsRefCache; this.reportReader = reportReader; this.treeRootHolder = treeRootHolder; } @@ -76,12 +73,11 @@ public class PersistTestsStep implements ComputationStep { public void execute() { DbSession session = dbClient.openSession(true); try { - TestDepthTraversalTypeAwareVisitor visitor = new TestDepthTraversalTypeAwareVisitor(session, dbComponentsRefCache); + TestDepthTraversalTypeAwareVisitor visitor = new TestDepthTraversalTypeAwareVisitor(session); visitor.visit(treeRootHolder.getRoot()); session.commit(); if (visitor.hasUnprocessedCoverageDetails) { - String projectKey = dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()).getKey(); - LOG.warn("Some coverage tests are not taken into account during analysis of project '{}'", projectKey); + LOG.warn("Some coverage tests are not taken into account during analysis of project '{}'", visitor.getProjectKey()); } } finally { MyBatis.closeQuietly(session); @@ -95,17 +91,17 @@ public class PersistTestsStep implements ComputationStep { private class TestDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor { final DbSession session; - final DbComponentsRefCache dbComponentsRefCache; final Map<String, FileSourceDto> existingFileSourcesByUuid; final String projectUuid; + final String projectKey; boolean hasUnprocessedCoverageDetails = false; - public TestDepthTraversalTypeAwareVisitor(DbSession session, DbComponentsRefCache dbComponentsRefCache) { + public TestDepthTraversalTypeAwareVisitor(DbSession session) { super(Component.Type.FILE, Order.PRE_ORDER); this.session = session; - this.dbComponentsRefCache = dbComponentsRefCache; this.existingFileSourcesByUuid = new HashMap<>(); this.projectUuid = treeRootHolder.getRoot().getUuid(); + this.projectKey = treeRootHolder.getRoot().getKey(); session.select("org.sonar.core.source.db.FileSourceMapper.selectHashesForProject", ImmutableMap.of("projectUuid", treeRootHolder.getRoot().getUuid(), "dataType", Type.TEST), new ResultHandler() { @@ -164,11 +160,11 @@ public class PersistTestsStep implements ComputationStep { BatchReport.Component component) { Set<String> unprocessedCoverageDetailNames = new HashSet<>(coveredFilesByName.rowKeySet()); unprocessedCoverageDetailNames.removeAll(testsByName.keySet()); - boolean hasUnprocessedCoverageDetails = !unprocessedCoverageDetailNames.isEmpty(); - if (hasUnprocessedCoverageDetails) { + boolean hasUnprocessedCoverage = !unprocessedCoverageDetailNames.isEmpty(); + if (hasUnprocessedCoverage) { LOG.trace("The following test coverages for file '{}' have not been taken into account: {}", component.getPath(), Joiner.on(", ").join(unprocessedCoverageDetailNames)); } - return hasUnprocessedCoverageDetails; + return hasUnprocessedCoverage; } private List<FileSourceDb.Test> addCoveredFilesToTests(Multimap<String, FileSourceDb.Test.Builder> testsByName, @@ -247,7 +243,11 @@ public class PersistTestsStep implements ComputationStep { } private String getUuid(int fileRef) { - return dbComponentsRefCache.getByRef(fileRef).getUuid(); + return treeRootHolder.getComponentByRef(fileRef).getUuid(); + } + + public String getProjectKey() { + return projectKey; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PopulateComponentsUuidAndKeyStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PopulateComponentsUuidAndKeyStep.java index 84d521f6fe3..7c17fe3488b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PopulateComponentsUuidAndKeyStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PopulateComponentsUuidAndKeyStep.java @@ -115,7 +115,7 @@ public class PopulateComponentsUuidAndKeyStep implements ComputationStep { feedComponent((ComponentImpl) component, componentKey, componentContext.componentUuidsByKey); } - private void feedComponent(ComponentImpl component, String componentKey, Map<String, String> componentUuidByKey) { + private static void feedComponent(ComponentImpl component, String componentKey, Map<String, String> componentUuidByKey) { component.setKey(componentKey); String componentUuid = componentUuidByKey.get(componentKey); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java index b5928672a23..9dddd4cf078 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PurgeDatastoresStep.java @@ -24,33 +24,35 @@ import org.sonar.core.computation.dbcleaner.ProjectCleaner; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.purge.IdUuidPair; -import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.ProjectSettingsRepository; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; public class PurgeDatastoresStep implements ComputationStep { private final ProjectCleaner projectCleaner; private final DbClient dbClient; - private final DbComponentsRefCache dbComponentsRefCache; + private final DbIdsRepository dbIdsRepository; + private final TreeRootHolder treeRootHolder; private final ProjectSettingsRepository projectSettingsRepository; - private final BatchReportReader reportReader; - public PurgeDatastoresStep(DbClient dbClient, ProjectCleaner projectCleaner, DbComponentsRefCache dbComponentsRefCache, ProjectSettingsRepository projectSettingsRepository, BatchReportReader reportReader) { + public PurgeDatastoresStep(DbClient dbClient, ProjectCleaner projectCleaner, DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder, + ProjectSettingsRepository projectSettingsRepository) { this.projectCleaner = projectCleaner; this.dbClient = dbClient; - this.dbComponentsRefCache = dbComponentsRefCache; + this.dbIdsRepository = dbIdsRepository; + this.treeRootHolder = treeRootHolder; this.projectSettingsRepository = projectSettingsRepository; - this.reportReader = reportReader; } @Override public void execute() { DbSession session = dbClient.openSession(true); try { - DbComponentsRefCache.DbComponent project = dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()); - projectCleaner.purge(session, new IdUuidPair(project.getId(), project.getUuid()), projectSettingsRepository.getProjectSettings(project.getKey())); + Component project = treeRootHolder.getRoot(); + projectCleaner.purge(session, new IdUuidPair(dbIdsRepository.getComponentId(project), project.getUuid()), projectSettingsRepository.getProjectSettings(project.getKey())); session.commit(); } finally { MyBatis.closeQuietly(session); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java index 6064899b711..3a7c955b556 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java @@ -21,6 +21,7 @@ package org.sonar.server.computation.step; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSortedMap; +import java.util.Collections; import java.util.Date; import java.util.Map; import javax.annotation.Nullable; @@ -75,17 +76,25 @@ public class QualityProfileEventsStep implements ComputationStep { } // Load current profiles - Map<String, QualityProfile> previousProfiles = QPMeasureData.fromJson(previousMeasure.get().getData()).getProfilesByKey(); Optional<BatchReport.Measure> currentMeasure = measureRepository.findCurrent(projectComponent, CoreMetrics.QUALITY_PROFILES); if (!currentMeasure.isPresent()) { throw new IllegalStateException("Missing measure " + CoreMetrics.QUALITY_PROFILES + " for component " + projectComponent.getRef()); } Map<String, QualityProfile> currentProfiles = QPMeasureData.fromJson(currentMeasure.get().getStringValue()).getProfilesByKey(); + Map<String, QualityProfile> previousProfiles = parseJsonData(previousMeasure); detectNewOrUpdatedProfiles(projectComponent, previousProfiles, currentProfiles); detectNoMoreUsedProfiles(projectComponent, previousProfiles, currentProfiles); } + private static Map<String, QualityProfile> parseJsonData(Optional<MeasureDto> previousMeasure) { + String data = previousMeasure.get().getData(); + if (data == null) { + return Collections.emptyMap(); + } + return QPMeasureData.fromJson(data).getProfilesByKey(); + } + private void detectNoMoreUsedProfiles(Component context, Map<String, QualityProfile> previousProfiles, Map<String, QualityProfile> currentProfiles) { for (QualityProfile previousProfile : previousProfiles.values()) { if (!currentProfiles.containsKey(previousProfile.getQpKey())) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java index f5ec9173f2f..cbc929306ea 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java @@ -25,8 +25,8 @@ import java.util.Map; import java.util.Set; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.server.computation.batch.BatchReportReader; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.issue.IssueCache; import org.sonar.server.computation.issue.RuleCache; import org.sonar.server.issue.notification.IssueChangeNotification; @@ -50,16 +50,16 @@ public class SendIssueNotificationsStep implements ComputationStep { private final IssueCache issueCache; private final RuleCache rules; - private final DbComponentsRefCache dbComponentsRefCache; + private final TreeRootHolder treeRootHolder; private final NotificationService service; private final BatchReportReader reportReader; private NewIssuesNotificationFactory newIssuesNotificationFactory; - public SendIssueNotificationsStep(IssueCache issueCache, RuleCache rules, DbComponentsRefCache dbComponentsRefCache, NotificationService service, + public SendIssueNotificationsStep(IssueCache issueCache, RuleCache rules, TreeRootHolder treeRootHolder, NotificationService service, BatchReportReader reportReader, NewIssuesNotificationFactory newIssuesNotificationFactory) { this.issueCache = issueCache; this.rules = rules; - this.dbComponentsRefCache = dbComponentsRefCache; + this.treeRootHolder = treeRootHolder; this.service = service; this.reportReader = reportReader; this.newIssuesNotificationFactory = newIssuesNotificationFactory; @@ -67,13 +67,13 @@ public class SendIssueNotificationsStep implements ComputationStep { @Override public void execute() { - DbComponent project = dbComponentsRefCache.getByRef(reportReader.readMetadata().getRootComponentRef()); + Component project = treeRootHolder.getRoot(); if (service.hasProjectSubscribersForTypes(project.getUuid(), NOTIF_TYPES)) { doExecute(project); } } - private void doExecute(DbComponent project) { + private void doExecute(Component project) { NewIssuesStatistics newIssuesStats = new NewIssuesStatistics(); CloseableIterator<DefaultIssue> issues = issueCache.traverse(); String projectName = reportReader.readComponent(reportReader.readMetadata().getRootComponentRef()).getName(); @@ -97,7 +97,7 @@ public class SendIssueNotificationsStep implements ComputationStep { sendNewIssuesStatistics(newIssuesStats, project, projectName); } - private void sendNewIssuesStatistics(NewIssuesStatistics statistics, DbComponent project, String projectName) { + private void sendNewIssuesStatistics(NewIssuesStatistics statistics, Component project, String projectName) { if (statistics.hasIssues()) { NewIssuesStatistics.Stats globalStatistics = statistics.globalStatistics(); long analysisDate = reportReader.readMetadata().getAnalysisDate(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java index 53c19ad76b7..7fda85767cd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java @@ -78,7 +78,8 @@ public class ValidateProjectStep implements ComputationStep { return input.key(); } }); - ValidateProjectsVisitor visitor = new ValidateProjectsVisitor(session, dbClient.componentDao(), settings.getBoolean(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION), modulesByKey); + ValidateProjectsVisitor visitor = new ValidateProjectsVisitor(session, dbClient.componentDao(), + settings.getBoolean(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION), modulesByKey); visitor.visit(treeRootHolder.getRoot()); if (!visitor.validationMessages.isEmpty()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java b/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java index 40d8a4f3201..d1cfe313845 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/config/ws/PropertiesWs.java @@ -36,7 +36,7 @@ public class PropertiesWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/dashboard/template/ProjectTimeMachineDashboard.java b/server/sonar-server/src/main/java/org/sonar/server/dashboard/template/ProjectTimeMachineDashboard.java index f34b456cb94..13c85036c1d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/dashboard/template/ProjectTimeMachineDashboard.java +++ b/server/sonar-server/src/main/java/org/sonar/server/dashboard/template/ProjectTimeMachineDashboard.java @@ -55,7 +55,7 @@ public final class ProjectTimeMachineDashboard extends DashboardTemplate { return dashboard; } - private void addFirstColumn(Dashboard dashboard) { + private static void addFirstColumn(Dashboard dashboard) { Widget timelineWidget = dashboard.addWidget("timeline", 1); timelineWidget.setProperty(METRIC1, CoreMetrics.COMPLEXITY_KEY); timelineWidget.setProperty(METRIC2, CoreMetrics.TECHNICAL_DEBT_KEY); @@ -83,7 +83,7 @@ public final class ProjectTimeMachineDashboard extends DashboardTemplate { duplicationTimeMachineWidget.setProperty(METRIC4, CoreMetrics.DUPLICATED_FILES_KEY); } - private void addSecondColumn(Dashboard dashboard) { + private static void addSecondColumn(Dashboard dashboard) { Widget rulesTimeMachineWidget = addTimeMachineWidgetOnSecondColumn(dashboard); rulesTimeMachineWidget.setProperty(METRIC1, CoreMetrics.VIOLATIONS_KEY); rulesTimeMachineWidget.setProperty(METRIC2, CoreMetrics.BLOCKER_VIOLATIONS_KEY); @@ -110,15 +110,15 @@ public final class ProjectTimeMachineDashboard extends DashboardTemplate { testsTimeMachineWidget.setProperty(METRIC8, CoreMetrics.TEST_EXECUTION_TIME_KEY); } - private Widget addTimeMachineWidgetOnFirstColumn(Dashboard dashboard) { + private static Widget addTimeMachineWidgetOnFirstColumn(Dashboard dashboard) { return addTimeMachineWidget(dashboard, 1); } - private Widget addTimeMachineWidgetOnSecondColumn(Dashboard dashboard) { + private static Widget addTimeMachineWidgetOnSecondColumn(Dashboard dashboard) { return addTimeMachineWidget(dashboard, 2); } - private Widget addTimeMachineWidget(Dashboard dashboard, int columnIndex) { + private static Widget addTimeMachineWidget(Dashboard dashboard, int columnIndex) { Widget widget = dashboard.addWidget("time_machine", columnIndex); widget.setProperty("displaySparkLine", "true"); return widget; diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MigrationStepModule.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MigrationStepModule.java index cde817e8024..a6371738726 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MigrationStepModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MigrationStepModule.java @@ -70,6 +70,7 @@ import org.sonar.server.db.migrations.v52.DropDependenciesComponentColumns; import org.sonar.server.db.migrations.v52.FeedDependenciesComponentUuids; import org.sonar.server.db.migrations.v52.FeedEventsComponentUuid; import org.sonar.server.db.migrations.v52.FeedFileSourcesDataType; +import org.sonar.server.db.migrations.v52.FeedMetricsBooleans; import org.sonar.server.db.migrations.v52.FeedProjectLinksComponentUuid; import org.sonar.server.db.migrations.v52.MoveProjectProfileAssociation; @@ -144,6 +145,7 @@ public class MigrationStepModule extends Module { FeedDependenciesComponentUuids.class, DropDependenciesComponentColumns.class, FeedFileSourcesDataType.class, + FeedMetricsBooleans.class, AddDependenciesColumns.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java index 0beb52287df..849447578a6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java @@ -25,9 +25,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; - import javax.annotation.Nullable; - import org.apache.commons.dbutils.DbUtils; import org.sonar.api.utils.System2; import org.sonar.core.persistence.Database; @@ -103,7 +101,7 @@ public class ReplaceIssueFiltersProjectKeyByUuid extends BaseDataChange { return newFields.toString(); } - private void append(PreparedStatement pstmt, StringBuilder newFields, @Nullable String projectKey) throws SQLException { + private static void append(PreparedStatement pstmt, StringBuilder newFields, @Nullable String projectKey) throws SQLException { if (projectKey != null) { pstmt.setString(1, projectKey); ResultSet rs = null; diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddNewCharacteristics.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddNewCharacteristics.java index 395ff13cc37..6c885e1e5f6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddNewCharacteristics.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddNewCharacteristics.java @@ -19,13 +19,13 @@ */ package org.sonar.server.db.migrations.v51; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import java.sql.SQLException; import java.util.Date; import java.util.List; - import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.utils.MessageException; @@ -34,9 +34,6 @@ import org.sonar.core.persistence.Database; import org.sonar.server.db.migrations.BaseDataChange; import org.sonar.server.db.migrations.Select; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; - /** * See http://jira.sonarsource.com/browse/SONAR-6187 * @@ -97,7 +94,7 @@ public class AddNewCharacteristics extends BaseDataChange { * * If the characteristic 'Usability' is already at the right place, nothing will be done. */ - private int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException { + private static int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException { Characteristic security = characteristicsContext.findCharacteristicByKey(SECURITY_KEY); Characteristic usability = characteristicsContext.findCharacteristicByKey(USABILITY_KEY); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleans.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleans.java new file mode 100644 index 00000000000..bb97aa65a94 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleans.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.server.db.migrations.v52; + +import java.sql.SQLException; +import org.sonar.core.persistence.Database; +import org.sonar.server.db.migrations.BaseDataChange; + +public class FeedMetricsBooleans extends BaseDataChange { + + public FeedMetricsBooleans(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.prepareUpsert("update metrics set optimized_best_value=?, hidden=?, delete_historical_data=? " + + "where user_managed=? or optimized_best_value is null or hidden is null or delete_historical_data is null") + .setBoolean(1, false) + .setBoolean(2, false) + .setBoolean(3, false) + .setBoolean(4, true) + .execute().commit(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java index d48de3632f3..34848604a87 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java @@ -20,6 +20,12 @@ package org.sonar.server.debt; +import java.io.Reader; +import java.io.StringReader; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; import org.apache.commons.lang.StringUtils; import org.codehaus.stax2.XMLInputFactory2; import org.codehaus.staxmate.SMInputFactory; @@ -30,14 +36,6 @@ import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.server.debt.DebtModelXMLExporter.DebtModel; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; - -import java.io.Reader; -import java.io.StringReader; - import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC; import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC_KEY; import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC_NAME; @@ -74,7 +72,7 @@ public class DebtCharacteristicsXMLImporter { return debtModel; } - private SMInputFactory initStax() { + private static SMInputFactory initStax() { XMLInputFactory xmlFactory = XMLInputFactory2.newInstance(); xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelOperations.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelOperations.java index b9d79cf1ebd..6156e38e636 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelOperations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelOperations.java @@ -22,6 +22,10 @@ package org.sonar.server.debt; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import java.util.Date; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.ibatis.session.SqlSession; import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtCharacteristic; @@ -38,12 +42,6 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; import org.sonar.server.util.Validation; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.Date; -import java.util.List; - @ServerSide public class DebtModelOperations { @@ -150,7 +148,7 @@ public class DebtModelOperations { } } - private int getOrder(CharacteristicDto characteristicDto) { + private static int getOrder(CharacteristicDto characteristicDto) { Integer order = characteristicDto.getOrder(); if (order == null) { throw new IllegalArgumentException(String.format("The order of the characteristic '%s' should not be null", characteristicDto.getKey())); diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java index 428983b2de6..be554876a30 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java @@ -24,14 +24,13 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.debt.DebtCharacteristic; -import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic; -import org.xml.sax.InputSource; - +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.xml.transform.OutputKeys; @@ -41,14 +40,13 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamResult; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; +import org.sonar.api.server.debt.DebtCharacteristic; +import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic; +import org.xml.sax.InputSource; import static com.google.common.collect.Lists.newArrayList; @@ -147,7 +145,7 @@ public class DebtModelXMLExporter { return result; } - private void processProperty(String key, @Nullable String val, String text, StringBuilder xml) { + private static void processProperty(String key, @Nullable String val, String text, StringBuilder xml) { xml.append("<" + PROPERTY + "><" + PROPERTY_KEY + ">"); xml.append(StringEscapeUtils.escapeXml(key)); xml.append("</" + PROPERTY_KEY + ">"); @@ -164,7 +162,7 @@ public class DebtModelXMLExporter { xml.append("</" + PROPERTY + ">"); } - private String prettyFormatXml(String xml) { + private static String prettyFormatXml(String xml) { try { Transformer serializer = SAXTransformerFactory.newInstance().newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); @@ -240,7 +238,7 @@ public class DebtModelXMLExporter { }); } - private List<DebtCharacteristic> sortByOrder(List<DebtCharacteristic> characteristics) { + private static List<DebtCharacteristic> sortByOrder(List<DebtCharacteristic> characteristics) { Collections.sort(characteristics, new Ordering<DebtCharacteristic>() { @Override public int compare(@Nullable DebtCharacteristic left, @Nullable DebtCharacteristic right) { @@ -253,7 +251,7 @@ public class DebtModelXMLExporter { return characteristics; } - private List<DebtCharacteristic> sortByName(List<DebtCharacteristic> characteristics) { + private static List<DebtCharacteristic> sortByName(List<DebtCharacteristic> characteristics) { Collections.sort(characteristics, new Ordering<DebtCharacteristic>() { @Override public int compare(@Nullable DebtCharacteristic left, @Nullable DebtCharacteristic right) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java index 7b30834dfd9..2f119bc0e13 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java @@ -23,28 +23,26 @@ package org.sonar.server.debt; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Iterables; +import java.io.Reader; +import java.io.StringReader; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.codehaus.stax2.XMLInputFactory2; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; -import org.sonar.api.server.ServerSide; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.utils.Duration; import org.sonar.api.utils.ValidationMessages; import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; - -import java.io.Reader; -import java.io.StringReader; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC; import static org.sonar.server.debt.DebtModelXMLExporter.CHARACTERISTIC_KEY; @@ -88,7 +86,7 @@ public class DebtRulesXMLImporter { return ruleDebts; } - private SMInputFactory initStax() { + private static SMInputFactory initStax() { XMLInputFactory xmlFactory = XMLInputFactory2.newInstance(); xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); @@ -141,7 +139,7 @@ public class DebtRulesXMLImporter { return null; } - private Property processProperty(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException { + private static Property processProperty(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException { SMInputCursor c = cursor.childElementCursor(); String key = null; int value = 0; @@ -183,7 +181,7 @@ public class DebtRulesXMLImporter { } @CheckForNull - private RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String coefficient, @Nullable String offset, ValidationMessages validationMessages) { + private static RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String coefficient, @Nullable String offset, ValidationMessages validationMessages) { if ("linear_threshold".equals(function) && coefficient != null) { validationMessages.addWarningText(String.format("Linear with threshold function is no longer used, remediation function of '%s' is replaced by linear.", ruleKey)); return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.Type.LINEAR.name()).setCoefficient(coefficient); diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java index cddf4b3ed6d..e5ddea9269b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java @@ -21,17 +21,15 @@ package org.sonar.server.duplication.ws; import com.google.common.annotations.VisibleForTesting; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.DbSession; import org.sonar.server.component.db.ComponentDao; -import javax.annotation.Nullable; - -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Maps.newHashMap; @ServerSide @@ -65,7 +63,7 @@ public class DuplicationsJsonWriter { } } - private void writeDuplication(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication, JsonWriter json) { + private static void writeDuplication(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication, JsonWriter json) { String ref = null; ComponentDto componentDto = duplication.file(); if (componentDto != null) { @@ -104,13 +102,13 @@ public class DuplicationsJsonWriter { } } - private void addFile(JsonWriter json, ComponentDto file) { + private static void addFile(JsonWriter json, ComponentDto file) { json.prop("key", file.key()); json.prop("uuid", file.uuid()); json.prop("name", file.longName()); } - private void addProject(JsonWriter json, @Nullable ComponentDto project, @Nullable ComponentDto subProject) { + private static void addProject(JsonWriter json, @Nullable ComponentDto project, @Nullable ComponentDto subProject) { if (project != null) { json.prop("project", project.key()); json.prop("projectUuid", project.uuid()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java index 2a000de2ea0..33473039147 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxySearchRequestBuilder.java @@ -20,6 +20,8 @@ package org.sonar.server.es.request; +import java.io.IOException; +import java.util.Arrays; import org.elasticsearch.action.ListenableActionFuture; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; @@ -31,9 +33,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.sonar.api.utils.log.Profiler; import org.sonar.server.es.EsClient; -import java.io.IOException; -import java.util.Arrays; - public class ProxySearchRequestBuilder extends SearchRequestBuilder { public ProxySearchRequestBuilder(Client client) { @@ -82,7 +81,7 @@ public class ProxySearchRequestBuilder extends SearchRequestBuilder { return message.toString(); } - private String xContentToString(ToXContent toXContent) { + private static String xContentToString(ToXContent toXContent) { try { XContentBuilder builder = XContentFactory.jsonBuilder(); toXContent.toXContent(builder, ToXContent.EMPTY_PARAMS); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java index 1894564147d..83356b5a1c1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java @@ -21,15 +21,14 @@ package org.sonar.server.issue; import com.google.common.base.Strings; -import org.sonar.api.server.ServerSide; +import java.util.Collection; +import java.util.Map; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.server.ServerSide; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; -import java.util.Collection; -import java.util.Map; - @ServerSide public class CommentAction extends Action { @@ -55,7 +54,7 @@ public class CommentAction extends Action { return true; } - private String comment(Map<String, Object> properties) { + private static String comment(Map<String, Object> properties) { String param = (String) properties.get(COMMENT_PROPERTY); if (Strings.isNullOrEmpty(param)) { throw new IllegalArgumentException("Missing parameter : '" + COMMENT_PROPERTY + "'"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index 83db935db6a..69fbceaf448 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -26,8 +26,13 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.issue.ActionPlan; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueComment; @@ -36,6 +41,7 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.FieldDiffs; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; import org.sonar.api.utils.SonarException; import org.sonar.api.web.UserRole; import org.sonar.core.issue.ActionPlanStats; @@ -55,14 +61,6 @@ import org.sonar.server.user.UserSession; import org.sonar.server.util.RubyUtils; import org.sonar.server.util.Validation; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Lists.newArrayList; /** @@ -385,7 +383,7 @@ public class InternalRubyIssueService { } } - private Date checkAndReturnDeadline(String deadLineParam, Result<ActionPlan> result) { + private static Date checkAndReturnDeadline(String deadLineParam, Result<ActionPlan> result) { Date deadLine = null; if (!Strings.isNullOrEmpty(deadLineParam)) { try { @@ -492,7 +490,7 @@ public class InternalRubyIssueService { return execute(props); } - private void overrideProps(Map<String, Object> props, Map<String, Object> overrideProps) { + private static void overrideProps(Map<String, Object> props, Map<String, Object> overrideProps) { for (Map.Entry<String, Object> entry : overrideProps.entrySet()) { props.put(entry.getKey(), entry.getValue()); } @@ -609,32 +607,32 @@ public class InternalRubyIssueService { return issueBulkChangeService.execute(issueBulkChangeQuery, userSession); } - private void checkMandatoryParameter(String value, String paramName, Result result) { + private static void checkMandatoryParameter(String value, String paramName, Result result) { if (Strings.isNullOrEmpty(value)) { result.addError(Result.Message.ofL10n(Validation.CANT_BE_EMPTY_MESSAGE, paramName)); } } - private void checkMandatorySizeParameter(String value, String paramName, Integer size, Result result) { + private static void checkMandatorySizeParameter(String value, String paramName, Integer size, Result result) { checkMandatoryParameter(value, paramName, result); if (!Strings.isNullOrEmpty(value) && value.length() > size) { result.addError(Result.Message.ofL10n(Validation.IS_TOO_LONG_MESSAGE, paramName, size)); } } - private void checkOptionalSizeParameter(String value, String paramName, Integer size, Result result) { + private static void checkOptionalSizeParameter(String value, String paramName, Integer size, Result result) { if (!Strings.isNullOrEmpty(value) && value.length() > size) { result.addError(Result.Message.ofL10n(Validation.IS_TOO_LONG_MESSAGE, paramName, size)); } } - private void checkOptionalSizeParameter(String value, String paramName, Integer size) { + private static void checkOptionalSizeParameter(String value, String paramName, Integer size) { if (!Strings.isNullOrEmpty(value) && value.length() > size) { throw new BadRequestException(Validation.IS_TOO_LONG_MESSAGE, paramName, size); } } - public int maxPageSize() { + public static int maxPageSize() { return QueryContext.MAX_LIMIT; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java index 768a274f59c..29f84cafcca 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java @@ -25,10 +25,18 @@ import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.core.notification.NotificationManager; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.utils.log.Logger; @@ -36,6 +44,7 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.notification.NotificationManager; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.db.DbClient; @@ -46,17 +55,6 @@ import org.sonar.server.issue.notification.IssueChangeNotification; import org.sonar.server.rule.DefaultRuleFinder; import org.sonar.server.user.UserSession; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.newHashSet; @@ -167,7 +165,7 @@ public class IssueBulkChangeService { return bulkActions; } - private void applyAction(Action action, ActionContext actionContext, IssueBulkChangeQuery issueBulkChangeQuery, IssueBulkChangeResult result) { + private static void applyAction(Action action, ActionContext actionContext, IssueBulkChangeQuery issueBulkChangeQuery, IssueBulkChangeResult result) { Issue issue = actionContext.issue(); try { if (action.supports(issue) && action.execute(issueBulkChangeQuery.properties(action.key()), actionContext)) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java index 74e252e36a4..174f943f7cd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java @@ -20,20 +20,18 @@ package org.sonar.server.issue; +import com.google.common.base.Strings; import java.util.Collection; import java.util.Map; - -import org.sonar.api.server.ServerSide; import org.sonar.api.issue.Issue; import org.sonar.api.issue.condition.Condition; import org.sonar.api.issue.condition.IsUnResolved; import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; -import com.google.common.base.Strings; - @ServerSide public class SetSeverityAction extends Action { @@ -69,7 +67,7 @@ public class SetSeverityAction extends Action { return issueUpdater.setManualSeverity((DefaultIssue) context.issue(), severity(properties), context.issueChangeContext()); } - private String severity(Map<String, Object> properties) { + private static String severity(Map<String, Object> properties) { String param = (String) properties.get("severity"); if (Strings.isNullOrEmpty(param)) { throw new IllegalArgumentException("Missing parameter : 'severity'"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java index 19845815146..6683c79382d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java @@ -20,21 +20,19 @@ package org.sonar.server.issue; +import com.google.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; import java.util.Collection; import java.util.Map; - import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.server.ServerSide; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; import org.sonar.server.user.UserSession; -import com.google.common.base.Predicate; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; - @ServerSide public class TransitionAction extends Action { @@ -77,7 +75,7 @@ public class TransitionAction extends Action { }, null) != null; } - private String transition(Map<String, Object> properties) { + private static String transition(Map<String, Object> properties) { String param = (String) properties.get("transition"); if (Strings.isNullOrEmpty(param)) { throw new IllegalArgumentException("Missing parameter : 'transition'"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java index 98335080499..7d9b9259753 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java @@ -22,10 +22,15 @@ package org.sonar.server.issue.actionplan; import com.google.common.base.Function; import com.google.common.collect.Iterables; -import org.sonar.api.server.ServerSide; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import javax.annotation.CheckForNull; import org.sonar.api.issue.ActionPlan; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.issue.ActionPlanDeadlineComparator; import org.sonar.core.issue.ActionPlanStats; @@ -45,13 +50,6 @@ import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; -import javax.annotation.CheckForNull; - -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; /** @@ -205,11 +203,11 @@ public class ActionPlanService { return resourceDto; } - private void checkUserCanAccessProject(String projectKey, UserSession userSession) { + private static void checkUserCanAccessProject(String projectKey, UserSession userSession) { userSession.checkProjectPermission(UserRole.USER, projectKey); } - private void checkUserIsProjectAdministrator(String projectKey, UserSession userSession) { + private static void checkUserIsProjectAdministrator(String projectKey, UserSession userSession) { userSession.checkProjectPermission(UserRole.ADMIN, projectKey); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java index c9b8a51d959..95d980508f0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanWs.java @@ -41,17 +41,17 @@ public class ActionPlanWs implements WebService { controller.done(); } - private void defineSearchAction(NewController controller) { + private static void defineSearchAction(NewController controller) { WebService.NewAction action = controller.createAction("search") .setDescription("Get a list of action plans. Requires Browse permission on project") .setSince("3.6") .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-search.json")); + .setResponseExample(Resources.getResource(ActionPlanWs.class, "example-search.json")); addProjectParam(action); addFormatParam(action); } - private void defineCreateAction(NewController controller) { + private static void defineCreateAction(NewController controller) { WebService.NewAction action = controller.createAction("create") .setDescription("Create an action plan. Requires Administer permission on project") .setSince("3.6") @@ -64,7 +64,7 @@ public class ActionPlanWs implements WebService { addFormatParam(action); } - private void defineUpdateAction(NewController controller) { + private static void defineUpdateAction(NewController controller) { WebService.NewAction action = controller.createAction("update") .setDescription("Update an action plan. Requires Administer permission on project") .setSince("3.6") @@ -77,7 +77,7 @@ public class ActionPlanWs implements WebService { addFormatParam(action); } - private void defineDeleteAction(NewController controller) { + private static void defineDeleteAction(NewController controller) { WebService.NewAction action = controller.createAction("delete") .setDescription("Delete an action plan. Requires Administer permission on project") .setSince("3.6") @@ -87,7 +87,7 @@ public class ActionPlanWs implements WebService { addFormatParam(action); } - private void defineOpenAction(NewController controller) { + private static void defineOpenAction(NewController controller) { WebService.NewAction action = controller.createAction("open") .setDescription("Open an action plan. Requires Administer permission on project") .setSince("3.6") @@ -97,7 +97,7 @@ public class ActionPlanWs implements WebService { addFormatParam(action); } - private void defineCloseAction(NewController controller) { + private static void defineCloseAction(NewController controller) { WebService.NewAction action = controller.createAction("close") .setDescription("Close an action plan. Requires Administer permission on project") .setSince("3.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java index 37311ccfdea..8727dde142c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java @@ -75,12 +75,12 @@ public class IssuesWs implements WebService { defineBulkChangeAction(controller); } - private void defineChangelogAction(NewController controller) { + private static void defineChangelogAction(NewController controller) { WebService.NewAction action = controller.createAction(CHANGELOG_ACTION) .setDescription("Display changelog of an issue") .setSince("4.1") .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-changelog.json")); + .setResponseExample(Resources.getResource(IssuesWs.class, "example-changelog.json")); action.createParam("issue") .setDescription("Key of the issue") @@ -89,7 +89,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineAssignAction(NewController controller) { + private static void defineAssignAction(NewController controller) { WebService.NewAction action = controller.createAction(ASSIGN_ACTION) .setDescription("Assign/Unassign an issue. Requires authentication and Browse permission on project") .setSince("3.6") @@ -106,7 +106,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineAddCommentAction(NewController controller) { + private static void defineAddCommentAction(NewController controller) { WebService.NewAction action = controller.createAction(ADD_COMMENT_ACTION) .setDescription("Add a comment. Requires authentication and Browse permission on project") .setSince("3.6") @@ -123,7 +123,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineDeleteCommentAction(NewController controller) { + private static void defineDeleteCommentAction(NewController controller) { WebService.NewAction action = controller.createAction(DELETE_COMMENT_ACTION) .setDescription("Delete a comment. Requires authentication and Browse permission on project") .setSince("3.6") @@ -136,7 +136,7 @@ public class IssuesWs implements WebService { .setExampleValue("392160d3-a4f2-4c52-a565-e4542cfa2096"); } - private void defineEditCommentAction(NewController controller) { + private static void defineEditCommentAction(NewController controller) { WebService.NewAction action = controller.createAction(EDIT_COMMENT_ACTION) .setDescription("Edit a comment. Requires authentication and User role on project") .setSince("3.6") @@ -153,7 +153,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineSetSeverityAction(NewController controller) { + private static void defineSetSeverityAction(NewController controller) { WebService.NewAction action = controller.createAction(SET_SEVERITY_ACTION) .setDescription("Change severity. Requires authentication and Browse permission on project") .setSince("3.6") @@ -171,7 +171,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void definePlanAction(NewController controller) { + private static void definePlanAction(NewController controller) { WebService.NewAction action = controller.createAction(PLAN_ACTION) .setDescription("Plan/Unplan an issue. Requires authentication and Browse permission on project") .setSince("3.6") @@ -188,7 +188,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineDoTransitionAction(NewController controller) { + private static void defineDoTransitionAction(NewController controller) { WebService.NewAction action = controller.createAction(DO_TRANSITION_ACTION) .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project") .setSince("3.6") @@ -206,12 +206,12 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineTransitionsAction(NewController controller) { + private static void defineTransitionsAction(NewController controller) { WebService.NewAction action = controller.createAction(TRANSITIONS_ACTION) .setDescription("Get Possible Workflow Transitions for an Issue. Requires Browse permission on project") .setSince("3.6") .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-transitions.json")); + .setResponseExample(Resources.getResource(IssuesWs.class, "example-transitions.json")); action.createParam("issue") .setDescription("Key of the issue") @@ -219,7 +219,7 @@ public class IssuesWs implements WebService { .setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef"); } - private void defineCreateAction(NewController controller) { + private static void defineCreateAction(NewController controller) { WebService.NewAction action = controller.createAction(CREATE_ACTION) .setDescription("Create a manual issue. Requires authentication and Browse permission on project") .setSince("3.6") @@ -248,7 +248,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineDoActionAction(NewController controller) { + private static void defineDoActionAction(NewController controller) { WebService.NewAction action = controller.createAction(DO_ACTION_ACTION) .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project") .setSince("3.6") @@ -265,7 +265,7 @@ public class IssuesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineBulkChangeAction(NewController controller) { + private static void defineBulkChangeAction(NewController controller) { WebService.NewAction action = controller.createAction(BULK_CHANGE_ACTION) .setDescription("Bulk change on issues. Requires authentication and User role on project(s)") .setSince("3.7") diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index c5ebc6cdf5a..69166273420 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -684,7 +684,7 @@ public class SearchAction implements IssuesWsAction { json.endArray(); } - private void writeUsers(JsonWriter json, Map<String, User> usersByLogin) { + private static void writeUsers(JsonWriter json, Map<String, User> usersByLogin) { json.name("users").beginArray(); for (User user : usersByLogin.values()) { json.beginObject() diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java index 15dfb40895d..9a5968cc965 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java @@ -23,16 +23,6 @@ import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; -import org.apache.commons.dbutils.DbUtils; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; -import org.sonar.core.component.SnapshotDto; -import org.sonar.core.persistence.Database; -import org.sonar.core.persistence.dialect.MsSql; -import org.sonar.core.persistence.dialect.Oracle; - -import javax.annotation.Nullable; - import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -40,6 +30,14 @@ import java.sql.SQLException; import java.util.Comparator; import java.util.Date; import java.util.List; +import javax.annotation.Nullable; +import org.apache.commons.dbutils.DbUtils; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.core.component.SnapshotDto; +import org.sonar.core.persistence.Database; +import org.sonar.core.persistence.dialect.MsSql; +import org.sonar.core.persistence.dialect.Oracle; class MeasureFilterSql { @@ -257,7 +255,7 @@ class MeasureFilterSql { /** * Replace escape percent and underscore by adding a slash just before */ - private String escapePercentAndUnderscrore(String value) { + private static String escapePercentAndUnderscrore(String value) { return value.replaceAll("%", "\\\\%").replaceAll("_", "\\\\_"); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ManualMeasuresWs.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ManualMeasuresWs.java index d585104d646..7a5b3e7900d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ManualMeasuresWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ManualMeasuresWs.java @@ -36,7 +36,7 @@ public class ManualMeasuresWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.10") diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java index 32abdb0fa67..f25def734f5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java @@ -37,6 +37,7 @@ import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DaoUtils; import org.sonar.core.persistence.DbSession; import org.sonar.server.es.SearchOptions; +import org.sonar.server.exceptions.NotFoundException; import static com.google.common.collect.Lists.newArrayList; @@ -57,6 +58,14 @@ public class MetricDao implements DaoComponent { }); } + public MetricDto selectByKey(DbSession session, String key) { + MetricDto metric = selectNullableByKey(session, key); + if (metric == null) { + throw new NotFoundException(String.format("Metric '%s' not found", key)); + } + return metric; + } + public List<MetricDto> selectEnabled(DbSession session) { return mapper(session).selectAllEnabled(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java index d1511cea26c..09c73b50494 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java @@ -116,7 +116,7 @@ public class CreateAction implements MetricsWsAction { } } - private MetricDto newMetricTemplate(Request request) { + private static MetricDto newMetricTemplate(Request request) { String key = request.mandatoryParam(PARAM_KEY); String name = request.mandatoryParam(PARAM_NAME); String type = Metric.ValueType.valueOf(request.mandatoryParam(PARAM_TYPE)).name(); @@ -158,6 +158,9 @@ public class CreateAction implements MetricsWsAction { .setUserManaged(true) .setDirection(0) .setQualitative(false) + .setHidden(false) + .setOptimizedBestValue(false) + .setDeleteHistoricalData(false) .setOrigin("GUI"); dbClient.metricDao().insert(dbSession, metric); diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/MetricsWs.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/MetricsWs.java index 64360c053c6..c48cd47d533 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/MetricsWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/MetricsWs.java @@ -47,7 +47,7 @@ public class MetricsWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java b/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java index 5a7f011a7ba..bcfcecbddb8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java @@ -20,8 +20,12 @@ package org.sonar.server.permission; -import org.sonar.api.server.ServerSide; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.security.DefaultGroups; +import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.permission.GlobalPermissions; @@ -36,12 +40,6 @@ import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.issue.index.IssueAuthorizationIndexer; import org.sonar.server.user.UserSession; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.List; -import java.util.Map; - /** * Used by ruby code <pre>Internal.permissions</pre> */ @@ -261,7 +259,7 @@ public class InternalPermissionService { } } - private Object badRequestIfNullResult(@Nullable Object component, String objectType, String objectKey) { + private static Object badRequestIfNullResult(@Nullable Object component, String objectType, String objectKey) { if (component == null) { throw new BadRequestException(String.format(NOT_FOUND_FORMAT, objectType, objectKey)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionFinder.java b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionFinder.java index e6d65ddd300..0f380a30dbe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionFinder.java @@ -21,9 +21,12 @@ package org.sonar.server.permission; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.security.DefaultGroups; +import org.sonar.api.server.ServerSide; import org.sonar.api.utils.Paging; import org.sonar.core.permission.GroupWithPermission; import org.sonar.core.permission.GroupWithPermissionDto; @@ -38,11 +41,6 @@ import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; import org.sonar.server.exceptions.NotFoundException; -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; @ServerSide @@ -97,7 +95,7 @@ public class PermissionFinder { return new UserWithPermissionQueryResult(toUserWithPermissionList(dtos), hasMoreResults); } - private List<UserWithPermission> toUserWithPermissionList(List<UserWithPermissionDto> dtos) { + private static List<UserWithPermission> toUserWithPermissionList(List<UserWithPermissionDto> dtos) { List<UserWithPermission> users = newArrayList(); for (UserWithPermissionDto dto : dtos) { users.add(dto.toUserWithPermission()); @@ -106,7 +104,7 @@ public class PermissionFinder { } @Nullable - private Long componentId(String componentKey) { + private Long componentId(@Nullable String componentKey) { if (componentKey == null) { return null; } else { @@ -135,13 +133,13 @@ public class PermissionFinder { return dto.getId(); } - private int offset(PermissionQuery query) { + private static int offset(PermissionQuery query) { int pageSize = query.pageSize(); int pageIndex = query.pageIndex(); return (pageIndex - 1) * pageSize; } - private int limit(PermissionQuery query) { + private static int limit(PermissionQuery query) { // Add one to page size in order to be able to know if there's more results or not return query.pageSize() + 1; } @@ -176,7 +174,7 @@ public class PermissionFinder { } } - private List<GroupWithPermission> pagedGroups(Collection<GroupWithPermissionDto> dtos, Paging paging) { + private static List<GroupWithPermission> pagedGroups(Collection<GroupWithPermissionDto> dtos, Paging paging) { List<GroupWithPermission> groups = newArrayList(); int index = 0; for (GroupWithPermissionDto dto : dtos) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionQueryParser.java b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionQueryParser.java index 0384a735ef6..8d16f383bb6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionQueryParser.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionQueryParser.java @@ -20,6 +20,8 @@ package org.sonar.server.permission; +import org.sonar.api.server.ws.WebService.SelectionMode; + import org.sonar.core.permission.PermissionQuery; import org.sonar.core.user.GroupMembershipQuery; import org.sonar.server.util.RubyUtils; @@ -28,9 +30,6 @@ import java.util.Map; public class PermissionQueryParser { - private static final String SELECTED_MEMBERSHIP = "selected"; - private static final String DESELECTED_MEMBERSHIP = "deselected"; - private PermissionQueryParser(){ // Utility class } @@ -48,10 +47,10 @@ public class PermissionQueryParser { } private static String membership(Map<String, Object> params) { - String selected = (String) params.get("selected"); - if (SELECTED_MEMBERSHIP.equals(selected)) { + SelectionMode selectionMode = SelectionMode.fromParam((String) params.get("selected")); + if (SelectionMode.SELECTED == selectionMode) { return GroupMembershipQuery.IN; - } else if (DESELECTED_MEMBERSHIP.equals(selected)) { + } else if (SelectionMode.DESELECTED == selectionMode) { return GroupMembershipQuery.OUT; } else { return GroupMembershipQuery.ANY; diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWs.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWs.java index 4ad77024acf..1bc071d8dbf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWs.java @@ -41,7 +41,7 @@ public class PermissionsWs implements WebService { controller.done(); } - private void defineAddAction(NewController controller) { + private static void defineAddAction(NewController controller) { NewAction action = controller.createAction("add") .setDescription("Add a global or a project permission. Requires Administer System permission for global permissions, " + "requires Administer permission on project for project permissions") @@ -65,7 +65,7 @@ public class PermissionsWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineRemoveAction(NewController controller) { + private static void defineRemoveAction(NewController controller) { NewAction action = controller.createAction("remove") .setDescription("Remove a global or a project permission. Requires Administer System permission for global permissions, " + "requires Administer permission on project for project permissions") diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/BackendCleanup.java b/server/sonar-server/src/main/java/org/sonar/server/platform/BackendCleanup.java index 01fefe7ee5c..ea59dff20af 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/BackendCleanup.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/BackendCleanup.java @@ -19,6 +19,8 @@ */ package org.sonar.server.platform; +import java.sql.Connection; +import java.sql.SQLException; import org.apache.commons.dbutils.DbUtils; import org.elasticsearch.index.query.QueryBuilders; import org.sonar.api.server.ServerSide; @@ -32,9 +34,6 @@ import org.sonar.server.search.IndexDefinition; import org.sonar.server.source.index.SourceLineIndexDefinition; import org.sonar.server.view.index.ViewIndexDefinition; -import java.sql.Connection; -import java.sql.SQLException; - @ServerSide public class BackendCleanup { @@ -138,7 +137,7 @@ public class BackendCleanup { } } - private void deleteWhereResourceIdNotNull(String tableName, Connection connection) { + private static void deleteWhereResourceIdNotNull(String tableName, Connection connection) { try { connection.prepareStatement("DELETE FROM " + tableName + " WHERE resource_id IS NOT NULL").execute(); // commit is useless on some databases @@ -148,7 +147,7 @@ public class BackendCleanup { } } - private void deleteManualRules(Connection connection) { + private static void deleteManualRules(Connection connection) { try { connection.prepareStatement("DELETE FROM rules WHERE rules.plugin_name='manual'").execute(); // commit is useless on some databases diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java index 52293fdc6c8..8c36a26c2c7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java @@ -38,7 +38,7 @@ import java.util.List; public class BulkDeleteAction implements ProjectsWsAction { private static final String ACTION = "bulk_delete"; - public static final String PARAM_UUIDS = "ids"; + public static final String PARAM_IDS = "ids"; public static final String PARAM_KEYS = "keys"; private final ComponentCleanerService componentCleanerService; @@ -61,8 +61,8 @@ public class BulkDeleteAction implements ProjectsWsAction { .setHandler(this); action - .createParam(PARAM_UUIDS) - .setDescription("List of project UUIDs to delete") + .createParam(PARAM_IDS) + .setDescription("List of project ids to delete") .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d,c526ef20-131b-4486-9357-063fa64b5079"); action @@ -74,7 +74,7 @@ public class BulkDeleteAction implements ProjectsWsAction { @Override public void handle(Request request, Response response) throws Exception { userSession.checkGlobalPermission(UserRole.ADMIN); - List<String> uuids = request.paramAsStrings(PARAM_UUIDS); + List<String> uuids = request.paramAsStrings(PARAM_IDS); List<String> keys = request.paramAsStrings(PARAM_KEYS); DbSession dbSession = dbClient.openSession(false); @@ -96,6 +96,6 @@ public class BulkDeleteAction implements ProjectsWsAction { return dbClient.componentDao().selectByKeys(dbSession, keys); } - throw new IllegalArgumentException("UUIDs or keys must be provided"); + throw new IllegalArgumentException("ids or keys must be provided"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java index 4665a53ff2d..46a8659d781 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java @@ -37,7 +37,7 @@ import org.sonar.server.user.UserSession; public class DeleteAction implements ProjectsWsAction { private static final String ACTION = "delete"; - public static final String PARAM_UUID = "id"; + public static final String PARAM_ID = "id"; public static final String PARAM_KEY = "key"; private final ComponentCleanerService componentCleanerService; @@ -60,8 +60,8 @@ public class DeleteAction implements ProjectsWsAction { .setHandler(this); action - .createParam(PARAM_UUID) - .setDescription("Project UUID") + .createParam(PARAM_ID) + .setDescription("Project id") .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d"); action @@ -72,7 +72,7 @@ public class DeleteAction implements ProjectsWsAction { @Override public void handle(Request request, Response response) throws Exception { - String uuid = request.param(PARAM_UUID); + String uuid = request.param(PARAM_ID); String key = request.param(PARAM_KEY); checkPermissions(uuid, key); @@ -109,6 +109,6 @@ public class DeleteAction implements ProjectsWsAction { return dbClient.componentDao().selectByKey(dbSession, key); } - throw new IllegalArgumentException("UUID or key must be provided"); + throw new IllegalArgumentException("Id or key must be provided"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGatesWs.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGatesWs.java index bb8bff8abba..514e8d4a655 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGatesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGatesWs.java @@ -31,7 +31,6 @@ public class QGatesWs implements WebService { static final String PARAM_PAGE_SIZE = "pageSize"; static final String PARAM_PAGE = "page"; static final String PARAM_QUERY = "query"; - static final String PARAM_SELECTED = "selected"; static final String PARAM_NAME = "name"; static final String PARAM_ERROR = "error"; static final String PARAM_WARNING = "warning"; diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java index 24ccf061c53..69268b929ec 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SearchAction.java @@ -20,6 +20,8 @@ package org.sonar.server.qualitygate.ws; +import org.sonar.api.server.ws.WebService.Param; + import com.google.common.io.Resources; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; @@ -54,11 +56,7 @@ public class SearchAction implements QGateWsAction { .setDescription("To search for projects containing this string. If this parameter is set, \"selected\" is set to \"all\".") .setExampleValue("abc"); - action.createParam(QGatesWs.PARAM_SELECTED) - .setDescription("If \"selected\", search for projects associated to the quality gate") - .setDefaultValue(ProjectQgateAssociationQuery.IN) - .setPossibleValues(ProjectQgateAssociationQuery.AVAILABLE_MEMBERSHIP) - .setExampleValue(ProjectQgateAssociationQuery.OUT); + action.addSelectionModeParam(); action.createParam(QGatesWs.PARAM_PAGE) .setDescription("Page number") @@ -74,7 +72,7 @@ public class SearchAction implements QGateWsAction { public void handle(Request request, Response response) { QgateProjectFinder.Association associations = projectFinder.find(ProjectQgateAssociationQuery.builder() .gateId(request.mandatoryParam(QGatesWs.PARAM_GATE_ID)) - .membership(request.param(QGatesWs.PARAM_QUERY) == null ? request.param(QGatesWs.PARAM_SELECTED) : ProjectQgateAssociationQuery.ANY) + .membership(request.param(QGatesWs.PARAM_QUERY) == null ? request.param(Param.SELECTED) : ProjectQgateAssociationQuery.ANY) .projectSearch(request.param(QGatesWs.PARAM_QUERY)) .pageIndex(request.paramAsInt(QGatesWs.PARAM_PAGE)) .pageSize(request.paramAsInt(QGatesWs.PARAM_PAGE_SIZE)) @@ -83,7 +81,7 @@ public class SearchAction implements QGateWsAction { writer.beginObject().prop("more", associations.hasMoreResults()); writer.name("results").beginArray(); for (ProjectQgateAssociation project : associations.projects()) { - writer.beginObject().prop("id", project.id()).prop("name", project.name()).prop(QGatesWs.PARAM_SELECTED, project.isMember()).endObject(); + writer.beginObject().prop("id", project.id()).prop("name", project.name()).prop(Param.SELECTED, project.isMember()).endObject(); } writer.endArray().endObject().close(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java index 9b53247017f..ce575a74b2c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java @@ -21,6 +21,8 @@ package org.sonar.server.qualitygate.ws; import com.google.common.io.Resources; +import java.util.Collection; +import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -30,10 +32,6 @@ import org.sonar.core.qualitygate.db.QualityGateDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.qualitygate.QualityGates; -import javax.annotation.Nullable; - -import java.util.Collection; - public class ShowAction implements QGateWsAction { private final QualityGates qualityGates; @@ -82,7 +80,7 @@ public class ShowAction implements QGateWsAction { writer.endObject().close(); } - private void checkOneOfIdOrNamePresent(@Nullable Long qGateId, @Nullable String qGateName) { + private static void checkOneOfIdOrNamePresent(@Nullable Long qGateId, @Nullable String qGateName) { if (qGateId == null && qGateName == null) { throw new BadRequestException("Either one of 'id' or 'name' is required."); } else if (qGateId != null && qGateName != null) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java index bb6555adf7b..e650081860d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java @@ -23,14 +23,25 @@ import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import java.io.Reader; +import java.io.Writer; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.CompareToBuilder; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; -import org.sonar.api.server.ServerSide; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; import org.sonar.api.utils.text.XmlWriter; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.QualityProfileDto; @@ -38,19 +49,6 @@ import org.sonar.server.db.DbClient; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.search.IndexClient; -import javax.annotation.Nullable; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; - -import java.io.Reader; -import java.io.Writer; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - @ServerSide public class QProfileBackuper { @@ -79,7 +77,7 @@ public class QProfileBackuper { writeXml(writer, profile, activeRules.iterator()); } - private void writeXml(Writer writer, QualityProfileDto profile, Iterator<ActiveRule> activeRules) { + private static void writeXml(Writer writer, QualityProfileDto profile, Iterator<ActiveRule> activeRules) { XmlWriter xml = XmlWriter.of(writer).declaration(); xml.begin("profile"); xml.prop("name", profile.getName()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java index da3c19f3033..c6eb4c00038 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java @@ -19,9 +19,19 @@ */ package org.sonar.server.qualityprofile; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.profiles.ProfileExporter; import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; @@ -29,24 +39,13 @@ import org.sonar.api.rules.ActiveRuleParam; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.ServerSide; import org.sonar.api.utils.ValidationMessages; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - @ServerSide public class QProfileExporters { @@ -169,7 +168,7 @@ public class QProfileExporters { throw new BadRequestException("No such importer : " + importerKey); } - private void processValidationMessages(ValidationMessages messages, QProfileResult result) { + private static void processValidationMessages(ValidationMessages messages, QProfileResult result) { if (!messages.getErrors().isEmpty()) { throw new BadRequestException(messages); } @@ -177,7 +176,7 @@ public class QProfileExporters { result.addInfos(messages.getInfos()); } - private RuleActivation toRuleActivation(org.sonar.api.rules.ActiveRule activeRule) { + private static RuleActivation toRuleActivation(org.sonar.api.rules.ActiveRule activeRule) { RuleActivation ruleActivation = new RuleActivation(activeRule.getRule().ruleKey()); ruleActivation.setSeverity(activeRule.getSeverity().name()); for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java index 267b066a80b..10b67a6499f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java @@ -122,11 +122,11 @@ public class QProfileProjectOperations { return qualityProfile; } - private void checkPermission(UserSession userSession) { + private static void checkPermission(UserSession userSession) { userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); } - private void checkPermission(UserSession userSession, String projectKey) { + private static void checkPermission(UserSession userSession, String projectKey) { if (!userSession.hasGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN) && !userSession.hasProjectPermission(UserRole.ADMIN, projectKey)) { throw new ForbiddenException("Insufficient privileges"); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java index e2f65444e5e..3edc390eaa9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java @@ -20,18 +20,15 @@ package org.sonar.server.qualityprofile; +import com.google.common.base.Strings; import java.util.List; - import javax.annotation.CheckForNull; - -import org.sonar.api.server.ServerSide; import org.sonar.api.component.Component; +import org.sonar.api.server.ServerSide; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.user.UserSession; import org.sonar.server.util.Validation; -import com.google.common.base.Strings; - /** * Use {@link org.sonar.server.qualityprofile.QProfileService} instead */ @@ -121,7 +118,7 @@ public class QProfiles { projectOperations.removeAllProjects(profileKey, userSession); } - private void checkProfileNameParam(String name) { + private static void checkProfileNameParam(String name) { if (Strings.isNullOrEmpty(name)) { throw new BadRequestException("quality_profiles.please_type_profile_name"); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProfilesWs.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProfilesWs.java index b5ee1953bd5..af618145579 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProfilesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProfilesWs.java @@ -44,12 +44,12 @@ public class ProfilesWs implements WebService { controller.done(); } - private void defineListAction(NewController controller) { + private static void defineListAction(NewController controller) { WebService.NewAction action = controller.createAction("list") .setDescription("Get a list of profiles") .setSince("3.3") .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-list.json")); + .setResponseExample(Resources.getResource(ProfilesWs.class, "example-list.json")); action.createParam("language") .setDescription("Profile language") @@ -60,12 +60,12 @@ public class ProfilesWs implements WebService { RailsHandler.addJsonOnlyFormatParam(action); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { WebService.NewAction action = controller.createAction("index") .setDescription("Get a profile") .setSince("3.3") .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "example-index.json")); + .setResponseExample(Resources.getResource(ProfilesWs.class, "example-index.json")); action.createParam("language") .setDescription("Profile language") @@ -77,7 +77,7 @@ public class ProfilesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineBackupAction(NewController controller) { + private static void defineBackupAction(NewController controller) { WebService.NewAction action = controller.createAction("backup") .setDescription("Backup a quality profile. Requires Administer Quality Profiles permission") .setSince("3.1") @@ -94,7 +94,7 @@ public class ProfilesWs implements WebService { RailsHandler.addFormatParam(action); } - private void defineRestoreAction(NewController controller) { + private static void defineRestoreAction(NewController controller) { WebService.NewAction action = controller.createAction("restore") .setDescription("Restore a quality profile backup. Requires Administer Quality Profiles permission") .setSince("3.1") @@ -107,7 +107,7 @@ public class ProfilesWs implements WebService { RailsHandler.addJsonOnlyFormatParam(action); } - private void defineDestroyAction(NewController controller) { + private static void defineDestroyAction(NewController controller) { WebService.NewAction action = controller.createAction("destroy") .setDescription("Delete a quality profile. Requires Administer Quality Profiles permission") .setSince("3.3") @@ -124,7 +124,7 @@ public class ProfilesWs implements WebService { .setExampleValue("Sonar way"); } - private void defineSetAsDefaultAction(NewController controller) { + private static void defineSetAsDefaultAction(NewController controller) { WebService.NewAction action = controller.createAction("set_as_default") .setDescription("Set a quality profile as Default. Requires Administer Quality Profiles permission") .setSince("3.3") diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java index f4b3bc9658f..fb6349e77e4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProjectsAction.java @@ -19,6 +19,9 @@ */ package org.sonar.server.qualityprofile.ws; +import org.sonar.api.server.ws.WebService.Param; + +import org.sonar.api.server.ws.WebService.SelectionMode; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; @@ -46,15 +49,10 @@ import java.util.List; public class ProjectsAction implements QProfileWsAction { private static final String PARAM_KEY = "key"; - private static final String PARAM_SELECTED = "selected"; private static final String PARAM_QUERY = "query"; private static final String PARAM_PAGE_SIZE = "pageSize"; private static final String PARAM_PAGE = "page"; - private static final String SELECTION_ALL = "all"; - private static final String SELECTION_SELECTED = "selected"; - private static final String SELECTION_DESELECTED = "deselected"; - private final DbClient dbClient; private final UserSession userSession; @@ -74,10 +72,7 @@ public class ProjectsAction implements QProfileWsAction { .setDescription("A quality profile key.") .setRequired(true) .setExampleValue("sonar-way-java-12345"); - projects.createParam(PARAM_SELECTED) - .setDescription("If specified, return only selected or deselected projects.") - .setPossibleValues(SELECTION_SELECTED, SELECTION_DESELECTED, SELECTION_ALL) - .setDefaultValue(SELECTION_ALL); + projects.addSelectionModeParam(); projects.createParam(PARAM_QUERY) .setDescription("If specified, return only projects whose name match the query."); projects.createParam(PARAM_PAGE_SIZE) @@ -94,7 +89,7 @@ public class ProjectsAction implements QProfileWsAction { try { checkProfileExists(profileKey, session); - String selected = request.param(PARAM_SELECTED); + String selected = request.param(Param.SELECTED); String query = request.param(PARAM_QUERY); int pageSize = request.mandatoryParamAsInt(PARAM_PAGE_SIZE); int page = request.mandatoryParamAsInt(PARAM_PAGE); @@ -151,9 +146,10 @@ public class ProjectsAction implements QProfileWsAction { private List<ProjectQprofileAssociationDto> loadProjects(String profileKey, DbSession session, String selected, String query) { List<ProjectQprofileAssociationDto> projects = Lists.newArrayList(); - if (SELECTION_SELECTED.equals(selected)) { + SelectionMode selectionMode = SelectionMode.fromParam(selected); + if (SelectionMode.SELECTED == selectionMode) { projects.addAll(dbClient.qualityProfileDao().selectSelectedProjects(profileKey, query, session)); - } else if (SELECTION_DESELECTED.equals(selected)) { + } else if (SelectionMode.DESELECTED == selectionMode) { projects.addAll(dbClient.qualityProfileDao().selectDeselectedProjects(profileKey, query, session)); } else { projects.addAll(dbClient.qualityProfileDao().selectProjectAssociations(profileKey, query, session)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java index ca56bdab178..69bec5d8b1f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java @@ -21,13 +21,18 @@ package org.sonar.server.rule; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import java.io.Reader; +import java.util.Collection; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rules.RuleParam; import org.sonar.api.rules.RuleRepository; +import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.rule.RulesDefinition; @@ -40,13 +45,6 @@ import org.sonar.server.debt.DebtModelXMLExporter; import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt; import org.sonar.server.debt.DebtRulesXMLImporter; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.io.Reader; -import java.util.Collection; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; /** @@ -124,7 +122,7 @@ public class DeprecatedRulesDefinitionLoader { } } - private DebtRemediationFunction remediationFunction(DebtRemediationFunction.Type function, @Nullable String coefficient, @Nullable String offset, + private static DebtRemediationFunction remediationFunction(DebtRemediationFunction.Type function, @Nullable String coefficient, @Nullable String offset, RulesDefinition.DebtRemediationFunctions functions, String repoKey, String ruleKey) { if (DebtRemediationFunction.Type.LINEAR.equals(function) && coefficient != null) { return functions.linear(coefficient); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index d17ef7ef29b..ef3a54b65bc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -25,6 +25,14 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; @@ -49,11 +57,6 @@ import org.sonar.server.db.DbClient; import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.startup.RegisterDebtModel; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.*; - import static com.google.common.collect.Lists.newArrayList; /** @@ -160,7 +163,7 @@ public class RegisterRules implements Startable { } @CheckForNull - private CharacteristicDto characteristic(RulesDefinition.Rule ruleDef, @Nullable Integer overridingCharacteristicId, Map<String, CharacteristicDto> allCharacteristics) { + private static CharacteristicDto characteristic(RulesDefinition.Rule ruleDef, @Nullable Integer overridingCharacteristicId, Map<String, CharacteristicDto> allCharacteristics) { String subCharacteristic = ruleDef.debtSubCharacteristic(); String repo = ruleDef.repository().key(); String ruleKey = ruleDef.key(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java index f98fe84c8cb..fa4850d7621 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java @@ -21,9 +21,11 @@ package org.sonar.server.rule; import com.google.common.base.Strings; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.builder.EqualsBuilder; -import org.sonar.api.server.ServerSide; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; import org.sonar.core.permission.GlobalPermissions; @@ -36,9 +38,6 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - /** * @deprecated to be dropped in 4.4 */ @@ -151,7 +150,7 @@ public class RuleOperations { .isEquals(); } - private void checkPermission(UserSession userSession) { + private static void checkPermission(UserSession userSession) { userSession.checkLoggedIn(); userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java index 8d469629b0a..442d452f1d0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java @@ -22,12 +22,16 @@ package org.sonar.server.rule; import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; -import org.sonar.api.server.ServerSide; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; +import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.utils.System2; import org.sonar.core.persistence.DbSession; @@ -39,11 +43,6 @@ import org.sonar.core.technicaldebt.db.CharacteristicDto; import org.sonar.server.db.DbClient; import org.sonar.server.user.UserSession; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Set; - import static com.google.common.collect.Lists.newArrayList; @ServerSide @@ -141,7 +140,7 @@ public class RuleUpdater { } } - private void updateName(RuleUpdate update, Context context) { + private static void updateName(RuleUpdate update, Context context) { String name = update.getName(); if (Strings.isNullOrEmpty(name)) { throw new IllegalArgumentException("The name is missing"); @@ -150,7 +149,7 @@ public class RuleUpdater { } } - private void updateDescription(RuleUpdate update, Context context) { + private static void updateDescription(RuleUpdate update, Context context) { String description = update.getMarkdownDescription(); if (Strings.isNullOrEmpty(description)) { throw new IllegalArgumentException("The description is missing"); @@ -159,7 +158,7 @@ public class RuleUpdater { } } - private void updateSeverity(RuleUpdate update, Context context) { + private static void updateSeverity(RuleUpdate update, Context context) { String severity = update.getSeverity(); if (Strings.isNullOrEmpty(severity) || !Severity.ALL.contains(severity)) { throw new IllegalArgumentException("The severity is invalid"); @@ -168,7 +167,7 @@ public class RuleUpdater { } } - private void updateStatus(RuleUpdate update, Context context) { + private static void updateStatus(RuleUpdate update, Context context) { RuleStatus status = update.getStatus(); if (status == null) { throw new IllegalArgumentException("The status is missing"); @@ -177,7 +176,7 @@ public class RuleUpdater { } } - private void updateTags(RuleUpdate update, Context context) { + private static void updateTags(RuleUpdate update, Context context) { Set<String> tags = update.getTags(); if (tags == null || tags.isEmpty()) { context.rule.setTags(Collections.<String>emptySet()); @@ -186,7 +185,7 @@ public class RuleUpdater { } } - private void updateDebtSubCharacteristic(RuleUpdate update, Context context) { + private static void updateDebtSubCharacteristic(RuleUpdate update, Context context) { if (update.getDebtSubCharacteristicKey() == null) { // set to "none" Integer id = context.rule.getDefaultSubCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null; @@ -216,7 +215,7 @@ public class RuleUpdater { } } - private void updateDebtRemediationFunction(RuleUpdate update, Context context) { + private static void updateDebtRemediationFunction(RuleUpdate update, Context context) { Integer subChar = context.rule.getSubCharacteristicId(); boolean noChar = (context.rule.getDefaultSubCharacteristicId() == null && subChar == null) || diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java index 3fc1d2c0b86..8ad8f75fef0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java @@ -23,17 +23,40 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; +import java.io.Serializable; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; import org.elasticsearch.action.count.CountRequestBuilder; import org.elasticsearch.action.count.CountResponse; -import org.elasticsearch.action.get.*; +import org.elasticsearch.action.get.GetRequestBuilder; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.get.MultiGetItemResponse; +import org.elasticsearch.action.get.MultiGetRequest; +import org.elasticsearch.action.get.MultiGetRequestBuilder; +import org.elasticsearch.action.get.MultiGetResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchScrollRequestBuilder; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.BoolFilterBuilder; +import org.elasticsearch.index.query.FilterBuilder; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; @@ -49,12 +72,6 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.core.persistence.Dto; import org.sonar.server.exceptions.NotFoundException; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.io.Serializable; -import java.util.*; - /** * @deprecated replaced by {@link org.sonar.server.es.BaseIndex} */ @@ -273,7 +290,7 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial } } - private Map mapUuidPathField(IndexField field) { + private static Map mapUuidPathField(IndexField field) { return ImmutableMap.of( "type", "string", "index", "analyzed", diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/HtmlTextDecorator.java b/server/sonar-server/src/main/java/org/sonar/server/source/HtmlTextDecorator.java index 0a6d74e96ea..c55cbf8f5f3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/HtmlTextDecorator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/HtmlTextDecorator.java @@ -20,15 +20,13 @@ package org.sonar.server.source; import com.google.common.io.Closeables; -import org.sonar.api.utils.log.Loggers; - -import javax.annotation.Nullable; - import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.Collection; import java.util.List; +import javax.annotation.Nullable; +import org.sonar.api.utils.log.Loggers; import static com.google.common.collect.Lists.newArrayList; @@ -127,11 +125,11 @@ class HtmlTextDecorator { } } - private boolean canAddLine(int currentLine, @Nullable Integer from) { + private static boolean canAddLine(int currentLine, @Nullable Integer from) { return from == null || currentLine >= from; } - private boolean shouldStop(int currentLine, @Nullable Integer to) { + private static boolean shouldStop(int currentLine, @Nullable Integer to) { return to != null && to < currentLine; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java b/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java index 7962fa25364..e17ae11c323 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java @@ -72,8 +72,9 @@ public class FileSourceDao implements DaoComponent { ResultSet rs = null; Reader reader = null; try { - pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type = '" + Type.SOURCE +"'"); + pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type=?"); pstmt.setString(1, fileUuid); + pstmt.setString(2, Type.SOURCE); rs = pstmt.executeQuery(); if (rs.next()) { reader = rs.getCharacterStream(1); diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/JdbcDriverDeployer.java b/server/sonar-server/src/main/java/org/sonar/server/startup/JdbcDriverDeployer.java index 8a38ae75e6a..bf1e09ca327 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/startup/JdbcDriverDeployer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/JdbcDriverDeployer.java @@ -19,17 +19,15 @@ */ package org.sonar.server.startup; +import java.io.File; +import java.io.IOException; +import javax.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.sonar.api.config.Settings; import org.sonar.home.cache.FileHashes; import org.sonar.process.ProcessProperties; import org.sonar.server.platform.DefaultServerFileSystem; -import javax.annotation.Nullable; - -import java.io.File; -import java.io.IOException; - public class JdbcDriverDeployer { private final DefaultServerFileSystem fileSystem; @@ -66,7 +64,7 @@ public class JdbcDriverDeployer { } } - private String driverIndexContent(@Nullable File deployedDriver) { + private static String driverIndexContent(@Nullable File deployedDriver) { if (deployedDriver != null) { String hash = new FileHashes().of(deployedDriver); return deployedDriver.getName() + "|" + hash; diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/LogServerId.java b/server/sonar-server/src/main/java/org/sonar/server/startup/LogServerId.java index 5fcbeb02a3e..8cbfb2bdbbe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/startup/LogServerId.java +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/LogServerId.java @@ -61,7 +61,7 @@ public final class LogServerId { } } - private void addQuotedValue(PropertyDto property, StringBuilder message) { + private static void addQuotedValue(PropertyDto property, StringBuilder message) { message.append("\""); message.append(property.getValue()); message.append("\"\n"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java index d8e7e988bc1..4e70fa30931 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipFinder.java @@ -20,6 +20,7 @@ package org.sonar.server.user; +import java.util.List; import org.sonar.api.server.ServerSide; import org.sonar.core.user.GroupMembership; import org.sonar.core.user.GroupMembershipDao; @@ -29,8 +30,6 @@ import org.sonar.core.user.UserDao; import org.sonar.core.user.UserDto; import org.sonar.server.exceptions.NotFoundException; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; @ServerSide @@ -88,7 +87,7 @@ public class GroupMembershipFinder { return userDto.getId(); } - private List<GroupMembership> toGroupMembership(List<GroupMembershipDto> dtos) { + private static List<GroupMembership> toGroupMembership(List<GroupMembershipDto> dtos) { List<GroupMembership> groups = newArrayList(); for (GroupMembershipDto groupMembershipDto : dtos) { groups.add(groupMembershipDto.toGroupMembership()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipService.java b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipService.java index c5932c8b5c0..067a659dc5e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/GroupMembershipService.java @@ -20,12 +20,11 @@ package org.sonar.server.user; +import java.util.Map; import org.sonar.api.server.ServerSide; import org.sonar.core.user.GroupMembershipQuery; import org.sonar.server.util.RubyUtils; -import java.util.Map; - /** * Used by ruby code <pre>Internal.group_membership</pre> */ @@ -55,7 +54,7 @@ public class GroupMembershipService { return builder.build(); } - private String membership(Map<String, Object> params) { + private static String membership(Map<String, Object> params) { String selected = (String) params.get("selected"); if (SELECTED_MEMBERSHIP.equals(selected)) { return GroupMembershipQuery.IN; diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java index 302b522b58c..44532f11e2e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java @@ -20,6 +20,8 @@ package org.sonar.server.user.index; +import java.sql.Connection; +import java.util.Iterator; import org.apache.commons.dbutils.DbUtils; import org.elasticsearch.action.update.UpdateRequest; import org.sonar.core.persistence.DbSession; @@ -28,9 +30,6 @@ import org.sonar.server.es.BaseIndexer; import org.sonar.server.es.BulkIndexer; import org.sonar.server.es.EsClient; -import java.sql.Connection; -import java.util.Iterator; - public class UserIndexer extends BaseIndexer { private final DbClient dbClient; @@ -71,7 +70,7 @@ public class UserIndexer extends BaseIndexer { return maxUpdatedAt; } - private UpdateRequest newUpsertRequest(UserDoc user) { + private static UpdateRequest newUpsertRequest(UserDoc user) { return new UpdateRequest(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, user.login()) .doc(user.getFields()) .upsert(user.getFields()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/FavoritesWs.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/FavoritesWs.java index 52c3c89639f..facfd4ec20f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/FavoritesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/FavoritesWs.java @@ -36,7 +36,7 @@ public class FavoritesWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/GroupsAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/GroupsAction.java index 9538cd93104..b713e9a6ed2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/GroupsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/GroupsAction.java @@ -20,12 +20,12 @@ package org.sonar.server.user.ws; import java.util.List; -import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.server.ws.WebService.NewController; import org.sonar.api.server.ws.WebService.Param; +import org.sonar.api.server.ws.WebService.SelectionMode; import org.sonar.api.utils.Paging; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.permission.GlobalPermissions; @@ -39,15 +39,10 @@ import org.sonar.server.user.UserSession; public class GroupsAction implements UsersWsAction { private static final String PARAM_LOGIN = "login"; - private static final String PARAM_SELECTED = "selected"; - private static final String SELECTION_ALL = "all"; - private static final String SELECTION_SELECTED = PARAM_SELECTED; - private static final String SELECTION_DESELECTED = "deselected"; - - private static final String FIELD_SELECTED = PARAM_SELECTED; - private static final String FIELD_DESCRIPTION = "description"; private static final String FIELD_NAME = "name"; + private static final String FIELD_DESCRIPTION = "description"; + private static final String FIELD_SELECTED = "selected"; private final DbClient dbClient; private final UserSession userSession; @@ -70,14 +65,9 @@ public class GroupsAction implements UsersWsAction { .setExampleValue("admin") .setRequired(true); - action.createParam(PARAM_SELECTED) - .setDescription("If specified, only show groups the user is member of (selected) or not (deselected).") - .setPossibleValues(SELECTION_SELECTED, SELECTION_DESELECTED, SELECTION_ALL) - .setDefaultValue(SELECTION_ALL); + action.addSelectionModeParam(); - action.createParam(Param.TEXT_QUERY) - .setDescription("If specified, only show groups whose name contains the query.") - .setExampleValue("user"); + action.addSearchQuery("users", "group names"); action.addPagingParams(25); } @@ -90,7 +80,7 @@ public class GroupsAction implements UsersWsAction { int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); int page = request.mandatoryParamAsInt(Param.PAGE); String queryString = request.param(Param.TEXT_QUERY); - String selected = request.param(PARAM_SELECTED); + String selected = request.mandatoryParam(Param.SELECTED); GroupMembershipQuery query = GroupMembershipQuery.builder() .login(login) @@ -116,7 +106,7 @@ public class GroupsAction implements UsersWsAction { } } - private void writeGroups(JsonWriter json, List<GroupMembershipDto> groups) { + private static void writeGroups(JsonWriter json, List<GroupMembershipDto> groups) { json.name("groups").beginArray(); for (GroupMembershipDto group : groups) { json.beginObject() @@ -128,17 +118,18 @@ public class GroupsAction implements UsersWsAction { json.endArray(); } - private void writePaging(JsonWriter json, Paging paging) { + private static void writePaging(JsonWriter json, Paging paging) { json.prop("p", paging.pageIndex()) .prop("ps", paging.pageSize()) .prop("total", paging.total()); } - private String getMembership(@Nullable String selected) { + private String getMembership(String selected) { + SelectionMode selectionMode = SelectionMode.fromParam(selected); String membership = GroupMembershipQuery.ANY; - if (SELECTION_SELECTED.equals(selected)) { + if (SelectionMode.SELECTED == selectionMode) { membership = GroupMembershipQuery.IN; - } else if (SELECTION_DESELECTED.equals(selected)) { + } else if (SelectionMode.DESELECTED == selectionMode) { membership = GroupMembershipQuery.OUT; } return membership; diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java index d3cf1fa69fe..22fa4a1b4ed 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java @@ -105,7 +105,7 @@ public class UpdateAction implements UsersWsAction { json.endObject().close(); } - private void writeUser(JsonWriter json, UserDoc user) { + private static void writeUser(JsonWriter json, UserDoc user) { json.name("user").beginObject() .prop("login", user.login()) .prop("name", user.name()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UserPropertiesWs.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UserPropertiesWs.java index f4db43de3e9..976f19834e8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UserPropertiesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UserPropertiesWs.java @@ -36,7 +36,7 @@ public class UserPropertiesWs implements WebService { controller.done(); } - private void defineIndexAction(NewController controller) { + private static void defineIndexAction(NewController controller) { controller.createAction("index") .setDescription("Documentation of this web service is available <a href=\"http://redirect.sonarsource.com/doc/old-web-service-api.html\">here</a>") .setSince("2.6") diff --git a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UsersAction.java b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UsersAction.java index 526ddd114b9..6cb3a52d35f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UsersAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/UsersAction.java @@ -20,12 +20,12 @@ package org.sonar.server.usergroups.ws; import java.util.List; -import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.server.ws.WebService.NewController; import org.sonar.api.server.ws.WebService.Param; +import org.sonar.api.server.ws.WebService.SelectionMode; import org.sonar.api.utils.Paging; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.permission.GlobalPermissions; @@ -40,13 +40,8 @@ import org.sonar.server.user.UserSession; public class UsersAction implements UserGroupsWsAction { private static final String PARAM_ID = "id"; - private static final String PARAM_SELECTED = "selected"; - private static final String SELECTION_ALL = "all"; - private static final String SELECTION_SELECTED = PARAM_SELECTED; - private static final String SELECTION_DESELECTED = "deselected"; - - private static final String FIELD_SELECTED = PARAM_SELECTED; + private static final String FIELD_SELECTED = "selected"; private static final String FIELD_NAME = "name"; private static final String FIELD_LOGIN = "login"; @@ -71,10 +66,7 @@ public class UsersAction implements UserGroupsWsAction { .setExampleValue("42") .setRequired(true); - action.createParam(PARAM_SELECTED) - .setDescription("If specified, only show users who belong to a group (selected=selected) or only those who do not (selected=deselected).") - .setPossibleValues(SELECTION_SELECTED, SELECTION_DESELECTED, SELECTION_ALL) - .setDefaultValue(SELECTION_ALL); + action.addSelectionModeParam(); action.addSearchQuery("freddy", "names", "logins"); @@ -89,7 +81,7 @@ public class UsersAction implements UserGroupsWsAction { int pageSize = request.mandatoryParamAsInt(Param.PAGE_SIZE); int page = request.mandatoryParamAsInt(Param.PAGE); String queryString = request.param(Param.TEXT_QUERY); - String selected = request.param(PARAM_SELECTED); + String selected = request.mandatoryParam(Param.SELECTED); UserMembershipQuery query = UserMembershipQuery.builder() .groupId(groupId) @@ -115,7 +107,7 @@ public class UsersAction implements UserGroupsWsAction { } } - private void writeMembers(JsonWriter json, List<UserMembershipDto> users) { + private static void writeMembers(JsonWriter json, List<UserMembershipDto> users) { json.name("users").beginArray(); for (UserMembershipDto user : users) { json.beginObject() @@ -127,17 +119,18 @@ public class UsersAction implements UserGroupsWsAction { json.endArray(); } - private void writePaging(JsonWriter json, Paging paging) { + private static void writePaging(JsonWriter json, Paging paging) { json.prop(Param.PAGE, paging.pageIndex()) .prop(Param.PAGE_SIZE, paging.pageSize()) .prop("total", paging.total()); } - private String getMembership(@Nullable String selected) { + private String getMembership(String selected) { + SelectionMode selectionMode = SelectionMode.fromParam(selected); String membership = GroupMembershipQuery.ANY; - if (SELECTION_SELECTED.equals(selected)) { + if (SelectionMode.SELECTED == selectionMode) { membership = GroupMembershipQuery.IN; - } else if (SELECTION_DESELECTED.equals(selected)) { + } else if (SelectionMode.DESELECTED == selectionMode) { membership = GroupMembershipQuery.OUT; } return membership; diff --git a/server/sonar-server/src/main/java/org/sonar/server/util/CloseableIterator.java b/server/sonar-server/src/main/java/org/sonar/server/util/CloseableIterator.java index b63a332be9c..b718dbe094d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/util/CloseableIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/util/CloseableIterator.java @@ -20,6 +20,7 @@ package org.sonar.server.util; import com.google.common.base.Throwables; +import java.io.Closeable; import java.util.Iterator; import java.util.NoSuchElementException; import javax.annotation.CheckForNull; @@ -59,28 +60,8 @@ public abstract class CloseableIterator<O> implements Iterator<O>, AutoCloseable public static <T> CloseableIterator<T> from(final Iterator<T> iterator) { // early fail requireNonNull(iterator); - checkArgument(!(iterator instanceof CloseableIterator), "This method does not support creating a CloseableIterator from a CloseableIterator"); - return new CloseableIterator<T>() { - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next(); - } - - @Override - protected T doNext() { - throw new UnsupportedOperationException("hasNext has been override, doNext is never called"); - } - - @Override - protected void doClose() throws Exception { - // do nothing - } - }; + checkArgument(!(iterator instanceof AutoCloseable), "This method does not support creating a CloseableIterator from an Iterator which is Closeable"); + return new RegularIteratorWrapper<>(iterator); } private O nextObject = null; @@ -157,4 +138,31 @@ public abstract class CloseableIterator<O> implements Iterator<O>, AutoCloseable protected abstract void doClose() throws Exception; + private static class RegularIteratorWrapper<T> extends CloseableIterator<T> { + private final Iterator<T> iterator; + + public RegularIteratorWrapper(Iterator<T> iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return iterator.next(); + } + + @Override + protected T doNext() { + throw new UnsupportedOperationException("hasNext has been override, doNext is never called"); + } + + @Override + protected void doClose() throws Exception { + // do nothing + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java index 93944e0cb38..1c12e86c163 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java @@ -20,6 +20,8 @@ package org.sonar.server.view.index; +import java.util.List; +import java.util.Map; import org.elasticsearch.action.update.UpdateRequest; import org.sonar.core.component.ComponentDto; import org.sonar.core.component.UuidWithProjectUuidDto; @@ -30,9 +32,6 @@ import org.sonar.server.es.BulkIndexer; import org.sonar.server.es.EsClient; import org.sonar.server.issue.index.IssueIndex; -import java.util.List; -import java.util.Map; - import static com.google.common.collect.Maps.newHashMap; public class ViewIndexer extends BaseIndexer { @@ -121,7 +120,7 @@ public class ViewIndexer extends BaseIndexer { } } - private UpdateRequest newUpsertRequest(ViewDoc doc) { + private static UpdateRequest newUpsertRequest(ViewDoc doc) { return new UpdateRequest(ViewIndexDefinition.INDEX, ViewIndexDefinition.TYPE_VIEW, doc.uuid()) .doc(doc.getFields()) .upsert(doc.getFields()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DbComponentsRefCacheTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImplTest.java index 67c20dfa9b2..6f369f7906b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DbComponentsRefCacheTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImplTest.java @@ -17,29 +17,26 @@ * 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.server.computation.batch; -package org.sonar.server.computation.component; - +import java.io.File; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class DbComponentsRefCacheTest { - - @Test - public void add_and_get_component() throws Exception { - DbComponentsRefCache cache = new DbComponentsRefCache(); - cache.addComponent(1, new DbComponentsRefCache.DbComponent(10L, "Key", "Uuid")); +public class BatchReportDirectoryHolderImplTest { - assertThat(cache.getByRef(1)).isNotNull(); - assertThat(cache.getByRef(1).getId()).isEqualTo(10L); - assertThat(cache.getByRef(1).getKey()).isEqualTo("Key"); - assertThat(cache.getByRef(1).getUuid()).isEqualTo("Uuid"); + @Test(expected = IllegalStateException.class) + public void getDirectory_throws_ISE_if_holder_is_empty() { + new BatchReportDirectoryHolderImpl().getDirectory(); } - @Test(expected = IllegalArgumentException.class) - public void fail_on_unknown_ref() throws Exception { - new DbComponentsRefCache().getByRef(1); - } + @Test + public void getDirectory_returns_File_set_with_setDirectory() { + File file = new File(""); + BatchReportDirectoryHolderImpl holder = new BatchReportDirectoryHolderImpl(); + holder.setDirectory(file); + assertThat(holder.getDirectory()).isSameAs(file); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/ComponentTreeRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/ComponentTreeRule.java new file mode 100644 index 00000000000..db6e2bc5710 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/ComponentTreeRule.java @@ -0,0 +1,127 @@ +/* + * 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.server.computation.batch; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import java.util.Objects; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.ComponentImpl; +import org.sonar.server.computation.component.DumbComponent; + +public class ComponentTreeRule implements TestRule { + + @CheckForNull + private final BatchReportReader batchReportReader; + private final BUILD_OPTIONS buildOptions; + + private Component root; + + private ComponentTreeRule(BatchReportReader batchReportReader, BUILD_OPTIONS buildOptions) { + this.batchReportReader = batchReportReader; + this.buildOptions = buildOptions; + } + + public static ComponentTreeRule from(BatchReportReader batchReportReader, BUILD_OPTIONS buildOptions) { + return new ComponentTreeRule(Objects.requireNonNull(batchReportReader), buildOptions); + } + + public static ComponentTreeRule from(BatchReportReader batchReportReader) { + return new ComponentTreeRule(Objects.requireNonNull(batchReportReader), BUILD_OPTIONS.NONE); + } + + @Override + public Statement apply(final Statement statement, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + statement.evaluate(); + } finally { + clear(); + } + } + }; + } + + private void clear() { + this.root = null; + } + + public enum BUILD_OPTIONS { + NONE(false, false), KEY(false, true), UUID(true, false), KEY_AND_UUID(true, true); + private final boolean uuid; + private final boolean key; + + BUILD_OPTIONS(boolean uuid, boolean key) { + this.uuid = uuid; + this.key = key; + } + } + + public Component getRoot() { + if (root == null) { + buildComponentRoot(buildOptions); + } + return this.root; + } + + private Component buildComponentRoot(BUILD_OPTIONS build_options) { + int rootComponentRef = batchReportReader.readMetadata().getRootComponentRef(); + return newComponent(batchReportReader.readComponent(rootComponentRef), build_options); + } + + private DumbComponent newComponent(BatchReport.Component component, BUILD_OPTIONS build_options) { + return new DumbComponent( + ComponentImpl.convertType(component.getType()), + component.getRef(), + build_options.uuid ? uuidOf(component.getRef()) : null, + build_options.key ? keyOf(component.getRef()) : null, + buildChildren(component, build_options)); + } + + private Component[] buildChildren(BatchReport.Component component, final BUILD_OPTIONS build_options) { + return Iterables.toArray( + Iterables.transform( + component.getChildRefList(), + new Function<Integer, Component>() { + @Override + public Component apply(@Nonnull Integer componentRef) { + return newComponent(batchReportReader.readComponent(componentRef), build_options); + } + } + ), Component.class); + } + + public String keyOf(int ref) { + return "key_" + ref; + } + + public String uuidOf(int ref) { + return "uuid_" + ref; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java index b3ca3251ddd..c715cfbabb2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java @@ -19,15 +19,21 @@ */ package org.sonar.server.computation.batch; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; +import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER; + public class TreeRootHolderRule implements TestRule, TreeRootHolder { private Component root; + private Map<Integer, Component> componentsByRef = new HashMap<>(); @Override public Statement apply(final Statement statement, Description description) { @@ -55,7 +61,26 @@ public class TreeRootHolderRule implements TestRule, TreeRootHolder { return root; } + @Override + public Component getComponentByRef(int ref) { + if (root == null) { + throw new IllegalStateException("Root has not been set in " + TreeRootHolder.class.getSimpleName()); + } + + Component component = componentsByRef.get(ref); + if (component == null) { + throw new IllegalArgumentException(String.format("Component '%s' hasn't been found", ref)); + } + return component; + } + public void setRoot(Component newRoot) { this.root = Objects.requireNonNull(newRoot); + new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) { + @Override + public void visitAny(Component component) { + componentsByRef.put(component.getRef(), component); + } + }.visit(root); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComponentImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComponentImplTest.java index d5ba594bfa8..3100ab9a1f9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComponentImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComponentImplTest.java @@ -29,6 +29,11 @@ import static org.assertj.core.api.Assertions.assertThat; public class ComponentImplTest { private ComponentImpl component = new ComponentImpl(BatchReport.Component.newBuilder().build(), Collections.<Component>emptyList()); + @Test(expected = NullPointerException.class) + public void constructor_throws_NPE_if_component_arg_is_Null() { + new ComponentImpl(null, null); + } + @Test(expected = UnsupportedOperationException.class) public void getUuid_throws_UOE_if_uuid_has_not_been_set_yet() { component.getUuid(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DbIdsRepositoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DbIdsRepositoryTest.java new file mode 100644 index 00000000000..d2c30aae8b4 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DbIdsRepositoryTest.java @@ -0,0 +1,62 @@ +/* + * 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.server.computation.component; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DbIdsRepositoryTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + Component component = DumbComponent.DUMB_PROJECT; + + @Test + public void add_and_get_component() throws Exception { + DbIdsRepository cache = new DbIdsRepository(); + cache.setComponentId(component, 10L); + + assertThat(cache.getComponentId(component)).isEqualTo(10L); + } + + @Test + public void fail_on_unknown_ref() throws Exception { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Component ref '" + component.getRef() + "' has no component id"); + + new DbIdsRepository().getComponentId(DumbComponent.DUMB_PROJECT); + } + + @Test + public void fail_if_component_id_already_set() throws Exception { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Component ref '" + component.getRef() + "' has already a component id"); + + DbIdsRepository cache = new DbIdsRepository(); + cache.setComponentId(component, 10L); + cache.setComponentId(component, 11L); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java index 1613b765545..679b708ef18 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java @@ -19,29 +19,62 @@ */ package org.sonar.server.computation.component; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class TreeRootHolderImplTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + TreeRootHolderImpl treeRootHolder = new TreeRootHolderImpl(); - Component component = mock(Component.class); + Component project = DumbComponent.DUMB_PROJECT; - @Test(expected = NullPointerException.class) + @Test public void setRoot_throws_NPE_if_arg_is_null() { + thrown.expect(NullPointerException.class); treeRootHolder.setRoot(null); } - @Test(expected = IllegalStateException.class) + @Test public void getRoot_throws_ISE_if_root_has_not_been_set_yet() { + thrown.expect(IllegalStateException.class); treeRootHolder.getRoot(); } @Test public void verify_setRoot_getRoot() { - treeRootHolder.setRoot(component); - assertThat(treeRootHolder.getRoot()).isSameAs(component); + treeRootHolder.setRoot(project); + assertThat(treeRootHolder.getRoot()).isSameAs(project); + } + + @Test + public void get_by_ref() throws Exception { + Component file = new DumbComponent(Component.Type.FILE, 4, null, null); + Component directory = new DumbComponent(Component.Type.DIRECTORY, 3, null, null, file); + Component module = new DumbComponent(Component.Type.MODULE, 2, null, null, directory); + Component project = new DumbComponent(Component.Type.PROJECT, 1, null, null, module); + treeRootHolder.setRoot(project); + + assertThat(treeRootHolder.getComponentByRef(1)).isEqualTo(project); + assertThat(treeRootHolder.getComponentByRef(2)).isEqualTo(module); + assertThat(treeRootHolder.getComponentByRef(3)).isEqualTo(directory); + assertThat(treeRootHolder.getComponentByRef(4)).isEqualTo(file); + } + + @Test + public void fail_to_get_by_ref_if_root_not_set() throws Exception { + thrown.expect(IllegalStateException.class); + treeRootHolder.getComponentByRef(project.getRef()); + } + + @Test + public void fail_to_get_by_ref_if_ref_not_found() throws Exception { + thrown.expect(IllegalArgumentException.class); + treeRootHolder.setRoot(project); + treeRootHolder.getComponentByRef(123); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ApplyPermissionsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ApplyPermissionsStepTest.java index 286f58c79cd..e77c23cfff9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ApplyPermissionsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ApplyPermissionsStepTest.java @@ -45,7 +45,7 @@ import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; @@ -63,8 +63,10 @@ public class ApplyPermissionsStepTest extends BaseStepTest { @ClassRule public static EsTester esTester = new EsTester().addDefinitions(new IssueIndexDefinition(new Settings())); + @ClassRule public static DbTester dbTester = new DbTester(); + @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); @@ -74,7 +76,7 @@ public class ApplyPermissionsStepTest extends BaseStepTest { Settings settings; - DbComponentsRefCache dbComponentsRefCache; + DbIdsRepository dbIdsRepository; IssueAuthorizationIndexer issueAuthorizationIndexer; ApplyPermissionsStep step; @@ -94,10 +96,10 @@ public class ApplyPermissionsStepTest extends BaseStepTest { issueAuthorizationIndexer = new IssueAuthorizationIndexer(dbClient, esTester.client()); issueAuthorizationIndexer.setEnabled(true); - dbComponentsRefCache = new DbComponentsRefCache(); + dbIdsRepository = new DbIdsRepository(); - step = new ApplyPermissionsStep(dbClient, dbComponentsRefCache, issueAuthorizationIndexer, new PermissionFacade(roleDao, null, - new ResourceDao(dbTester.myBatis(), System2.INSTANCE), permissionTemplateDao, settings), treeRootHolder); + step = new ApplyPermissionsStep(dbClient, dbIdsRepository, issueAuthorizationIndexer, new PermissionFacade(roleDao, null, + new ResourceDao(dbTester.myBatis(), System2.INSTANCE), permissionTemplateDao, settings), treeRootHolder); } @After @@ -116,8 +118,9 @@ public class ApplyPermissionsStepTest extends BaseStepTest { dbClient.permissionTemplateDao().addGroupPermission(permissionTemplateDto.getId(), null, UserRole.USER); dbSession.commit(); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(projectDto.getId(), PROJECT_KEY, PROJECT_UUID)); - treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_KEY, PROJECT_UUID)); + Component project = new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY); + dbIdsRepository.setComponentId(project, projectDto.getId()); + treeRootHolder.setRoot(project); step.execute(); dbSession.commit(); @@ -143,8 +146,9 @@ public class ApplyPermissionsStepTest extends BaseStepTest { dbSession.commit(); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(projectDto.getId(), PROJECT_KEY, PROJECT_UUID)); - treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_KEY, PROJECT_UUID)); + Component project = new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY); + dbIdsRepository.setComponentId(project, projectDto.getId()); + treeRootHolder.setRoot(project); step.execute(); dbSession.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java index c28d1a7a30c..de876131970 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java @@ -23,38 +23,35 @@ package org.sonar.server.computation.step; import java.io.IOException; import org.junit.Rule; import org.junit.Test; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.core.component.ComponentDto; import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DumbComponent; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class IndexComponentsStepTest extends BaseStepTest { private static final String PROJECT_KEY = "PROJECT_KEY"; @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); ResourceIndexerDao resourceIndexerDao = mock(ResourceIndexerDao.class); - DbComponentsRefCache dbComponentsRefCache = new DbComponentsRefCache(); - IndexComponentsStep sut = new IndexComponentsStep(resourceIndexerDao, dbComponentsRefCache, reportReader); + DbIdsRepository dbIdsRepository = new DbIdsRepository(); + IndexComponentsStep sut = new IndexComponentsStep(resourceIndexerDao, dbIdsRepository, treeRootHolder); @Test public void call_indexProject_of_dao() throws IOException { - dbComponentsRefCache.addComponent(1, new DbComponent(123L, PROJECT_KEY, "PROJECT_UUID")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - - ComponentDto project = mock(ComponentDto.class); - when(project.getId()).thenReturn(123L); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "PROJECT_UUID", PROJECT_KEY); + dbIdsRepository.setComponentId(project, 123L); + treeRootHolder.setRoot(project); sut.execute(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexSourceLinesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexSourceLinesStepTest.java index 9ff56d86d8c..d088251659c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexSourceLinesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexSourceLinesStepTest.java @@ -27,10 +27,10 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.Settings; -import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.persistence.DbTester; -import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; import org.sonar.server.source.db.FileSourceDao; @@ -43,43 +43,37 @@ import static org.assertj.core.api.Assertions.assertThat; public class IndexSourceLinesStepTest extends BaseStepTest { - private static final String PROJECT_KEY = "PROJECT_KEY"; - @ClassRule public static DbTester dbTester = new DbTester(); + @ClassRule public static EsTester esTester = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings())); + @Rule - public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); DbClient dbClient; - DbComponentsRefCache dbComponentsRefCache; @Before public void setUp() { dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new FileSourceDao(null)); - dbComponentsRefCache = new DbComponentsRefCache(); } @Override protected ComputationStep step() { SourceLineIndexer sourceLineIndexer = new SourceLineIndexer(dbClient, esTester.client()); sourceLineIndexer.setEnabled(true); - return new IndexSourceLinesStep(sourceLineIndexer, dbComponentsRefCache, reportReader); + return new IndexSourceLinesStep(sourceLineIndexer, treeRootHolder); } @Test public void index_source() throws Exception { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - dbTester.prepareDbUnit(getClass(), "index_source.xml"); Connection connection = dbTester.openConnection(); FileSourceTesting.updateDataColumn(connection, "FILE1_UUID", FileSourceTesting.newRandomData(1).build()); connection.close(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", "PROJECT_KEY")); step().execute(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java index 3970cbcc031..b36e572c090 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java @@ -28,10 +28,10 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.Settings; -import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.persistence.DbTester; -import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; import org.sonar.server.source.db.FileSourceDao; @@ -46,40 +46,36 @@ public class IndexTestsStepTest extends BaseStepTest { @ClassRule public static DbTester dbTester = new DbTester(); + @ClassRule public static EsTester esTester = new EsTester().addDefinitions(new TestIndexDefinition(new Settings())); + @Rule - public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); DbClient dbClient; - DbComponentsRefCache dbComponentsRefCache; @Before public void setUp() { dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new FileSourceDao(null)); esTester.truncateIndices(); - dbComponentsRefCache = new DbComponentsRefCache(); } @Override protected ComputationStep step() { TestIndexer testIndexer = new TestIndexer(dbClient, esTester.client()); testIndexer.setEnabled(true); - return new IndexTestsStep(testIndexer, dbComponentsRefCache, reportReader); + return new IndexTestsStep(testIndexer, treeRootHolder); } @Test public void index_test() throws Exception { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, "PROJECT_KEY", "ABCD")); - dbTester.prepareDbUnit(getClass(), "index_source.xml"); Connection connection = dbTester.openConnection(); TestTesting.updateDataColumn(connection, "FILE1_UUID", TestTesting.newRandomTests(1)); connection.close(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", "PROJECT_KEY")); step().execute(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java index e91e84168f9..6ff9fce279b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java @@ -26,6 +26,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.sonar.api.utils.System2; import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.component.ComponentDto; @@ -36,7 +37,7 @@ import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.batch.BatchReportReaderRule; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.test.DbTests; @@ -45,22 +46,28 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @Category(DbTests.class) -public class PersistComponentsStepTest extends BaseStepTest { +public class PersistComponentsAndSnapshotsStepTest extends BaseStepTest { private static final String PROJECT_KEY = "PROJECT_KEY"; @ClassRule public static DbTester dbTester = new DbTester(); - @Rule - public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule + public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + + System2 system2 = mock(System2.class); + + DbIdsRepository dbIdsRepository; + DbSession session; + DbClient dbClient; - DbComponentsRefCache dbComponentsRefCache; - PersistComponentsStep sut; + PersistComponentsAndSnapshotsStep sut; @Before public void setup() throws Exception { @@ -68,8 +75,10 @@ public class PersistComponentsStepTest extends BaseStepTest { session = dbTester.myBatis().openSession(false); dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); - dbComponentsRefCache = new DbComponentsRefCache(); - sut = new PersistComponentsStep(dbClient, dbComponentsRefCache, reportReader, treeRootHolder); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + + dbIdsRepository = new DbIdsRepository(); + sut = new PersistComponentsAndSnapshotsStep(system2, dbClient, treeRootHolder, reportReader, dbIdsRepository); } @Override @@ -84,10 +93,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_components() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -118,79 +123,77 @@ public class PersistComponentsStepTest extends BaseStepTest { .setLanguage("java") .build()); - treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, - new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", - new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", - new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java"))))); + Component file = new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java"); + Component directory = new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", file); + Component module = new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", directory); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, module); + treeRootHolder.setRoot(project); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); - ComponentDto project = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY); - assertThat(project).isNotNull(); - assertThat(project.name()).isEqualTo("Project"); - assertThat(project.description()).isEqualTo("Project description"); - assertThat(project.path()).isNull(); - assertThat(project.uuid()).isEqualTo("ABCD"); - assertThat(project.moduleUuid()).isNull(); - assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); - assertThat(project.projectUuid()).isEqualTo(project.uuid()); - assertThat(project.qualifier()).isEqualTo("TRK"); - assertThat(project.scope()).isEqualTo("PRJ"); - assertThat(project.parentProjectId()).isNull(); - - ComponentDto module = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY"); - assertThat(module).isNotNull(); - assertThat(module.name()).isEqualTo("Module"); - assertThat(module.description()).isEqualTo("Module description"); - assertThat(module.path()).isEqualTo("module"); - assertThat(module.uuid()).isEqualTo("BCDE"); - assertThat(module.moduleUuid()).isEqualTo(project.uuid()); - assertThat(module.moduleUuidPath()).isEqualTo(project.moduleUuidPath() + module.uuid() + "."); - assertThat(module.projectUuid()).isEqualTo(project.uuid()); - assertThat(module.qualifier()).isEqualTo("BRC"); - assertThat(module.scope()).isEqualTo("PRJ"); - assertThat(module.parentProjectId()).isEqualTo(project.getId()); - - ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir"); - assertThat(directory).isNotNull(); - assertThat(directory.name()).isEqualTo("src/main/java/dir"); - assertThat(directory.description()).isNull(); - assertThat(directory.path()).isEqualTo("src/main/java/dir"); - assertThat(directory.uuid()).isEqualTo("CDEF"); - assertThat(directory.moduleUuid()).isEqualTo(module.uuid()); - assertThat(directory.moduleUuidPath()).isEqualTo(module.moduleUuidPath()); - assertThat(directory.projectUuid()).isEqualTo(project.uuid()); - assertThat(directory.qualifier()).isEqualTo("DIR"); - assertThat(directory.scope()).isEqualTo("DIR"); - assertThat(directory.parentProjectId()).isEqualTo(module.getId()); - - ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java"); - assertThat(file).isNotNull(); - assertThat(file.name()).isEqualTo("Foo.java"); - assertThat(file.description()).isNull(); - assertThat(file.path()).isEqualTo("src/main/java/dir/Foo.java"); - assertThat(file.language()).isEqualTo("java"); - assertThat(file.uuid()).isEqualTo("DEFG"); - assertThat(file.moduleUuid()).isEqualTo(module.uuid()); - assertThat(file.moduleUuidPath()).isEqualTo(module.moduleUuidPath()); - assertThat(file.projectUuid()).isEqualTo(project.uuid()); - assertThat(file.qualifier()).isEqualTo("FIL"); - assertThat(file.scope()).isEqualTo("FIL"); - assertThat(file.parentProjectId()).isEqualTo(module.getId()); - - assertThat(dbComponentsRefCache.getByRef(1).getId()).isEqualTo(project.getId()); - assertThat(dbComponentsRefCache.getByRef(2).getId()).isEqualTo(module.getId()); - assertThat(dbComponentsRefCache.getByRef(3).getId()).isEqualTo(directory.getId()); - assertThat(dbComponentsRefCache.getByRef(4).getId()).isEqualTo(file.getId()); + ComponentDto projectDto = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY); + assertThat(projectDto).isNotNull(); + assertThat(projectDto.name()).isEqualTo("Project"); + assertThat(projectDto.description()).isEqualTo("Project description"); + assertThat(projectDto.path()).isNull(); + assertThat(projectDto.uuid()).isEqualTo("ABCD"); + assertThat(projectDto.moduleUuid()).isNull(); + assertThat(projectDto.moduleUuidPath()).isEqualTo("." + projectDto.uuid() + "."); + assertThat(projectDto.projectUuid()).isEqualTo(projectDto.uuid()); + assertThat(projectDto.qualifier()).isEqualTo("TRK"); + assertThat(projectDto.scope()).isEqualTo("PRJ"); + assertThat(projectDto.parentProjectId()).isNull(); + + ComponentDto moduleDto = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY"); + assertThat(moduleDto).isNotNull(); + assertThat(moduleDto.name()).isEqualTo("Module"); + assertThat(moduleDto.description()).isEqualTo("Module description"); + assertThat(moduleDto.path()).isEqualTo("module"); + assertThat(moduleDto.uuid()).isEqualTo("BCDE"); + assertThat(moduleDto.moduleUuid()).isEqualTo(projectDto.uuid()); + assertThat(moduleDto.moduleUuidPath()).isEqualTo(projectDto.moduleUuidPath() + moduleDto.uuid() + "."); + assertThat(moduleDto.projectUuid()).isEqualTo(projectDto.uuid()); + assertThat(moduleDto.qualifier()).isEqualTo("BRC"); + assertThat(moduleDto.scope()).isEqualTo("PRJ"); + assertThat(moduleDto.parentProjectId()).isEqualTo(projectDto.getId()); + + ComponentDto directoryDto = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir"); + assertThat(directoryDto).isNotNull(); + assertThat(directoryDto.name()).isEqualTo("src/main/java/dir"); + assertThat(directoryDto.description()).isNull(); + assertThat(directoryDto.path()).isEqualTo("src/main/java/dir"); + assertThat(directoryDto.uuid()).isEqualTo("CDEF"); + assertThat(directoryDto.moduleUuid()).isEqualTo(moduleDto.uuid()); + assertThat(directoryDto.moduleUuidPath()).isEqualTo(moduleDto.moduleUuidPath()); + assertThat(directoryDto.projectUuid()).isEqualTo(projectDto.uuid()); + assertThat(directoryDto.qualifier()).isEqualTo("DIR"); + assertThat(directoryDto.scope()).isEqualTo("DIR"); + assertThat(directoryDto.parentProjectId()).isEqualTo(moduleDto.getId()); + + ComponentDto fileDto = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java"); + assertThat(fileDto).isNotNull(); + assertThat(fileDto.name()).isEqualTo("Foo.java"); + assertThat(fileDto.description()).isNull(); + assertThat(fileDto.path()).isEqualTo("src/main/java/dir/Foo.java"); + assertThat(fileDto.language()).isEqualTo("java"); + assertThat(fileDto.uuid()).isEqualTo("DEFG"); + assertThat(fileDto.moduleUuid()).isEqualTo(moduleDto.uuid()); + assertThat(fileDto.moduleUuidPath()).isEqualTo(moduleDto.moduleUuidPath()); + assertThat(fileDto.projectUuid()).isEqualTo(projectDto.uuid()); + assertThat(fileDto.qualifier()).isEqualTo("FIL"); + assertThat(fileDto.scope()).isEqualTo("FIL"); + assertThat(fileDto.parentProjectId()).isEqualTo(moduleDto.getId()); + + assertThat(dbIdsRepository.getComponentId(project)).isEqualTo(projectDto.getId()); + assertThat(dbIdsRepository.getComponentId(module)).isEqualTo(moduleDto.getId()); + assertThat(dbIdsRepository.getComponentId(directory)).isEqualTo(directoryDto.getId()); + assertThat(dbIdsRepository.getComponentId(file)).isEqualTo(fileDto.getId()); } @Test public void persist_file_directly_attached_on_root_directory() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -213,6 +216,7 @@ public class PersistComponentsStepTest extends BaseStepTest { treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.DIRECTORY, 2, "CDEF", PROJECT_KEY + ":/", new DumbComponent(Component.Type.FILE, 3, "DEFG", PROJECT_KEY + ":pom.xml")))); + sut.execute(); ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:/"); @@ -228,10 +232,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_unit_test() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -255,6 +255,7 @@ public class PersistComponentsStepTest extends BaseStepTest { treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.DIRECTORY, 2, "CDEF", PROJECT_KEY + ":src/test/java/dir", new DumbComponent(Component.Type.FILE, 3, "DEFG", PROJECT_KEY + ":src/test/java/dir/FooTest.java")))); + sut.execute(); ComponentDto file = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY + ":src/test/java/dir/FooTest.java"); @@ -274,10 +275,6 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, module); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -286,28 +283,29 @@ public class PersistComponentsStepTest extends BaseStepTest { .addChildRef(2) .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(2) - .setType(Constants.ComponentType.MODULE) - .setKey("MODULE_KEY") - .setName("Module") - .addChildRef(3) - .build()); + .setRef(2) + .setType(Constants.ComponentType.MODULE) + .setKey("MODULE_KEY") + .setName("Module") + .addChildRef(3) + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(3) - .setType(Constants.ComponentType.DIRECTORY) - .setPath("src/main/java/dir") - .addChildRef(4) - .build()); + .setRef(3) + .setType(Constants.ComponentType.DIRECTORY) + .setPath("src/main/java/dir") + .addChildRef(4) + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(4) - .setType(Constants.ComponentType.FILE) - .setPath("src/main/java/dir/Foo.java") - .build()); + .setRef(4) + .setType(Constants.ComponentType.FILE) + .setPath("src/main/java/dir/Foo.java") + .build()); treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java"))))); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); @@ -341,10 +339,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void compute_parent_project_id() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -384,6 +378,7 @@ public class PersistComponentsStepTest extends BaseStepTest { new DumbComponent(Component.Type.MODULE, 3, "CDEF", "SUB_MODULE_1_KEY", new DumbComponent(Component.Type.MODULE, 4, "DEFG", "SUB_MODULE_2_KEY", new DumbComponent(Component.Type.DIRECTORY, 5, "EFGH", "SUB_MODULE_2_KEY:src/main/java/dir")))))); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5); @@ -411,10 +406,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_multi_modules() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -447,6 +438,7 @@ public class PersistComponentsStepTest extends BaseStepTest { new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_A", new DumbComponent(Component.Type.MODULE, 3, "DEFG", "SUB_MODULE_A")), new DumbComponent(Component.Type.MODULE, 4, "CDEF", "MODULE_B"))); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); @@ -487,17 +479,13 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, directory, file); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(1) - .setType(Constants.ComponentType.PROJECT) - .setKey(PROJECT_KEY) - .setName("Project") - .addChildRef(2) - .build()); + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .setName("Project") + .addChildRef(2) + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(2) .setType(Constants.ComponentType.MODULE) @@ -506,21 +494,22 @@ public class PersistComponentsStepTest extends BaseStepTest { .addChildRef(3) .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(3) - .setType(Constants.ComponentType.DIRECTORY) - .setPath("src/main/java/dir") - .addChildRef(4) - .build()); + .setRef(3) + .setType(Constants.ComponentType.DIRECTORY) + .setPath("src/main/java/dir") + .addChildRef(4) + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(4) - .setType(Constants.ComponentType.FILE) - .setPath("src/main/java/dir/Foo.java") - .build()); + .setRef(4) + .setType(Constants.ComponentType.FILE) + .setPath("src/main/java/dir/Foo.java") + .build()); treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java"))))); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); @@ -574,27 +563,24 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, module); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(1) - .setType(Constants.ComponentType.PROJECT) - .setKey(PROJECT_KEY) - .setName("New project name") - .addChildRef(2) - .build()); + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .setName("New project name") + .addChildRef(2) + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() - .setRef(2) - .setType(Constants.ComponentType.MODULE) - .setKey("MODULE_KEY") - .setName("New module name") - .setPath("New path") - .build()); + .setRef(2) + .setType(Constants.ComponentType.MODULE) + .setKey("MODULE_KEY") + .setName("New module name") + .setPath("New path") + .build()); treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY"))); + sut.execute(); ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY); @@ -612,10 +598,6 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, module); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -634,6 +616,7 @@ public class PersistComponentsStepTest extends BaseStepTest { treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY"))); + sut.execute(); ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY); @@ -651,10 +634,6 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, module); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -672,6 +651,7 @@ public class PersistComponentsStepTest extends BaseStepTest { treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY"))); + sut.execute(); ComponentDto moduleReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY"); @@ -690,10 +670,6 @@ public class PersistComponentsStepTest extends BaseStepTest { dbClient.componentDao().insert(session, directory, file); session.commit(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -733,6 +709,7 @@ public class PersistComponentsStepTest extends BaseStepTest { new DumbComponent(Component.Type.MODULE, 3, "BCDE", "MODULE_B", new DumbComponent(Component.Type.DIRECTORY, 4, "CDEF", "MODULE_B:src/main/java/dir", new DumbComponent(Component.Type.FILE, 5, "DEFG", "MODULE_B:src/main/java/dir/Foo.java")))))); + sut.execute(); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java index 04b283d6a83..c61cfb73622 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java @@ -27,7 +27,6 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; @@ -36,9 +35,10 @@ import org.sonar.core.metric.db.MetricDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; -import org.sonar.server.computation.language.LanguageRepository; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.metric.persistence.MetricDao; @@ -46,7 +46,6 @@ import org.sonar.test.DbTests; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; @Category(DbTests.class) public class PersistDuplicationsStepTest extends BaseStepTest { @@ -59,14 +58,14 @@ public class PersistDuplicationsStepTest extends BaseStepTest { @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); - DbSession session; + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - DbClient dbClient; + DbIdsRepository dbIdsRepository = new DbIdsRepository(); - Settings projectSettings; - LanguageRepository languageRepository; + DbSession session; - DbComponentsRefCache dbComponentsRefCache; + DbClient dbClient; PersistDuplicationsStep sut; @@ -76,10 +75,7 @@ public class PersistDuplicationsStepTest extends BaseStepTest { session = dbTester.myBatis().openSession(false); dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new MetricDao()); - projectSettings = new Settings(); - dbComponentsRefCache = new DbComponentsRefCache(); - languageRepository = mock(LanguageRepository.class); - sut = new PersistDuplicationsStep(dbClient, dbComponentsRefCache, reportReader); + sut = new PersistDuplicationsStep(dbClient, dbIdsRepository, treeRootHolder, reportReader); } @Override @@ -135,16 +131,18 @@ public class PersistDuplicationsStepTest extends BaseStepTest { @Test public void persist_duplications_on_same_file_linked_on_a_module() throws Exception { - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "MODULE_KEY", "BCDE")); - dbComponentsRefCache.addComponent(3, new DbComponent(3L, "MODULE_KEY:file", "CDEF")); + Component file = new DumbComponent(Component.Type.FILE, 3, "CDEF", "MODULE_KEY:file"); + Component module = new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", file); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, module); + treeRootHolder.setRoot(project); - saveDuplicationMetric(); + dbIdsRepository.setComponentId(project, 1); + dbIdsRepository.setComponentId(module, 3); + dbIdsRepository.setComponentId(file, 2); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); + saveDuplicationMetric(); + // TODO remove this when snapshot id will come from the DbIdsRepo reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -192,17 +190,18 @@ public class PersistDuplicationsStepTest extends BaseStepTest { @Test public void persist_duplications_on_same_file_linked_on_a_folder() { - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "PROJECT_KEY:dir", "BCDE")); - dbComponentsRefCache.addComponent(3, new DbComponent(3L, "PROJECT_KEY:file", "CDEF")); + Component file = new DumbComponent(Component.Type.FILE, 3, "CDEF", "PROJECT_KEY:file"); + Component directory = new DumbComponent(Component.Type.DIRECTORY, 2, "BCDE", "PROJECT_KEY:dir", file); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, directory); + treeRootHolder.setRoot(project); + dbIdsRepository.setComponentId(project, 1); + dbIdsRepository.setComponentId(directory, 3); + dbIdsRepository.setComponentId(file, 2); saveDuplicationMetric(); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - + // TODO remove this when snapshot id will come from the DbIdsRepo reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -250,17 +249,20 @@ public class PersistDuplicationsStepTest extends BaseStepTest { @Test public void persist_duplications_on_same_file_linked_on_sub_folder() { - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "PROJECT_KEY:dir", "BCDE")); - dbComponentsRefCache.addComponent(3, new DbComponent(3L, "PROJECT_KEY:dir", "CDEF")); - dbComponentsRefCache.addComponent(10, new DbComponent(10L, "PROJECT_KEY:file", "DEFG")); + Component file = new DumbComponent(Component.Type.FILE, 10, "DEFG", "PROJECT_KEY:file"); + Component directory1 = new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "PROJECT_KEY:dir1", file); + Component directory2 = new DumbComponent(Component.Type.DIRECTORY, 2, "BCDE", "PROJECT_KEY:dir2", directory1); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, directory2); + treeRootHolder.setRoot(project); - saveDuplicationMetric(); + dbIdsRepository.setComponentId(project, 1); + dbIdsRepository.setComponentId(directory1, 2); + dbIdsRepository.setComponentId(directory2, 3); + dbIdsRepository.setComponentId(file, 10); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); + saveDuplicationMetric(); + // TODO remove this when snapshot id will come from the DbIdsRepo reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -314,10 +316,30 @@ public class PersistDuplicationsStepTest extends BaseStepTest { @Test public void persist_duplications_on_different_files() { - dbComponentsRefCache.addComponent(3, new DbComponent(3L, "PROJECT_KEY:file2", "CDEF")); saveDuplicationMetric(); - initReportWithProjectAndFile(); + Component file2 = new DumbComponent(Component.Type.FILE, 3, "CDEF", "PROJECT_KEY:file2"); + Component file = new DumbComponent(Component.Type.FILE, 2, "BCDE", "PROJECT_KEY:file"); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, file, file2); + treeRootHolder.setRoot(project); + + dbIdsRepository.setComponentId(project, 1); + dbIdsRepository.setComponentId(file, 2); + + // TODO remove this when snapshot id will come from the DbIdsRepo + reportReader.putComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .setSnapshotId(10L) + .addChildRef(2) + .build()); + reportReader.putComponent(BatchReport.Component.newBuilder() + .setRef(2) + .setType(Constants.ComponentType.FILE) + .setSnapshotId(11L) + .setPath("file") + .build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(3) .setType(Constants.ComponentType.FILE) @@ -379,13 +401,14 @@ public class PersistDuplicationsStepTest extends BaseStepTest { } private void initReportWithProjectAndFile() { - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "PROJECT_KEY:file", "BCDE")); + Component file = new DumbComponent(Component.Type.FILE, 2, "BCDE", "PROJECT_KEY:file"); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, file); + treeRootHolder.setRoot(project); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); + dbIdsRepository.setComponentId(project, 1); + dbIdsRepository.setComponentId(file, 2); + // TODO remove this when snapshot id will come from the DbIdsRepo reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistEventsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistEventsStepTest.java index d2e0cef4d6c..d359abcec66 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistEventsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistEventsStepTest.java @@ -32,8 +32,9 @@ import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.event.db.EventDao; import org.sonar.test.DbTests; @@ -44,15 +45,16 @@ import static org.mockito.Mockito.when; @Category(DbTests.class) public class PersistEventsStepTest extends BaseStepTest { - private static final String PROJECT_KEY = "PROJECT_KEY"; - @ClassRule public static DbTester dbTester = new DbTester(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); DbSession session; - DbComponentsRefCache dbComponentsRefCache; PersistEventsStep step; @Before @@ -63,8 +65,7 @@ public class PersistEventsStepTest extends BaseStepTest { System2 system2 = mock(System2.class); when(system2.now()).thenReturn(1225630680000L); - dbComponentsRefCache = new DbComponentsRefCache(); - step = new PersistEventsStep(dbClient, system2, dbComponentsRefCache, reportReader); + step = new PersistEventsStep(dbClient, system2, treeRootHolder, reportReader); } @Override @@ -81,11 +82,10 @@ public class PersistEventsStepTest extends BaseStepTest { public void nothing_to_do_when_no_events_in_report() throws Exception { dbTester.prepareDbUnit(getClass(), "nothing_to_do_when_no_events_in_report.xml"); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) .setAnalysisDate(150000000L) .build()); @@ -103,11 +103,10 @@ public class PersistEventsStepTest extends BaseStepTest { public void persist_report_events() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) .setAnalysisDate(150000000L) .build()); @@ -138,12 +137,11 @@ public class PersistEventsStepTest extends BaseStepTest { public void persist_report_events_with_component_children() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "MODULE_KEY", "BCDE")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", null))); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) .setAnalysisDate(150000000L) .build()); @@ -179,11 +177,10 @@ public class PersistEventsStepTest extends BaseStepTest { public void create_version_event() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) .setAnalysisDate(150000000L) .build()); @@ -203,11 +200,10 @@ public class PersistEventsStepTest extends BaseStepTest { public void keep_one_event_by_version() throws Exception { dbTester.prepareDbUnit(getClass(), "keep_one_event_by_version.xml"); - dbComponentsRefCache.addComponent(1, new DbComponent(1L, PROJECT_KEY, "ABCD")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) .setAnalysisDate(150000000L) .build()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java index fd140226a40..355b1b5dd9d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java @@ -39,7 +39,9 @@ import org.sonar.core.persistence.DbTester; import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceDto.Type; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; @@ -63,6 +65,10 @@ public class PersistFileSourcesStepTest extends BaseStepTest { @ClassRule public static DbTester dbTester = new DbTester(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); @@ -71,7 +77,6 @@ public class PersistFileSourcesStepTest extends BaseStepTest { DbSession session; DbClient dbClient; - DbComponentsRefCache dbComponentsRefCache; PersistFileSourcesStep sut; long now = 123456789L; @@ -84,8 +89,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest { System2 system2 = mock(System2.class); when(system2.now()).thenReturn(now); - dbComponentsRefCache = new DbComponentsRefCache(); - sut = new PersistFileSourcesStep(dbClient, system2, dbComponentsRefCache, reportReader); + sut = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader); } @Override @@ -124,13 +128,9 @@ public class PersistFileSourcesStepTest extends BaseStepTest { @Test public void persist_last_line() throws Exception { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, PROJECT_UUID)); - dbComponentsRefCache.addComponent(FILE_REF, new DbComponentsRefCache.DbComponent(2L, "PROJECT_KEY:file", FILE_UUID)); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY, + new DumbComponent(Component.Type.FILE, FILE_REF, FILE_UUID, "PROJECT_KEY:file"))); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -428,19 +428,14 @@ public class PersistFileSourcesStepTest extends BaseStepTest { sut.execute(); failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException e) { - assertThat(e).hasMessage("Cannot persist sources of src/Foo.java").hasCauseInstanceOf(IllegalArgumentException.class); + assertThat(e).hasMessage("Cannot persist sources of MODULE_KEY:src/Foo.java").hasCauseInstanceOf(IllegalArgumentException.class); } } private void initBasicReport(int numberOfLines) throws IOException { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, PROJECT_UUID)); - dbComponentsRefCache.addComponent(2, new DbComponentsRefCache.DbComponent(2L, "MODULE_KEY", "MODULE")); - dbComponentsRefCache.addComponent(FILE_REF, new DbComponentsRefCache.DbComponent(3L, "MODULE_KEY:src/Foo.java", FILE_UUID)); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "MODULE", "MODULE_KEY", + new DumbComponent(Component.Type.FILE, FILE_REF, FILE_UUID, "MODULE_KEY:src/Foo.java")))); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) @@ -455,7 +450,6 @@ public class PersistFileSourcesStepTest extends BaseStepTest { reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(FILE_REF) .setType(Constants.ComponentType.FILE) - .setPath("src/Foo.java") .setLines(numberOfLines) .build()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java index 3f056cd33fd..81ac6888078 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java @@ -45,7 +45,10 @@ import org.sonar.core.persistence.DbTester; import org.sonar.core.rule.RuleDto; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.issue.RuleCache; import org.sonar.server.computation.issue.RuleCacheLoader; import org.sonar.server.computation.measure.MetricCache; @@ -65,14 +68,21 @@ public class PersistMeasuresStepTest extends BaseStepTest { private static final String METRIC_KEY = "metric-key"; private static final RuleKey RULE_KEY = RuleKey.of("repo", "rule-key"); + private static final long FILE_COMPONENT_ID = 3L; + private static final long FILE_SNAPSHOT_ID = 3L; + @ClassRule public static DbTester dbTester = new DbTester(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); DbClient dbClient; DbSession session; - DbComponentsRefCache dbComponentsRefCache; + DbIdsRepository dbIdsRepository = new DbIdsRepository(); MetricDto metric; RuleDto rule; @@ -82,8 +92,6 @@ public class PersistMeasuresStepTest extends BaseStepTest { public void setUp() { dbTester.truncateTables(); - dbComponentsRefCache = new DbComponentsRefCache(); - dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new ComponentDao(), new MetricDao(), new RuleDao(System2.INSTANCE)); session = dbClient.openSession(false); @@ -97,7 +105,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { MetricCache metricCache = new MetricCache(dbClient); session.commit(); - sut = new PersistMeasuresStep(dbClient, ruleCache, metricCache, dbComponentsRefCache, reportReader); + sut = new PersistMeasuresStep(dbClient, ruleCache, metricCache, dbIdsRepository, treeRootHolder, reportReader); } @After @@ -107,8 +115,15 @@ public class PersistMeasuresStepTest extends BaseStepTest { @Test public void insert_measures_from_report() throws Exception { - ComponentDto project = addComponent(1, "project-key"); - ComponentDto file = addComponent(2, "file-key"); + ComponentDto projectDto = addComponent(1, "project-key"); + ComponentDto fileDto = addComponent(2, "file-key"); + + Component file = new DumbComponent(Component.Type.FILE, 2, "CDEF", "MODULE_KEY:file"); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, file); + treeRootHolder.setRoot(project); + + dbIdsRepository.setComponentId(project, projectDto.getId()); + dbIdsRepository.setComponentId(file, fileDto.getId()); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setAnalysisDate(new Date().getTime()) @@ -172,7 +187,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { Map<String, Object> dto = dtos.get(0); assertThat(dto.get("snapshotId")).isNotNull(); - assertThat(dto.get("componentId")).isEqualTo(project.getId()); + assertThat(dto.get("componentId")).isEqualTo(projectDto.getId()); assertThat(dto.get("metricId")).isEqualTo(metric.getId().longValue()); assertThat(dto.get("ruleId")).isEqualTo(rule.getId().longValue()); assertThat(dto.get("textValue")).isEqualTo("measure-data"); @@ -180,7 +195,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { dto = dtos.get(1); assertThat(dto.get("snapshotId")).isNotNull(); - assertThat(dto.get("componentId")).isEqualTo(file.getId()); + assertThat(dto.get("componentId")).isEqualTo(fileDto.getId()); assertThat(dto.get("metricId")).isEqualTo(metric.getId().longValue()); assertThat(dto.get("ruleId")).isEqualTo(rule.getId().longValue()); assertThat(dto.get("value")).isEqualTo(123.123d); @@ -210,7 +225,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setPersonId(5432) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, componentDto.getId(), FILE_SNAPSHOT_ID); assertThat(measure).isEqualToComparingFieldByField(new MeasureDto() .setComponentId(componentDto.getId()) @@ -241,11 +256,11 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, componentDto.getId(), FILE_SNAPSHOT_ID); assertThat(measure).isEqualToComparingFieldByField(new MeasureDto() .setComponentId(componentDto.getId()) - .setSnapshotId(3L) + .setSnapshotId(FILE_SNAPSHOT_ID) .setMetricId(metric.getId())); } @@ -260,7 +275,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isEqualTo(1.0); @@ -270,7 +285,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - measure = sut.toMeasureDto(batchMeasure, component); + measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isEqualTo(0.0); @@ -279,7 +294,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - measure = sut.toMeasureDto(batchMeasure, component); + measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isNull(); } @@ -295,7 +310,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isEqualTo(3.2); @@ -304,7 +319,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - measure = sut.toMeasureDto(batchMeasure, component); + measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isNull(); } @@ -320,7 +335,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isEqualTo(3.0); @@ -329,7 +344,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - measure = sut.toMeasureDto(batchMeasure, component); + measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isNull(); } @@ -345,7 +360,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - MeasureDto measure = sut.toMeasureDto(batchMeasure, component); + MeasureDto measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isEqualTo(3.0); @@ -354,7 +369,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { .setMetricKey(METRIC_KEY) .build(); - measure = sut.toMeasureDto(batchMeasure, component); + measure = sut.toMeasureDto(batchMeasure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); assertThat(measure.getValue()).isNull(); } @@ -368,7 +383,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { BatchReport.Component component = defaultComponent() .build(); addComponent(component.getRef(), "component-key"); - sut.toMeasureDto(measure, component); + sut.toMeasureDto(measure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); } @Test(expected = IllegalStateException.class) @@ -379,7 +394,7 @@ public class PersistMeasuresStepTest extends BaseStepTest { BatchReport.Component component = defaultComponent() .build(); addComponent(component.getRef(), "component-key"); - sut.toMeasureDto(measure, component); + sut.toMeasureDto(measure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); } @Test(expected = IllegalStateException.class) @@ -390,40 +405,19 @@ public class PersistMeasuresStepTest extends BaseStepTest { BatchReport.Component component = defaultComponent() .build(); addComponent(component.getRef(), "component-key"); - sut.toMeasureDto(measure, component); - } - - private MeasureDto expectedFullMeasure() { - return new MeasureDto() - .setComponentId(2L) - .setSnapshotId(3L) - .setCharacteristicId(123456) - .setPersonId(5432) - .setValue(123.123d) - .setVariation(1, 1.1d) - .setVariation(2, 2.2d) - .setVariation(3, 3.3d) - .setVariation(4, 4.4d) - .setVariation(5, 5.5d) - .setAlertStatus("WARN") - .setAlertText("Open issues > 0") - .setDescription("measure-description") - .setSeverity(Severity.CRITICAL) - .setMetricId(metric.getId()) - .setRuleId(rule.getId()); + sut.toMeasureDto(measure, FILE_COMPONENT_ID, FILE_SNAPSHOT_ID); } private BatchReport.Component.Builder defaultComponent() { return BatchReport.Component.newBuilder() .setRef(1) - .setSnapshotId(3); + .setSnapshotId(FILE_SNAPSHOT_ID); } private ComponentDto addComponent(int ref, String key) { ComponentDto componentDto = new ComponentDto().setKey(key).setUuid(Uuids.create()); dbClient.componentDao().insert(session, componentDto); session.commit(); - dbComponentsRefCache.addComponent(ref, new DbComponentsRefCache.DbComponent(componentDto.getId(), key, componentDto.uuid())); return componentDto; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java index 31496f7335d..ff51c4d33ef 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java @@ -33,7 +33,9 @@ import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.metric.db.MetricDto; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.computation.measure.MetricCache; import org.sonar.server.db.DbClient; @@ -48,6 +50,10 @@ public class PersistNumberOfDaysSinceLastCommitStepTest extends BaseStepTest { @ClassRule public static DbTester db = new DbTester(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); @@ -59,8 +65,6 @@ public class PersistNumberOfDaysSinceLastCommitStepTest extends BaseStepTest { Settings projectSettings; LanguageRepository languageRepository; - DbComponentsRefCache dbComponentsRefCache; - @Before public void setUp() throws Exception { db.truncateTables(); @@ -70,9 +74,8 @@ public class PersistNumberOfDaysSinceLastCommitStepTest extends BaseStepTest { projectSettings = new Settings(); languageRepository = mock(LanguageRepository.class); when(metricCache.get(anyString())).thenReturn(new MetricDto().setId(10)); - dbComponentsRefCache = new DbComponentsRefCache(); - sut = new PersistNumberOfDaysSinceLastCommitStep(System2.INSTANCE, dbClient, sourceLineIndex, metricCache, dbComponentsRefCache, reportReader); + sut = new PersistNumberOfDaysSinceLastCommitStep(System2.INSTANCE, dbClient, sourceLineIndex, metricCache, treeRootHolder, reportReader); } @Override @@ -120,25 +123,21 @@ public class PersistNumberOfDaysSinceLastCommitStepTest extends BaseStepTest { } private void initReportWithProjectAndFile() { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, "PROJECT_KEY", "project-uuid")); - dbComponentsRefCache.addComponent(2, new DbComponentsRefCache.DbComponent(2L, "PROJECT_KEY:file", "file-uuid")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "project-uuid", null, + new DumbComponent(Component.Type.FILE, 2, "file-uuid", null))); reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) .setSnapshotId(1000) .build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) - .setKey("PROJECT_KEY") - .setSnapshotId(10L) .addChildRef(2) .build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(2) .setType(Constants.ComponentType.FILE) - .setSnapshotId(11L) .build()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistProjectLinksStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistProjectLinksStepTest.java index 7f89e57ee36..a7ec2f5be22 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistProjectLinksStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistProjectLinksStepTest.java @@ -34,7 +34,9 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.component.db.ComponentLinkDao; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.test.DbTests; @@ -46,15 +48,16 @@ import static org.mockito.Mockito.when; @Category(DbTests.class) public class PersistProjectLinksStepTest extends BaseStepTest { - private static final String PROJECT_KEY = "PROJECT_KEY"; - @ClassRule public static DbTester dbTester = new DbTester(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); DbSession session; - DbComponentsRefCache dbComponentsRefCache; PersistProjectLinksStep step; @@ -70,8 +73,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { when(i18n.message(Locale.ENGLISH, "project_links.ci", null)).thenReturn("Continuous integration"); when(i18n.message(Locale.ENGLISH, "project_links.issue", null)).thenReturn("Issues"); - dbComponentsRefCache = new DbComponentsRefCache(); - step = new PersistProjectLinksStep(dbClient, i18n, dbComponentsRefCache, reportReader); + step = new PersistProjectLinksStep(dbClient, i18n, treeRootHolder, reportReader); } @Override @@ -88,16 +90,10 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void add_links_on_project_and_module() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - dbComponentsRefCache.addComponent(2, new DbComponentsRefCache.DbComponent(2L, "MODULE_KEY", "BCDE")); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", null))); // project and 1 module - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .setAnalysisDate(150000000L) - .build()); - reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -123,12 +119,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void nothing_to_do_when_link_already_exists() throws Exception { dbTester.prepareDbUnit(getClass(), "nothing_to_do_when_link_already_exists.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) @@ -145,15 +136,16 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void do_not_add_links_on_file() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null, + new DumbComponent(Component.Type.FILE, 2, "BCDE", null))); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .addChildRef(2) + .build()); + reportReader.putComponent(BatchReport.Component.newBuilder() + .setRef(2) .setType(Constants.ComponentType.FILE) .addLink(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) .build()); @@ -167,12 +159,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void update_link() throws Exception { dbTester.prepareDbUnit(getClass(), "update_link.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) @@ -189,12 +176,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void delete_link() throws Exception { dbTester.prepareDbUnit(getClass(), "delete_link.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) @@ -210,12 +192,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void not_delete_custom_link() throws Exception { dbTester.prepareDbUnit(getClass(), "not_delete_custom_link.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) @@ -231,12 +208,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest { public void fail_when_trying_to_add_same_link_type_multiple_times() throws Exception { dbTester.prepareDbUnit(getClass(), "empty.xml"); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, "ABCD")); - - reportReader.setMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .build()); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, "ABCD", null)); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java index 31afd166b45..9306b3b0566 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java @@ -40,8 +40,6 @@ import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.computation.batch.BatchReportReaderRule; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; @@ -53,6 +51,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class PersistTestsStepTest extends BaseStepTest { + private static final String PROJECT_UUID = "PROJECT"; private static final String PROJECT_KEY = "PROJECT_KEY"; private static final int TEST_FILE_REF_1 = 3; @@ -68,16 +67,18 @@ public class PersistTestsStepTest extends BaseStepTest { @ClassRule public static DbTester db = new DbTester(); + @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public LogTester log = new LogTester(); DbSession session; DbClient dbClient; - DbComponentsRefCache dbComponentsRefCache; Component root; PersistTestsStep sut; @@ -93,8 +94,7 @@ public class PersistTestsStepTest extends BaseStepTest { System2 system2 = mock(System2.class); when(system2.now()).thenReturn(now); - dbComponentsRefCache = new DbComponentsRefCache(); - sut = new PersistTestsStep(dbClient, system2, dbComponentsRefCache, reportReader, treeRootHolder); + sut = new PersistTestsStep(dbClient, system2, reportReader, treeRootHolder); initBasicReport(); @@ -218,8 +218,8 @@ public class PersistTestsStepTest extends BaseStepTest { public void aggregate_coverage_details() { reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newTest(1))); reportReader.putCoverageDetails(TEST_FILE_REF_1, Arrays.asList( - newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 1, 3), - newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 2, 4))); + newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 1, 3), + newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 2, 4))); sut.execute(); @@ -300,13 +300,6 @@ public class PersistTestsStepTest extends BaseStepTest { } private void initBasicReport() { - dbComponentsRefCache.addComponent(1, new DbComponent(1L, "PROJECT_KEY", PROJECT_UUID)); - dbComponentsRefCache.addComponent(2, new DbComponent(2L, "MODULE_KEY", "MODULE")); - dbComponentsRefCache.addComponent(3, new DbComponent(3L, "TEST_FILE1_KEY", TEST_FILE_UUID_1)); - dbComponentsRefCache.addComponent(4, new DbComponent(4L, "TEST_FILE2_KEY", TEST_FILE_UUID_2)); - dbComponentsRefCache.addComponent(5, new DbComponent(5L, "MAIN_FILE1_KEY", MAIN_FILE_UUID_1)); - dbComponentsRefCache.addComponent(6, new DbComponent(6L, "MAIN_FILE2_KEY", MAIN_FILE_UUID_2)); - reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) .setProjectKey(PROJECT_KEY) diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java index 3852abbb4df..38925d25ed5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java @@ -32,7 +32,10 @@ import org.sonar.core.computation.dbcleaner.ProjectCleaner; import org.sonar.core.persistence.DbSession; import org.sonar.core.purge.IdUuidPair; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DbIdsRepository; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.component.ProjectSettingsRepository; import org.sonar.server.db.DbClient; @@ -49,11 +52,15 @@ public class PurgeDatastoresStepTest extends BaseStepTest { @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + + DbIdsRepository dbIdsRepository = new DbIdsRepository(); + ProjectCleaner projectCleaner = mock(ProjectCleaner.class); - DbComponentsRefCache dbComponentsRefCache = new DbComponentsRefCache(); ProjectSettingsRepository projectSettingsRepository = mock(ProjectSettingsRepository.class); - PurgeDatastoresStep sut = new PurgeDatastoresStep(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS), projectCleaner, dbComponentsRefCache, projectSettingsRepository, reportReader); + PurgeDatastoresStep sut = new PurgeDatastoresStep(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS), projectCleaner, dbIdsRepository, treeRootHolder, projectSettingsRepository); @Before public void setUp() throws Exception { @@ -62,7 +69,9 @@ public class PurgeDatastoresStepTest extends BaseStepTest { @Test public void call_purge_method_of_the_purge_task() throws IOException { - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(123L, PROJECT_KEY, "UUID-1234")); + Component project = new DumbComponent(Component.Type.PROJECT, 1, "UUID-1234", PROJECT_KEY); + treeRootHolder.setRoot(project); + dbIdsRepository.setComponentId(project, 123L); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java index cf1ca6f6a36..7fb81786e6c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java @@ -32,7 +32,9 @@ import org.sonar.api.utils.System2; import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.server.computation.batch.BatchReportReaderRule; -import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.issue.IssueCache; import org.sonar.server.computation.issue.RuleCache; import org.sonar.server.issue.notification.IssueChangeNotification; @@ -54,6 +56,10 @@ public class SendIssueNotificationsStepTest extends BaseStepTest { @Rule public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -64,11 +70,10 @@ public class SendIssueNotificationsStepTest extends BaseStepTest { @Before public void setUp() throws Exception { issueCache = new IssueCache(temp.newFile(), System2.INSTANCE); - DbComponentsRefCache dbComponentsRefCache = new DbComponentsRefCache(); NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class, Mockito.RETURNS_DEEP_STUBS); - sut = new SendIssueNotificationsStep(issueCache, mock(RuleCache.class), dbComponentsRefCache, notifService, reportReader, newIssuesNotificationFactory); + sut = new SendIssueNotificationsStep(issueCache, mock(RuleCache.class), treeRootHolder, notifService, reportReader, newIssuesNotificationFactory); - dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, PROJECT_UUID)); + treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY)); reportReader.setMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) @@ -93,7 +98,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest { @Test public void send_notifications_if_subscribers() { issueCache.newAppender().append(new DefaultIssue() - .setSeverity(Severity.BLOCKER)).close(); + .setSeverity(Severity.BLOCKER)).close(); when(notifService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true); diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/MigrationStepModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/MigrationStepModuleTest.java index b0522568bed..abc50e78e77 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/MigrationStepModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/MigrationStepModuleTest.java @@ -29,6 +29,6 @@ public class MigrationStepModuleTest { public void verify_count_of_added_MigrationStep_types() throws Exception { ComponentContainer container = new ComponentContainer(); new MigrationStepModule().configure(container); - assertThat(container.size()).isEqualTo(54); + assertThat(container.size()).isEqualTo(55); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest.java new file mode 100644 index 00000000000..aac8b55a99b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest.java @@ -0,0 +1,53 @@ +/* + * 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.server.db.migrations.v52; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.migrations.MigrationStep; + +public class FeedMetricsBooleansTest { + @ClassRule + public static DbTester db = new DbTester().schema(FeedMetricsBooleansTest.class, "schema.sql"); + + MigrationStep migration; + + @Before + public void setUp() { + db.executeUpdateSql("truncate table metrics"); + + migration = new FeedMetricsBooleans(db.database()); + } + + @Test + public void migrate_empty_db() throws Exception { + migration.execute(); + } + + @Test + public void migrate() throws Exception { + db.prepareDbUnit(this.getClass(), "migrate.xml"); + migration.execute(); + db.assertDbUnit(this.getClass(), "migrate-result.xml", "metrics"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/persistence/MetricDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/persistence/MetricDaoTest.java index 1f77cf7c866..8fae20fa06a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/metric/persistence/MetricDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/persistence/MetricDaoTest.java @@ -28,6 +28,7 @@ import org.junit.experimental.categories.Category; import org.sonar.core.metric.db.MetricDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.test.DbTests; import static org.assertj.core.api.Assertions.assertThat; @@ -82,6 +83,11 @@ public class MetricDaoTest { assertThat(result.isEnabled()).isFalse(); } + @Test(expected = NotFoundException.class) + public void get_nullable_by_key() { + dao.selectByKey(session, "unknown"); + } + @Test public void get_manual_metric() { dbTester.prepareDbUnit(getClass(), "manual_metric.xml"); @@ -99,9 +105,9 @@ public class MetricDaoTest { assertThat(result.isUserManaged()).isTrue(); assertThat(result.getWorstValue()).isNull(); assertThat(result.getBestValue()).isNull(); - assertThat(result.isOptimizedBestValue()).isNull(); - assertThat(result.isDeleteHistoricalData()).isNull(); - assertThat(result.isHidden()).isNull(); + assertThat(result.isOptimizedBestValue()).isFalse(); + assertThat(result.isDeleteHistoricalData()).isFalse(); + assertThat(result.isHidden()).isFalse(); assertThat(result.isEnabled()).isTrue(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java index 03682b2ffdb..8d28df8418a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java @@ -38,7 +38,7 @@ import static org.mockito.Mockito.when; import static org.sonar.updatecenter.common.PluginUpdate.Status.COMPATIBLE; import static org.sonar.updatecenter.common.Version.create; -public class AbstractUpdateCenterBasedPluginsWsActionTest { +public abstract class AbstractUpdateCenterBasedPluginsWsActionTest { protected static final String DUMMY_CONTROLLER_KEY = "dummy"; protected static final String JSON_EMPTY_PLUGIN_LIST = "{" + diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java index c447745f4e0..40385f5e2e6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java @@ -76,7 +76,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.server.project.ws.BulkDeleteAction.PARAM_KEYS; -import static org.sonar.server.project.ws.BulkDeleteAction.PARAM_UUIDS; +import static org.sonar.server.project.ws.BulkDeleteAction.PARAM_IDS; @Category(DbTests.class) public class BulkDeleteActionTest { @@ -127,7 +127,7 @@ public class BulkDeleteActionTest { long snapshotId4 = insertNewProjectInDbAndReturnSnapshotId(4); ws.newPostRequest("api/projects", ACTION) - .setParam(PARAM_UUIDS, "project-uuid-1, project-uuid-3, project-uuid-4").execute(); + .setParam(PARAM_IDS, "project-uuid-1, project-uuid-3, project-uuid-4").execute(); dbSession.commit(); assertThat(dbClient.componentDao().selectByUuids(dbSession, Arrays.asList("project-uuid-1", "project-uuid-3", "project-uuid-4"))).isEmpty(); @@ -180,7 +180,7 @@ public class BulkDeleteActionTest { public void web_service_returns_204() throws Exception { insertNewProjectInDbAndReturnSnapshotId(1); - WsTester.Result result = ws.newPostRequest("api/projects", ACTION).setParam(PARAM_UUIDS, "project-uuid-1").execute(); + WsTester.Result result = ws.newPostRequest("api/projects", ACTION).setParam(PARAM_IDS, "project-uuid-1").execute(); result.assertNoContent(); } @@ -190,7 +190,7 @@ public class BulkDeleteActionTest { userSessionRule.setGlobalPermissions(UserRole.CODEVIEWER, UserRole.ISSUE_ADMIN, UserRole.USER); expectedException.expect(ForbiddenException.class); - ws.newPostRequest("api/projects", ACTION).setParam(PARAM_UUIDS, "whatever-the-uuid").execute(); + ws.newPostRequest("api/projects", ACTION).setParam(PARAM_IDS, "whatever-the-uuid").execute(); } @Test @@ -199,7 +199,7 @@ public class BulkDeleteActionTest { dbClient.componentDao().insert(dbSession, ComponentTesting.newFileDto(ComponentTesting.newProjectDto(), "file-uuid")); dbSession.commit(); - ws.newPostRequest("api/projects", ACTION).setParam(PARAM_UUIDS, "file-uuid").execute(); + ws.newPostRequest("api/projects", ACTION).setParam(PARAM_IDS, "file-uuid").execute(); } @Test @@ -209,7 +209,7 @@ public class BulkDeleteActionTest { dbSession.commit(); when(resourceType.getBooleanProperty(anyString())).thenReturn(false); - ws.newPostRequest("api/projects", ACTION).setParam(PARAM_UUIDS, "project-uuid").execute(); + ws.newPostRequest("api/projects", ACTION).setParam(PARAM_IDS, "project-uuid").execute(); } private long insertNewProjectInDbAndReturnSnapshotId(int id) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java index 37d48248a46..404e52eba11 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java @@ -74,7 +74,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.server.project.ws.DeleteAction.PARAM_KEY; -import static org.sonar.server.project.ws.DeleteAction.PARAM_UUID; +import static org.sonar.server.project.ws.DeleteAction.PARAM_ID; @Category(DbTests.class) public class DeleteActionTest { @@ -123,7 +123,7 @@ public class DeleteActionTest { long snapshotId2 = insertNewProjectInDbAndReturnSnapshotId(2); newRequest() - .setParam(PARAM_UUID, "project-uuid-1").execute(); + .setParam(PARAM_ID, "project-uuid-1").execute(); dbSession.commit(); assertThat(dbClient.componentDao().selectNullableByUuid(dbSession, "project-uuid-1")).isNull(); @@ -152,7 +152,7 @@ public class DeleteActionTest { insertNewProjectInDbAndReturnSnapshotId(1); userSessionRule.login("login").addProjectUuidPermissions(UserRole.ADMIN, "project-uuid-1"); - newRequest().setParam(PARAM_UUID, "project-uuid-1").execute(); + newRequest().setParam(PARAM_ID, "project-uuid-1").execute(); dbSession.commit(); assertThat(dbClient.componentDao().selectNullableByUuid(dbSession, "project-uuid-1")).isNull(); @@ -193,7 +193,7 @@ public class DeleteActionTest { public void web_service_returns_204() throws Exception { insertNewProjectInDbAndReturnSnapshotId(1); - WsTester.Result result = newRequest().setParam(PARAM_UUID, "project-uuid-1").execute(); + WsTester.Result result = newRequest().setParam(PARAM_ID, "project-uuid-1").execute(); result.assertNoContent(); } @@ -203,7 +203,7 @@ public class DeleteActionTest { userSessionRule.setGlobalPermissions(UserRole.CODEVIEWER, UserRole.ISSUE_ADMIN, UserRole.USER); expectedException.expect(ForbiddenException.class); - newRequest().setParam(PARAM_UUID, "whatever-the-uuid").execute(); + newRequest().setParam(PARAM_ID, "whatever-the-uuid").execute(); } @Test @@ -212,7 +212,7 @@ public class DeleteActionTest { dbClient.componentDao().insert(dbSession, ComponentTesting.newFileDto(ComponentTesting.newProjectDto(), "file-uuid")); dbSession.commit(); - newRequest().setParam(PARAM_UUID, "file-uuid").execute(); + newRequest().setParam(PARAM_ID, "file-uuid").execute(); } @Test @@ -222,7 +222,7 @@ public class DeleteActionTest { dbSession.commit(); when(resourceType.getBooleanProperty(anyString())).thenReturn(false); - newRequest().setParam(PARAM_UUID, "project-uuid").execute(); + newRequest().setParam(PARAM_ID, "project-uuid").execute(); } private long insertNewProjectInDbAndReturnSnapshotId(int id) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java index c35d5cb0a06..d57b5baa082 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/GroupsActionTest.java @@ -26,6 +26,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.sonar.api.server.ws.WebService.Param; +import org.sonar.api.server.ws.WebService.SelectionMode; import org.sonar.api.utils.System2; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; @@ -111,6 +112,7 @@ public class GroupsActionTest { tester.newGetRequest("api/users", "groups") .setParam("login", "john") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all.json"); } @@ -125,7 +127,12 @@ public class GroupsActionTest { tester.newGetRequest("api/users", "groups") .setParam("login", "john") - .setParam("selected", "selected") + .execute() + .assertJson(getClass(), "selected.json"); + + tester.newGetRequest("api/users", "groups") + .setParam("login", "john") + .setParam(Param.SELECTED, SelectionMode.SELECTED.value()) .execute() .assertJson(getClass(), "selected.json"); } @@ -140,7 +147,7 @@ public class GroupsActionTest { tester.newGetRequest("api/users", "groups") .setParam("login", "john") - .setParam("selected", "deselected") + .setParam(Param.SELECTED, SelectionMode.DESELECTED.value()) .execute() .assertJson(getClass(), "deselected.json"); } @@ -165,6 +172,7 @@ public class GroupsActionTest { tester.newGetRequest("api/users", "groups") .setParam("login", "john") .setParam(Param.PAGE_SIZE, "1") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_page1.json"); @@ -172,6 +180,7 @@ public class GroupsActionTest { .setParam("login", "john") .setParam(Param.PAGE_SIZE, "1") .setParam(Param.PAGE, "2") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_page2.json"); } @@ -187,12 +196,14 @@ public class GroupsActionTest { tester.newGetRequest("api/users", "groups") .setParam("login", "john") .setParam("q", "users") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_users.json"); tester.newGetRequest("api/users", "groups") .setParam("login", "john") .setParam("q", "admin") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_admin.json"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java index 30c31d346cc..c7288d6d818 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/UsersActionTest.java @@ -26,6 +26,8 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.sonar.api.server.ws.WebService.Param; +import org.sonar.api.server.ws.WebService.SelectionMode; import org.sonar.api.utils.System2; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; @@ -122,6 +124,7 @@ public class UsersActionTest { newUsersRequest() .setParam("id", group.getId().toString()) + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all.json"); } @@ -136,7 +139,12 @@ public class UsersActionTest { newUsersRequest() .setParam("id", group.getId().toString()) - .setParam("selected", "selected") + .execute() + .assertJson(getClass(), "selected.json"); + + newUsersRequest() + .setParam("id", group.getId().toString()) + .setParam(Param.SELECTED, SelectionMode.SELECTED.value()) .execute() .assertJson(getClass(), "selected.json"); } @@ -151,7 +159,7 @@ public class UsersActionTest { newUsersRequest() .setParam("id", group.getId().toString()) - .setParam("selected", "deselected") + .setParam(Param.SELECTED, SelectionMode.DESELECTED.value()) .execute() .assertJson(getClass(), "deselected.json"); } @@ -167,6 +175,7 @@ public class UsersActionTest { newUsersRequest() .setParam("id", group.getId().toString()) .setParam("ps", "1") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_page1.json"); @@ -174,6 +183,7 @@ public class UsersActionTest { .setParam("id", group.getId().toString()) .setParam("ps", "1") .setParam("p", "2") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all_page2.json"); } @@ -189,6 +199,7 @@ public class UsersActionTest { newUsersRequest() .setParam("id", group.getId().toString()) .setParam("q", "ace") + .setParam(Param.SELECTED, SelectionMode.ALL.value()) .execute() .assertJson(getClass(), "all.json"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/util/CloseableIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/util/CloseableIteratorTest.java index 83f06bf385b..057e5975c3b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/util/CloseableIteratorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/util/CloseableIteratorTest.java @@ -19,7 +19,9 @@ */ package org.sonar.server.util; +import java.io.IOException; import java.util.Collections; +import java.util.Iterator; import java.util.NoSuchElementException; import org.junit.Test; @@ -117,7 +119,7 @@ public class CloseableIteratorTest { } @Test(expected = NoSuchElementException.class) - public void emptyIterator_next_throws_NoSuchElemetException() { + public void emptyIterator_next_throws_NoSuchElementException() { CloseableIterator.emptyCloseableIterator().next(); } @@ -131,6 +133,35 @@ public class CloseableIteratorTest { CloseableIterator.from(new SimpleCloseableIterator()); } + @Test(expected = IllegalArgumentException.class) + public void from_iterator_throws_IAE_if_arg_is_a_AutoCloseable() { + CloseableIterator.from(new CloseableIt()); + } + + private static class CloseableIt implements Iterator<String>, AutoCloseable { + private final Iterator<String> delegate = Collections.<String>emptyList().iterator(); + + @Override + public void remove() { + delegate.remove(); + } + + @Override + public String next() { + return delegate.next(); + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public void close() throws IOException { + // no need to implement it for real + } + } + @Test public void verify_has_next_from_iterator_with_empty_iterator() { assertThat(CloseableIterator.from(Collections.<String>emptyList().iterator()).hasNext()).isFalse(); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate-result.xml new file mode 100644 index 00000000000..026d2dbe83a --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate-result.xml @@ -0,0 +1,7 @@ +<dataset> + + <metrics id="1" user_managed="[true]" optimized_best_value="[false]" hidden="[false]" delete_historical_data="[false]" /> + <metrics id="2" user_managed="[false]" optimized_best_value="[true]" hidden="[true]" delete_historical_data="[true]" /> + <metrics id="3" user_managed="[false]" optimized_best_value="[false]" hidden="[false]" delete_historical_data="[false]"/> + +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate.xml new file mode 100644 index 00000000000..7fee0be9b77 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/migrate.xml @@ -0,0 +1,7 @@ +<dataset> + + <metrics id="1" user_managed="[true]" optimized_best_value="[null]" hidden="[null]" delete_historical_data="[null]"/> + <metrics id="2" user_managed="[false]" optimized_best_value="[true]" hidden="[true]" delete_historical_data="[true]"/> + <metrics id="3" user_managed="[false]" optimized_best_value="[null]" hidden="[null]" delete_historical_data="[null]"/> + +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/schema.sql new file mode 100644 index 00000000000..d65487d7677 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v52/FeedMetricsBooleansTest/schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE "METRICS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "USER_MANAGED" BOOLEAN DEFAULT FALSE, + "OPTIMIZED_BEST_VALUE" BOOLEAN, + "HIDDEN" BOOLEAN, + "DELETE_HISTORICAL_DATA" BOOLEAN +); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/metric/persistence/MetricDaoTest/manual_metric.xml b/server/sonar-server/src/test/resources/org/sonar/server/metric/persistence/MetricDaoTest/manual_metric.xml index 630722c4d75..8dc260a5fae 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/metric/persistence/MetricDaoTest/manual_metric.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/metric/persistence/MetricDaoTest/manual_metric.xml @@ -1,7 +1,7 @@ <dataset> <metrics id="1" name="manual" val_type="INT" description="Manual metric" domain="" short_name="Manual metric" - qualitative="[false]" enabled="[true]" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="[null]" - origin="GUI" delete_historical_data="[null]" user_managed="[true]"/> + qualitative="[false]" enabled="[true]" worst_value="[null]" optimized_best_value="[false]" best_value="[null]" direction="0" hidden="[false]" + origin="GUI" delete_historical_data="[false]" user_managed="[true]"/> </dataset> diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee index ff402d13cb0..7375822b38e 100644 --- a/server/sonar-web/Gruntfile.coffee +++ b/server/sonar-web/Gruntfile.coffee @@ -46,6 +46,7 @@ module.exports = (grunt) -> '<%= BUILD_PATH %>/js/libs/third-party/latinize.js' '<%= BUILD_PATH %>/js/libs/third-party/underscore.js' '<%= BUILD_PATH %>/js/libs/third-party/backbone.js' + '<%= BUILD_PATH %>/js/libs/third-party/backbone-super.js' '<%= BUILD_PATH %>/js/libs/third-party/backbone.marionette.js' '<%= BUILD_PATH %>/js/libs/third-party/handlebars.js' '<%= BUILD_PATH %>/js/libs/third-party/underscore.js' @@ -123,6 +124,7 @@ module.exports = (grunt) -> 'build-app:coding-rules' 'build-app:computation' 'build-app:drilldown' + 'build-app:groups' 'build-app:markdown' 'build-app:measures' 'build-app:nav' @@ -156,6 +158,7 @@ module.exports = (grunt) -> 'casper:ui' 'casper:workspace' 'casper:users' + 'casper:groups' 'casper:provisioning' 'casper:computation' ] @@ -215,6 +218,9 @@ module.exports = (grunt) -> '<%= BUILD_PATH %>/js/apps/users/templates.js': [ '<%= SOURCE_PATH %>/js/apps/users/templates/**/*.hbs' ] + '<%= BUILD_PATH %>/js/apps/groups/templates.js': [ + '<%= SOURCE_PATH %>/js/apps/groups/templates/**/*.hbs' + ] '<%= BUILD_PATH %>/js/apps/provisioning/templates.js': [ '<%= SOURCE_PATH %>/js/apps/provisioning/templates/**/*.hbs' ] @@ -319,6 +325,8 @@ module.exports = (grunt) -> src: ['src/test/js/provisioning*.js'] computation: src: ['src/test/js/computation*.js'] + groups: + src: ['src/test/js/groups-spec.js'] uglify: build: diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml index 857e044aa93..5203f26cf38 100644 --- a/server/sonar-web/pom.xml +++ b/server/sonar-web/pom.xml @@ -16,6 +16,7 @@ <sonar.sources>src/main/js,src/main/less</sonar.sources> <sonar.exclusions>src/main/js/libs/third-party/**/*,src/main/js/libs/require.js</sonar.exclusions> <sonar.javascript.lcov.reportPath>target/js-coverage/lcov.info</sonar.javascript.lcov.reportPath> + <grunt.arguments>maven-build-skip-tests-${skipWebTests}-${jsCoverage} --port=${jsTestPort} --no-color</grunt.arguments> </properties> <build> @@ -130,7 +131,7 @@ <goal>grunt</goal> </goals> <configuration> - <arguments>maven-build-skip-tests-${skipWebTests}-${jsCoverage} --port=${jsTestPort} --no-color</arguments> + <arguments>${grunt.arguments}</arguments> </configuration> </execution> </executions> @@ -201,6 +202,12 @@ </dependency> </dependencies> </profile> + <profile> + <id>dev</id> + <properties> + <grunt.arguments>maven-quick-build</grunt.arguments> + </properties> + </profile> </profiles> </project> diff --git a/server/sonar-web/src/main/js/apps/groups/app.js b/server/sonar-web/src/main/js/apps/groups/app.js new file mode 100644 index 00000000000..55c6dfef534 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/app.js @@ -0,0 +1,47 @@ +define([ + './layout', + './groups', + './header-view', + './search-view', + './list-view', + './list-footer-view' +], function (Layout, Groups, HeaderView, SearchView, ListView, ListFooterView) { + + var App = new Marionette.Application(), + init = function (options) { + // Layout + this.layout = new Layout({ el: options.el }); + this.layout.render(); + + // Collection + this.groups = new Groups(); + + // Header View + this.headerView = new HeaderView({ collection: this.groups }); + this.layout.headerRegion.show(this.headerView); + + // Search View + this.searchView = new SearchView({ collection: this.groups }); + this.layout.searchRegion.show(this.searchView); + + // List View + this.listView = new ListView({ collection: this.groups }); + this.layout.listRegion.show(this.listView); + + // List Footer View + this.listFooterView = new ListFooterView({ collection: this.groups }); + this.layout.listFooterRegion.show(this.listFooterView); + + // Go! + this.groups.fetch(); + }; + + App.on('start', function (options) { + window.requestMessages().done(function () { + init.call(App, options); + }); + }); + + return App; + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/create-view.js b/server/sonar-web/src/main/js/apps/groups/create-view.js new file mode 100644 index 00000000000..8d5cfce55aa --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/create-view.js @@ -0,0 +1,30 @@ +define([ + './group', + './form-view' +], function (Group, FormView) { + + return FormView.extend({ + + sendRequest: function () { + var that = this, + group = new Group({ + name: this.$('#create-group-name').val(), + description: this.$('#create-group-description').val() + }); + this.disableForm(); + return group.save(null, { + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + that.collection.refresh(); + that.close(); + }).fail(function (jqXHR) { + that.enableForm(); + that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/delete-view.js b/server/sonar-web/src/main/js/apps/groups/delete-view.js new file mode 100644 index 00000000000..137aa768da2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/delete-view.js @@ -0,0 +1,32 @@ +define([ + 'components/common/modal-form', + './templates' +], function (ModalForm) { + + return ModalForm.extend({ + template: Templates['groups-delete'], + + onFormSubmit: function (e) { + this._super(e); + this.sendRequest(); + }, + + sendRequest: function () { + var that = this, + collection = this.model.collection; + return this.model.destroy({ + wait: true, + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + collection.total--; + that.close(); + }).fail(function (jqXHR) { + that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/form-view.js b/server/sonar-web/src/main/js/apps/groups/form-view.js new file mode 100644 index 00000000000..e79ea6eec65 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/form-view.js @@ -0,0 +1,25 @@ +define([ + 'components/common/modal-form', + './templates' +], function (ModalForm) { + + return ModalForm.extend({ + template: Templates['groups-form'], + + onRender: function () { + this._super(); + this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); + }, + + onClose: function () { + this._super(); + this.$('[data-toggle="tooltip"]').tooltip('destroy'); + }, + + onFormSubmit: function (e) { + this._super(e); + this.sendRequest(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/group.js b/server/sonar-web/src/main/js/apps/groups/group.js new file mode 100644 index 00000000000..406f9ba3a3a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/group.js @@ -0,0 +1,35 @@ +define(function () { + + return Backbone.Model.extend({ + urlRoot: function () { + return baseUrl + '/api/usergroups'; + }, + + sync: function (method, model, options) { + var opts = options || {}; + if (method === 'create') { + _.defaults(opts, { + url: this.urlRoot() + '/create', + type: 'POST', + data: _.pick(model.toJSON(), 'name', 'description') + }); + } + if (method === 'update') { + _.defaults(opts, { + url: this.urlRoot() + '/update', + type: 'POST', + data: _.pick(model.toJSON(), 'id', 'name', 'description') + }); + } + if (method === 'delete') { + _.defaults(opts, { + url: this.urlRoot() + '/delete', + type: 'POST', + data: { id: this.id } + }); + } + return Backbone.ajax(opts); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/groups.js b/server/sonar-web/src/main/js/apps/groups/groups.js new file mode 100644 index 00000000000..dcfbb8c731b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/groups.js @@ -0,0 +1,40 @@ +define([ + './group' +], function (Group) { + + return Backbone.Collection.extend({ + model: Group, + + url: function () { + return baseUrl + '/api/usergroups/search'; + }, + + parse: function (r) { + this.total = +r.total; + this.p = +r.p; + this.ps = +r.ps; + return r.groups; + }, + + fetch: function (options) { + var d = (options && options.data) || {}; + this.q = d.q; + return this._super(options); + }, + + fetchMore: function () { + var p = this.p + 1; + return this.fetch({ add: true, remove: false, data: { p: p, ps: this.ps, q: this.q } }); + }, + + refresh: function () { + return this.fetch({ reset: true, data: { q: this.q } }); + }, + + hasMore: function () { + return this.total > this.p * this.ps; + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/header-view.js b/server/sonar-web/src/main/js/apps/groups/header-view.js new file mode 100644 index 00000000000..da6f7f60919 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/header-view.js @@ -0,0 +1,25 @@ +define([ + './create-view', + './templates' +], function (CreateView) { + + return Marionette.ItemView.extend({ + template: Templates['groups-header'], + + events: { + 'click #groups-create': 'onCreateClick' + }, + + onCreateClick: function (e) { + e.preventDefault(); + this.createGroup(); + }, + + createGroup: function () { + new CreateView({ + collection: this.collection + }).render(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/layout.js b/server/sonar-web/src/main/js/apps/groups/layout.js new file mode 100644 index 00000000000..a60fb06f35f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/layout.js @@ -0,0 +1,16 @@ +define([ + './templates' +], function () { + + return Marionette.Layout.extend({ + template: Templates['groups-layout'], + + regions: { + headerRegion: '#groups-header', + searchRegion: '#groups-search', + listRegion: '#groups-list', + listFooterRegion: '#groups-list-footer' + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/list-footer-view.js b/server/sonar-web/src/main/js/apps/groups/list-footer-view.js new file mode 100644 index 00000000000..3c0fbe198c5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/list-footer-view.js @@ -0,0 +1,34 @@ +define([ + './templates' +], function () { + + return Marionette.ItemView.extend({ + template: Templates['groups-list-footer'], + + collectionEvents: { + 'all': 'render' + }, + + events: { + 'click #groups-fetch-more': 'onMoreClick' + }, + + onMoreClick: function (e) { + e.preventDefault(); + this.fetchMore(); + }, + + fetchMore: function () { + this.collection.fetchMore(); + }, + + serializeData: function () { + return _.extend(this._super(), { + total: this.collection.total, + count: this.collection.length, + more: this.collection.hasMore() + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/list-item-view.js b/server/sonar-web/src/main/js/apps/groups/list-item-view.js new file mode 100644 index 00000000000..43eaa5b0d24 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/list-item-view.js @@ -0,0 +1,59 @@ +define([ + './update-view', + './delete-view', + './users-view', + './templates' +], function (UpdateView, DeleteView, UsersView) { + + return Marionette.ItemView.extend({ + tagName: 'li', + className: 'panel panel-vertical', + template: Templates['groups-list-item'], + + events: { + 'click .js-group-update': 'onUpdateClick', + 'click .js-group-delete': 'onDeleteClick', + 'click .js-group-users': 'onUsersClick' + }, + + onRender: function () { + this.$el.attr('data-id', this.model.id); + this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); + }, + + onClose: function () { + this.$('[data-toggle="tooltip"]').tooltip('destroy'); + }, + + onUpdateClick: function (e) { + e.preventDefault(); + this.updateGroup(); + }, + + onDeleteClick: function (e) { + e.preventDefault(); + this.deleteGroup(); + }, + + onUsersClick: function (e) { + e.preventDefault(); + this.showUsers(); + }, + + updateGroup: function () { + new UpdateView({ + model: this.model, + collection: this.model.collection + }).render(); + }, + + deleteGroup: function () { + new DeleteView({ model: this.model }).render(); + }, + + showUsers: function () { + new UsersView({ model: this.model }).render(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/list-view.js b/server/sonar-web/src/main/js/apps/groups/list-view.js new file mode 100644 index 00000000000..138c36b7619 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/list-view.js @@ -0,0 +1,11 @@ +define([ + './list-item-view', + './templates' +], function (ListItemView) { + + return Marionette.CollectionView.extend({ + tagName: 'ul', + itemView: ListItemView + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/search-view.js b/server/sonar-web/src/main/js/apps/groups/search-view.js new file mode 100644 index 00000000000..1540d7eb36e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/search-view.js @@ -0,0 +1,49 @@ +define([ + './templates' +], function () { + + return Marionette.ItemView.extend({ + template: Templates['groups-search'], + + events: { + 'submit #groups-search-form': 'onFormSubmit', + 'search #groups-search-query': 'debouncedOnKeyUp', + 'keyup #groups-search-query': 'debouncedOnKeyUp' + }, + + initialize: function () { + this._bufferedValue = null; + this.debouncedOnKeyUp = _.debounce(this.onKeyUp, 400); + }, + + onRender: function () { + this.delegateEvents(); + }, + + onFormSubmit: function (e) { + e.preventDefault(); + this.debouncedOnKeyUp(); + }, + + onKeyUp: function () { + var q = this.getQuery(); + if (q === this._bufferedValue) { + return; + } + this._bufferedValue = this.getQuery(); + if (this.searchRequest != null) { + this.searchRequest.abort(); + } + this.searchRequest = this.search(q); + }, + + getQuery: function () { + return this.$('#groups-search-query').val(); + }, + + search: function (q) { + return this.collection.fetch({ reset: true, data: { q: q } }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-delete.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-delete.hbs new file mode 100644 index 00000000000..0644817633e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-delete.hbs @@ -0,0 +1,13 @@ +<form id="delete-group-form" autocomplete="off"> + <div class="modal-head"> + <h2>Delete Group</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + Are you sure you want to delete "{{name}}"? + </div> + <div class="modal-foot"> + <button id="delete-group-submit">Delete</button> + <a href="#" class="js-modal-close" id="delete-group-cancel">Cancel</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-form.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-form.hbs new file mode 100644 index 00000000000..a0927b33a73 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-form.hbs @@ -0,0 +1,24 @@ +<form id="create-group-form" autocomplete="off"> + <div class="modal-head"> + <h2>{{#if id}}Update{{else}}Create{{/if}} Group</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + <div class="modal-field"> + <label for="create-group-name">Name<em class="mandatory">*</em></label> + {{! keep this fake field to hack browser autofill }} + <input id="create-group-name-fake" name="name-fake" type="text" class="hidden"> + <input id="create-group-name" name="name" type="text" size="50" maxlength="255" required value="{{name}}"> + </div> + <div class="modal-field"> + <label for="create-group-description">Description</label> + {{! keep this fake field to hack browser autofill }} + <textarea id="create-group-description-fake" name="description-fake" class="hidden"></textarea> + <textarea id="create-group-description" name="description">{{description}}</textarea> + </div> + </div> + <div class="modal-foot"> + <button id="create-group-submit">{{#if id}}Update{{else}}Create{{/if}}</button> + <a href="#" class="js-modal-close" id="create-group-cancel">Cancel</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-header.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-header.hbs new file mode 100644 index 00000000000..19ba74febf8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-header.hbs @@ -0,0 +1,9 @@ +<header class="page-header"> + <h1 class="page-title">{{t 'user_groups.page'}}</h1> + <div class="page-actions"> + <div class="button-group"> + <button id="groups-create">Create Group</button> + </div> + </div> + <p class="page-description">{{t 'user_groups.page.description'}}</p> +</header> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-layout.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-layout.hbs new file mode 100644 index 00000000000..4cad08c767e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-layout.hbs @@ -0,0 +1,6 @@ +<div class="page"> + <div id="groups-header"></div> + <div id="groups-search"></div> + <div id="groups-list"></div> + <div id="groups-list-footer"></div> +</div> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-list-footer.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-footer.hbs new file mode 100644 index 00000000000..841ab40ecd9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-footer.hbs @@ -0,0 +1,6 @@ +<footer class="spacer-top note text-center"> + {{count}}/{{total}} shown + {{#if more}} + <a id="groups-fetch-more" class="spacer-left" href="#">show more</a> + {{/if}} +</footer> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs new file mode 100644 index 00000000000..611cc382493 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-list-item.hbs @@ -0,0 +1,16 @@ +<div class="pull-right big-spacer-left nowrap"> + <a class="js-group-update icon-edit little-spacer-right" title="Update Details" data-toggle="tooltip" href="#"></a> + <a class="js-group-delete icon-delete" title="Deactivate" data-toggle="tooltip" href="#"></a> +</div> + +<div class="display-inline-block text-top width-20"> + <strong class="js-group-name">{{name}}</strong> +</div> + +<div class="display-inline-block text-top big-spacer-left width-10"> + Members: <a class="js-group-users" href="#">{{membersCount}}</a> +</div> + +<div class="display-inline-block text-top big-spacer-left width-50"> + <span class="js-group-description">{{description}}</span> +</div> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs new file mode 100644 index 00000000000..5e81ec0b32a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs @@ -0,0 +1,6 @@ +<div class="panel panel-vertical bordered-bottom spacer-bottom"> + <form id="groups-search-form" class="search-box"> + <button id="groups-search-submit" class="search-box-submit button-clean"><i class="icon-search"></i></button> + <input id="groups-search-query" class="search-box-input" type="search" name="q" placeholder="Search" maxlength="100"> + </form> +</div> diff --git a/server/sonar-web/src/main/js/apps/groups/templates/groups-users.hbs b/server/sonar-web/src/main/js/apps/groups/templates/groups-users.hbs new file mode 100644 index 00000000000..eb346c0e31b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/templates/groups-users.hbs @@ -0,0 +1,10 @@ +<div class="modal-head"> + <h2>Update users</h2> +</div> +<div class="modal-body"> + <div class="js-modal-messages"></div> + <div id="groups-users"></div> +</div> +<div class="modal-foot"> + <a href="#" class="js-modal-close" id="groups-users-done">Done</a> +</div> diff --git a/server/sonar-web/src/main/js/apps/groups/update-view.js b/server/sonar-web/src/main/js/apps/groups/update-view.js new file mode 100644 index 00000000000..71383a1793d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/update-view.js @@ -0,0 +1,29 @@ +define([ + './form-view' +], function (FormView) { + + return FormView.extend({ + + sendRequest: function () { + var that = this; + this.model.set({ + name: this.$('#create-group-name').val(), + description: this.$('#create-group-description').val() + }); + this.disableForm(); + return this.model.save(null, { + statusCode: { + // do not show global error + 400: null + } + }).done(function () { + that.collection.refresh(); + that.close(); + }).fail(function (jqXHR) { + that.enableForm(); + that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/groups/users-view.js b/server/sonar-web/src/main/js/apps/groups/users-view.js new file mode 100644 index 00000000000..7786d63fe44 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/users-view.js @@ -0,0 +1,42 @@ +define([ + 'components/common/modals', + 'components/common/select-list', + './templates' +], function (Modal) { + + return Modal.extend({ + template: Templates['groups-users'], + + onRender: function () { + this._super(); + new window.SelectList({ + el: this.$('#groups-users'), + width: '100%', + readOnly: false, + focusSearch: false, + format: function (item) { + return item.name + '<br><span class="note">' + item.login + '</span>'; + }, + queryParam: 'q', + searchUrl: baseUrl + '/api/usergroups/users?ps=100&id=' + this.model.id, + selectUrl: baseUrl + '/api/usergroups/add_user', + deselectUrl: baseUrl + '/api/usergroups/remove_user', + extra: { + groupId: this.model.id + }, + selectParameter: 'userLogin', + selectParameterValue: 'login', + parse: function (r) { + this.more = false; + return r.users; + } + }); + }, + + onClose: function () { + this.model.collection.refresh(); + this._super(); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs index 875bc3bc4a5..f7f1bd1b215 100644 --- a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs +++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs @@ -1,5 +1,5 @@ -<div class="issue-meta"> - <a class="issue-action issue-action-with-options js-issue-filter" href="#"> +<li class="issue-meta"> + <button class="button-link issue-action issue-action-with-options js-issue-filter"> <i class="icon-filter icon-half-transparent"></i> <i class="icon-dropdown"></i> - </a> -</div> + </button> +</li> diff --git a/server/sonar-web/src/main/js/apps/provisioning/delete-view.js b/server/sonar-web/src/main/js/apps/provisioning/delete-view.js index 2ff38de169f..7797227aa40 100644 --- a/server/sonar-web/src/main/js/apps/provisioning/delete-view.js +++ b/server/sonar-web/src/main/js/apps/provisioning/delete-view.js @@ -6,8 +6,8 @@ define([ return ModalForm.extend({ template: Templates['provisioning-delete'], - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); + onFormSubmit: function (e) { + this._super(e); this.sendRequest(); }, diff --git a/server/sonar-web/src/main/js/apps/provisioning/form-view.js b/server/sonar-web/src/main/js/apps/provisioning/form-view.js index ed7fe702f2c..ce359c6fa07 100644 --- a/server/sonar-web/src/main/js/apps/provisioning/form-view.js +++ b/server/sonar-web/src/main/js/apps/provisioning/form-view.js @@ -7,17 +7,17 @@ define([ template: Templates['provisioning-form'], onRender: function () { - ModalForm.prototype.onRender.apply(this, arguments); + this._super(); this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); }, onClose: function () { - ModalForm.prototype.onClose.apply(this, arguments); + this._super(); this.$('[data-toggle="tooltip"]').tooltip('destroy'); }, - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); + onFormSubmit: function (e) { + this._super(e); this.sendRequest(); } diff --git a/server/sonar-web/src/main/js/apps/provisioning/list-footer-view.js b/server/sonar-web/src/main/js/apps/provisioning/list-footer-view.js index 6dc243e7b67..902b9322372 100644 --- a/server/sonar-web/src/main/js/apps/provisioning/list-footer-view.js +++ b/server/sonar-web/src/main/js/apps/provisioning/list-footer-view.js @@ -23,7 +23,7 @@ define([ }, serializeData: function () { - return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + return _.extend(this._super(), { total: this.collection.total, count: this.collection.length, more: this.collection.hasMore() diff --git a/server/sonar-web/src/main/js/apps/provisioning/projects.js b/server/sonar-web/src/main/js/apps/provisioning/projects.js index 44f1c5b4b5a..a5c26347c59 100644 --- a/server/sonar-web/src/main/js/apps/provisioning/projects.js +++ b/server/sonar-web/src/main/js/apps/provisioning/projects.js @@ -19,7 +19,7 @@ define([ fetch: function (options) { var d = (options && options.data) || {}; this.q = d.q; - return Backbone.Collection.prototype.fetch.apply(this, arguments); + return this._super(options); }, fetchMore: function () { diff --git a/server/sonar-web/src/main/js/apps/users/change-password-view.js b/server/sonar-web/src/main/js/apps/users/change-password-view.js index cd2a892fc2b..6187333c9e6 100644 --- a/server/sonar-web/src/main/js/apps/users/change-password-view.js +++ b/server/sonar-web/src/main/js/apps/users/change-password-view.js @@ -6,8 +6,8 @@ define([ return ModalForm.extend({ template: Templates['users-change-password'], - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); + onFormSubmit: function (e) { + this._super(e); this.sendRequest(); }, diff --git a/server/sonar-web/src/main/js/apps/users/deactivate-view.js b/server/sonar-web/src/main/js/apps/users/deactivate-view.js index cf4c4654984..37c71d4a94b 100644 --- a/server/sonar-web/src/main/js/apps/users/deactivate-view.js +++ b/server/sonar-web/src/main/js/apps/users/deactivate-view.js @@ -6,8 +6,8 @@ define([ return ModalForm.extend({ template: Templates['users-deactivate'], - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); + onFormSubmit: function (e) { + this._super(e); this.sendRequest(); }, diff --git a/server/sonar-web/src/main/js/apps/users/form-view.js b/server/sonar-web/src/main/js/apps/users/form-view.js index 2cf2e1ac3f1..f1c7f602d14 100644 --- a/server/sonar-web/src/main/js/apps/users/form-view.js +++ b/server/sonar-web/src/main/js/apps/users/form-view.js @@ -9,23 +9,23 @@ define([ template: Templates['users-form'], events: function () { - return _.extend(ModalForm.prototype.events.apply(this, arguments), { + return _.extend(this._super(), { 'click #create-user-add-scm-account': 'onAddScmAccountClick' }); }, onRender: function () { - ModalForm.prototype.onRender.apply(this, arguments); + this._super(); this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); }, onClose: function () { - ModalForm.prototype.onClose.apply(this, arguments); + this._super(); this.$('[data-toggle="tooltip"]').tooltip('destroy'); }, - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); + onFormSubmit: function (e) { + this._super(e); this.sendRequest(); }, diff --git a/server/sonar-web/src/main/js/apps/users/groups-view.js b/server/sonar-web/src/main/js/apps/users/groups-view.js index b242f435773..6a874070b66 100644 --- a/server/sonar-web/src/main/js/apps/users/groups-view.js +++ b/server/sonar-web/src/main/js/apps/users/groups-view.js @@ -9,28 +9,34 @@ define([ itemTemplate: Templates['users-group'], onRender: function () { - Modal.prototype.onRender.apply(this, arguments); + this._super(); new window.SelectList({ el: this.$('#users-groups'), width: '100%', - readOnly: true, + readOnly: false, focusSearch: false, format: function (item) { return item.name + '<br><span class="note">' + item.description + '</span>'; }, + queryParam: 'q', searchUrl: baseUrl + '/api/users/groups?ps=100&login=' + this.model.id, - selectUrl: baseUrl + '/api/qualityprofiles/add_project', - deselectUrl: baseUrl + '/api/qualityprofiles/remove_project', + selectUrl: baseUrl + '/api/usergroups/add_user', + deselectUrl: baseUrl + '/api/usergroups/remove_user', extra: { - profileKey: key + userLogin: this.model.id }, - selectParameter: 'projectUuid', - selectParameterValue: 'uuid', + selectParameter: 'groupId', + selectParameterValue: 'id', parse: function (r) { this.more = false; return r.groups; } }); + }, + + onClose: function () { + this.model.collection.refresh(); + Modal.prototype.onClose.apply(this, arguments); } }); diff --git a/server/sonar-web/src/main/js/apps/users/list-footer-view.js b/server/sonar-web/src/main/js/apps/users/list-footer-view.js index 968ad0990d5..cf802586354 100644 --- a/server/sonar-web/src/main/js/apps/users/list-footer-view.js +++ b/server/sonar-web/src/main/js/apps/users/list-footer-view.js @@ -23,7 +23,7 @@ define([ }, serializeData: function () { - return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + return _.extend(this._super(), { total: this.collection.total, count: this.collection.length, more: this.collection.hasMore() diff --git a/server/sonar-web/src/main/js/apps/users/list-item-view.js b/server/sonar-web/src/main/js/apps/users/list-item-view.js index c6a81a2e71f..99aa973579b 100644 --- a/server/sonar-web/src/main/js/apps/users/list-item-view.js +++ b/server/sonar-web/src/main/js/apps/users/list-item-view.js @@ -101,7 +101,7 @@ define([ scmAccountsLimit = scmAccounts.length > this.scmLimit ? this.scmLimit - 1 : this.scmLimit, groups = this.model.get('groups'), groupsLimit = groups.length > this.groupsLimit ? this.groupsLimit - 1 : this.groupsLimit; - return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + return _.extend(this._super(), { firstScmAccounts: _.first(scmAccounts, scmAccountsLimit), moreScmAccountsCount: scmAccounts.length - scmAccountsLimit, firstGroups: _.first(groups, groupsLimit), diff --git a/server/sonar-web/src/main/js/apps/users/users.js b/server/sonar-web/src/main/js/apps/users/users.js index adf80b669da..9c45979a9fe 100644 --- a/server/sonar-web/src/main/js/apps/users/users.js +++ b/server/sonar-web/src/main/js/apps/users/users.js @@ -19,7 +19,7 @@ define([ fetch: function (options) { var d = (options && options.data) || {}; this.q = d.q; - return Backbone.Collection.prototype.fetch.apply(this, arguments); + return this._super(options); }, fetchMore: function () { diff --git a/server/sonar-web/src/main/js/components/common/select-list.js b/server/sonar-web/src/main/js/components/common/select-list.js index 480fbd9d690..5716ed2fa10 100644 --- a/server/sonar-web/src/main/js/components/common/select-list.js +++ b/server/sonar-web/src/main/js/components/common/select-list.js @@ -329,7 +329,8 @@ define(function () { search: function () { var query = this.$('.select-list-search-control input').val(), hasQuery = query.length > 0, - that = this; + that = this, + data = {}; this.$('.select-list-check-control').toggleClass('disabled', hasQuery); this.$('.select-list-search-control').toggleClass('disabled', !hasQuery); @@ -338,10 +339,11 @@ define(function () { this.showFetchSpinner(); this.currentFilter = 'all'; + data[this.settings.queryParam] = query; this.collection.fetch({ url: this.settings.searchUrl, reset: true, - data: { query: query }, + data: data, success: function () { that.hideFetchSpinner(); }, @@ -431,6 +433,8 @@ define(function () { return r.results; }, + queryParam: 'query', + labels: { selected: 'Selected', deselected: 'Deselected', diff --git a/server/sonar-web/src/main/js/components/issue/issue-view.js b/server/sonar-web/src/main/js/components/issue/issue-view.js index 98d095d221d..1b166bb87df 100644 --- a/server/sonar-web/src/main/js/components/issue/issue-view.js +++ b/server/sonar-web/src/main/js/components/issue/issue-view.js @@ -56,6 +56,14 @@ define([ this.$el.attr('data-key', this.model.get('key')); }, + disableControls: function () { + this.$(':input').prop('disabled', true); + }, + + enableControls: function () { + this.$(':input').prop('disabled', false); + }, + resetIssue: function (options) { var that = this; var key = this.model.get('key'), @@ -132,6 +140,7 @@ define([ var commentKey = $(e.target).closest('[data-comment-key]').data('comment-key'), confirmMsg = $(e.target).data('confirm-msg'); if (confirm(confirmMsg)) { + this.disableControls(); return $.ajax({ type: 'POST', url: baseUrl + '/api/issues/delete_comment?key=' + commentKey @@ -218,6 +227,7 @@ define([ action: function (action) { var that = this; + this.disableControls(); return $.post(baseUrl + '/api/issues/do_action', { issue: this.model.id, actionKey: action diff --git a/server/sonar-web/src/main/js/components/issue/manual-issue-view.js b/server/sonar-web/src/main/js/components/issue/manual-issue-view.js index c1f60b486e6..1b043544a44 100644 --- a/server/sonar-web/src/main/js/components/issue/manual-issue-view.js +++ b/server/sonar-web/src/main/js/components/issue/manual-issue-view.js @@ -1,10 +1,9 @@ define([ + './models/issue', './templates' -], function () { +], function (Issue) { - var $ = jQuery, - API_ISSUE = baseUrl + '/api/issues/show', - API_ADD_MANUAL_ISSUE = baseUrl + '/api/issues/create'; + var $ = jQuery; return Marionette.ItemView.extend({ template: Templates['manual-issue'], @@ -44,68 +43,35 @@ define([ } }, - showSpinner: function () { - this.$('.js-submit').hide(); - this.$('.js-spinner').show(); - }, - - hideSpinner: function () { - this.$('.js-submit').show(); - this.$('.js-spinner').hide(); - }, - - validateFields: function () { - var message = this.$('[name=message]'); - if (!message.val()) { - message.addClass('invalid').focus(); - return false; - } - return true; - }, - formSubmit: function (e) { var that = this; e.preventDefault(); - if (!this.validateFields()) { - return; - } - this.showSpinner(); - var data = $(e.currentTarget).serialize(); - $.post(API_ADD_MANUAL_ISSUE, data) - .done(function (r) { - if (typeof r === 'string') { - r = JSON.parse(r); - } - that.addIssue(r.issue.key); - }).fail(function (r) { - that.hideSpinner(); - if (r.responseJSON && r.responseJSON.errors) { - that.showError(_.pluck(r.responseJSON.errors, 'msg').join('. ')); - } - }); + var issue = new Issue({ + component: this.options.component, + line: this.options.line, + message: this.$('[name="message"]').val(), + rule: this.$('[name="rule"]').val() + }); + issue.save().done(function () { + that.addIssue(issue); + }); }, - addIssue: function (key) { + addIssue: function (issue) { var that = this; - return $.get(API_ISSUE, { key: key }).done(function (r) { - that.trigger('add', r.issue); + return issue.fetch().done(function () { + that.trigger('add', issue); that.close(); }); }, - showError: function (msg) { - this.$('.code-issue-errors').removeClass('hidden').text(msg); - }, - cancel: function (e) { e.preventDefault(); this.close(); }, serializeData: function () { - return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { - line: this.options.line, - component: this.options.component, + return _.extend(this._super(), { rules: _.sortBy(this.rules, 'name') }); } diff --git a/server/sonar-web/src/main/js/components/issue/models/issue.js b/server/sonar-web/src/main/js/components/issue/models/issue.js index 1c5e928818e..af5dd980c27 100644 --- a/server/sonar-web/src/main/js/components/issue/models/issue.js +++ b/server/sonar-web/src/main/js/components/issue/models/issue.js @@ -1,4 +1,4 @@ -define([], function () { +define(function () { return Backbone.Model.extend({ idAttribute: 'key', @@ -7,8 +7,40 @@ define([], function () { return baseUrl + '/api/issues/show?key=' + this.get('key'); }, + urlRoot: function () { + return baseUrl + '/api/issues'; + }, + parse: function (r) { return r.issue ? r.issue : r; + }, + + sync: function (method, model, options) { + var opts = options || {}; + opts.contentType = 'application/x-www-form-urlencoded'; + if (method === 'read') { + _.extend(opts, { + type: 'GET', + url: this.urlRoot() + '/show', + data: { key: model.id } + }); + } + if (method === 'create') { + _.extend(opts, { + type: 'POST', + url: this.urlRoot() + '/create', + data: { + component: model.get('component'), + line: model.get('line'), + message: model.get('message'), + rule: model.get('rule'), + severity: model.get('severity') + } + }); + } + var xhr = options.xhr = Backbone.ajax(opts); + model.trigger('request', model, xhr, options); + return xhr; } }); diff --git a/server/sonar-web/src/main/js/components/issue/templates/issue.hbs b/server/sonar-web/src/main/js/components/issue/templates/issue.hbs index f5a2d5e2fda..ba8e21bcc44 100644 --- a/server/sonar-web/src/main/js/components/issue/templates/issue.hbs +++ b/server/sonar-web/src/main/js/components/issue/templates/issue.hbs @@ -3,27 +3,29 @@ <table class="issue-table"> <tr> <td> - <div class="issue-message">{{message}} <a class="js-issue-rule issue-rule icon-ellipsis-h"></a></div> + <div class="issue-message"> + {{message}} <button class="button-link js-issue-rule issue-rule icon-ellipsis-h"></button> + </div> </td> <td class="issue-table-meta-cell issue-table-meta-cell-first"> - <div class="issue-meta-list"> - <div class="issue-meta"> - <a class="issue-action issue-action-with-options js-issue-show-changelog" title="{{dt creationDate}}"> + <ul class="list-inline issue-meta-list"> + <li class="issue-meta"> + <button class="button-link issue-action issue-action-with-options js-issue-show-changelog" title="{{dt creationDate}}"> <span class="issue-meta-label">{{fromNow creationDate}}</span> <i class="icon-dropdown"></i> - </a> - </div> + </button> + </li> {{#if line}} - <div class="issue-meta"> + <li class="issue-meta"> <span class="issue-meta-label" title="{{t 'line_number'}}">L{{line}}</span> - </div> + </li> {{/if}} - <div class="issue-meta"> - <a class="issue-action js-issue-permalink icon-link" href="{{permalink}}" target="_blank"></a> - </div> - </div> + <li class="issue-meta"> + <a class="js-issue-permalink icon-link" href="{{permalink}}" target="_blank"></a> + </li> + </ul> </td> </tr> </table> @@ -31,38 +33,38 @@ <table class="issue-table"> <tr> <td> - <div class="issue-meta-list"> - <div class="issue-meta"> + <ul class="list-inline issue-meta-list"> + <li class="issue-meta"> {{#inArray actions "set_severity"}} - <a class="issue-action issue-action-with-options js-issue-set-severity"> + <button class="button-link issue-action issue-action-with-options js-issue-set-severity"> <span class="issue-meta-label">{{severityHelper severity}}</span> <i class="icon-dropdown"></i> - </a> + </button> {{else}} {{severityHelper severity}} {{/inArray}} - </div> + </li> - <div class="issue-meta"> + <li class="issue-meta"> {{#notEmpty transitions}} - <a class="issue-action issue-action-with-options js-issue-transition"> + <button class="button-link issue-action issue-action-with-options js-issue-transition"> <span class="issue-meta-label">{{statusHelper status resolution}}</span> <i class="icon-dropdown"></i> - </a> + </button> {{else}} {{statusHelper status resolution}} {{/notEmpty}} - </div> + </li> - <div class="issue-meta"> + <li class="issue-meta"> {{#inArray actions "assign"}} - <a class="issue-action issue-action-with-options js-issue-assign"> + <button class="button-link issue-action issue-action-with-options js-issue-assign"> {{#if assignee}} {{#ifShowAvatars}} <span class="text-top">{{avatarHelper assigneeEmail 16}}</span> {{/ifShowAvatars}} {{/if}} <span class="issue-meta-label">{{#if assignee}}{{default assigneeName assignee}}{{else}}{{t 'unassigned'}}{{/if}}</span> <i class="icon-dropdown"></i> - </a> + </button> {{else}} {{#if assignee}} {{#ifShowAvatars}} @@ -71,64 +73,66 @@ {{/if}} <span class="issue-meta-label">{{#if assignee}}{{default assigneeName assignee}}{{else}}{{t 'unassigned'}}{{/if}}</span> {{/inArray}} - </div> - - {{#inArray actions "assign_to_me"}} - <a class="js-issue-assign-to-me"></a> - {{/inArray}} + </li> - <div class="issue-meta"> + <li class="issue-meta"> {{#inArray actions "plan"}} - <a class="issue-action issue-action-with-options js-issue-plan"> + <button class="button-link issue-action issue-action-with-options js-issue-plan"> <span class="issue-meta-label">{{#if actionPlan}}{{default actionPlanName actionPlan}}{{else}}{{t 'issue.unplanned'}}{{/if}}</span> <i class="icon-dropdown"></i> - </a> + </button> {{else}} <span class="issue-meta-label">{{#if actionPlan}}{{default actionPlanName actionPlan}}{{else}}{{t 'issue.unplanned'}}{{/if}}</span> {{/inArray}} - </div> + </li> {{#if debt}} - <div class="issue-meta"> + <li class="issue-meta"> <span class="issue-meta-label"> {{tp 'issue.x_debt' debt}} </span> - </div> + </li> {{/if}} {{#inArray actions "comment"}} - <div class="issue-meta"> - <a class="issue-action js-issue-comment"><span - class="issue-meta-label">{{t 'issue.comment.formlink' }}</span></a> - </div> + <li class="issue-meta"> + <button class="button-link issue-action js-issue-comment"><span + class="issue-meta-label">{{t 'issue.comment.formlink' }}</span></button> + </li> {{/inArray}} {{#ifHasExtraActions actions}} - <div class="issue-meta"> - <a class="issue-action issue-action-with-options js-issue-more"> + <li class="issue-meta"> + <button class="button-link issue-action issue-action-with-options js-issue-more"> <span class="issue-meta-label">{{t 'more'}}</span> <i class="icon-dropdown"></i> - </a> - </div> + </button> + </li> {{/ifHasExtraActions}} - </div> + </ul> + + {{#inArray actions "assign_to_me"}} + <button class="button-link hidden js-issue-assign-to-me"></button> + {{/inArray}} </td> <td class="issue-table-meta-cell"> - <div class="issue-meta js-issue-tags"> - {{#inArray actions "set_tags"}} - <a class="issue-action issue-action-with-options js-issue-edit-tags"> + <ul class="list-inline"> + <li class="issue-meta js-issue-tags"> + {{#inArray actions "set_tags"}} + <button class="button-link issue-action issue-action-with-options js-issue-edit-tags"> <span> <i class="icon-tags icon-half-transparent"></i> <span>{{#if tags}}{{join tags ', '}}{{else}}{{t 'issue.no_tag'}}{{/if}}</span> </span> <i class="icon-dropdown"></i> - </a> - {{else}} - <span> + </button> + {{else}} + <span> <i class="icon-tags icon-half-transparent"></i> <span>{{#if tags}}{{join tags ', '}}{{else}}{{t 'issue.no_tag'}}{{/if}}</span> </span> - {{/inArray}} - </div> + {{/inArray}} + </li> + </ul> </td> </tr> </table> @@ -138,15 +142,16 @@ {{#each comments}} <div class="issue-comment" data-comment-key="{{key}}"> <div class="issue-comment-author" title="{{userName}}"> - {{#ifShowAvatars}}{{avatarHelper email 16}}{{else}}<i class="icon-comment icon-half-transparent"></i>{{/ifShowAvatars}} {{userName}} + {{#ifShowAvatars}}{{avatarHelper email 16}}{{else}} + <i class="icon-comment icon-half-transparent"></i>{{/ifShowAvatars}} {{userName}} </div> <div class="issue-comment-text markdown">{{{show html htmlText}}}</div> <div class="issue-comment-age">({{fromNow createdAt}})</div> <div class="issue-comment-actions"> {{#if updatable}} - <a class="js-issue-comment-edit icon-edit icon-half-transparent"></a> - <a class="js-issue-comment-delete icon-delete icon-half-transparent" - data-confirm-msg="{{t 'issue.comment.delete_confirm_message'}}"></a> + <button class="js-issue-comment-edit button-link icon-edit icon-half-transparent"></button> + <button class="js-issue-comment-delete button-link icon-delete icon-half-transparent" + data-confirm-msg="{{t 'issue.comment.delete_confirm_message'}}"></button> {{/if}} </div> </div> diff --git a/server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs b/server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs index bbcf333395b..0a38fa2136f 100644 --- a/server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs +++ b/server/sonar-web/src/main/js/components/issue/templates/manual-issue.hbs @@ -1,19 +1,6 @@ -<form action="" class="js-manual-issue-form code-issue-create-form"> - {{! no manual rules }} - {{! <div class="warning" style="margin: 10px"> }} - {{! <% if is_admin %> }} - {{! <%= message('issue.manual.no_rules.admin') -%> }} - {{! <a href="<%= ApplicationController.root_context -%>/manual_rules/index"><%= message('manage') -%></a> }} - {{! <% else %> }} - {{! <%= message('issue.manual.no_rules.non_admin') -%> }} - {{! <% end %> }} - {{! <%= link_to_function message('cancel'), 'closeCreateIssueForm(this)' -%> }} - {{! </div> }} +<form class="js-manual-issue-form spacer-top spacer-bottom"> - <input type="hidden" name="line" value="{{line}}"> - <input type="hidden" name="component" value="{{component}}"> - - <div class="code-issue-name"> + <div class="spacer-bottom"> <select name="rule"> {{#each rules}} <option value="{{key}}">{{name}}</option> @@ -21,24 +8,13 @@ </select> </div> - <div class="code-issue-msg"> - <table class="width100"> - <tr> - <td> - <textarea rows="4" name="message" class="width100 marginbottom5"></textarea> - </td> - </tr> - <tr> - <td class="js-submit"> - <input type="submit" value="{{t 'create'}}"> - <a class="js-cancel" href="#">{{t 'cancel'}}</a> - </td> - <td class="js-spinner" style="display: none;"> - <i class="spinner"></i> - </td> - </tr> - </table> - <div class="code-issue-errors alert alert-danger hidden"></div> + <div class="spacer-top"> + <textarea rows="4" name="message" class="width-100" required></textarea> + </div> + + <div class="spacer-top"> + <input type="submit" value="{{t 'create'}}"> + <a class="js-cancel" href="#">{{t 'cancel'}}</a> </div> </form> diff --git a/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js b/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js index 4816ff2eada..10e9c713e09 100644 --- a/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js +++ b/server/sonar-web/src/main/js/components/issue/views/comment-form-view.js @@ -49,6 +49,14 @@ define([ this.options.detailView.updateAfterAction(false); }, + disableForm: function () { + this.$(':input').prop('disabled', true); + }, + + enableForm: function () { + this.$(':input').prop('disabled', false); + }, + submit: function () { var that = this; var text = this.ui.textarea.val(), @@ -61,9 +69,15 @@ define([ } else { data.issue = this.options.issue.id; } - return $.post(url, data).done(function () { - that.options.detailView.updateAfterAction(true); - }); + this.disableForm(); + this.options.detailView.disableControls(); + return $.post(url, data) + .done(function () { + that.options.detailView.updateAfterAction(true); + }).fail(function () { + that.enableForm(); + that.options.detailView.enableControls(); + }); } }); diff --git a/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js b/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js index 6558537e225..9d56b101591 100644 --- a/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js +++ b/server/sonar-web/src/main/js/components/issue/views/transitions-form-view.js @@ -20,6 +20,7 @@ define([ submit: function (transition) { var that = this; + this.options.view.disableControls(); return $.ajax({ type: 'POST', url: baseUrl + '/api/issues/do_transition', diff --git a/server/sonar-web/src/main/js/components/source-viewer/main.js b/server/sonar-web/src/main/js/components/source-viewer/main.js index 07dfc55580d..a007145f354 100644 --- a/server/sonar-web/src/main/js/components/source-viewer/main.js +++ b/server/sonar-web/src/main/js/components/source-viewer/main.js @@ -479,8 +479,8 @@ define([ line: line, row: $(e.currentTarget).closest('.source-line') }); - popup.on('onManualIssueAdded', function (data) { - that.addIssue(new Issue(data)); + popup.on('onManualIssueAdded', function (issue) { + that.addIssue(issue); }); popup.render(); }, diff --git a/server/sonar-web/src/main/js/libs/third-party/backbone-super.js b/server/sonar-web/src/main/js/libs/third-party/backbone-super.js new file mode 100644 index 00000000000..9562638918e --- /dev/null +++ b/server/sonar-web/src/main/js/libs/third-party/backbone-super.js @@ -0,0 +1,111 @@ +// This is a plugin, constructed from parts of Backbone.js and John Resig's inheritance script. +// (See http://backbonejs.org, http://ejohn.org/blog/simple-javascript-inheritance/) +// No credit goes to me as I did absolutely nothing except patch these two together. +(function(root, factory) { + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'backbone'], function(_, Backbone) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + factory( _, Backbone); + }); + + // Next for Node.js or CommonJS. + } else if (typeof exports !== 'undefined' && typeof require === 'function') { + var _ = require('underscore'), + Backbone = require('backbone'); + factory(_, Backbone); + + // Finally, as a browser global. + } else { + factory(root._, root.Backbone); + } + +}(this, function factory(_, Backbone) { + Backbone.Model.extend = Backbone.Collection.extend = Backbone.Router.extend = Backbone.View.extend = function(protoProps, classProps) { + var child = inherits(this, protoProps, classProps); + child.extend = this.extend; + return child; + }; + var unImplementedSuper = function(method){throw "Super does not implement this method: " + method;}; + + var fnTest = /\b_super\b/; + + var makeWrapper = function(parentProto, name, fn) { + var wrapper = function() { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = parentProto[name] || unImplementedSuper(name); + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret; + try { + ret = fn.apply(this, arguments); + } finally { + this._super = tmp; + } + return ret; + }; + + //we must move properties from old function to new + for (var prop in fn) { + wrapper[prop] = fn[prop]; + delete fn[prop]; + } + + return wrapper; + }; + + var ctor = function(){}, inherits = function(parent, protoProps, staticProps) { + var child, parentProto = parent.prototype; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent's constructor. + if (protoProps && protoProps.hasOwnProperty('constructor')) { + child = protoProps.constructor; + } else { + child = function(){ return parent.apply(this, arguments); }; + } + + // Inherit class (static) properties from parent. + _.extend(child, parent, staticProps); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function. + ctor.prototype = parentProto; + child.prototype = new ctor(); + + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) { + _.extend(child.prototype, protoProps); + + // Copy the properties over onto the new prototype + for (var name in protoProps) { + // Check if we're overwriting an existing function + if (typeof protoProps[name] == "function" && fnTest.test(protoProps[name])) { + child.prototype[name] = makeWrapper(parentProto, name, protoProps[name]); + } + } + } + + // Add static properties to the constructor function, if supplied. + if (staticProps) _.extend(child, staticProps); + + // Correctly set child's `prototype.constructor`. + child.prototype.constructor = child; + + // Set a convenience property in case the parent's prototype is needed later. + child.__super__ = parentProto; + + return child; + }; + + return inherits; +})); + diff --git a/server/sonar-web/src/main/less/components/issues.less b/server/sonar-web/src/main/less/components/issues.less index 2aa096c1ba9..ed6db933ad5 100644 --- a/server/sonar-web/src/main/less/components/issues.less +++ b/server/sonar-web/src/main/less/components/issues.less @@ -91,8 +91,12 @@ .issue-rule { padding: 0 3px; - background-color: fade(@blue, 30%); + background: fade(@blue, 30%); opacity: 0.5; + + &:hover { + background: fade(@blue, 30%); + } } .issue-component { @@ -121,15 +125,10 @@ } .issue-meta-list { - .clearfix; padding-left: @leftPadding; - font-size: 0; } .issue-meta { - display: inline-block; - vertical-align: top; - margin-right: 10px; line-height: 1.5; font-size: @smallFontSize; } @@ -157,14 +156,6 @@ .text-ellipsis; } -.issue-action { - border-bottom: none; -} - -.issue-action + .issue-action { - margin-left: 4px; -} - .issue-changelog { max-width: 540px; max-height: 320px; diff --git a/server/sonar-web/src/main/less/components/panels.less b/server/sonar-web/src/main/less/components/panels.less index 44c2e82b69d..55c32ad5de1 100644 --- a/server/sonar-web/src/main/less/components/panels.less +++ b/server/sonar-web/src/main/less/components/panels.less @@ -25,7 +25,7 @@ } .panel + .panel { - margin-top: 20px; + margin-top: 10px; padding-top: 20px; border-top: 1px solid @barBorderColor; } diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less index 656491eb0b6..81e7bb940e5 100644 --- a/server/sonar-web/src/main/less/init/forms.less +++ b/server/sonar-web/src/main/less/init/forms.less @@ -139,6 +139,34 @@ input[type=button] { color: @baseFontColor; } +.button-link { + display: inline; + height: auto; + margin: 0; + padding: 0; + border: none; + background: transparent; + color: @darkBlue; + font-weight: 400; + font-size: inherit; + line-height: inherit; + .trans; + + &:hover, &:active, &:focus { + background: transparent; + color: @blue; + } + + &[disabled], + &[disabled]:hover, + &[disabled]:active, + &[disabled]:focus { + color: @secondFontColor; + background: transparent; + cursor: default; + } +} + .button-group { display: inline-block; vertical-align: middle; diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb index 51a6a8063ba..3c7c9873495 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb @@ -23,175 +23,7 @@ class GroupsController < ApplicationController before_filter :admin_required def index - @groups = Group.find(:all, :order => 'name') - if params[:id] - @group = Group.find(params[:id]) - else - @group = Group.new - end - end - - def create_form - @group = Group.new - render :partial => 'groups/create_form' - end - - def edit_form - require_parameters :id - @group = Group.find(params[:id]) - render :partial => 'groups/edit_form' - end - - def create - verify_post_request - group = Group.new(params[:group]) - if group.save - flash[:notice] = 'The new group is created.' - render :text => 'ok', :status => 200 - else - @group = group - @errors = [] - group.errors.full_messages.each{|msg| @errors<<msg} - render :partial => 'groups/create_form', :status => 400 - end - end - - def update - verify_post_request - require_parameters :id - - @group = Group.find(params[:id]) - if @group.update_attributes(params[:group]) - flash[:notice] = 'Group is updated.' - render :text => 'ok', :status => 200 - else - @errors = [] - @group.errors.full_messages.each{|msg| @errors<<msg} - render :partial => 'groups/edit_form', :status => 400 - end - end - - def delete - verify_post_request - require_parameters :id - group = Group.find(params[:id]) - call_backend do - Internal.permission_templates.removeGroupFromTemplates(group.name) - if group.destroy - flash[:notice] = 'Group is deleted.' - end - end - to_index(group.errors, nil) - end - - # TO BE REMOVED ? - def select_user - @group = Group.find(params[:id]) - render :partial => 'groups/select_user' - end - - # TO BE REMOVED ? - def set_users - @group = Group.find(params[:id]) - if @group.set_users(params[:users]) - flash[:notice] = 'Group is updated.' - end - - redirect_to(:action => 'index') - end - - - # Used for selection of group members - # - # GET /groups/search_users?group=<group_name>&page=1&pageSize=10 - # - # - def search_users - require_parameters :group, :page, :pageSize - - group = Group.first(:conditions => {:name => params[:group]}) - group_id = group.id - selected = params[:selected]||'all' - query = params[:query] - page_id = params[:page].to_i - page_size = [params[:pageSize].to_i, 1000].min - - conditions = ['users.active=?'] - condition_values = [true] - if selected=='selected' - conditions << "groups_users.group_id=?" - condition_values << group_id - elsif selected=='deselected' - conditions << "groups_users.group_id is null" - end - if query - conditions << "users.name like ?" - condition_values << "%#{query}%" - end - - users = User.find(:all, - :select => 'users.id,users.name,users.login,groups_users.group_id', - :joins => "left join groups_users on users.id=groups_users.user_id and groups_users.group_id=#{group_id}", - :conditions => [conditions.join(' and ')].concat(condition_values), - :offset => (page_id-1) * page_size, - :limit => page_size + 1, - :order => 'users.name') - - more = false - if users.size>page_size - users = users[0...page_size] - more = true - end - - respond_to do |format| - format.json { - render :json => { - :more => more, - :results => users.map {|user| {:id => user.id, :name => user.name, :login => user.login, :selected => (user.group_id != nil)}} - } - } - end - end - - def add_member - verify_post_request - require_parameters :group, :user - - user = User.find(:first, :conditions => {:login => params[:user], :active => true}) - group = Group.first(:conditions => {:name => params[:group]}) - status = 400 - if user && group - group.users << user - status = 200 if group.save - end - render :status => status, :text => '{}' - end - - def remove_member - verify_post_request - require_parameters :group, :user - - user = User.find(:first, :conditions => {:login => params[:user], :active => true}) - user_id = user.id - group = Group.first(:conditions => {:name => params[:group]}) - status = 400 - if group - user_from_group = group.users.find(user_id) - if user_from_group - group.users.delete(user_from_group) - status = 200 if group.save - else - status = 200 - end - end - render :status => status, :text => '{}' - end - - private - def to_index(errors, id) - flash[:error] = errors.full_messages.join("<br/>\n") unless errors.empty? - redirect_to(:action => 'index', :id => id) end end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/component_issues/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/component_issues/index.html.erb index f5d868da748..c1b98d59ad0 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/component_issues/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/component_issues/index.html.erb @@ -6,9 +6,9 @@ <script> var config = { - resource: '<%= @resource.uuid -%>', - resourceQualifier: '<%= @resource.qualifier -%>', - resourceName: '<%= @resource.name -%>', - periodDate: <% if @period %>'<%= @snapshot.period_datetime(@period) -%>'<% else %>null<% end %> + resource: '<%= escape_javascript @resource.uuid -%>', + resourceQualifier: '<%= escape_javascript @resource.qualifier -%>', + resourceName: '<%= escape_javascript @resource.name -%>', + periodDate: <% if @period %>'<%= escape_javascript @snapshot.period_datetime(@period) -%>'<% else %>null<% end %> }; </script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_create_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_create_form.html.erb deleted file mode 100644 index 1e4036728b9..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_create_form.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<% form_for :group, @group, :url => { :action => 'create', :id => @group.id}, :html => { :id =>'group_form'} do |f| %> - <fieldset> - <div class="modal-head"> - <h2>Add new group</h2> - </div> - - <div class="modal-body"> - <% if @errors - @errors.each do |error| - %> - <p class="error"><%= h error -%></p> - <% end - end - %> - <div class="modal-field"> - <label for="group[]">Name:<em class="mandatory">*</em></label> - <%= f.text_field :name, :size => 25 %><br/> - <span class="desc">Ex: my-group</span> - </div> - <div class="modal-field"> - <label for="group[]">Description:</label> - <%= f.text_area :description, :rows => 3, :cols => 25 %> - </div> - </div> - - <div class="modal-foot"> - <%= submit_tag 'Create' %> - <%= link_to 'Cancel', { :controller => 'groups', :action => 'index', :id => nil}, { :class => 'action' } %><br/> - </div> - </fieldset> -<% end %> -<script> - $j("#group_form").modalForm(); -</script>
\ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_edit_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_edit_form.html.erb deleted file mode 100644 index cc649f85432..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_edit_form.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<% form_for :group, @group, :url => { :action => 'update', :id => @group.id}, :html => { :id =>'group_form'} do |f| %> - <fieldset> - <div class="modal-head"> - <h2>Edit group: <%= h @group.name_was -%></h2> - </div> - - <div class="modal-body"> - <% if @errors - @errors.each do |error| - %> - <p class="error"><%= h error -%></p> - <% end - end - %> - <div class="modal-field"> - <label for="group[]">Name:<em class="mandatory">*</em></label> - <%= f.text_field :name, :size => 25 %><br/> - <span class="desc">Ex: my-group</span> - </div> - <div class="modal-field"> - <label for="group[]">Description:</label> - <%= f.text_area :description, :rows => 3, :cols => 25 %> - </div> - </div> - - <div class="modal-foot"> - <%= submit_tag 'Save' -%> - <%= link_to 'Cancel', { :controller => 'groups', :action => 'index', :id => nil}, { :class => 'action' } %><br/> - </div> - </fieldset> -<% end %> -<script> - $j("#group_form").modalForm(); -</script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_select_user.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_select_user.html.erb deleted file mode 100644 index edaa5ee2a55..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/_select_user.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -<div class="modal-head"> - <h2>Group: <%= h @group.name -%></h2> -</div> - -<div class="modal-body"> - <div id="select-list-group"></div> -</div> - -<div class="modal-foot"> - <%= link_to 'Close', { :controller => 'groups', :action => 'index', :id => nil}, { :class => 'action' } %><br/> -</div> - -<script> - var selectList = new SelectList({ - el: '#select-list-group', - width: '100%', - format: function (item) { return item.name + ' <div class="note">' + item.login + '</div>'; }, - searchUrl: baseUrl + '/groups/search_users?group=<%= @group.name -%>', - selectUrl: baseUrl + '/groups/add_member', - deselectUrl: baseUrl + '/groups/remove_member', - extra: { - group: '<%= @group.name -%>' - }, - selectParameter: 'user', - selectParameterValue: 'login', - labels: { - selected: 'Members', - deselected: 'Non-members', - all: 'All' - }, - tooltips: { - select: 'Click to add this user to the group <%= h @group.name -%>', - deselect: 'Click to remove this member from the group <%= h @group.name -%>' - } - }); -</script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb index fbf1a00fa7e..fedf3bd424c 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb @@ -1,65 +1,6 @@ -<% content_for :script do %> - <script>require(['components/common/select-list']);</script> -<% end %> - -<div class="page"> - <header class="page-header"> - <h1 class="page-title"><%= message('user_groups.page') -%></h1> - <% if is_admin? %> - <div class="page-actions"> - <a id="link-create-group" href="<%= ApplicationController.root_context -%>/groups/create_form" class="open-modal link-action">Add new group</a> - </div> - <% end %> - <p class="page-description"><%= message('user_groups.page.description') -%> </p> - </header> - - - <table width="100%"> - <tr> - <td valign="top"> - - <table class="data width100 sortable" id="groups"> - <thead> - <tr> - <th class="text-left sortfirstasc"><a>Name</a></th> - <th class="text-left"><a>Description</a></th> - <th class="text-left"><a>Members</a></th> - <th class="text-right nosort" nowrap><a>Operations</a></th> - </tr> - </thead> - <tbody> - <tr id="group-anyone"> - <td class="text-left"><%= h 'Anyone' -%></td> - <td class="text-left" style="word-break:break-all"><%= message('user_groups.anyone.description') -%></td> - <td class="text-left"> </td> - <td> </td> - </tr> - <% @groups.each do |group| %> - <tr id="group-<%= group.name.parameterize -%>"> - <td class="text-left"><%= h group.name -%></td> - <td class="text-left" style="word-break:break-all"><%= h group.description -%></td> - <td class="text-left"> - <span id="count-<%= group.name.parameterize -%>"><%= group.users.count -%></span> - (<a id="select-<%= group.name.parameterize -%>" class="open-modal link-action" href="<%= ApplicationController.root_context -%>/groups/select_user/<%= group.id -%>">select</a>) - </td> - <td class="text-right"> - <a id='edit-<%= group.name.parameterize -%>' class='open-modal link-action' href="<%= ApplicationController.root_context -%>/groups/edit_form/<%= group.id -%>">Edit</a> - - <%= link_to_action message('delete'), "#{ApplicationController.root_context}/groups/delete/#{group.id}", - :class => 'link-action link-red', - :id => "delete-#{group.name.parameterize}", - :confirm_button => message('delete'), - :confirm_title => 'Delete group: ' + group.name, - :confirm_msg => 'Are you sure that you want to delete this group? Members will not be deleted.', - :confirm_msg_params => [group.name] - -%> - </td> - </tr> - <% end %> - </tbody> - </table> - <script>jQuery('#groups').sortable();</script> - </td> - </tr> - </table> -</div> +<div id="groups"></div> +<script> + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); +</script> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/916_feed_metrics_booleans.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/916_feed_metrics_booleans.rb new file mode 100644 index 00000000000..347164f04ef --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/916_feed_metrics_booleans.rb @@ -0,0 +1,31 @@ +# +# 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. +# + +# +# SonarQube 5.2 +# SONAR-6571 +# +class FeedMetricsBooleans < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.server.db.migrations.v52.FeedMetricsBooleans') + end + +end diff --git a/server/sonar-web/src/test/js/groups-spec.js b/server/sonar-web/src/test/js/groups-spec.js new file mode 100644 index 00000000000..ef21e8a8fea --- /dev/null +++ b/server/sonar-web/src/test/js/groups-spec.js @@ -0,0 +1,355 @@ +/* globals casper: false */ +var lib = require('../lib'), + testName = lib.testName('Groups'); + +lib.initMessages(); +lib.changeWorkingDirectory('groups-spec'); +lib.configureCasper(); + +casper.test.begin(testName('List'), 7, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + test.assertExists('#groups-list ul'); + test.assertElementCount('#groups-list li[data-id]', 2); + test.assertSelectorContains('#groups-list .js-group-name', 'sonar-users'); + test.assertSelectorContains('#groups-list .js-group-description', + 'Any new users created will automatically join this group'); + test.assertElementCount('#groups-list .js-group-update', 2); + test.assertElementCount('#groups-list .js-group-delete', 2); + test.assertSelectorContains('#groups-list-footer', '2/2'); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Search'), 4, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 2); + lib.clearRequestMock(this.searchMock); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search-filtered.json', + { data: { q: 'adm' } }); + casper.evaluate(function () { + jQuery('#groups-search-query').val('adm'); + }); + casper.click('#groups-search-submit'); + casper.waitForSelectorTextChange('#groups-list-footer'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 1); + lib.clearRequestMock(this.searchMock); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + casper.evaluate(function () { + jQuery('#groups-search-query').val(''); + }); + casper.click('#groups-search-submit'); + casper.waitForSelectorTextChange('#groups-list-footer'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 2); + test.assert(casper.evaluate(function () { + return jQuery('#groups-search-query').val() === ''; + })); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Show More'), 4, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search-big-1.json'); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 1); + test.assertSelectorContains('#groups-list-footer', '1/2'); + lib.clearRequestMock(this.searchMock); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search-big-2.json', { data: { p: '2' } }); + casper.click('#groups-fetch-more'); + casper.waitForSelectorTextChange('#groups-list-footer'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 2); + test.assertSelectorContains('#groups-list-footer', '2/2'); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Show Users'), 2, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/users*', 'users.json'); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + test.assertTextDoesntExist('Bob'); + casper.click('[data-id="1"] .js-group-users'); + casper.waitForText('Bob'); + }) + + .then(function () { + test.assertTextExist('John'); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Create'), 4, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + this.createMock = lib.mockRequestFromFile('/api/usergroups/create', 'error.json', { status: 400 }); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + jQuery.ajaxSetup({ dataType: 'json' }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 2); + casper.click('#groups-create'); + casper.waitForSelector('#create-group-form'); + }) + + .then(function () { + casper.click('#create-group-submit'); + casper.waitForSelector('.alert.alert-danger'); + }) + + .then(function () { + lib.clearRequestMock(this.searchMock); + lib.mockRequestFromFile('/api/usergroups/search', 'search-created.json'); + lib.clearRequestMock(this.createMock); + lib.mockRequest('/api/usergroups/create', '{}', + { data: { name: 'name', description: 'description' } }); + casper.evaluate(function () { + jQuery('#create-group-name').val('name'); + jQuery('#create-group-description').val('description'); + }); + casper.click('#create-group-submit'); + casper.waitForSelectorTextChange('#groups-list-footer'); + }) + + .then(function () { + test.assertElementCount('#groups-list li[data-id]', 3); + test.assertSelectorContains('#groups-list .js-group-name', 'name'); + test.assertSelectorContains('#groups-list .js-group-description', 'description'); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Update'), 2, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + this.updateMock = lib.mockRequestFromFile('/api/usergroups/update', 'error.json', { status: 400 }); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + jQuery.ajaxSetup({ dataType: 'json' }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + casper.click('[data-id="2"] .js-group-update'); + casper.waitForSelector('#create-group-form'); + }) + + .then(function () { + casper.click('#create-group-submit'); + casper.waitForSelector('.alert.alert-danger'); + }) + + .then(function () { + lib.clearRequestMock(this.searchMock); + lib.mockRequestFromFile('/api/usergroups/search', 'search-updated.json'); + lib.clearRequestMock(this.updateMock); + lib.mockRequest('/api/usergroups/update', '{}', + { data: { id: '2', name: 'guys', description: 'cool guys' } }); + casper.evaluate(function () { + jQuery('#create-group-name').val('guys'); + jQuery('#create-group-description').val('cool guys'); + }); + casper.click('#create-group-submit'); + casper.waitForText('guys'); + }) + + .then(function () { + test.assertSelectorContains('[data-id="2"] .js-group-name', 'guys'); + test.assertSelectorContains('[data-id="2"] .js-group-description', 'cool guys'); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); + + +casper.test.begin(testName('Delete'), 1, function (test) { + casper + .start(lib.buildUrl('groups'), function () { + lib.setDefaultViewport(); + this.searchMock = lib.mockRequestFromFile('/api/usergroups/search', 'search.json'); + this.updateMock = lib.mockRequestFromFile('/api/usergroups/delete', 'error.json', { status: 400 }); + }) + + .then(function () { + casper.evaluate(function () { + require(['apps/groups/app'], function (App) { + App.start({ el: '#groups' }); + }); + jQuery.ajaxSetup({ dataType: 'json' }); + }); + }) + + .then(function () { + casper.waitForText('sonar-users'); + }) + + .then(function () { + casper.click('[data-id="1"] .js-group-delete'); + casper.waitForSelector('#delete-group-form'); + }) + + .then(function () { + casper.click('#delete-group-submit'); + casper.waitForSelector('.alert.alert-danger'); + }) + + .then(function () { + lib.clearRequestMock(this.updateMock); + lib.mockRequest('/api/usergroups/delete', '{}', { data: { id: '1'} }); + casper.click('#delete-group-submit'); + casper.waitWhileSelector('[data-id="1"]'); + }) + + .then(function () { + test.assert(true); + }) + + .then(function () { + lib.sendCoverage(); + }) + .run(function () { + test.done(); + }); +}); diff --git a/server/sonar-web/src/test/json/groups-spec/error.json b/server/sonar-web/src/test/json/groups-spec/error.json new file mode 100644 index 00000000000..dc1b261128c --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/error.json @@ -0,0 +1,7 @@ +{ + "errors": [ + { + "msg": "Some error message" + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search-big-1.json b/server/sonar-web/src/test/json/groups-spec/search-big-1.json new file mode 100644 index 00000000000..654742be995 --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search-big-1.json @@ -0,0 +1,13 @@ +{ + "total": 2, + "p": 1, + "ps": 1, + "groups": [ + { + "id": "1", + "name": "sonar-users", + "description": "Any new users created will automatically join this group", + "membersCount": 3 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search-big-2.json b/server/sonar-web/src/test/json/groups-spec/search-big-2.json new file mode 100644 index 00000000000..9d8a35bd3b6 --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search-big-2.json @@ -0,0 +1,13 @@ +{ + "total": 2, + "p": 2, + "ps": 1, + "groups": [ + { + "id": "2", + "name": "sonar-administrators", + "description": "System administrators", + "membersCount": 1 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search-created.json b/server/sonar-web/src/test/json/groups-spec/search-created.json new file mode 100644 index 00000000000..1222b0dc009 --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search-created.json @@ -0,0 +1,25 @@ +{ + "total": 3, + "p": 1, + "ps": 50, + "groups": [ + { + "id": "1", + "name": "sonar-users", + "description": "Any new users created will automatically join this group", + "membersCount": 3 + }, + { + "id": "2", + "name": "sonar-administrators", + "description": "System administrators", + "membersCount": 1 + }, + { + "id": "3", + "name": "name", + "description": "description", + "membersCount": 0 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search-filtered.json b/server/sonar-web/src/test/json/groups-spec/search-filtered.json new file mode 100644 index 00000000000..58a0ead4a40 --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search-filtered.json @@ -0,0 +1,13 @@ +{ + "total": 1, + "p": 1, + "ps": 50, + "groups": [ + { + "id": "2", + "name": "sonar-administrators", + "description": "System administrators", + "membersCount": 1 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search-updated.json b/server/sonar-web/src/test/json/groups-spec/search-updated.json new file mode 100644 index 00000000000..ed1484f245d --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search-updated.json @@ -0,0 +1,19 @@ +{ + "total": 2, + "p": 1, + "ps": 50, + "groups": [ + { + "id": "1", + "name": "sonar-users", + "description": "Any new users created will automatically join this group", + "membersCount": 3 + }, + { + "id": "2", + "name": "guys", + "description": "cool guys", + "membersCount": 1 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/search.json b/server/sonar-web/src/test/json/groups-spec/search.json new file mode 100644 index 00000000000..a347403c5ae --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/search.json @@ -0,0 +1,19 @@ +{ + "total": 2, + "p": 1, + "ps": 50, + "groups": [ + { + "id": "1", + "name": "sonar-users", + "description": "Any new users created will automatically join this group", + "membersCount": 3 + }, + { + "id": "2", + "name": "sonar-administrators", + "description": "System administrators", + "membersCount": 1 + } + ] +} diff --git a/server/sonar-web/src/test/json/groups-spec/users.json b/server/sonar-web/src/test/json/groups-spec/users.json new file mode 100644 index 00000000000..dfb28ec4555 --- /dev/null +++ b/server/sonar-web/src/test/json/groups-spec/users.json @@ -0,0 +1,17 @@ +{ + "users": [ + { + "login": "bob", + "name": "Bob", + "selected": true + }, + { + "login": "john", + "name": "John", + "selected": true + } + ], + "p": 1, + "ps": 100, + "total": 2 +} diff --git a/server/sonar-web/src/test/views/groups.jade b/server/sonar-web/src/test/views/groups.jade new file mode 100644 index 00000000000..ddf0a29c6a3 --- /dev/null +++ b/server/sonar-web/src/test/views/groups.jade @@ -0,0 +1,5 @@ +extends layouts/main + +block body + #content + #groups diff --git a/server/sonar-web/src/test/views/layouts/main.jade b/server/sonar-web/src/test/views/layouts/main.jade index 901a1042055..ccc08eb3ad8 100644 --- a/server/sonar-web/src/test/views/layouts/main.jade +++ b/server/sonar-web/src/test/views/layouts/main.jade @@ -10,6 +10,7 @@ html script(src='/js/libs/third-party/latinize.js') script(src='/js/libs/third-party/underscore.js') script(src='/js/libs/third-party/backbone.js') + script(src='/js/libs/third-party/backbone-super.js') script(src='/js/libs/third-party/backbone.marionette.js') script(src='/js/libs/third-party/handlebars.js') script(src='/js/libs/third-party/underscore.js') diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java index c41ce080065..14455532309 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java @@ -19,14 +19,12 @@ */ package org.sonar.batch.protocol.output; -import org.sonar.batch.protocol.ProtobufUtil; -import org.sonar.batch.protocol.output.BatchReport.Issues; - -import javax.annotation.CheckForNull; - import java.io.File; import java.util.Collections; import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.batch.protocol.ProtobufUtil; +import org.sonar.batch.protocol.output.BatchReport.Issues; public class BatchReportReader { @@ -161,7 +159,7 @@ public class BatchReportReader { return null; } - private boolean doesFileExists(File file) { + private static boolean doesFileExists(File file) { return file.exists() && file.isFile(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java index 9118dc3450e..d10f6ba5fab 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java @@ -23,6 +23,7 @@ import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import java.util.Map; import org.sonar.api.batch.SonarIndex; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.Measure; @@ -33,8 +34,6 @@ import org.sonar.api.resources.ResourceUtils; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; -import java.util.Map; - public class DefaultFileLinesContext implements FileLinesContext { private final SonarIndex index; @@ -143,7 +142,7 @@ public class DefaultFileLinesContext implements FileLinesContext { * @see #loadData(String, Converter) * @see #save() */ - private boolean shouldSave(Map<Integer, Object> lines) { + private static boolean shouldSave(Map<Integer, Object> lines) { return !(lines instanceof ImmutableMap); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java index 446c0820244..6f17ffc0341 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/AbstractNewCoverageFileAnalyzer.java @@ -175,7 +175,7 @@ public abstract class AbstractNewCoverageFileAnalyzer implements Decorator { context.saveMeasure(newUncoveredConditions); } - private Map<Integer, Integer> parseCountByLine(@Nullable Measure measure) { + private static Map<Integer, Integer> parseCountByLine(@Nullable Measure measure) { if (measure != null && measure.hasData()) { return KeyValueFormat.parseIntInt(measure.getData()); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java index cd57819911a..53dd6a49d51 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/CountUnresolvedIssuesDecorator.java @@ -146,7 +146,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { } } - private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) { + private static void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) { if (context.getMeasure(CoreMetrics.VIOLATIONS) == null) { Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.VIOLATIONS); Double sum = MeasureUtils.sum(true, childrenIssues); @@ -161,7 +161,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { } } - private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) { + private static void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) { Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity); if (context.getMeasure(metric) == null) { Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric)); @@ -248,11 +248,11 @@ public class CountUnresolvedIssuesDecorator implements Decorator { context.saveMeasure(measure); } - private void saveMeasure(DecoratorContext context, Metric metric, int value) { + private static void saveMeasure(DecoratorContext context, Metric metric, int value) { context.saveMeasure(metric, (double) (value + sumChildren(context, metric))); } - private int sumChildren(DecoratorContext context, Metric metric) { + private static int sumChildren(DecoratorContext context, Metric metric) { int sum = 0; if (!ResourceUtils.isFile(context.getResource())) { sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue(); @@ -260,7 +260,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { return sum; } - private Multiset<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) { + private static Multiset<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) { Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity); if (rulesBag == null) { rulesBag = HashMultiset.create(); @@ -289,11 +289,11 @@ public class CountUnresolvedIssuesDecorator implements Decorator { return count; } - private boolean isAfter(Issue issue, @Nullable Date date) { + private static boolean isAfter(Issue issue, @Nullable Date date) { return date == null || (issue.creationDate() != null && DateUtils.truncatedCompareTo(issue.creationDate(), date, Calendar.SECOND) > 0); } - private boolean shouldSaveNewMetrics(DecoratorContext context) { + private static boolean shouldSaveNewMetrics(DecoratorContext context) { return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java b/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java index 1255c64a35f..68b5e1a8330 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/compute/TimeMachineConfigurationPersister.java @@ -74,7 +74,7 @@ public final class TimeMachineConfigurationPersister implements Decorator { return true; } - private void updatePeriodParams(Snapshot snapshot, PastSnapshot pastSnapshot) { + private static void updatePeriodParams(Snapshot snapshot, PastSnapshot pastSnapshot) { int periodIndex = pastSnapshot.getIndex(); snapshot.setPeriodMode(periodIndex, pastSnapshot.getMode()); snapshot.setPeriodModeParameter(periodIndex, pastSnapshot.getModeParameter()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java index c2f4c97b9a6..237f27bae67 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java @@ -21,6 +21,12 @@ package org.sonar.batch.debt; import com.google.common.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorBarriers; import org.sonar.api.batch.DecoratorContext; @@ -47,14 +53,6 @@ import org.sonar.api.rules.RuleFinder; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; @@ -182,7 +180,7 @@ public final class DebtDecorator implements Decorator { } } - private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) { + private static void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) { measure.setValue(value); if (inMemory) { measure.setPersistenceMode(PersistenceMode.MEMORY); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java index 7ce8c610ba5..dc95f8e8679 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java @@ -22,6 +22,8 @@ package org.sonar.batch.debt; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import java.util.List; +import javax.annotation.Nullable; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.debt.DebtCharacteristic; import org.sonar.api.batch.debt.DebtModel; @@ -32,9 +34,6 @@ import org.sonar.api.utils.log.Profiler; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.technicaldebt.db.CharacteristicDto; -import javax.annotation.Nullable; -import java.util.List; - public class DebtModelProvider extends ProviderAdapter { private DebtModel model; @@ -48,7 +47,7 @@ public class DebtModelProvider extends ProviderAdapter { return model; } - private DebtModel load(CharacteristicDao dao) { + private static DebtModel load(CharacteristicDao dao) { DefaultDebtModel debtModel = new DefaultDebtModel(); List<CharacteristicDto> allCharacteristics = dao.selectEnabledCharacteristics(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java index 9d6e0fca14c..fb882a7a0eb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java @@ -22,16 +22,6 @@ package org.sonar.batch.debt; import com.google.common.base.Function; import com.google.common.collect.Ordering; -import org.apache.commons.lang.time.DateUtils; -import org.sonar.api.batch.BatchSide; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.core.issue.IssueUpdater; - -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -39,6 +29,14 @@ import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.time.DateUtils; +import org.sonar.api.batch.BatchSide; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.core.issue.IssueUpdater; import static com.google.common.collect.Lists.newArrayList; @@ -82,7 +80,7 @@ public class IssueChangelogDebtCalculator { * SONAR-5059 */ @CheckForNull - private Long subtractNeverNegative(@Nullable Long value, Long with) { + private static Long subtractNeverNegative(@Nullable Long value, @Nullable Long with) { Long result = (value != null ? value : 0) - (with != null ? with : 0); return result > 0 ? result : null; } @@ -103,7 +101,7 @@ public class IssueChangelogDebtCalculator { return Collections.emptyList(); } - private List<FieldDiffs> changesOnField(Collection<FieldDiffs> fieldDiffs) { + private static List<FieldDiffs> changesOnField(Collection<FieldDiffs> fieldDiffs) { List<FieldDiffs> diffs = newArrayList(); for (FieldDiffs fieldDiff : fieldDiffs) { if (fieldDiff.diffs().containsKey(IssueUpdater.TECHNICAL_DEBT)) { @@ -114,7 +112,7 @@ public class IssueChangelogDebtCalculator { } @CheckForNull - private Long newValue(FieldDiffs fieldDiffs) { + private static Long newValue(FieldDiffs fieldDiffs) { for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) { if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) { return entry.getValue().newValueLong(); @@ -124,7 +122,7 @@ public class IssueChangelogDebtCalculator { } @CheckForNull - private Long oldValue(FieldDiffs fieldDiffs) { + private static Long oldValue(FieldDiffs fieldDiffs) { for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) { if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) { return entry.getValue().oldValueLong(); @@ -133,11 +131,11 @@ public class IssueChangelogDebtCalculator { return null; } - private boolean isAfter(@Nullable Date currentDate, @Nullable Date pastDate) { + private static boolean isAfter(@Nullable Date currentDate, @Nullable Date pastDate) { return pastDate == null || (currentDate != null && DateUtils.truncatedCompareTo(currentDate, pastDate, Calendar.SECOND) > 0); } - private boolean isLesserOrEqual(@Nullable Date currentDate, @Nullable Date pastDate) { + private static boolean isLesserOrEqual(@Nullable Date currentDate, @Nullable Date pastDate) { return (currentDate != null) && (pastDate == null || (DateUtils.truncatedCompareTo(currentDate, pastDate, Calendar.SECOND) <= 0)); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java index 9909fc47476..c334520aa79 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java @@ -21,6 +21,10 @@ package org.sonar.batch.debt; import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import javax.annotation.Nullable; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorBarriers; import org.sonar.api.batch.DecoratorContext; @@ -39,12 +43,6 @@ import org.sonar.api.resources.Resource; import org.sonar.batch.components.Period; import org.sonar.batch.components.TimeMachineConfiguration; -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.Date; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; /** @@ -109,7 +107,7 @@ public final class NewDebtDecorator implements Decorator { return result; } - private boolean shouldSaveNewMetrics(DecoratorContext context) { + private static boolean shouldSaveNewMetrics(DecoratorContext context) { return context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT) == null; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java index 666ec9b633f..38dc3bd658d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java @@ -22,6 +22,10 @@ package org.sonar.batch.debt; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.DependedUpon; @@ -35,12 +39,6 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; -import javax.annotation.Nullable; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - /** * Decorator that computes Sqale Rating metric */ @@ -95,7 +93,7 @@ public class SqaleRatingDecorator implements Decorator { } } - private Measure createRatingMeasure(int rating) { + private static Measure createRatingMeasure(int rating) { return new Measure(CoreMetrics.SQALE_RATING).setIntValue(rating).setData(toRatingLetter(rating)); } @@ -132,7 +130,7 @@ public class SqaleRatingDecorator implements Decorator { return sum; } - private long getMeasureValue(DecoratorContext context, Metric metric) { + private static long getMeasureValue(DecoratorContext context, Metric metric) { Measure measure = context.getMeasure(metric); if (measure != null) { return measure.getValue().longValue(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingSettings.java b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingSettings.java index bb186b177fc..ca80f62a96b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingSettings.java @@ -20,13 +20,12 @@ package org.sonar.batch.debt; -import org.sonar.api.batch.BatchSide; +import javax.annotation.Nullable; import org.sonar.api.CoreProperties; +import org.sonar.api.batch.BatchSide; import org.sonar.api.config.Settings; import org.sonar.api.measures.Metric; -import javax.annotation.Nullable; - @BatchSide public class SqaleRatingSettings { @@ -76,7 +75,7 @@ public class SqaleRatingSettings { return getMetricForKey(settings.getString(CoreProperties.SIZE_METRIC), metrics); } - private Metric getMetricForKey(String sizeMetricKey, Metric[] metrics) { + private static Metric getMetricForKey(String sizeMetricKey, Metric[] metrics) { for (Metric metric : metrics) { if (metric.getKey().equals(sizeMetricKey)) { return metric; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java index f17b42c18db..2586d4f898a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java @@ -21,6 +21,7 @@ package org.sonar.batch.issue; import com.google.common.base.Objects; import com.google.common.base.Strings; +import javax.annotation.Nullable; import org.sonar.api.batch.debt.DebtRemediationFunction; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; @@ -35,8 +36,6 @@ import org.sonar.api.utils.Duration; import org.sonar.api.utils.MessageException; import org.sonar.core.issue.DefaultIssueBuilder; -import javax.annotation.Nullable; - /** * Initialize the issues raised during scan. */ @@ -98,7 +97,7 @@ public class ModuleIssues { return false; } - private void validateRule(DefaultIssue issue, Rule rule) { + private static void validateRule(DefaultIssue issue, @Nullable Rule rule) { RuleKey ruleKey = issue.ruleKey(); if (rule == null) { throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java index c886d93bf51..9a8af6d87ef 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java @@ -22,6 +22,8 @@ package org.sonar.batch.rule; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import java.util.List; +import javax.annotation.Nullable; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.debt.DebtCharacteristic; import org.sonar.api.batch.debt.DebtModel; @@ -38,9 +40,6 @@ import org.sonar.core.rule.RuleDao; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; -import javax.annotation.Nullable; -import java.util.List; - /** * Loads all enabled and non manual rules */ @@ -88,7 +87,7 @@ public class RulesProvider extends ProviderAdapter { return rulesBuilder.build(); } - private DebtCharacteristic effectiveCharacteristic(RuleDto ruleDto, RuleKey ruleKey, DefaultDebtModel debtModel) { + private static DebtCharacteristic effectiveCharacteristic(RuleDto ruleDto, RuleKey ruleKey, DefaultDebtModel debtModel) { Integer subCharacteristicId = ruleDto.getSubCharacteristicId(); Integer defaultSubCharacteristicId = ruleDto.getDefaultSubCharacteristicId(); Integer effectiveSubCharacteristicId = subCharacteristicId != null ? subCharacteristicId : defaultSubCharacteristicId; @@ -121,7 +120,7 @@ public class RulesProvider extends ProviderAdapter { * Return true is the characteristic has not been overridden and a default characteristic is existing or * if the characteristic has been overridden but is not disabled */ - private boolean hasCharacteristic(RuleDto ruleDto) { + private static boolean hasCharacteristic(RuleDto ruleDto) { Integer subCharacteristicId = ruleDto.getSubCharacteristicId(); return (subCharacteristicId == null && ruleDto.getDefaultSubCharacteristicId() != null) || (subCharacteristicId != null && !RuleDto.DISABLED_CHARACTERISTIC_ID.equals(subCharacteristicId)); diff --git a/sonar-core/src/main/java/org/sonar/core/component/Module.java b/sonar-core/src/main/java/org/sonar/core/component/Module.java index 25a015e55e6..1d905291e5d 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/Module.java +++ b/sonar-core/src/main/java/org/sonar/core/component/Module.java @@ -19,6 +19,7 @@ */ package org.sonar.core.component; +import javax.annotation.Nullable; import org.sonar.core.platform.ComponentContainer; import static com.google.common.base.Preconditions.checkNotNull; @@ -36,11 +37,11 @@ public abstract class Module { protected abstract void configureModule(); - protected <T> T getComponentByType(Class<T> tClass) { - return container.getComponentByType(tClass); - } + protected void add(@Nullable Object... objects) { + if (objects == null) { + return; + } - protected void add(Object... objects) { for (Object object : objects) { if (object != null) { container.addComponent(object, true); diff --git a/sonar-core/src/main/java/org/sonar/core/component/SnapshotDto.java b/sonar-core/src/main/java/org/sonar/core/component/SnapshotDto.java index 63b5b9136fc..6d669eb1d40 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/SnapshotDto.java +++ b/sonar-core/src/main/java/org/sonar/core/component/SnapshotDto.java @@ -19,6 +19,9 @@ */ package org.sonar.core.component; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + public final class SnapshotDto { /** @@ -73,20 +76,25 @@ public final class SnapshotDto { return this; } + @CheckForNull public Long getParentId() { return parentId; } - public SnapshotDto setParentId(Long parentId) { + public SnapshotDto setParentId(@Nullable Long parentId) { this.parentId = parentId; return this; } + /** + * Root id is null on project's snapshot + */ + @CheckForNull public Long getRootId() { return rootId; } - public SnapshotDto setRootId(Long rootId) { + public SnapshotDto setRootId(@Nullable Long rootId) { this.rootId = rootId; return this; } @@ -118,11 +126,12 @@ public final class SnapshotDto { return this; } + @CheckForNull public Integer getPurgeStatus() { return purgeStatus; } - public SnapshotDto setPurgeStatus(Integer purgeStatus) { + public SnapshotDto setPurgeStatus(@Nullable Integer purgeStatus) { this.purgeStatus = purgeStatus; return this; } @@ -154,11 +163,15 @@ public final class SnapshotDto { return this; } + /** + * Version is only available on projects and modules + */ + @CheckForNull public String getVersion() { return version; } - public SnapshotDto setVersion(String version) { + public SnapshotDto setVersion(@Nullable String version) { this.version = version; return this; } @@ -190,7 +203,7 @@ public final class SnapshotDto { return this; } - public SnapshotDto setPeriodMode(int index, String p) { + public SnapshotDto setPeriodMode(int index, @Nullable String p) { switch (index) { case 1: period1Mode = p; @@ -213,6 +226,7 @@ public final class SnapshotDto { return this; } + @CheckForNull public String getPeriodMode(int index) { switch (index) { case 1: @@ -230,7 +244,7 @@ public final class SnapshotDto { } } - public SnapshotDto setPeriodParam(int index, String p) { + public SnapshotDto setPeriodParam(int index, @Nullable String p) { switch (index) { case 1: period1Param = p; @@ -253,6 +267,7 @@ public final class SnapshotDto { return this; } + @CheckForNull public String getPeriodModeParameter(int periodIndex) { switch (periodIndex) { case 1: @@ -270,7 +285,7 @@ public final class SnapshotDto { } } - public SnapshotDto setPeriodDate(int index, Long date) { + public SnapshotDto setPeriodDate(int index, @Nullable Long date) { switch (index) { case 1: period1Date = date; @@ -293,6 +308,7 @@ public final class SnapshotDto { return this; } + @CheckForNull public Long getPeriodDate(int periodIndex) { switch (periodIndex) { case 1: @@ -310,10 +326,6 @@ public final class SnapshotDto { } } - public Long getKey() { - return id; - } - public SnapshotDto setCreatedAt(Long createdAt) { this.createdAt = createdAt; return this; diff --git a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricDto.java b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricDto.java index 233e3616075..9e725c435bb 100644 --- a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricDto.java +++ b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricDto.java @@ -47,13 +47,13 @@ public class MetricDto { private Double bestValue; - private Boolean optimizedBestValue; + private boolean optimizedBestValue; private String origin; - private Boolean hidden; + private boolean hidden; - private Boolean deleteHistoricalData; + private boolean deleteHistoricalData; private boolean enabled; @@ -162,15 +162,11 @@ public class MetricDto { return this; } - /** - * @return null for manual metrics - */ - @CheckForNull - public Boolean isOptimizedBestValue() { + public boolean isOptimizedBestValue() { return optimizedBestValue; } - public MetricDto setOptimizedBestValue(@Nullable Boolean optimizedBestValue) { + public MetricDto setOptimizedBestValue(boolean optimizedBestValue) { this.optimizedBestValue = optimizedBestValue; return this; } @@ -184,28 +180,20 @@ public class MetricDto { return this; } - /** - * @return null for manual metrics - */ - @CheckForNull - public Boolean isHidden() { + public boolean isHidden() { return hidden; } - public MetricDto setHidden(@Nullable Boolean hidden) { + public MetricDto setHidden(boolean hidden) { this.hidden = hidden; return this; } - /** - * @return null for manual metrics - */ - @CheckForNull - public Boolean isDeleteHistoricalData() { + public boolean isDeleteHistoricalData() { return deleteHistoricalData; } - public MetricDto setDeleteHistoricalData(@Nullable Boolean deleteHistoricalData) { + public MetricDto setDeleteHistoricalData(boolean deleteHistoricalData) { this.deleteHistoricalData = deleteHistoricalData; return this; } diff --git a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java index 99dbf58c3e2..6857590edda 100644 --- a/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java +++ b/sonar-core/src/main/java/org/sonar/core/notification/DefaultNotificationManager.java @@ -25,6 +25,11 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; +import java.io.IOException; +import java.io.InvalidClassException; +import java.util.Arrays; +import java.util.List; +import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.RequiresDB; @@ -35,13 +40,6 @@ import org.sonar.core.notification.db.NotificationQueueDao; import org.sonar.core.notification.db.NotificationQueueDto; import org.sonar.core.properties.PropertiesDao; -import javax.annotation.Nullable; - -import java.io.IOException; -import java.io.InvalidClassException; -import java.util.Arrays; -import java.util.List; - /** * @since 2.10 */ @@ -175,7 +173,7 @@ public class DefaultNotificationManager implements NotificationManager { return Arrays.asList(notificationChannels); } - private void addUsersToRecipientListForChannel(List<String> users, SetMultimap<String, NotificationChannel> recipients, NotificationChannel channel) { + private static void addUsersToRecipientListForChannel(List<String> users, SetMultimap<String, NotificationChannel> recipients, NotificationChannel channel) { for (String username : users) { recipients.put(username, channel); } diff --git a/sonar-core/src/main/java/org/sonar/core/permission/PermissionQuery.java b/sonar-core/src/main/java/org/sonar/core/permission/PermissionQuery.java index 003469b1550..3f651d3f528 100644 --- a/sonar-core/src/main/java/org/sonar/core/permission/PermissionQuery.java +++ b/sonar-core/src/main/java/org/sonar/core/permission/PermissionQuery.java @@ -22,12 +22,10 @@ package org.sonar.core.permission; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; -import org.apache.commons.lang.StringUtils; - +import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - -import java.util.Set; +import org.apache.commons.lang.StringUtils; /** * Query used to get users and groups from a permission @@ -70,7 +68,7 @@ public class PermissionQuery { this.pageIndex = builder.pageIndex; } - private String searchToSql(@Nullable String s) { + private static String searchToSql(@Nullable String s) { String sql = null; if (s != null) { sql = StringUtils.replace(StringUtils.upperCase(s), "%", "/%"); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index 9cf108aa1ae..a07de30f906 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -35,7 +35,7 @@ import java.util.List; @ServerSide public class DatabaseVersion { - public static final int LAST_VERSION = 915; + public static final int LAST_VERSION = 916; /** * List of all the tables.n diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java index 37d41bac024..34aa6008dee 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java @@ -22,15 +22,14 @@ package org.sonar.core.resource; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import org.apache.commons.lang.StringUtils; -import org.apache.ibatis.session.SqlSession; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; - import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.session.SqlSession; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; /** * Class used to rename the key of a project and its resources. @@ -117,11 +116,11 @@ public class ResourceKeyUpdaterDao { } } - private String computeNewKey(ResourceDto resource, String stringToReplace, String replacementString) { + private static String computeNewKey(ResourceDto resource, String stringToReplace, String replacementString) { return resource.getKey().replaceAll(stringToReplace, replacementString); } - private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ResourceKeyUpdaterMapper mapper) { + private static void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ResourceKeyUpdaterMapper mapper) { for (ResourceDto resource : resources) { String resourceKey = resource.getKey(); resource.setKey(newKey + resourceKey.substring(oldKey.length(), resourceKey.length())); diff --git a/sonar-core/src/main/java/org/sonar/core/timemachine/Periods.java b/sonar-core/src/main/java/org/sonar/core/timemachine/Periods.java index cc7bb97cf8b..09214a071de 100644 --- a/sonar-core/src/main/java/org/sonar/core/timemachine/Periods.java +++ b/sonar-core/src/main/java/org/sonar/core/timemachine/Periods.java @@ -19,22 +19,20 @@ */ package org.sonar.core.timemachine; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.BatchSide; import org.sonar.api.CoreProperties; -import org.sonar.api.server.ServerSide; +import org.sonar.api.batch.BatchSide; import org.sonar.api.batch.RequiresDB; import org.sonar.api.config.Settings; import org.sonar.api.database.model.Snapshot; import org.sonar.api.i18n.I18n; - -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; +import org.sonar.api.server.ServerSide; import static org.sonar.api.utils.DateUtils.longToDate; @@ -133,7 +131,7 @@ public class Periods { } @CheckForNull - private String convertDate(Date date) { + private static String convertDate(Date date) { if (date != null) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MMM dd"); return dateFormat.format(date); @@ -141,7 +139,7 @@ public class Periods { return null; } - private Locale getLocale() { + private static Locale getLocale() { return Locale.ENGLISH; } @@ -168,7 +166,7 @@ public class Periods { } } - private Integer findByDays(String property) { + private static Integer findByDays(String property) { try { return Integer.parseInt(property); } catch (NumberFormatException e) { @@ -176,7 +174,7 @@ public class Periods { } } - private Date findByDate(String property) { + private static Date findByDate(String property) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); try { return format.parse(property); diff --git a/sonar-core/src/main/java/org/sonar/core/user/GroupMembershipQuery.java b/sonar-core/src/main/java/org/sonar/core/user/GroupMembershipQuery.java index 1fae7519f9d..75f1fdb44b0 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/GroupMembershipQuery.java +++ b/sonar-core/src/main/java/org/sonar/core/user/GroupMembershipQuery.java @@ -64,7 +64,7 @@ public class GroupMembershipQuery { this.pageIndex = builder.pageIndex; } - private String groupSearchToSql(@Nullable String s) { + private static String groupSearchToSql(@Nullable String s) { String sql = null; if (s != null) { sql = StringUtils.replace(StringUtils.upperCase(s), "%", "/%"); diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index a985006dde7..714266f33da 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -339,6 +339,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('912'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('913'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('914'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('915'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('916'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/test/java/org/sonar/core/component/ModuleTest.java b/sonar-core/src/test/java/org/sonar/core/component/ModuleTest.java new file mode 100644 index 00000000000..5a2f061a7c8 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/component/ModuleTest.java @@ -0,0 +1,80 @@ +/* + * 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.core.component; + +import org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModuleTest { + ComponentContainer container = new ComponentContainer(); + int initialSize = sizeOf(container); + + @Test(expected = NullPointerException.class) + public void configure_throws_NPE_if_container_is_empty() { + new Module() { + @Override + protected void configureModule() { + // empty + } + }.configure(null); + } + + @Test + public void module_with_empty_configureModule_method_adds_no_component() { + new Module() { + @Override + protected void configureModule() { + // empty + } + }.configure(container); + + assertThat(sizeOf(container)).isSameAs(initialSize); + } + + @Test + public void add_method_supports_null_and_adds_nothing_to_container() { + new Module() { + @Override + protected void configureModule() { + add(null); + } + }.configure(container); + + assertThat(sizeOf(container)).isEqualTo(initialSize); + } + + @Test + public void add_method_filters_out_null_inside_vararg_parameter() { + new Module() { + @Override + protected void configureModule() { + add(new Object(), null, ""); + } + }.configure(container); + + assertThat(sizeOf(container)).isEqualTo(initialSize + 2); + } + + private static int sizeOf(ComponentContainer container) { + return container.getPicoContainer().getComponentAdapters().size(); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/component/SnapshotDtoTest.java b/sonar-core/src/test/java/org/sonar/core/component/SnapshotDtoTest.java index 5e84ecbbd65..26a4aad9825 100644 --- a/sonar-core/src/test/java/org/sonar/core/component/SnapshotDtoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/component/SnapshotDtoTest.java @@ -59,7 +59,6 @@ public class SnapshotDtoTest { .setPeriodDate(5, parseDate("2014-06-05").getTime()); assertThat(snapshotDto.getId()).isEqualTo(10L); - assertThat(snapshotDto.getKey()).isEqualTo(10L); assertThat(snapshotDto.getParentId()).isEqualTo(2L); assertThat(snapshotDto.getRootId()).isEqualTo(3L); assertThat(snapshotDto.getRootProjectId()).isEqualTo(20L); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/FieldDiffs.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/FieldDiffs.java index faa300d2590..b8b6eaefc2a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/FieldDiffs.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/FieldDiffs.java @@ -22,13 +22,11 @@ package org.sonar.api.issue.internal; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Maps; - -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - import java.io.Serializable; import java.util.Date; import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; /** * PLUGINS MUST NOT USE THIS CLASS, EXCEPT FOR UNIT TESTING. @@ -168,7 +166,7 @@ public class FieldDiffs implements Serializable { } @CheckForNull - private Long toLong(Serializable value) { + private static Long toLong(@Nullable Serializable value) { if (value != null && !"".equals(value)) { try { return Long.valueOf((String) value); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageFormula.java index 64283bfdce1..976508710d6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageFormula.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageFormula.java @@ -20,9 +20,8 @@ package org.sonar.api.measures; -import org.sonar.api.resources.ResourceUtils; - import java.util.List; +import org.sonar.api.resources.ResourceUtils; import static com.google.common.collect.Lists.newArrayList; @@ -130,7 +129,7 @@ public class AverageFormula implements Formula { return result; } - private boolean shouldDecorateResource(FormulaData data, FormulaContext context) { + private static boolean shouldDecorateResource(FormulaData data, FormulaContext context) { return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric())); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java index 0014f8df527..d0489dd9a65 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java @@ -22,16 +22,10 @@ package org.sonar.api.rules; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.EqualsBuilder; -import org.apache.commons.lang.builder.HashCodeBuilder; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; -import org.sonar.api.database.DatabaseProperties; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.SonarException; -import org.sonar.check.Cardinality; - +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.persistence.Column; @@ -48,11 +42,15 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.database.DatabaseProperties; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.SonarException; +import org.sonar.check.Cardinality; @Entity @Table(name = "rules") @@ -560,7 +558,7 @@ public class Rule { } @CheckForNull - private String removeNewLineCharacters(@Nullable String text) { + private static String removeNewLineCharacters(@Nullable String text) { String removedCRLF = StringUtils.remove(text, "\n"); removedCRLF = StringUtils.remove(removedCRLF, "\r"); removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java index cc8c3b7b897..1d2abe1cdf3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleParamType.java @@ -19,12 +19,11 @@ */ package org.sonar.api.server.rule; +import java.util.List; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.sonar.api.PropertyType; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; /** @@ -75,7 +74,7 @@ public final class RuleParamType { this.multiple = multiple; } - private String valuesToCsv(String... values) { + private static String valuesToCsv(String... values) { StringBuilder sb = new StringBuilder(); for (String value : values) { sb.append(StringEscapeUtils.escapeCsv(value)); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java index 49c9c0adf2c..418caff6e84 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java @@ -19,7 +19,9 @@ */ package org.sonar.api.server.ws; +import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -33,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import org.apache.commons.io.FilenameUtils; @@ -385,6 +388,18 @@ public interface WebService extends Definable<WebService.Context> { .setDefaultValue(defaultAscending); return this; } + + /** + * Add 'selected=(selected|deselected|all)' for select-list oriented WS. + */ + public NewAction addSelectionModeParam() { + createParam(Param.SELECTED) + .setDescription("Depending on the value, show only selected items (selected=selected), deselected items (selected=deselected), " + + "or all items with their selection status (selected=all).") + .setDefaultValue(SelectionMode.SELECTED.value()) + .setPossibleValues(SelectionMode.possibleValues()); + return this; + } } @Immutable @@ -592,6 +607,36 @@ public interface WebService extends Definable<WebService.Context> { } } + enum SelectionMode { + SELECTED("selected"), DESELECTED("deselected"), ALL("all"); + + private final String paramValue; + + private static final Map<String, SelectionMode> BY_VALUE = Maps.uniqueIndex(Arrays.asList(values()), new Function<SelectionMode, String>() { + @Override + public String apply(@Nonnull SelectionMode input) { + return input.paramValue; + } + }); + + private SelectionMode(String paramValue) { + this.paramValue = paramValue; + } + + public String value() { + return paramValue; + } + + public static SelectionMode fromParam(String paramValue) { + Preconditions.checkArgument(BY_VALUE.containsKey(paramValue)); + return BY_VALUE.get(paramValue); + } + + public static Collection<String> possibleValues() { + return BY_VALUE.keySet(); + } + } + @Immutable class Param { public static final String TEXT_QUERY = "q"; @@ -601,6 +646,7 @@ public interface WebService extends Definable<WebService.Context> { public static final String SORT = "s"; public static final String ASCENDING = "asc"; public static final String FACETS = "facets"; + public static final String SELECTED = "selected"; private final String key, deprecatedKey, description, exampleValue, defaultValue; private final boolean required; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java index 6f0eccafb45..4f88279c6d5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Durations.java @@ -20,16 +20,14 @@ package org.sonar.api.utils; +import java.util.Locale; +import javax.annotation.CheckForNull; import org.sonar.api.CoreProperties; import org.sonar.api.batch.BatchSide; import org.sonar.api.config.Settings; import org.sonar.api.i18n.I18n; import org.sonar.api.server.ServerSide; -import javax.annotation.CheckForNull; - -import java.util.Locale; - /** * Used through ruby code <pre>Internal.durations</pre> * @@ -133,15 +131,15 @@ public class Durations { return i18n.message(locale, key, null, parameter); } - private boolean displayHours(int days, int hours) { + private static boolean displayHours(int days, int hours) { return hours > 0 && days < 10; } - private boolean displayMinutes(int days, int hours, int minutes) { + private static boolean displayMinutes(int days, int hours, int minutes) { return minutes > 0 && hours < 10 && days == 0; } - private void addSpaceIfNeeded(StringBuilder message) { + private static void addSpaceIfNeeded(StringBuilder message) { if (message.length() > 0) { message.append(" "); } |