diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-05-20 15:55:34 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-05-20 16:21:10 +0200 |
commit | 28d2cf2e08e4d8dd42904e8fc1db92e50905e54b (patch) | |
tree | 19dec5638bb87720ae45522ebbed1d5cad4cb659 | |
parent | e490ee5188bc25eab1cd03fbd9f2df55301db370 (diff) | |
download | sonarqube-28d2cf2e08e4d8dd42904e8fc1db92e50905e54b.tar.gz sonarqube-28d2cf2e08e4d8dd42904e8fc1db92e50905e54b.zip |
SONAR-5305 Do only one SQL to retun all measures
35 files changed, 751 insertions, 350 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDao.java b/sonar-core/src/main/java/org/sonar/api/package-info.java index c8a3e92ca88..8e7e23ac52d 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDao.java +++ b/sonar-core/src/main/java/org/sonar/api/package-info.java @@ -18,28 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.core.measure.db; +@ParametersAreNonnullByDefault +package org.sonar.api; -import org.apache.ibatis.session.SqlSession; -import org.sonar.api.ServerComponent; -import org.sonar.core.persistence.MyBatis; - -public class MeasureDao implements ServerComponent { - - private MyBatis mybatis; - - public MeasureDao(MyBatis mybatis) { - this.mybatis = mybatis; - } - - public MeasureDto findByComponentKeyAndMetricKey(String componentKey, String metricKey) { - SqlSession session = mybatis.openSession(false); - try { - MeasureMapper mapper = session.getMapper(MeasureMapper.class); - return mapper.findByComponentKeyAndMetricKey(componentKey, metricKey); - } finally { - MyBatis.closeQuietly(session); - } - } - -} +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-core/src/main/java/org/sonar/core/cluster/package-info.java b/sonar-core/src/main/java/org/sonar/core/cluster/package-info.java new file mode 100644 index 00000000000..404b0c30ca4 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/cluster/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@ParametersAreNonnullByDefault +package org.sonar.core.cluster; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java index 44bfdb9de51..3aad9a41f25 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java @@ -27,6 +27,7 @@ import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.issue.IssueQuery; +import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.rule.RuleDto; @@ -94,11 +95,11 @@ public class IssueDao implements BatchComponent, ServerComponent { /** * The returned IssueDto list contains only the issue id and the sort column */ - public List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, SqlSession session){ + public List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, SqlSession session) { return selectIssueIds(query, userId, query.maxResults(), session); } - private List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, Integer maxResults, SqlSession session){ + private List<IssueDto> selectIssueIds(IssueQuery query, @Nullable Integer userId, Integer maxResults, SqlSession session) { IssueMapper mapper = session.getMapper(IssueMapper.class); return mapper.selectIssueIds(query, query.componentRoots(), userId, query.requiredRole(), maxResults); } @@ -112,7 +113,7 @@ public class IssueDao implements BatchComponent, ServerComponent { } } - public List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, SqlSession session){ + public List<IssueDto> selectIssues(IssueQuery query, @Nullable Integer userId, SqlSession session) { IssueMapper mapper = session.getMapper(IssueMapper.class); return mapper.selectIssues(query, query.componentRoots(), userId, query.requiredRole()); } @@ -141,22 +142,12 @@ public class IssueDao implements BatchComponent, ServerComponent { } // TODO replace by aggregation in IssueIndex - public List<RuleDto> findRulesByComponent(String componentKey) { - SqlSession session = mybatis.openSession(false); - try { - return session.getMapper(IssueMapper.class).findRulesByComponent(componentKey); - } finally { - MyBatis.closeQuietly(session); - } + public List<RuleDto> findRulesByComponent(String componentKey, DbSession session) { + return session.getMapper(IssueMapper.class).findRulesByComponent(componentKey); } // TODO replace by aggregation in IssueIndex - public List<String> findSeveritiesByComponent(String componentKey) { - SqlSession session = mybatis.openSession(false); - try { - return session.getMapper(IssueMapper.class).findSeveritiesByComponent(componentKey); - } finally { - MyBatis.closeQuietly(session); - } + public List<String> findSeveritiesByComponent(String componentKey, DbSession session) { + return session.getMapper(IssueMapper.class).findSeveritiesByComponent(componentKey); } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java index edbfe96b28a..886a06d31ee 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java @@ -21,15 +21,18 @@ package org.sonar.core.measure.db; import com.google.common.base.Charsets; +import org.sonar.core.persistence.Dto; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -public class MeasureDto { +public class MeasureDto extends Dto<MeasureKey>{ private Integer id; - private Integer snapshotId; + private String metricKey; + + private String componentKey; private Double value; @@ -37,6 +40,10 @@ public class MeasureDto { private byte[] data; + private MeasureDto(){ + // Nothing here + } + public Integer getId() { return id; } @@ -46,12 +53,13 @@ public class MeasureDto { return this; } - public Integer getSnapshotId() { - return snapshotId; + private MeasureDto setMetricKey(String metricKey) { + this.metricKey = metricKey; + return this; } - public MeasureDto setSnapshotId(Integer snapshotId) { - this.snapshotId = snapshotId; + private MeasureDto setComponentKey(String componentKey) { + this.componentKey = componentKey; return this; } @@ -83,4 +91,15 @@ public class MeasureDto { } return textValue; } + + @Override + public MeasureKey getKey() { + return MeasureKey.of(componentKey, metricKey); + } + + public static MeasureDto createFor(MeasureKey key){ + return new MeasureDto() + .setComponentKey(key.componentKey()) + .setMetricKey(key.metricKey()); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureKey.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureKey.java new file mode 100644 index 00000000000..2945d9845b3 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureKey.java @@ -0,0 +1,79 @@ +/* + * 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.measure.db; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import java.io.Serializable; + +public class MeasureKey implements Serializable { + + private final String componentKey; + private final String metricKey; + + private MeasureKey(String componentKey, String metricKey) { + this.componentKey = componentKey; + this.metricKey = metricKey; + } + + public String componentKey() { + return componentKey; + } + + public String metricKey() { + return metricKey; + } + + public static MeasureKey of(String componentKey, String metricKey) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(componentKey), "Component key must be set"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(metricKey), "Metric key must be set"); + return new MeasureKey(componentKey, metricKey); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MeasureKey that = (MeasureKey) o; + + if (!componentKey.equals(that.componentKey)) { + return false; + } + if (!metricKey.equals(that.metricKey)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = componentKey.hashCode(); + result = 31 * result + metricKey.hashCode(); + return result; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java index fd27c9b1d82..fc3db6097b4 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java @@ -22,8 +22,12 @@ package org.sonar.core.measure.db; import org.apache.ibatis.annotations.Param; +import java.util.List; + public interface MeasureMapper { - MeasureDto findByComponentKeyAndMetricKey(@Param("componentKey") String componentKey, @Param("metricKey") String metricKey); + MeasureDto selectByKey(@Param("key") MeasureKey key); + + List<MeasureDto> selectByComponentAndMetrics(@Param("componentKey") String componentKey, @Param("metricKeys") List<String> metricKeys); } diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java index 771c8134ef6..7d5247d3fe3 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java @@ -24,14 +24,7 @@ import org.sonar.core.dashboard.ActiveDashboardDao; import org.sonar.core.dashboard.DashboardDao; import org.sonar.core.duplication.DuplicationDao; import org.sonar.core.graph.jdbc.GraphDao; -import org.sonar.core.issue.db.ActionPlanDao; -import org.sonar.core.issue.db.ActionPlanStatsDao; -import org.sonar.core.issue.db.IssueChangeDao; -import org.sonar.core.issue.db.IssueDao; -import org.sonar.core.issue.db.IssueFilterDao; -import org.sonar.core.issue.db.IssueFilterFavouriteDao; -import org.sonar.core.issue.db.IssueStatsDao; -import org.sonar.core.measure.db.MeasureDao; +import org.sonar.core.issue.db.*; import org.sonar.core.notification.db.NotificationQueueDao; import org.sonar.core.permission.PermissionDao; import org.sonar.core.permission.PermissionTemplateDao; @@ -48,11 +41,7 @@ import org.sonar.core.source.db.SnapshotSourceDao; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.technicaldebt.db.RequirementDao; import org.sonar.core.template.LoadedTemplateDao; -import org.sonar.core.user.AuthorDao; -import org.sonar.core.user.AuthorizationDao; -import org.sonar.core.user.GroupMembershipDao; -import org.sonar.core.user.RoleDao; -import org.sonar.core.user.UserDao; +import org.sonar.core.user.*; import java.util.List; @@ -81,7 +70,6 @@ public final class DaoUtils { IssueFilterDao.class, IssueFilterFavouriteDao.class, LoadedTemplateDao.class, - MeasureDao.class, NotificationQueueDao.class, PermissionDao.class, PermissionTemplateDao.class, diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java index 5ac44bf1c4e..785416c5bad 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java @@ -24,6 +24,7 @@ import com.google.common.base.Strings; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; +import org.sonar.api.DaoComponent; import org.sonar.api.ServerComponent; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; @@ -33,7 +34,7 @@ import javax.annotation.Nullable; import java.util.List; import java.util.Map; -public class PropertiesDao implements BatchComponent, ServerComponent { +public class PropertiesDao implements BatchComponent, ServerComponent, DaoComponent { private static final String NOTIFICATION_PREFIX = "notification."; private MyBatis mybatis; @@ -111,14 +112,8 @@ public class PropertiesDao implements BatchComponent, ServerComponent { } } - public List<PropertyDto> selectByQuery(PropertyQuery query) { - SqlSession session = mybatis.openSession(false); - PropertiesMapper mapper = session.getMapper(PropertiesMapper.class); - try { - return mapper.selectByQuery(query); - } finally { - MyBatis.closeQuietly(session); - } + public List<PropertyDto> selectByQuery(PropertyQuery query, DbSession session) { + return session.getMapper(PropertiesMapper.class).selectByQuery(query); } public void setProperty(PropertyDto property, SqlSession session) { diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index 3d6ac2c7deb..80059b30e91 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -23,8 +23,10 @@ import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.ibatis.session.SqlSession; +import org.sonar.api.DaoComponent; import org.sonar.api.component.Component; import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; @@ -37,7 +39,7 @@ import java.util.List; import static com.google.common.collect.Lists.newArrayList; -public class ResourceDao { +public class ResourceDao implements DaoComponent { private MyBatis mybatis; public ResourceDao(MyBatis mybatis) { @@ -108,16 +110,6 @@ public class ResourceDao { } @CheckForNull - public SnapshotDto getLastSnapshotByResourceId(long resourceId) { - SqlSession session = mybatis.openSession(false); - try { - return getLastSnapshotByResourceId(resourceId, session); - } finally { - MyBatis.closeQuietly(session); - } - } - - @CheckForNull public SnapshotDto getLastSnapshotByResourceId(long resourceId, SqlSession session) { return session.getMapper(ResourceMapper.class).selectLastSnapshotByResourceId(resourceId); } @@ -184,13 +176,8 @@ public class ResourceDao { } @CheckForNull - public ComponentDto selectComponentByKey(String key) { - SqlSession session = mybatis.openSession(false); - try { - return session.getMapper(ResourceMapper.class).selectComponentByKey(key); - } finally { - MyBatis.closeQuietly(session); - } + public ComponentDto selectComponentByKey(String key, DbSession session) { + return session.getMapper(ResourceMapper.class).selectComponentByKey(key); } @CheckForNull @@ -200,16 +187,6 @@ public class ResourceDao { } @CheckForNull - public Component findById(Long id) { - SqlSession session = mybatis.openSession(false); - try { - return findById(id, session); - } finally { - MyBatis.closeQuietly(session); - } - } - - @CheckForNull public Component findById(Long id, SqlSession session) { ResourceDto resourceDto = getResource(id, session); return resourceDto != null ? toComponent(resourceDto) : null; diff --git a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml index e4770360adb..e7553613f56 100644 --- a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml @@ -8,19 +8,37 @@ pm.snapshot_id as snapshotId, pm.value as value, pm.text_value as textValue, - pm.measure_data as data + pm.measure_data as data, + p.kee as componentKey, + metric.name as metricKey </sql> - <select id="findByComponentKeyAndMetricKey" parameterType="map" resultType="Measure"> + <select id="selectByKey" parameterType="map" resultType="Measure"> SELECT <include refid="measureColumns"/> FROM project_measures pm INNER JOIN snapshots s ON s.id=pm.snapshot_id AND s.islast=${_true} + INNER JOIN metrics metric ON metric.id=pm.metric_id + INNER JOIN projects p ON p.id=s.project_id AND p.enabled=${_true} + <where> + AND p.kee = #{key.componentKey} + AND metric.name = #{key.metricKey} + AND pm.rule_id IS NULL + AND pm.characteristic_id IS NULL + AND pm.person_id IS NULL + </where> + </select> + + <select id="selectByComponentAndMetrics" parameterType="map" resultType="Measure"> + SELECT metric.name as metric_name, + <include refid="measureColumns"/> + FROM project_measures pm + INNER JOIN snapshots s ON s.id=pm.snapshot_id AND s.islast=${_true} INNER JOIN projects p ON p.id=s.project_id AND p.enabled=${_true} INNER JOIN metrics metric ON metric.id=pm.metric_id <where> AND p.kee = #{componentKey} - AND metric.name = #{metricKey} + AND <foreach item="metricKey" index="index" collection="metricKeys" open="(" separator=" or " close=")">metric.name=#{metricKey}</foreach> AND pm.rule_id IS NULL AND pm.characteristic_id IS NULL AND pm.person_id IS NULL diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java index cf8dc4a3d3b..4f2f0e2c528 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java @@ -23,6 +23,7 @@ package org.sonar.core.issue.db; import com.google.common.base.Function; import com.google.common.collect.Iterables; import org.apache.ibatis.executor.result.DefaultResultHandler; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.sonar.api.issue.IssueQuery; @@ -30,6 +31,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.utils.DateUtils; import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.DbSession; import org.sonar.core.rule.RuleDto; import java.util.List; @@ -40,13 +42,21 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueDaoTest extends AbstractDaoTestCase { + DbSession session; + IssueDao dao; @Before public void createDao() { + session = getMyBatis().openSession(false); dao = new IssueDao(getMyBatis()); } + @After + public void tearDown() throws Exception { + session.close(); + } + @Test public void should_select_by_key() { setupData("shared", "should_select_by_key"); @@ -434,7 +444,7 @@ public class IssueDaoTest extends AbstractDaoTestCase { public void find_rules_by_component() { setupData("shared", "find_rules_by_component"); - List<RuleDto> results = dao.findRulesByComponent("Action.java"); + List<RuleDto> results = dao.findRulesByComponent("Action.java", session); assertThat(results).hasSize(3); } @@ -442,7 +452,7 @@ public class IssueDaoTest extends AbstractDaoTestCase { public void find_severities_by_component() { setupData("shared", "find_severities_by_component"); - List<String> results = dao.findSeveritiesByComponent("Action.java"); + List<String> results = dao.findSeveritiesByComponent("Action.java", session); assertThat(results).containsExactly(Severity.BLOCKER, Severity.MAJOR, Severity.BLOCKER); } diff --git a/sonar-core/src/test/java/org/sonar/core/measure/db/MeasureDaoTest.java b/sonar-core/src/test/java/org/sonar/core/measure/db/MeasureDaoTest.java deleted file mode 100644 index e00ccc5cdf9..00000000000 --- a/sonar-core/src/test/java/org/sonar/core/measure/db/MeasureDaoTest.java +++ /dev/null @@ -1,69 +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.core.measure.db; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.core.persistence.AbstractDaoTestCase; - -import static org.fest.assertions.Assertions.assertThat; - -public class MeasureDaoTest extends AbstractDaoTestCase { - - private MeasureDao dao; - - @Before - public void createDao() { - dao = new MeasureDao(getMyBatis()); - } - - @Test - public void find_value_by_component_key_and_metric_key() throws Exception { - setupData("shared"); - - MeasureDto result = dao.findByComponentKeyAndMetricKey("org.struts:struts-core:src/org/struts/RequestContext.java", "ncloc"); - assertThat(result.getId()).isEqualTo(22); - assertThat(result.getSnapshotId()).isEqualTo(5); - assertThat(result.getValue()).isEqualTo(10d); - } - - @Test - public void find_data_by_component_key_and_metric_key() throws Exception { - setupData("shared"); - - MeasureDto result = dao.findByComponentKeyAndMetricKey("org.struts:struts-core:src/org/struts/RequestContext.java", "authors_by_line"); - assertThat(result.getId()).isEqualTo(20); - assertThat(result.getSnapshotId()).isEqualTo(5); - assertThat(result.getData()).isNotNull(); - - assertThat(result.getData()).isEqualTo("0123456789012345678901234567890123456789"); - } - - @Test - public void find_text_value_by_component_key_and_metric_key() throws Exception { - setupData("shared"); - - MeasureDto result = dao.findByComponentKeyAndMetricKey("org.struts:struts-core:src/org/struts/RequestContext.java", "coverage_line_hits_data"); - assertThat(result.getId()).isEqualTo(21); - assertThat(result.getSnapshotId()).isEqualTo(5); - assertThat(result.getData()).isEqualTo("36=1;37=1;38=1;39=1;43=1;48=1;53=1"); - } -} diff --git a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java index 9e902a97357..ce832311193 100644 --- a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java @@ -25,6 +25,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.DbSession; import java.util.List; @@ -34,6 +35,8 @@ import static org.junit.Assert.assertThat; public class PropertiesDaoTest extends AbstractDaoTestCase { + private DbSession session; + private PropertiesDao dao; @Rule @@ -42,6 +45,7 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { @Before public void createDao() { dao = new PropertiesDao(getMyBatis()); + session = getMyBatis().openSession(false); } @Test @@ -146,11 +150,11 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { public void select_by_query() { setupData("select_by_query"); - List<PropertyDto> results = dao.selectByQuery(PropertyQuery.builder().setKey("user.two").setComponentId(10L).setUserId(100).build()); + List<PropertyDto> results = dao.selectByQuery(PropertyQuery.builder().setKey("user.two").setComponentId(10L).setUserId(100).build(), session); assertThat(results).hasSize(1); assertThat(results.get(0).getValue()).isEqualTo("two"); - results = dao.selectByQuery(PropertyQuery.builder().setKey("user.one").setUserId(100).build()); + results = dao.selectByQuery(PropertyQuery.builder().setKey("user.one").setUserId(100).build(), session); assertThat(results).hasSize(1); assertThat(results.get(0).getValue()).isEqualTo("one"); } diff --git a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java index c40a43ea280..fe284fbc8d8 100644 --- a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java @@ -29,6 +29,7 @@ import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.DbSession; import javax.annotation.Nullable; @@ -41,10 +42,13 @@ import static org.fest.assertions.Assertions.assertThat; public class ResourceDaoTest extends AbstractDaoTestCase { + private DbSession session; + private ResourceDao dao; @Before public void createDao() { + session = getMyBatis().openSession(false); dao = new ResourceDao(getMyBatis()); } @@ -213,8 +217,8 @@ public class ResourceDaoTest extends AbstractDaoTestCase { public void select_component_by_key() { setupData("fixture"); - assertThat(dao.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java")).isNotNull(); - assertThat(dao.selectComponentByKey("unknown")).isNull(); + assertThat(dao.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java", session)).isNotNull(); + assertThat(dao.selectComponentByKey("unknown", session)).isNull(); } @Test @@ -377,9 +381,9 @@ public class ResourceDaoTest extends AbstractDaoTestCase { public void should_find_component_by_id() { setupData("fixture"); - assertThat(dao.findById(1L)).isNotNull(); - assertThat(dao.findById(4L)).isNotNull(); - assertThat(dao.findById(555L)).isNull(); + assertThat(dao.findById(1L, session)).isNotNull(); + assertThat(dao.findById(4L, session)).isNotNull(); + assertThat(dao.findById(555L, session)).isNull(); } @Test @@ -443,16 +447,16 @@ public class ResourceDaoTest extends AbstractDaoTestCase { public void get_last_snapshot_by_resource_id() { setupData("fixture"); - SnapshotDto snapshotDto = dao.getLastSnapshotByResourceId(1L); + SnapshotDto snapshotDto = dao.getLastSnapshotByResourceId(1L, session); assertThat(snapshotDto.getId()).isEqualTo(1); - snapshotDto = dao.getLastSnapshotByResourceId(2L); + snapshotDto = dao.getLastSnapshotByResourceId(2L, session); assertThat(snapshotDto.getId()).isEqualTo(2L); - snapshotDto = dao.getLastSnapshotByResourceId(3L); + snapshotDto = dao.getLastSnapshotByResourceId(3L, session); assertThat(snapshotDto.getId()).isEqualTo(3L); - assertThat(dao.getLastSnapshotByResourceId(42L)).isNull(); + assertThat(dao.getLastSnapshotByResourceId(42L, session)).isNull(); } private List<String> getKeys(final List<Component> components) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index 6399bff81b3..97571eb20ad 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -20,10 +20,14 @@ package org.sonar.api.measures; import com.google.common.annotations.Beta; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.sonar.api.resources.Scopes; import org.sonar.api.utils.SonarException; +import javax.annotation.Nullable; + import java.lang.reflect.Field; import java.util.List; @@ -2286,4 +2290,13 @@ public final class CoreMetrics { public static List<Metric> getMetrics() { return METRICS; } + + public static Metric getMetric(final String key) { + return Iterables.find(METRICS, new Predicate<Metric>() { + @Override + public boolean apply(@Nullable Metric input) { + return input != null && input.getKey().equals(key); + } + }); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java b/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java index 786d1648bc4..cfa1d0bf6c8 100644 --- a/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java +++ b/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java @@ -44,7 +44,7 @@ public class LocalNonBlockingWorkQueue extends LinkedBlockingQueue<Runnable> this.offer(action, 1000, TimeUnit.SECONDS); latch.await(1500, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - throw new IllegalStateException("ES update has been interrupted: "); + throw new IllegalStateException("ES update has been interrupted", e); } } @@ -58,7 +58,7 @@ public class LocalNonBlockingWorkQueue extends LinkedBlockingQueue<Runnable> } latch.await(1500, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - throw new IllegalStateException("ES update has been interrupted: "); + throw new IllegalStateException("ES update has been interrupted", e); } } } diff --git a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java index 2535694121c..e632b6eef4b 100644 --- a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java +++ b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java @@ -20,6 +20,8 @@ package org.sonar.server.component.ws; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Multiset; import com.google.common.io.Resources; import org.sonar.api.component.Component; @@ -35,17 +37,20 @@ import org.sonar.api.utils.Durations; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; -import org.sonar.core.measure.db.MeasureDao; import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.properties.PropertyDto; import org.sonar.core.properties.PropertyQuery; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; import org.sonar.core.timemachine.Periods; +import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.RulesAggregation; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -54,22 +59,27 @@ import javax.annotation.Nullable; import java.util.Date; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; + public class ComponentAppAction implements RequestHandler { private static final String KEY = "key"; + private final DbClient dbClient; + private final ResourceDao resourceDao; - private final MeasureDao measureDao; private final PropertiesDao propertiesDao; + private final MeasureDao measureDao; private final IssueService issueService; private final Periods periods; private final Durations durations; private final I18n i18n; - public ComponentAppAction(ResourceDao resourceDao, MeasureDao measureDao, PropertiesDao propertiesDao, IssueService issueService, Periods periods, Durations durations, I18n i18n) { - this.resourceDao = resourceDao; - this.measureDao = measureDao; - this.propertiesDao = propertiesDao; + public ComponentAppAction(DbClient dbClient, IssueService issueService, Periods periods, Durations durations, I18n i18n) { + this.dbClient = dbClient; + this.resourceDao = dbClient.getDao(ResourceDao.class); + this.propertiesDao = dbClient.getDao(PropertiesDao.class); + this.measureDao = dbClient.getDao(MeasureDao.class); this.issueService = issueService; this.periods = periods; this.durations = durations; @@ -100,59 +110,74 @@ public class ComponentAppAction implements RequestHandler { JsonWriter json = response.newJsonWriter(); json.beginObject(); - ComponentDto component = resourceDao.selectComponentByKey(fileKey); - if (component == null) { - throw new NotFoundException(String.format("Component '%s' does not exists.", fileKey)); - } - Long projectId = component.projectId(); - Long subProjectId = component.subProjectId(); - // projectId and subProjectId can't be null here - if (projectId != null && subProjectId != null) { - List<PropertyDto> propertyDtos = propertiesDao.selectByQuery(PropertyQuery.builder() - .setKey("favourite") - .setComponentId(component.getId()) - .setUserId(userSession.userId()) - .build()); - boolean isFavourite = propertyDtos.size() == 1; - - json.prop("key", component.key()); - json.prop("path", component.path()); - json.prop("name", component.name()); - json.prop("q", component.qualifier()); - - Component subProject = componentById(subProjectId); - json.prop("subProjectName", subProject != null ? subProject.longName() : null); - - Component project = componentById(projectId); - json.prop("projectName", project != null ? project.longName() : null); - - json.prop("fav", isFavourite); - appendPeriods(json, projectId); - appendRulesAggregation(json, component.key()); - appendMeasures(json, fileKey); + DbSession session = dbClient.openSession(false); + try { + ComponentDto component = resourceDao.selectComponentByKey(fileKey, session); + if (component == null) { + throw new NotFoundException(String.format("Component '%s' does not exists.", fileKey)); + } + Long projectId = component.projectId(); + Long subProjectId = component.subProjectId(); + // projectId and subProjectId can't be null here + if (projectId != null && subProjectId != null) { + List<PropertyDto> propertyDtos = propertiesDao.selectByQuery(PropertyQuery.builder() + .setKey("favourite") + .setComponentId(component.getId()) + .setUserId(userSession.userId()) + .build(), + session + ); + boolean isFavourite = propertyDtos.size() == 1; + + json.prop("key", component.key()); + json.prop("path", component.path()); + json.prop("name", component.name()); + json.prop("q", component.qualifier()); + + Component subProject = componentById(subProjectId, session); + json.prop("subProjectName", subProject != null ? subProject.longName() : null); + + Component project = componentById(projectId, session); + json.prop("projectName", project != null ? project.longName() : null); + + json.prop("fav", isFavourite); + appendPeriods(json, projectId, session); + appendRulesAggregation(json, component.key(), session); + appendMeasures(json, fileKey, session); + } + } finally { + MyBatis.closeQuietly(session); } + json.endObject(); json.close(); } - private void appendMeasures(JsonWriter json, String fileKey) { + private void appendMeasures(JsonWriter json, String fileKey, DbSession session) { json.name("measures").beginObject(); - json.prop("fNcloc", formattedMeasure(fileKey, CoreMetrics.NCLOC)); - json.prop("fCoverage", formattedMeasure(fileKey, CoreMetrics.COVERAGE)); - json.prop("fDuplicationDensity", formattedMeasure(fileKey, CoreMetrics.DUPLICATED_LINES_DENSITY)); - json.prop("fDebt", formattedMeasure(fileKey, CoreMetrics.TECHNICAL_DEBT)); - json.prop("fIssues", formattedMeasure(fileKey, CoreMetrics.VIOLATIONS)); - json.prop("fBlockerIssues", formattedMeasure(fileKey, CoreMetrics.BLOCKER_VIOLATIONS)); - json.prop("fCriticalIssues", formattedMeasure(fileKey, CoreMetrics.CRITICAL_VIOLATIONS)); - json.prop("fMajorIssues", formattedMeasure(fileKey, CoreMetrics.MAJOR_VIOLATIONS)); - json.prop("fMinorIssues", formattedMeasure(fileKey, CoreMetrics.MINOR_VIOLATIONS)); - json.prop("fInfoIssues", formattedMeasure(fileKey, CoreMetrics.INFO_VIOLATIONS)); + + List<MeasureDto> measures = measureDao.findByComponentKeyAndMetricKeys(fileKey, + newArrayList(CoreMetrics.NCLOC_KEY, CoreMetrics.COVERAGE_KEY, CoreMetrics.DUPLICATED_LINES_DENSITY_KEY, CoreMetrics.TECHNICAL_DEBT_KEY, CoreMetrics.VIOLATIONS_KEY, + CoreMetrics.BLOCKER_VIOLATIONS_KEY, CoreMetrics.MAJOR_VIOLATIONS_KEY, CoreMetrics.MAJOR_VIOLATIONS_KEY, CoreMetrics.MINOR_VIOLATIONS_KEY, CoreMetrics.INFO_VIOLATIONS_KEY), + session + ); + + json.prop("fNcloc", formattedMeasure(CoreMetrics.NCLOC_KEY, measures)); + json.prop("fCoverage", formattedMeasure(CoreMetrics.COVERAGE_KEY, measures)); + json.prop("fDuplicationDensity", formattedMeasure(CoreMetrics.DUPLICATED_LINES_DENSITY_KEY, measures)); + json.prop("fDebt", formattedMeasure(CoreMetrics.TECHNICAL_DEBT_KEY, measures)); + json.prop("fIssues", formattedMeasure(CoreMetrics.VIOLATIONS_KEY, measures)); + json.prop("fBlockerIssues", formattedMeasure(CoreMetrics.BLOCKER_VIOLATIONS_KEY, measures)); + json.prop("fCriticalIssues", formattedMeasure(CoreMetrics.CRITICAL_VIOLATIONS_KEY, measures)); + json.prop("fMajorIssues", formattedMeasure(CoreMetrics.MAJOR_VIOLATIONS_KEY, measures)); + json.prop("fMinorIssues", formattedMeasure(CoreMetrics.MINOR_VIOLATIONS_KEY, measures)); + json.prop("fInfoIssues", formattedMeasure(CoreMetrics.INFO_VIOLATIONS_KEY, measures)); json.endObject(); } - private void appendPeriods(JsonWriter json, Long projectId) { + private void appendPeriods(JsonWriter json, Long projectId, DbSession session) { json.name("periods").beginArray(); - SnapshotDto snapshotDto = resourceDao.getLastSnapshotByResourceId(projectId); + SnapshotDto snapshotDto = resourceDao.getLastSnapshotByResourceId(projectId, session); if (snapshotDto != null) { for (int i = 1; i <= 5; i++) { String mode = snapshotDto.getPeriodMode(i); @@ -172,9 +197,9 @@ public class ComponentAppAction implements RequestHandler { json.endArray(); } - private void appendRulesAggregation(JsonWriter json, String componentKey) { + private void appendRulesAggregation(JsonWriter json, String componentKey, DbSession session) { json.name("severities").beginArray(); - Multiset<String> severities = issueService.findSeveritiesByComponent(componentKey); + Multiset<String> severities = issueService.findSeveritiesByComponent(componentKey, session); for (String severity : severities.elementSet()) { json.beginArray() .value(severity) @@ -185,7 +210,7 @@ public class ComponentAppAction implements RequestHandler { json.endArray(); json.name("rules").beginArray(); - RulesAggregation rulesAggregation = issueService.findRulesByComponent(componentKey); + RulesAggregation rulesAggregation = issueService.findRulesByComponent(componentKey, session); for (RulesAggregation.Rule rule : rulesAggregation.rules()) { json.beginArray() .value(rule.ruleKey().toString()) @@ -197,18 +222,19 @@ public class ComponentAppAction implements RequestHandler { } @CheckForNull - private Component componentById(@Nullable Long componentId) { + private Component componentById(@Nullable Long componentId, DbSession session) { if (componentId != null) { - return resourceDao.findById(componentId); + return resourceDao.findById(componentId, session); } return null; } @CheckForNull - private String formattedMeasure(String fileKey, Metric metric) { - MeasureDto measureDto = measureDao.findByComponentKeyAndMetricKey(fileKey, metric.getKey()); - if (measureDto != null) { - Double value = measureDto.getValue(); + private String formattedMeasure(final String metricKey, List<MeasureDto> measures) { + MeasureDto measure = measureByMetricKey(metricKey, measures); + if (measure != null) { + Metric metric = CoreMetrics.getMetric(measure.getKey().metricKey()); + Double value = measure.getValue(); if (value != null) { if (metric.getType().equals(Metric.ValueType.FLOAT)) { return i18n.formatDouble(UserSession.get().locale(), value); @@ -224,4 +250,14 @@ public class ComponentAppAction implements RequestHandler { return null; } + @CheckForNull + private static MeasureDto measureByMetricKey(final String metricKey, List<MeasureDto> measures) { + return Iterables.find(measures, new Predicate<MeasureDto>() { + @Override + public boolean apply(@Nullable MeasureDto input) { + return input != null && metricKey.equals(input.getKey().metricKey()); + } + }, null); + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java index 47e2df76129..d50ee8711be 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java +++ b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java @@ -22,8 +22,8 @@ package org.sonar.server.db; import com.google.common.base.Preconditions; import org.sonar.api.DaoComponent; import org.sonar.api.utils.System2; -import org.sonar.core.persistence.Dto; import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.Dto; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.search.IndexDefinition; import org.sonar.server.search.action.DtoIndexAction; @@ -31,6 +31,8 @@ import org.sonar.server.search.action.EmbeddedIndexAction; import org.sonar.server.search.action.IndexAction; import org.sonar.server.search.action.KeyIndexAction; +import javax.annotation.Nullable; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -107,18 +109,26 @@ import java.util.List; */ public abstract class BaseDao<M, E extends Dto<K>, K extends Serializable> implements Dao<E, K>, DaoComponent { - protected final IndexDefinition indexDefinition; + protected IndexDefinition indexDefinition; private Class<M> mapperClass; private System2 system2; - protected BaseDao(IndexDefinition indexDefinition, Class<M> mapperClass, System2 system2) { + private boolean hasIndex() { + return indexDefinition != null; + } + + protected BaseDao(@Nullable IndexDefinition indexDefinition, Class<M> mapperClass, System2 system2) { this.mapperClass = mapperClass; this.indexDefinition = indexDefinition; this.system2 = system2; } + protected BaseDao(Class<M> mapperClass, System2 system2) { + this(null, mapperClass, system2); + } + public String getIndexType() { - return this.indexDefinition.getIndexType(); + return indexDefinition != null ? this.indexDefinition.getIndexType() : null; } protected abstract E doGetByKey(K key, DbSession session); @@ -149,7 +159,9 @@ public abstract class BaseDao<M, E extends Dto<K>, K extends Serializable> imple public E update(E item, DbSession session) { item.setUpdatedAt(new Date(system2.now())); this.doUpdate(item, session); - session.enqueue(new DtoIndexAction<E>(this.getIndexType(), IndexAction.Method.UPDATE, item)); + if (hasIndex()) { + session.enqueue(new DtoIndexAction<E>(this.getIndexType(), IndexAction.Method.UPDATE, item)); + } return item; } @@ -168,7 +180,9 @@ public abstract class BaseDao<M, E extends Dto<K>, K extends Serializable> imple item.setCreatedAt(new Date(system2.now())); item.setUpdatedAt(item.getCreatedAt()); this.doInsert(item, session); - session.enqueue(new DtoIndexAction<E>(this.getIndexType(), IndexAction.Method.INSERT, item)); + if (hasIndex()) { + session.enqueue(new DtoIndexAction<E>(this.getIndexType(), IndexAction.Method.INSERT, item)); + } return item; } @@ -199,21 +213,29 @@ public abstract class BaseDao<M, E extends Dto<K>, K extends Serializable> imple public void deleteByKey(K key, DbSession session) { Preconditions.checkNotNull(key, "cannot delete Item with null key"); doDeleteByKey(key, session); - session.enqueue(new KeyIndexAction<K>(this.getIndexType(), IndexAction.Method.DELETE, key)); + if (hasIndex()) { + session.enqueue(new KeyIndexAction<K>(this.getIndexType(), IndexAction.Method.DELETE, key)); + } } protected void enqueueUpdate(Object nestedItem, K key, DbSession session) { - session.enqueue(new EmbeddedIndexAction<K>( - this.getIndexType(), IndexAction.Method.UPDATE, nestedItem, key)); + if (hasIndex()) { + session.enqueue(new EmbeddedIndexAction<K>( + this.getIndexType(), IndexAction.Method.UPDATE, nestedItem, key)); + } } public void enqueueDelete(Object nestedItem, K key, DbSession session) { - session.enqueue(new EmbeddedIndexAction<K>( - this.getIndexType(), IndexAction.Method.DELETE, nestedItem, key)); + if (hasIndex()) { + session.enqueue(new EmbeddedIndexAction<K>( + this.getIndexType(), IndexAction.Method.DELETE, nestedItem, key)); + } } public void enqueueInsert(Object nestedItem, K key, DbSession session) { - session.enqueue(new EmbeddedIndexAction<K>( - this.getIndexType(), IndexAction.Method.INSERT, nestedItem, key)); + if (hasIndex()) { + session.enqueue(new EmbeddedIndexAction<K>( + this.getIndexType(), IndexAction.Method.INSERT, nestedItem, key)); + } } } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index 774bca951be..29f7dece639 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -45,6 +45,7 @@ import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; import org.sonar.core.preview.PreviewCache; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; @@ -280,18 +281,18 @@ public class IssueService implements ServerComponent { } // TODO result should be replaced by an aggregation object in IssueIndex - public RulesAggregation findRulesByComponent(String componentKey) { + public RulesAggregation findRulesByComponent(String componentKey, DbSession session) { RulesAggregation rulesAggregation = new RulesAggregation(); - for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey)) { + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, session)) { rulesAggregation.add(ruleDto); } return rulesAggregation; } // TODO result should be replaced by an aggregation object in IssueIndex - public Multiset<String> findSeveritiesByComponent(String componentKey) { + public Multiset<String> findSeveritiesByComponent(String componentKey, DbSession session) { Multiset<String> aggregation = HashMultiset.create(); - for (String severity : issueDao.findSeveritiesByComponent(componentKey)) { + for (String severity : issueDao.findSeveritiesByComponent(componentKey, session)) { aggregation.add(severity); } return aggregation; diff --git a/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java b/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java new file mode 100644 index 00000000000..9303d974145 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java @@ -0,0 +1,86 @@ +/* + * 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.measure.persistence; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import org.sonar.api.DaoComponent; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.System2; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.measure.db.MeasureMapper; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.BaseDao; + +import java.util.Collections; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +public class MeasureDao extends BaseDao<MeasureMapper, MeasureDto, MeasureKey> implements ServerComponent, DaoComponent { + + public MeasureDao() { + this(System2.INSTANCE); + } + + @VisibleForTesting + public MeasureDao(System2 system) { + super(MeasureMapper.class, system); + } + + @Override + protected MeasureDto doGetByKey(MeasureKey key, DbSession session) { + return session.getMapper(MeasureMapper.class).selectByKey(key); + } + + public List<MeasureDto> findByComponentKeyAndMetricKeys(String componentKey, List<String> metricKeys, DbSession session){ + if (metricKeys.isEmpty()) { + return Collections.emptyList(); + } + List<MeasureDto> dtos = newArrayList(); + List<List<String>> partitions = Lists.partition(newArrayList(metricKeys), 1000); + for (List<String> partition : partitions) { + dtos.addAll(session.getMapper(MeasureMapper.class).selectByComponentAndMetrics(componentKey, partition)); + } + return dtos; + } + + @Override + protected MeasureDto doInsert(MeasureDto item, DbSession session) { + throw new IllegalStateException("Not implemented yet"); + } + + @Override + protected MeasureDto doUpdate(MeasureDto item, DbSession session) { + throw new IllegalStateException("Not implemented yet"); + } + + @Override + protected void doDeleteByKey(MeasureKey key, DbSession session) { + throw new IllegalStateException("Not implemented yet"); + } + + @Override + public Iterable<MeasureKey> keysOfRowsUpdatedAfter(long timestamp, DbSession session) { + throw new IllegalStateException("Not implemented yet"); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 6173d0bee2c..7521680a5c1 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -50,9 +50,6 @@ import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.measure.db.MeasureFilterDao; -import org.sonar.server.measure.MeasureFilterEngine; -import org.sonar.server.measure.MeasureFilterExecutor; -import org.sonar.server.measure.MeasureFilterFactory; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; @@ -102,6 +99,10 @@ import org.sonar.server.issue.filter.IssueFilterWriter; import org.sonar.server.issue.filter.IssueFilterWs; import org.sonar.server.issue.ws.IssueShowAction; import org.sonar.server.issue.ws.IssuesWs; +import org.sonar.server.measure.MeasureFilterEngine; +import org.sonar.server.measure.MeasureFilterExecutor; +import org.sonar.server.measure.MeasureFilterFactory; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.measure.ws.TimeMachineWs; import org.sonar.server.notifications.NotificationCenter; import org.sonar.server.notifications.NotificationService; @@ -129,11 +130,7 @@ import org.sonar.server.rule2.RuleService; import org.sonar.server.rule2.index.RuleIndex; import org.sonar.server.rule2.index.RuleNormalizer; import org.sonar.server.rule2.persistence.RuleDao; -import org.sonar.server.rule2.ws.RulesWebService; -import org.sonar.server.rule2.ws.SearchAction; -import org.sonar.server.rule2.ws.SetNoteAction; -import org.sonar.server.rule2.ws.SetTagsAction; -import org.sonar.server.rule2.ws.TagsAction; +import org.sonar.server.rule2.ws.*; import org.sonar.server.source.CodeColorizers; import org.sonar.server.source.DeprecatedSourceDecorator; import org.sonar.server.source.HtmlSourceDecorator; @@ -201,6 +198,7 @@ class ServerComponents { System2.INSTANCE, RuleDao.class, ActiveRuleDao.class, + MeasureDao.class, DbClient.class, MeasureFilterDao.class )); diff --git a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java index cbab7733a55..1ca84b94785 100644 --- a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java +++ b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java @@ -23,8 +23,11 @@ package org.sonar.server.source; import org.sonar.api.ServerComponent; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.web.UserRole; -import org.sonar.core.measure.db.MeasureDao; import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -34,6 +37,8 @@ import java.util.List; public class SourceService implements ServerComponent { + private final MyBatis myBatis; + private final HtmlSourceDecorator sourceDecorator; /** @@ -43,7 +48,8 @@ public class SourceService implements ServerComponent { private final MeasureDao measureDao; - public SourceService(HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator, MeasureDao measureDao) { + public SourceService(MyBatis myBatis, HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator, MeasureDao measureDao) { + this.myBatis = myBatis; this.sourceDecorator = sourceDecorator; this.deprecatedSourceDecorator = deprecatedSourceDecorator; this.measureDao = measureDao; @@ -85,10 +91,15 @@ public class SourceService implements ServerComponent { @CheckForNull private String findDataFromComponent(String fileKey, String metricKey) { - MeasureDto data = measureDao.findByComponentKeyAndMetricKey(fileKey, metricKey); - if (data != null) { - return data.getData(); + DbSession session = myBatis.openSession(false); + try { + MeasureDto data = measureDao.getByKey(MeasureKey.of(fileKey, metricKey), session); + if (data != null) { + return data.getData(); + } + return null; + } finally { + MyBatis.closeQuietly(session); } - return null; } } diff --git a/sonar-server/src/main/java/org/sonar/server/test/CoverageService.java b/sonar-server/src/main/java/org/sonar/server/test/CoverageService.java index 28ac0299002..5b5062fcd6b 100644 --- a/sonar-server/src/main/java/org/sonar/server/test/CoverageService.java +++ b/sonar-server/src/main/java/org/sonar/server/test/CoverageService.java @@ -28,8 +28,11 @@ import org.sonar.api.test.Testable; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.web.UserRole; import org.sonar.core.component.SnapshotPerspectives; -import org.sonar.core.measure.db.MeasureDao; import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -42,10 +45,12 @@ public class CoverageService implements ServerComponent { UT, IT, OVERALL } + private final MyBatis myBatis; private final MeasureDao measureDao; private final SnapshotPerspectives snapshotPerspectives; - public CoverageService(MeasureDao measureDao, SnapshotPerspectives snapshotPerspectives) { + public CoverageService(MyBatis myBatis, MeasureDao measureDao, SnapshotPerspectives snapshotPerspectives) { + this.myBatis = myBatis; this.measureDao = measureDao; this.snapshotPerspectives = snapshotPerspectives; } @@ -102,10 +107,15 @@ public class CoverageService implements ServerComponent { @CheckForNull private Map<Integer, Integer> findDataFromComponent(String fileKey, String metricKey) { - MeasureDto data = measureDao.findByComponentKeyAndMetricKey(fileKey, metricKey); - if (data != null) { - return KeyValueFormat.parseIntInt(data.getData()); + DbSession session = myBatis.openSession(false); + try { + MeasureDto data = measureDao.getByKey(MeasureKey.of(fileKey, metricKey), session); + if (data != null) { + return KeyValueFormat.parseIntInt(data.getData()); + } + return Maps.newHashMap(); + } finally { + MyBatis.closeQuietly(session); } - return Maps.newHashMap(); } } diff --git a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index cf3ffe6b7b4..ec3a179f57f 100644 --- a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -36,12 +36,10 @@ import org.sonar.server.platform.Platform; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; /** * Part of the current HTTP session @@ -61,7 +59,7 @@ public class UserSession { List<String> globalPermissions = null; HashMultimap<String, String> projectKeyByPermission = HashMultimap.create(); - HashMultimap<String, String> componentKeyByPermission = HashMultimap.create(); + Map<String, String> projectKeyByComponentKey = newHashMap(); List<String> projectPermissions = newArrayList(); UserSession() { @@ -163,11 +161,17 @@ public class UserSession { * Ensures that user implies the specified project permission on a component. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown. */ public UserSession checkComponentPermission(String projectPermission, String componentKey) { - ResourceDto project = resourceDao().getRootProjectByComponentKey(componentKey); - if (project == null) { - throw new NotFoundException(String.format("Component '%s' does not exist", componentKey)); + String projectKey = projectKeyByComponentKey.get(componentKey); + if (projectKey == null) { + ResourceDto project = resourceDao().getRootProjectByComponentKey(componentKey); + if (project == null) { + throw new NotFoundException(String.format("Component '%s' does not exist", componentKey)); + } + projectKey = project.getKey(); } - return checkProjectPermission(projectPermission, project.getKey()); + UserSession userSession = checkProjectPermission(projectPermission, projectKey); + projectKeyByComponentKey.put(componentKey, projectKey); + return userSession; } /** diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index 24d5adc52f9..ae95130a2d3 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -34,8 +34,9 @@ import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; -import org.sonar.core.measure.db.MeasureDao; import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.properties.PropertyDto; import org.sonar.core.properties.PropertyQuery; @@ -43,12 +44,15 @@ import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.timemachine.Periods; +import org.sonar.server.db.DbClient; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.RulesAggregation; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; import java.util.Date; +import java.util.List; import java.util.Locale; import static com.google.common.collect.Lists.newArrayList; @@ -63,15 +67,18 @@ public class ComponentAppActionTest { static final String COMPONENT_KEY = "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java"; @Mock - ResourceDao resourceDao; + DbSession session; @Mock - MeasureDao measureDao; + ResourceDao resourceDao; @Mock PropertiesDao propertiesDao; @Mock + MeasureDao measureDao; + + @Mock IssueService issueService; @Mock @@ -83,26 +90,35 @@ public class ComponentAppActionTest { @Mock I18n i18n; + List<MeasureDto> measures = newArrayList(); + WsTester tester; @Before public void setUp() throws Exception { - when(issueService.findSeveritiesByComponent(COMPONENT_KEY)).thenReturn(mock(Multiset.class)); - when(issueService.findRulesByComponent(COMPONENT_KEY)).thenReturn(mock(RulesAggregation.class)); + DbClient dbClient = mock(DbClient.class); + when(dbClient.openSession(false)).thenReturn(session); + when(dbClient.getDao(ResourceDao.class)).thenReturn(resourceDao); + when(dbClient.getDao(PropertiesDao.class)).thenReturn(propertiesDao); + when(dbClient.getDao(MeasureDao.class)).thenReturn(measureDao); - tester = new WsTester(new ComponentsWs(new ComponentAppAction(resourceDao, measureDao, propertiesDao, issueService, periods, durations, i18n))); + when(issueService.findSeveritiesByComponent(COMPONENT_KEY, session)).thenReturn(mock(Multiset.class)); + when(issueService.findRulesByComponent(COMPONENT_KEY, session)).thenReturn(mock(RulesAggregation.class)); + when(measureDao.findByComponentKeyAndMetricKeys(eq(COMPONENT_KEY), anyListOf(String.class), eq(session))).thenReturn(measures); + + tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, periods, durations, i18n))); } @Test public void app() throws Exception { - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java") .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L); - when(resourceDao.selectComponentByKey(COMPONENT_KEY)).thenReturn(file); - when(resourceDao.findById(5L)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); - when(resourceDao.findById(1L)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); - when(propertiesDao.selectByQuery(any(PropertyQuery.class))).thenReturn(newArrayList(new PropertyDto())); + when(resourceDao.selectComponentByKey(COMPONENT_KEY, session)).thenReturn(file); + when(resourceDao.findById(5L, session)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); + when(resourceDao.findById(1L, session)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); + when(propertiesDao.selectByQuery(any(PropertyQuery.class), eq(session))).thenReturn(newArrayList(new PropertyDto())); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); request.execute().assertJson(getClass(), "app.json"); @@ -110,7 +126,7 @@ public class ComponentAppActionTest { @Test public void app_with_measures() throws Exception { - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); addProjectSample(); @@ -124,7 +140,7 @@ public class ComponentAppActionTest { addMeasure(CoreMetrics.MINOR_VIOLATIONS_KEY, 4); addMeasure(CoreMetrics.INFO_VIOLATIONS_KEY, 2); - when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.TECHNICAL_DEBT_KEY)).thenReturn(new MeasureDto().setValue(182.0)); + measures.add(MeasureDto.createFor(MeasureKey.of(COMPONENT_KEY, CoreMetrics.TECHNICAL_DEBT_KEY)).setValue(182.0)); when(durations.format(any(Locale.class), any(Duration.class), eq(Durations.DurationFormat.SHORT))).thenReturn("3h 2min"); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -133,11 +149,11 @@ public class ComponentAppActionTest { @Test public void app_with_periods() throws Exception { - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); addProjectSample(); - when(resourceDao.getLastSnapshotByResourceId(eq(1L))).thenReturn( + when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriod1Mode("previous_analysis").setPeriod1Date(DateUtils.parseDate("2014-05-08")) ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); @@ -148,13 +164,13 @@ public class ComponentAppActionTest { @Test public void app_with_severities() throws Exception { - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); addProjectSample(); Multiset<String> severities = HashMultiset.create(); severities.add("MAJOR", 5); - when(issueService.findSeveritiesByComponent(COMPONENT_KEY)).thenReturn(severities); + when(issueService.findSeveritiesByComponent(COMPONENT_KEY, session)).thenReturn(severities); when(i18n.message(any(Locale.class), eq("severity.MAJOR"), isNull(String.class))).thenReturn("Major"); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -163,10 +179,10 @@ public class ComponentAppActionTest { @Test public void app_with_rules() throws Exception { - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, PROJECT_KEY).addComponent(COMPONENT_KEY, PROJECT_KEY); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); addProjectSample(); - when(issueService.findRulesByComponent(COMPONENT_KEY)).thenReturn( + when(issueService.findRulesByComponent(COMPONENT_KEY, session)).thenReturn( new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) ); @@ -177,19 +193,18 @@ public class ComponentAppActionTest { private void addProjectSample() { ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java") .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L); - when(resourceDao.selectComponentByKey(COMPONENT_KEY)).thenReturn(file); - when(resourceDao.findById(5L)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); - when(resourceDao.findById(1L)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); - + when(resourceDao.selectComponentByKey(COMPONENT_KEY, session)).thenReturn(file); + when(resourceDao.findById(5L, session)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); + when(resourceDao.findById(1L, session)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); } private void addMeasure(String metricKey, Integer value) { - when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, metricKey)).thenReturn(new MeasureDto().setValue(value.doubleValue())); + measures.add(MeasureDto.createFor(MeasureKey.of(COMPONENT_KEY, metricKey)).setValue(value.doubleValue())); when(i18n.formatInteger(any(Locale.class), eq(value.intValue()))).thenReturn(Integer.toString(value)); } private void addMeasure(String metricKey, Double value) { - when(measureDao.findByComponentKeyAndMetricKey(COMPONENT_KEY, metricKey)).thenReturn(new MeasureDto().setValue(value)); + measures.add(MeasureDto.createFor(MeasureKey.of(COMPONENT_KEY, metricKey)).setValue(value)); when(i18n.formatDouble(any(Locale.class), eq(value))).thenReturn(Double.toString(value)); } diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java index 76f461113f4..c6358a587b1 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java @@ -26,10 +26,8 @@ import org.sonar.api.i18n.I18n; import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.Durations; -import org.sonar.core.measure.db.MeasureDao; -import org.sonar.core.properties.PropertiesDao; -import org.sonar.core.resource.ResourceDao; import org.sonar.core.timemachine.Periods; +import org.sonar.server.db.DbClient; import org.sonar.server.issue.IssueService; import org.sonar.server.ws.WsTester; @@ -42,8 +40,7 @@ public class ComponentsWsTest { @Before public void setUp() throws Exception { - WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(ResourceDao.class), mock(MeasureDao.class), mock(PropertiesDao.class), - mock(IssueService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); + WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); controller = tester.controller("api/components"); } diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java index 745bc149649..27aebc4452c 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java @@ -20,6 +20,7 @@ package org.sonar.server.issue; +import com.google.common.collect.Multiset; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +46,12 @@ import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; import org.sonar.core.preview.PreviewCache; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.rule.RuleDto; import org.sonar.core.user.AuthorizationDao; import org.sonar.core.user.DefaultUser; import org.sonar.server.issue.actionplan.ActionPlanService; @@ -499,4 +502,30 @@ public class IssueServiceTest { verifyZeroInteractions(issueStorage); } + @Test + public void find_rules_by_component() throws Exception { + DbSession session = mock(DbSession.class); + String componentKey = "org.sonar.Sample"; + + when(issueDao.findRulesByComponent(componentKey, session)).thenReturn(newArrayList( + RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name"), + RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name") + )); + + RulesAggregation result = issueService.findRulesByComponent(componentKey, session); + assertThat(result.rules()).hasSize(1); + } + + @Test + public void find_rules_by_severity() throws Exception { + DbSession session = mock(DbSession.class); + String componentKey = "org.sonar.Sample"; + + when(issueDao.findSeveritiesByComponent(componentKey, session)).thenReturn(newArrayList("MAJOR", "MAJOR", "INFO")); + + Multiset<String> result = issueService.findSeveritiesByComponent(componentKey, session); + assertThat(result.count("MAJOR")).isEqualTo(2); + assertThat(result.count("INFO")).isEqualTo(1); + assertThat(result.count("UNKNOWN")).isEqualTo(0); + } } diff --git a/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java b/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java new file mode 100644 index 00000000000..27755289041 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java @@ -0,0 +1,96 @@ +/* + * 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.measure.persistence; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.DbSession; + +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; + +public class MeasureDaoTest extends AbstractDaoTestCase { + + DbSession session; + + MeasureDao dao; + + @Before + public void createDao() { + session = getMyBatis().openSession(false); + dao = new MeasureDao(); + } + + @After + public void tearDown() throws Exception { + session.close(); + } + + @Test + public void get_value_by_key() throws Exception { + setupData("shared"); + + MeasureDto result = dao.getByKey(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "ncloc"), session); + assertThat(result.getId()).isEqualTo(22); + assertThat(result.getValue()).isEqualTo(10d); + } + + @Test + public void get_data_by_key() throws Exception { + setupData("shared"); + + MeasureDto result = dao.getByKey(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "authors_by_line"), session); + assertThat(result.getId()).isEqualTo(20); + assertThat(result.getData()).isEqualTo("0123456789012345678901234567890123456789"); + } + + @Test + public void get_text_value_by_key() throws Exception { + setupData("shared"); + + MeasureDto result = dao.getByKey(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "coverage_line_hits_data"), session); + assertThat(result.getId()).isEqualTo(21); + assertThat(result.getData()).isEqualTo("36=1;37=1;38=1;39=1;43=1;48=1;53=1"); + } + + @Test + public void find_by_component_key_and_metrics() throws Exception { + setupData("shared"); + + List<MeasureDto> results = dao.findByComponentKeyAndMetricKeys("org.struts:struts-core:src/org/struts/RequestContext.java", + newArrayList("ncloc", "authors_by_line"), session); + assertThat(results).hasSize(2); + + results = dao.findByComponentKeyAndMetricKeys("org.struts:struts-core:src/org/struts/RequestContext.java", newArrayList("ncloc"), session); + assertThat(results).hasSize(1); + + MeasureDto result = results.get(0); + assertThat(result.getId()).isEqualTo(22); + assertThat(result.getValue()).isEqualTo(10d); + assertThat(result.getKey()).isNotNull(); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java index 9cb5eed7689..3290a6154eb 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java @@ -82,8 +82,7 @@ public class RegisterQualityProfilesMediumTest { // 2. Check Default Profile PropertiesDao propertiesDao = serverTester.get(PropertiesDao.class); - List<PropertyDto> xooDefault = propertiesDao.selectByQuery(PropertyQuery.builder() - .setKey("sonar.profile.xoo").build()); + List<PropertyDto> xooDefault = propertiesDao.selectByQuery(PropertyQuery.builder().setKey("sonar.profile.xoo").build(), session); assertThat(xooDefault).hasSize(1); assertThat(xooDefault.get(0).getValue()).isEqualTo("Basic"); diff --git a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java index 91eca92a641..591631d5b52 100644 --- a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java @@ -27,8 +27,11 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.web.UserRole; -import org.sonar.core.measure.db.MeasureDao; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.MockUserSession; import java.util.Collections; @@ -41,6 +44,9 @@ import static org.mockito.Mockito.*; public class SourceServiceTest { @Mock + DbSession session; + + @Mock HtmlSourceDecorator sourceDecorator; @Mock @@ -53,14 +59,16 @@ public class SourceServiceTest { @Before public void setUp() throws Exception { - service = new SourceService(sourceDecorator, deprecatedSourceDecorator, measureDao); + MyBatis myBatis = mock(MyBatis.class); + when(myBatis.openSession(false)).thenReturn(session); + service = new SourceService(myBatis, sourceDecorator, deprecatedSourceDecorator, measureDao); } @Test public void get_lines() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey).addComponent(componentKey, projectKey); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, projectKey, componentKey); service.getLinesAsHtml(componentKey); @@ -87,7 +95,7 @@ public class SourceServiceTest { public void get_block_of_lines() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey).addComponent(componentKey, projectKey);; + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, projectKey, componentKey); service.getLinesAsHtml(componentKey, 1, 2); @@ -98,7 +106,7 @@ public class SourceServiceTest { public void get_lines_from_deprecated_source_decorator_when_no_data_from_new_decorator() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey).addComponent(componentKey, projectKey);; + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, projectKey, componentKey); when(sourceDecorator.getDecoratedSourceAsHtml(eq(componentKey), anyInt(), anyInt())).thenReturn(Collections.<String>emptyList()); service.getLinesAsHtml(componentKey, 1, 2); @@ -110,13 +118,13 @@ public class SourceServiceTest { public void get_scm_author_data() throws Exception { String componentKey = "org.sonar.sample:Sample"; service.getScmAuthorData(componentKey); - verify(measureDao).findByComponentKeyAndMetricKey(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY), session); } @Test public void not_get_scm_author_data_if_no_data() throws Exception { String componentKey = "org.sonar.sample:Sample"; - when(measureDao.findByComponentKeyAndMetricKey(eq(componentKey), anyString())).thenReturn(null); + when(measureDao.getByKey(any(MeasureKey.class), eq(session))).thenReturn(null); assertThat(service.getScmAuthorData(componentKey)).isNull(); } @@ -124,13 +132,13 @@ public class SourceServiceTest { public void get_scm_date_data() throws Exception { String componentKey = "org.sonar.sample:Sample"; service.getScmDateData(componentKey); - verify(measureDao).findByComponentKeyAndMetricKey(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY), session); } @Test public void not_get_scm_date_data_if_no_data() throws Exception { String componentKey = "org.sonar.sample:Sample"; - when(measureDao.findByComponentKeyAndMetricKey(eq(componentKey), anyString())).thenReturn(null); + when(measureDao.getByKey(any(MeasureKey.class), eq(session))).thenReturn(null); assertThat(service.getScmDateData(componentKey)).isNull(); } } diff --git a/sonar-server/src/test/java/org/sonar/server/test/CoverageServiceTest.java b/sonar-server/src/test/java/org/sonar/server/test/CoverageServiceTest.java index 4d033dd383b..2311e57c20a 100644 --- a/sonar-server/src/test/java/org/sonar/server/test/CoverageServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/test/CoverageServiceTest.java @@ -30,13 +30,15 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.test.MutableTestable; import org.sonar.api.web.UserRole; import org.sonar.core.component.SnapshotPerspectives; -import org.sonar.core.measure.db.MeasureDao; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.MockUserSession; import java.util.Collections; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @@ -47,6 +49,9 @@ public class CoverageServiceTest { public ExpectedException thrown = ExpectedException.none(); @Mock + DbSession session; + + @Mock MeasureDao measureDao; @Mock @@ -58,14 +63,15 @@ public class CoverageServiceTest { @Before public void setUp() throws Exception { - service = new CoverageService(measureDao, snapshotPerspectives); + MyBatis myBatis = mock(MyBatis.class); + when(myBatis.openSession(false)).thenReturn(session); + service = new CoverageService(myBatis, measureDao, snapshotPerspectives); } @Test public void check_permission() throws Exception { String projectKey = "org.sonar.sample"; - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey); - MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey).addComponent(COMPONENT_KEY, projectKey); + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, projectKey, COMPONENT_KEY); service.checkPermission(COMPONENT_KEY); } @@ -73,43 +79,43 @@ public class CoverageServiceTest { @Test public void get_hits_data() throws Exception { service.getHits(COMPONENT_KEY, CoverageService.TYPE.UT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY), session); service.getHits(COMPONENT_KEY, CoverageService.TYPE.IT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY), session); service.getHits(COMPONENT_KEY, CoverageService.TYPE.OVERALL); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY), session); } @Test public void not_get_hits_data_if_no_data() throws Exception { - when(measureDao.findByComponentKeyAndMetricKey(eq(COMPONENT_KEY), anyString())).thenReturn(null); + when(measureDao.getByKey(any(MeasureKey.class), eq(session))).thenReturn(null); assertThat(service.getHits(COMPONENT_KEY, CoverageService.TYPE.UT)).isEqualTo(Collections.emptyMap()); } @Test public void get_conditions_data() throws Exception { service.getConditions(COMPONENT_KEY, CoverageService.TYPE.UT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.CONDITIONS_BY_LINE_KEY), session); service.getConditions(COMPONENT_KEY, CoverageService.TYPE.IT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY), session); service.getConditions(COMPONENT_KEY, CoverageService.TYPE.OVERALL); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY), session); } @Test public void get_covered_conditions_data() throws Exception { service.getCoveredConditions(COMPONENT_KEY, CoverageService.TYPE.UT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY), session); service.getCoveredConditions(COMPONENT_KEY, CoverageService.TYPE.IT); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY), session); service.getCoveredConditions(COMPONENT_KEY, CoverageService.TYPE.OVERALL); - verify(measureDao).findByComponentKeyAndMetricKey(COMPONENT_KEY, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY); + verify(measureDao).getByKey(MeasureKey.of(COMPONENT_KEY, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY), session); } @Test diff --git a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java index aa39fe5f4e5..8ca959dac2e 100644 --- a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java +++ b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java @@ -21,7 +21,6 @@ package org.sonar.server.user; import com.google.common.collect.HashMultimap; import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; import org.sonar.core.user.AuthorizationDao; import javax.annotation.Nullable; @@ -32,7 +31,6 @@ import java.util.Locale; import static com.google.common.collect.Lists.newArrayList; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class MockUserSession extends UserSession { @@ -88,8 +86,9 @@ public class MockUserSession extends UserSession { return this; } - public MockUserSession addComponent(String componentKey, String projectKey) { - when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(projectKey)); + public MockUserSession addComponentPermission(String projectPermission, String projectKey, String componentKey) { + this.projectKeyByComponentKey.put(componentKey, projectKey); + addProjectPermissions(projectPermission, projectKey); return this; } diff --git a/sonar-core/src/test/resources/org/sonar/core/measure/db/MeasureDaoTest/shared.xml b/sonar-server/src/test/resources/org/sonar/server/measure/persistence/MeasureDaoTest/shared.xml index 70097c0470f..70097c0470f 100644 --- a/sonar-core/src/test/resources/org/sonar/core/measure/db/MeasureDaoTest/shared.xml +++ b/sonar-server/src/test/resources/org/sonar/server/measure/persistence/MeasureDaoTest/shared.xml diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/source/internal/package-info.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/source/internal/package-info.java new file mode 100644 index 00000000000..1a92c5c0269 --- /dev/null +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/source/internal/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@ParametersAreNonnullByDefault +package org.sonar.wsclient.source.internal; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/source/package-info.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/source/package-info.java new file mode 100644 index 00000000000..546965bbac9 --- /dev/null +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/source/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@ParametersAreNonnullByDefault +package org.sonar.wsclient.source; + +import javax.annotation.ParametersAreNonnullByDefault; |