aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2011-04-14 12:58:19 +0200
committerFabrice Bellingard <bellingard@gmail.com>2011-04-20 08:57:56 +0200
commit8d294104c34f96f7e9e670f26483a3a0eaf81d87 (patch)
treeb73eb563cdd0b100ba518ce9d142ade17c83c152
parentda7c05cdf4028bb103299dbe7d3708733fc2f87d (diff)
downloadsonarqube-8d294104c34f96f7e9e670f26483a3a0eaf81d87.tar.gz
sonarqube-8d294104c34f96f7e9e670f26483a3a0eaf81d87.zip
[SONAR-2347] Close a review when its corresponding violation does not
exist anymore
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java117
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java380
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsDecoratorTest.java58
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ReviewsDecoratorTest/fixture.xml53
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java169
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java520
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java158
7 files changed, 807 insertions, 648 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java
index 040dc72dd72..1faef7c4f66 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java
@@ -1,46 +1,71 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.plugins.core.sensors;
-
-import org.sonar.api.batch.Decorator;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.DependedUpon;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-
-/**
- * Decorator that currently only closes a review when its corresponding violation has been fixed.
- */
-@DependedUpon("ViolationPersisterDecorator")
-public class ReviewsDecorator implements Decorator {
-
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- //
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-}
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.Query;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.ResourcePersister;
+
+/**
+ * Decorator that currently only closes a review when its corresponding violation has been fixed.
+ */
+@DependsUpon("ViolationPersisterDecorator")
+public class ReviewsDecorator implements Decorator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ReviewsDecorator.class);
+
+ private ResourcePersister resourcePersister;
+ private DatabaseSession databaseSession;
+
+ public ReviewsDecorator(ResourcePersister resourcePersister, DatabaseSession databaseSession) {
+ this.resourcePersister = resourcePersister;
+ this.databaseSession = databaseSession;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ Snapshot currentSnapshot = resourcePersister.getSnapshot(resource);
+ if (currentSnapshot != null) {
+ int resourceId = currentSnapshot.getResourceId();
+ int snapshotId = currentSnapshot.getId();
+ Query query = databaseSession.createNativeQuery("UPDATE reviews SET status='closed' " + "WHERE resource_id = " + resourceId
+ + " AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id = " + snapshotId + ")");
+ int rowUpdated = query.executeUpdate();
+ LOG.debug("- {} reviews set to 'closed' on resource #{}", rowUpdated, resourceId);
+ databaseSession.commit();
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java
index 8360475d550..455ec647f94 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java
@@ -1,186 +1,196 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.plugins.core.timemachine;
-
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.*;
-import org.sonar.api.database.model.RuleFailureModel;
-import org.sonar.api.database.model.SnapshotSource;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.Violation;
-import org.sonar.batch.components.PastViolationsLoader;
-import org.sonar.batch.index.ViolationPersister;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-@DependsUpon(DecoratorBarriers.END_OF_VIOLATIONS_GENERATION)
-@DependedUpon("ViolationPersisterDecorator") /* temporary workaround - see NewViolationsDecorator */
-public class ViolationPersisterDecorator implements Decorator {
-
- /**
- * Those chars would be ignored during generation of checksums.
- */
- private static final String SPACE_CHARS = "\t\n\r ";
-
- private RuleFinder ruleFinder;
- private PastViolationsLoader pastViolationsLoader;
- private ViolationPersister violationPersister;
-
- List<String> checksums = Lists.newArrayList();
-
- public ViolationPersisterDecorator(RuleFinder ruleFinder, PastViolationsLoader pastViolationsLoader, ViolationPersister violationPersister) {
- this.ruleFinder = ruleFinder;
- this.pastViolationsLoader = pastViolationsLoader;
- this.violationPersister = violationPersister;
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- if (context.getViolations().isEmpty()) {
- return;
- }
- // Load past violations
- List<RuleFailureModel> pastViolations = pastViolationsLoader.getPastViolations(resource);
- // Load current source and calculate checksums
- checksums = getChecksums(pastViolationsLoader.getSource(resource));
- // Save violations
- compareWithPastViolations(context, pastViolations);
- // Clear cache
- checksums.clear();
- }
-
- private void compareWithPastViolations(DecoratorContext context, List<RuleFailureModel> pastViolations) {
- Multimap<Rule, RuleFailureModel> pastViolationsByRule = LinkedHashMultimap.create();
- for (RuleFailureModel pastViolation : pastViolations) {
- Rule rule = ruleFinder.findById(pastViolation.getRuleId());
- pastViolationsByRule.put(rule, pastViolation);
- }
- // for each violation, search equivalent past violation
- for (Violation violation : context.getViolations()) {
- RuleFailureModel pastViolation = selectPastViolation(violation, pastViolationsByRule);
- if (pastViolation != null) {
- // remove violation, since would be updated and shouldn't affect other violations anymore
- pastViolationsByRule.remove(violation.getRule(), pastViolation);
- }
- String checksum = getChecksumForLine(checksums, violation.getLineId());
- violationPersister.saveViolation(context.getProject(), violation, pastViolation, checksum);
- }
- }
-
- /**
- * @return checksums, never null
- */
- private List<String> getChecksums(SnapshotSource source) {
- return source == null || source.getData() == null ? Collections.<String>emptyList() : getChecksums(source.getData());
- }
-
- /**
- * @param data can't be null
- */
- static List<String> getChecksums(String data) {
- String[] lines = data.split("\r?\n|\r", -1);
- List<String> result = Lists.newArrayList();
- for (String line : lines) {
- result.add(getChecksum(line));
- }
- return result;
- }
-
- static String getChecksum(String line) {
- String reducedLine = StringUtils.replaceChars(line, SPACE_CHARS, "");
- return DigestUtils.md5Hex(reducedLine);
- }
-
- /**
- * @return checksum or null if checksum not exists for line
- */
- private String getChecksumForLine(List<String> checksums, Integer line) {
- if (line == null || line < 1 || line > checksums.size()) {
- return null;
- }
- return checksums.get(line - 1);
- }
-
- /**
- * Search for past violation.
- */
- RuleFailureModel selectPastViolation(Violation violation, Multimap<Rule, RuleFailureModel> pastViolationsByRule) {
- Collection<RuleFailureModel> pastViolations = pastViolationsByRule.get(violation.getRule());
- if (pastViolations == null || pastViolations.isEmpty()) {
- // skip violation, if there is no past violations with same rule
- return null;
- }
- String dbFormattedMessage = RuleFailureModel.abbreviateMessage(violation.getMessage());
- RuleFailureModel found = selectPastViolationUsingLine(violation, dbFormattedMessage, pastViolations);
- if (found == null) {
- found = selectPastViolationUsingChecksum(violation, dbFormattedMessage, pastViolations);
- }
- return found;
- }
-
- /**
- * Search for past violation with same message and line.
- */
- private RuleFailureModel selectPastViolationUsingLine(Violation violation, String dbFormattedMessage, Collection<RuleFailureModel> pastViolations) {
- for (RuleFailureModel pastViolation : pastViolations) {
- if (ObjectUtils.equals(violation.getLineId(), pastViolation.getLine()) && StringUtils.equals(dbFormattedMessage, pastViolation.getMessage())) {
- return pastViolation;
- }
- }
- return null;
- }
-
- /**
- * Search for past violation with same message and checksum.
- */
- private RuleFailureModel selectPastViolationUsingChecksum(Violation violation, String dbFormattedMessage, Collection<RuleFailureModel> pastViolations) {
- String checksum = getChecksumForLine(checksums, violation.getLineId());
- // skip violation, which not attached to line
- if (checksum == null) {
- return null;
- }
- for (RuleFailureModel pastViolation : pastViolations) {
- String pastChecksum = pastViolation.getChecksum();
- if (StringUtils.equals(checksum, pastChecksum) && StringUtils.equals(dbFormattedMessage, pastViolation.getMessage())) {
- return pastViolation;
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.timemachine;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.plexus.util.StringInputStream;
+import org.sonar.api.batch.*;
+import org.sonar.api.database.model.RuleFailureModel;
+import org.sonar.api.database.model.SnapshotSource;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.Violation;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.components.PastViolationsLoader;
+import org.sonar.batch.index.ViolationPersister;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+@DependsUpon(DecoratorBarriers.END_OF_VIOLATIONS_GENERATION)
+@DependedUpon("ViolationPersisterDecorator") /* temporary workaround - see NewViolationsDecorator */
+public class ViolationPersisterDecorator implements Decorator {
+
+ /**
+ * Those chars would be ignored during generation of checksums.
+ */
+ private static final String SPACE_CHARS = "\t\n\r ";
+
+ private RuleFinder ruleFinder;
+ private PastViolationsLoader pastViolationsLoader;
+ private ViolationPersister violationPersister;
+
+ List<String> checksums = Lists.newArrayList();
+
+ public ViolationPersisterDecorator(RuleFinder ruleFinder, PastViolationsLoader pastViolationsLoader, ViolationPersister violationPersister) {
+ this.ruleFinder = ruleFinder;
+ this.pastViolationsLoader = pastViolationsLoader;
+ this.violationPersister = violationPersister;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (context.getViolations().isEmpty()) {
+ return;
+ }
+ // Load past violations
+ List<RuleFailureModel> pastViolations = pastViolationsLoader.getPastViolations(resource);
+ // Load current source and calculate checksums
+ checksums = getChecksums(pastViolationsLoader.getSource(resource));
+ // Save violations
+ compareWithPastViolations(context, pastViolations);
+ // Clear cache
+ checksums.clear();
+ }
+
+ private void compareWithPastViolations(DecoratorContext context, List<RuleFailureModel> pastViolations) {
+ Multimap<Rule, RuleFailureModel> pastViolationsByRule = LinkedHashMultimap.create();
+ for (RuleFailureModel pastViolation : pastViolations) {
+ Rule rule = ruleFinder.findById(pastViolation.getRuleId());
+ pastViolationsByRule.put(rule, pastViolation);
+ }
+ // for each violation, search equivalent past violation
+ for (Violation violation : context.getViolations()) {
+ RuleFailureModel pastViolation = selectPastViolation(violation, pastViolationsByRule);
+ if (pastViolation != null) {
+ // remove violation, since would be updated and shouldn't affect other violations anymore
+ pastViolationsByRule.remove(violation.getRule(), pastViolation);
+ }
+ String checksum = getChecksumForLine(checksums, violation.getLineId());
+ violationPersister.saveViolation(context.getProject(), violation, pastViolation, checksum);
+ }
+ violationPersister.commit();
+ }
+
+ /**
+ * @return checksums, never null
+ */
+ private List<String> getChecksums(SnapshotSource source) {
+ return source == null || source.getData() == null ? Collections.<String>emptyList() : getChecksums(source.getData());
+ }
+
+ static List<String> getChecksums(String data) {
+ List<String> result = Lists.newArrayList();
+ StringInputStream stream = new StringInputStream(data);
+ try {
+ List<String> lines = IOUtils.readLines(stream);
+ for (String line : lines) {
+ result.add(getChecksum(line));
+ }
+ } catch (IOException e) {
+ throw new SonarException("Unable to calculate checksums", e);
+
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ return result;
+ }
+
+ static String getChecksum(String line) {
+ String reducedLine = StringUtils.replaceChars(line, SPACE_CHARS, "");
+ return DigestUtils.md5Hex(reducedLine);
+ }
+
+ /**
+ * @return checksum or null if checksum not exists for line
+ */
+ private String getChecksumForLine(List<String> checksums, Integer line) {
+ if (line == null || line < 1 || line > checksums.size()) {
+ return null;
+ }
+ return checksums.get(line - 1);
+ }
+
+ /**
+ * Search for past violation.
+ */
+ RuleFailureModel selectPastViolation(Violation violation, Multimap<Rule, RuleFailureModel> pastViolationsByRule) {
+ Collection<RuleFailureModel> pastViolations = pastViolationsByRule.get(violation.getRule());
+ if (pastViolations==null || pastViolations.isEmpty()) {
+ // skip violation, if there is no past violations with same rule
+ return null;
+ }
+ String dbFormattedMessage = RuleFailureModel.abbreviateMessage(violation.getMessage());
+ RuleFailureModel found = selectPastViolationUsingLine(violation, dbFormattedMessage, pastViolations);
+ if (found == null) {
+ found = selectPastViolationUsingChecksum(violation, dbFormattedMessage, pastViolations);
+ }
+ return found;
+ }
+
+ /**
+ * Search for past violation with same message and line.
+ */
+ private RuleFailureModel selectPastViolationUsingLine(Violation violation, String dbFormattedMessage, Collection<RuleFailureModel> pastViolations) {
+ for (RuleFailureModel pastViolation : pastViolations) {
+ if (ObjectUtils.equals(violation.getLineId(), pastViolation.getLine()) && StringUtils.equals(dbFormattedMessage, pastViolation.getMessage())) {
+ return pastViolation;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search for past violation with same message and checksum.
+ */
+ private RuleFailureModel selectPastViolationUsingChecksum(Violation violation, String dbFormattedMessage, Collection<RuleFailureModel> pastViolations) {
+ String checksum = getChecksumForLine(checksums, violation.getLineId());
+ // skip violation, which not attached to line
+ if (checksum == null) {
+ return null;
+ }
+ for (RuleFailureModel pastViolation : pastViolations) {
+ String pastChecksum = pastViolation.getChecksum();
+ if (StringUtils.equals(checksum, pastChecksum) && StringUtils.equals(dbFormattedMessage, pastViolation.getMessage())) {
+ return pastViolation;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsDecoratorTest.java
new file mode 100644
index 00000000000..1fd4da1748a
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsDecoratorTest.java
@@ -0,0 +1,58 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.index.ResourcePersister;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.text.ParseException;
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ReviewsDecoratorTest extends AbstractDbUnitTestCase {
+
+ @Test
+ @Ignore("DBUnit needs Hibernate mapping...")
+ public void shouldSaveConfigurationInSnapshotsTable() throws ParseException {
+ setupData("fixture");
+
+ File resource = new File("Foo");
+ Snapshot snapshot = new Snapshot();
+ snapshot.setId(666);
+ snapshot.setResourceId(111);
+ ResourcePersister persister = mock(ResourcePersister.class);
+ when(persister.getSnapshot(resource)).thenReturn(snapshot);
+
+ ReviewsDecorator reviewsDecorator = new ReviewsDecorator(persister, getSession());
+ reviewsDecorator.decorate(resource, null);
+
+ //checkTables("shouldSaveConfigurationInSnapshotsTable", "snapshots");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ReviewsDecoratorTest/fixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ReviewsDecoratorTest/fixture.xml
new file mode 100644
index 00000000000..24cf1cfb55d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ReviewsDecoratorTest/fixture.xml
@@ -0,0 +1,53 @@
+<dataset>
+
+ <snapshots
+ id="11"
+ project_id="555"/>
+ <snapshots
+ id="111"
+ project_id="555"/>
+ <snapshots
+ id="22"
+ project_id="666"/>
+ <snapshots
+ id="222"
+ project_id="666"/>
+
+ <rule_failures
+ id="1"
+ permament_id="1"
+ snapshot_id="11"/>
+ <rule_failures
+ id="2"
+ permament_id="2"
+ snapshot_id="22"/>
+ <rule_failures
+ id="3"
+ permament_id="3"
+ snapshot_id="22"/>
+ <rule_failures
+ id="4"
+ permament_id="1"
+ snapshot_id="111"/>
+ <rule_failures
+ id="5"
+ permament_id="3"
+ snapshot_id="222"/>
+
+ <reviews
+ id="1"
+ status="open"
+ rule_failure_permanent_id="1"
+ resource_id="555"/>
+ <reviews
+ id="2"
+ status="open"
+ rule_failure_permanent_id="2"
+ resource_id="666"/>
+ <reviews
+ id="3"
+ status="open"
+ rule_failure_permanent_id="3"
+ resource_id="666"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java
index 64d2c85c2d7..7782e961922 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java
@@ -1,82 +1,87 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.batch.index;
-
-import org.sonar.api.database.DatabaseSession;
-import org.sonar.api.database.model.RuleFailureModel;
-import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.resources.Project;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.Violation;
-
-public final class ViolationPersister {
-
- private DatabaseSession session;
- private ResourcePersister resourcePersister;
- private RuleFinder ruleFinder;
-
- public ViolationPersister(DatabaseSession session, ResourcePersister resourcePersister, RuleFinder ruleFinder) {
- this.session = session;
- this.resourcePersister = resourcePersister;
- this.ruleFinder = ruleFinder;
- }
-
- void saveViolation(Project project, Violation violation) {
- saveViolation(project, violation, null, null);
- }
-
- public void saveViolation(Project project, Violation violation, RuleFailureModel pastViolation, String checksum) {
- Snapshot snapshot = resourcePersister.saveResource(project, violation.getResource());
-
- RuleFailureModel model = createModel(violation);
- if (pastViolation!=null) {
- model.setCreatedAt(pastViolation.getCreatedAt());
- model.setPermanentId(pastViolation.getPermanentId());
-
- } else {
- // avoid plugins setting date
- model.setCreatedAt(snapshot.getCreatedAt());
- }
- model.setSnapshotId(snapshot.getId());
- model.setChecksum(checksum);
- session.save(model);
-
- if (model.getPermanentId()==null) {
- model.setPermanentId(model.getId());
- session.save(model);
- }
-
- // the following fields can have been changed
- violation.setMessage(model.getMessage());// the message can be changed in the class RuleFailure (truncate + trim)
- violation.setCreatedAt(model.getCreatedAt());
- }
-
- private RuleFailureModel createModel(Violation violation) {
- RuleFailureModel model = new RuleFailureModel();
- Rule rule = ruleFinder.findByKey(violation.getRule().getRepositoryKey(), violation.getRule().getKey());
- model.setRuleId(rule.getId());
- model.setPriority(violation.getSeverity());
- model.setLine(violation.getLineId());
- model.setMessage(violation.getMessage());
- model.setCost(violation.getCost());
- return model;
- }
-}
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.index;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.RuleFailureModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.Violation;
+
+public final class ViolationPersister {
+
+ private DatabaseSession session;
+ private ResourcePersister resourcePersister;
+ private RuleFinder ruleFinder;
+
+ public ViolationPersister(DatabaseSession session, ResourcePersister resourcePersister, RuleFinder ruleFinder) {
+ this.session = session;
+ this.resourcePersister = resourcePersister;
+ this.ruleFinder = ruleFinder;
+ }
+
+ void saveViolation(Project project, Violation violation) {
+ saveViolation(project, violation, null, null);
+ }
+
+ public void saveViolation(Project project, Violation violation, RuleFailureModel pastViolation, String checksum) {
+ Snapshot snapshot = resourcePersister.saveResource(project, violation.getResource());
+
+ RuleFailureModel model = createModel(violation);
+ if (pastViolation!=null) {
+ model.setCreatedAt(pastViolation.getCreatedAt());
+ model.setPermanentId(pastViolation.getPermanentId());
+
+ } else {
+ // avoid plugins setting date
+ model.setCreatedAt(snapshot.getCreatedAt());
+ }
+ model.setSnapshotId(snapshot.getId());
+ model.setChecksum(checksum);
+ session.save(model);
+
+ if (model.getPermanentId()==null) {
+ model.setPermanentId(model.getId());
+ session.save(model);
+ }
+
+ // the following fields can have been changed
+ violation.setMessage(model.getMessage());// the message can be changed in the class RuleFailure (truncate + trim)
+ violation.setCreatedAt(model.getCreatedAt());
+ }
+
+ public void commit() {
+ session.commit();
+ }
+
+ private RuleFailureModel createModel(Violation violation) {
+ RuleFailureModel model = new RuleFailureModel();
+ Rule rule = ruleFinder.findByKey(violation.getRule().getRepositoryKey(), violation.getRule().getKey());
+ model.setRuleId(rule.getId());
+ model.setPriority(violation.getSeverity());
+ model.setLine(violation.getLineId());
+ model.setMessage(violation.getMessage());
+ model.setCost(violation.getCost());
+ return model;
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java b/sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java
index 09fb4894c88..6533b5ae5cf 100644
--- a/sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java
@@ -1,257 +1,263 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.jpa.session;
-
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.database.DatabaseSession;
-
-import java.util.*;
-
-import javax.persistence.EntityManager;
-import javax.persistence.NonUniqueResultException;
-import javax.persistence.PersistenceException;
-import javax.persistence.Query;
-
-public class JpaDatabaseSession extends DatabaseSession {
-
- private final DatabaseConnector connector;
- private EntityManager entityManager = null;
- private int index = 0;
- private boolean inTransaction = false;
-
- public JpaDatabaseSession(DatabaseConnector connector) {
- this.connector = connector;
- }
-
- /**
- * Note that usage of this method is discouraged, because it allows to construct and execute queries without additional exception handling,
- * which done in methods of this class.
- */
- public EntityManager getEntityManager() {
- return entityManager;
- }
-
- public void start() {
- entityManager = connector.createEntityManager();
- index = 0;
- }
-
- public void stop() {
- commit();
- if (entityManager != null && entityManager.isOpen()) {
- entityManager.close();
- entityManager = null;
- }
- }
-
- public void commit() {
- if (entityManager != null && inTransaction) {
- if (entityManager.isOpen()) {
- if (entityManager.getTransaction().getRollbackOnly()) {
- entityManager.getTransaction().rollback();
- } else {
- entityManager.getTransaction().commit();
- }
- entityManager.clear();
- index = 0;
- }
- inTransaction = false;
- }
- }
-
- public void rollback() {
- if (entityManager != null && inTransaction) {
- entityManager.getTransaction().rollback();
- inTransaction = false;
- }
- }
-
- public <T> T save(T model) {
- startTransaction();
- internalSave(model, true);
- return model;
- }
-
- public Object saveWithoutFlush(Object model) {
- startTransaction();
- internalSave(model, false);
- return model;
- }
-
- public boolean contains(Object model) {
- startTransaction();
- return entityManager.contains(model);
- }
-
- public void save(Object... models) {
- startTransaction();
- for (Object model : models) {
- save(model);
- }
- }
-
- private void internalSave(Object model, boolean flushIfNeeded) {
- try {
- entityManager.persist(model);
- } catch (PersistenceException e) {
- /*
- * See http://jira.codehaus.org/browse/SONAR-2234
- * In some cases Hibernate can throw exceptions without meaningful information about context, so we improve them here.
- */
- throw new PersistenceException("Unable to persist : " + model, e);
- }
- if (flushIfNeeded && (++index % BATCH_SIZE == 0)) {
- commit();
- }
- }
-
- public Object merge(Object model) {
- startTransaction();
- return entityManager.merge(model);
- }
-
- public void remove(Object model) {
- startTransaction();
- entityManager.remove(model);
- if (++index % BATCH_SIZE == 0) {
- commit();
- }
- }
-
- public void removeWithoutFlush(Object model) {
- startTransaction();
- entityManager.remove(model);
- }
-
- public <T> T reattach(Class<T> entityClass, Object primaryKey) {
- startTransaction();
- return entityManager.getReference(entityClass, primaryKey);
- }
-
- private void startTransaction() {
- if (!inTransaction) {
- entityManager.getTransaction().begin();
- inTransaction = true;
- }
- }
-
- /**
- * Note that not recommended to directly execute {@link Query#getSingleResult()}, because it will bypass exception handling,
- * which done in {@link #getSingleResult(Query, Object)}.
- */
- public Query createQuery(String hql) {
- startTransaction();
- return entityManager.createQuery(hql);
- }
-
- /**
- * @return the result or <code>defaultValue</code>, if not found
- * @throws NonUniqueResultException if more than one result
- */
- public <T> T getSingleResult(Query query, T defaultValue) {
- /*
- * See http://jira.codehaus.org/browse/SONAR-2225
- * By default Hibernate throws NonUniqueResultException without meaningful information about context,
- * so we improve it here by adding all results in error message.
- * Note that in some rare situations we can receive too many results, which may lead to OOME,
- * but actually it will mean that database is corrupted as we don't expect more than one result
- * and in fact org.hibernate.ejb.QueryImpl#getSingleResult() anyway does loading of several results under the hood.
- */
- List<T> result = query.getResultList();
-
- if (result.size() == 1) {
- return result.get(0);
-
- } else if (result.isEmpty()) {
- return defaultValue;
-
- } else {
- Set<T> uniqueResult = new HashSet<T>(result);
- if (uniqueResult.size() > 1) {
- throw new NonUniqueResultException("Expected single result, but got : " + result.toString());
- } else {
- return uniqueResult.iterator().next();
- }
- }
- }
-
- public <T> T getEntity(Class<T> entityClass, Object id) {
- startTransaction();
- return getEntityManager().find(entityClass, id);
- }
-
- /**
- * @return the result or <code>null</code>, if not found
- * @throws NonUniqueResultException if more than one result
- */
- public <T> T getSingleResult(Class<T> entityClass, Object... criterias) {
- try {
- return getSingleResult(getQueryForCriterias(entityClass, true, criterias), (T) null);
-
- } catch (NonUniqueResultException ex) {
- NonUniqueResultException e = new NonUniqueResultException("Expected single result for entitiy " + entityClass.getSimpleName()
- + " with criterias : " + StringUtils.join(criterias, ","));
- e.initCause(ex);
- throw e;
- }
- }
-
- public <T> List<T> getResults(Class<T> entityClass, Object... criterias) {
- return getQueryForCriterias(entityClass, true, criterias).getResultList();
- }
-
- public <T> List<T> getResults(Class<T> entityClass) {
- return getQueryForCriterias(entityClass, false, null).getResultList();
- }
-
- private Query getQueryForCriterias(Class<?> entityClass, boolean raiseError, Object... criterias) {
- if (criterias == null && raiseError) {
- throw new IllegalStateException("criterias parameter must be provided");
- }
- startTransaction();
- StringBuilder hql = new StringBuilder("SELECT o FROM ").append(entityClass.getSimpleName()).append(" o");
- if (criterias != null) {
- hql.append(" WHERE ");
- Map<String, Object> mappedCriterias = new HashMap<String, Object>();
- for (int i = 0; i < criterias.length; i += 2) {
- mappedCriterias.put((String) criterias[i], criterias[i + 1]);
- }
- buildCriteriasHQL(hql, mappedCriterias);
- Query query = getEntityManager().createQuery(hql.toString());
-
- for (Map.Entry<String, Object> entry : mappedCriterias.entrySet()) {
- query.setParameter(entry.getKey(), entry.getValue());
- }
- return query;
- }
- return getEntityManager().createQuery(hql.toString());
- }
-
- private void buildCriteriasHQL(StringBuilder hql, Map<String, Object> mappedCriterias) {
- for (Iterator<String> i = mappedCriterias.keySet().iterator(); i.hasNext();) {
- String criteria = i.next();
- hql.append("o.").append(criteria).append("=:").append(criteria);
- if (i.hasNext()) {
- hql.append(" AND ");
- }
- }
- }
-
-}
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.jpa.session;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.database.DatabaseSession;
+
+import java.util.*;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.PersistenceException;
+import javax.persistence.Query;
+
+public class JpaDatabaseSession extends DatabaseSession {
+
+ private final DatabaseConnector connector;
+ private EntityManager entityManager = null;
+ private int index = 0;
+ private boolean inTransaction = false;
+
+ public JpaDatabaseSession(DatabaseConnector connector) {
+ this.connector = connector;
+ }
+
+ /**
+ * Note that usage of this method is discouraged, because it allows to construct and execute queries without additional exception handling,
+ * which done in methods of this class.
+ */
+ public EntityManager getEntityManager() {
+ return entityManager;
+ }
+
+ public void start() {
+ entityManager = connector.createEntityManager();
+ index = 0;
+ }
+
+ public void stop() {
+ commit();
+ if (entityManager != null && entityManager.isOpen()) {
+ entityManager.close();
+ entityManager = null;
+ }
+ }
+
+ public void commit() {
+ if (entityManager != null && inTransaction) {
+ if (entityManager.isOpen()) {
+ if (entityManager.getTransaction().getRollbackOnly()) {
+ entityManager.getTransaction().rollback();
+ } else {
+ entityManager.getTransaction().commit();
+ }
+ entityManager.clear();
+ index = 0;
+ }
+ inTransaction = false;
+ }
+ }
+
+ public void rollback() {
+ if (entityManager != null && inTransaction) {
+ entityManager.getTransaction().rollback();
+ inTransaction = false;
+ }
+ }
+
+ public <T> T save(T model) {
+ startTransaction();
+ internalSave(model, true);
+ return model;
+ }
+
+ public Object saveWithoutFlush(Object model) {
+ startTransaction();
+ internalSave(model, false);
+ return model;
+ }
+
+ public boolean contains(Object model) {
+ startTransaction();
+ return entityManager.contains(model);
+ }
+
+ public void save(Object... models) {
+ startTransaction();
+ for (Object model : models) {
+ save(model);
+ }
+ }
+
+ private void internalSave(Object model, boolean flushIfNeeded) {
+ try {
+ entityManager.persist(model);
+ } catch (PersistenceException e) {
+ /*
+ * See http://jira.codehaus.org/browse/SONAR-2234
+ * In some cases Hibernate can throw exceptions without meaningful information about context, so we improve them here.
+ */
+ throw new PersistenceException("Unable to persist : " + model, e);
+ }
+ if (flushIfNeeded && (++index % BATCH_SIZE == 0)) {
+ commit();
+ }
+ }
+
+ public Object merge(Object model) {
+ startTransaction();
+ return entityManager.merge(model);
+ }
+
+ public void remove(Object model) {
+ startTransaction();
+ entityManager.remove(model);
+ if (++index % BATCH_SIZE == 0) {
+ commit();
+ }
+ }
+
+ public void removeWithoutFlush(Object model) {
+ startTransaction();
+ entityManager.remove(model);
+ }
+
+ public <T> T reattach(Class<T> entityClass, Object primaryKey) {
+ startTransaction();
+ return entityManager.getReference(entityClass, primaryKey);
+ }
+
+ private void startTransaction() {
+ if (!inTransaction) {
+ entityManager.getTransaction().begin();
+ inTransaction = true;
+ }
+ }
+
+ /**
+ * Note that not recommended to directly execute {@link Query#getSingleResult()}, because it will bypass exception handling,
+ * which done in {@link #getSingleResult(Query, Object)}.
+ */
+ public Query createQuery(String hql) {
+ startTransaction();
+ return entityManager.createQuery(hql);
+ }
+
+ @Override
+ public Query createNativeQuery(String sql) {
+ startTransaction();
+ return entityManager.createNativeQuery(sql);
+ }
+
+ /**
+ * @return the result or <code>defaultValue</code>, if not found
+ * @throws NonUniqueResultException if more than one result
+ */
+ public <T> T getSingleResult(Query query, T defaultValue) {
+ /*
+ * See http://jira.codehaus.org/browse/SONAR-2225
+ * By default Hibernate throws NonUniqueResultException without meaningful information about context,
+ * so we improve it here by adding all results in error message.
+ * Note that in some rare situations we can receive too many results, which may lead to OOME,
+ * but actually it will mean that database is corrupted as we don't expect more than one result
+ * and in fact org.hibernate.ejb.QueryImpl#getSingleResult() anyway does loading of several results under the hood.
+ */
+ List<T> result = query.getResultList();
+
+ if (result.size() == 1) {
+ return result.get(0);
+
+ } else if (result.isEmpty()) {
+ return defaultValue;
+
+ } else {
+ Set<T> uniqueResult = new HashSet<T>(result);
+ if (uniqueResult.size() > 1) {
+ throw new NonUniqueResultException("Expected single result, but got : " + result.toString());
+ } else {
+ return uniqueResult.iterator().next();
+ }
+ }
+ }
+
+ public <T> T getEntity(Class<T> entityClass, Object id) {
+ startTransaction();
+ return getEntityManager().find(entityClass, id);
+ }
+
+ /**
+ * @return the result or <code>null</code>, if not found
+ * @throws NonUniqueResultException if more than one result
+ */
+ public <T> T getSingleResult(Class<T> entityClass, Object... criterias) {
+ try {
+ return getSingleResult(getQueryForCriterias(entityClass, true, criterias), (T) null);
+
+ } catch (NonUniqueResultException ex) {
+ NonUniqueResultException e = new NonUniqueResultException("Expected single result for entitiy " + entityClass.getSimpleName()
+ + " with criterias : " + StringUtils.join(criterias, ","));
+ e.initCause(ex);
+ throw e;
+ }
+ }
+
+ public <T> List<T> getResults(Class<T> entityClass, Object... criterias) {
+ return getQueryForCriterias(entityClass, true, criterias).getResultList();
+ }
+
+ public <T> List<T> getResults(Class<T> entityClass) {
+ return getQueryForCriterias(entityClass, false, null).getResultList();
+ }
+
+ private Query getQueryForCriterias(Class<?> entityClass, boolean raiseError, Object... criterias) {
+ if (criterias == null && raiseError) {
+ throw new IllegalStateException("criterias parameter must be provided");
+ }
+ startTransaction();
+ StringBuilder hql = new StringBuilder("SELECT o FROM ").append(entityClass.getSimpleName()).append(" o");
+ if (criterias != null) {
+ hql.append(" WHERE ");
+ Map<String, Object> mappedCriterias = new HashMap<String, Object>();
+ for (int i = 0; i < criterias.length; i += 2) {
+ mappedCriterias.put((String) criterias[i], criterias[i + 1]);
+ }
+ buildCriteriasHQL(hql, mappedCriterias);
+ Query query = getEntityManager().createQuery(hql.toString());
+
+ for (Map.Entry<String, Object> entry : mappedCriterias.entrySet()) {
+ query.setParameter(entry.getKey(), entry.getValue());
+ }
+ return query;
+ }
+ return getEntityManager().createQuery(hql.toString());
+ }
+
+ private void buildCriteriasHQL(StringBuilder hql, Map<String, Object> mappedCriterias) {
+ for (Iterator<String> i = mappedCriterias.keySet().iterator(); i.hasNext();) {
+ String criteria = i.next();
+ hql.append("o.").append(criteria).append("=:").append(criteria);
+ if (i.hasNext()) {
+ hql.append(" AND ");
+ }
+ }
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java
index e36a22e8e3f..dc416bc5257 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java
@@ -1,78 +1,80 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.api.database;
-
-import org.sonar.api.BatchComponent;
-
-import javax.persistence.EntityManager;
-import javax.persistence.Query;
-import java.util.List;
-
-/**
- * This component should not accessible from plugin API
- *
- * @since 1.10
- */
-public abstract class DatabaseSession implements BatchComponent {
-
-
- // IMPORTANT : this value must be the same than the property
- // hibernate.jdbc.batch_size from /META-INF/persistence.xml (module sonar-database)
- public static final int BATCH_SIZE = 30;
-
-
- public abstract EntityManager getEntityManager();
-
- public abstract void start();
-
- public abstract void stop();
-
- public abstract void commit();
-
- public abstract void rollback();
-
- public abstract <T> T save(T entity);
-
- public abstract Object saveWithoutFlush(Object entity);
-
- public abstract boolean contains(Object entity);
-
- public abstract void save(Object... entities);
-
- public abstract Object merge(Object entity);
-
- public abstract void remove(Object entity);
-
- public abstract void removeWithoutFlush(Object entity);
-
- public abstract <T> T reattach(Class<T> entityClass, Object primaryKey);
-
- public abstract Query createQuery(String hql);
-
- public abstract <T> T getSingleResult(Query query, T defaultValue);
-
- public abstract <T> T getEntity(Class<T> entityClass, Object id);
-
- public abstract <T> T getSingleResult(Class<T> entityClass, Object... criterias);
-
- public abstract <T> List<T> getResults(Class<T> entityClass, Object... criterias);
-
- public abstract <T> List<T> getResults(Class<T> entityClass);
-}
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.database;
+
+import org.sonar.api.BatchComponent;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * This component should not accessible from plugin API
+ *
+ * @since 1.10
+ */
+public abstract class DatabaseSession implements BatchComponent {
+
+
+ // IMPORTANT : this value must be the same than the property
+ // hibernate.jdbc.batch_size from /META-INF/persistence.xml (module sonar-database)
+ public static final int BATCH_SIZE = 30;
+
+
+ public abstract EntityManager getEntityManager();
+
+ public abstract void start();
+
+ public abstract void stop();
+
+ public abstract void commit();
+
+ public abstract void rollback();
+
+ public abstract <T> T save(T entity);
+
+ public abstract Object saveWithoutFlush(Object entity);
+
+ public abstract boolean contains(Object entity);
+
+ public abstract void save(Object... entities);
+
+ public abstract Object merge(Object entity);
+
+ public abstract void remove(Object entity);
+
+ public abstract void removeWithoutFlush(Object entity);
+
+ public abstract <T> T reattach(Class<T> entityClass, Object primaryKey);
+
+ public abstract Query createQuery(String hql);
+
+ public abstract Query createNativeQuery(String sql);
+
+ public abstract <T> T getSingleResult(Query query, T defaultValue);
+
+ public abstract <T> T getEntity(Class<T> entityClass, Object id);
+
+ public abstract <T> T getSingleResult(Class<T> entityClass, Object... criterias);
+
+ public abstract <T> List<T> getResults(Class<T> entityClass, Object... criterias);
+
+ public abstract <T> List<T> getResults(Class<T> entityClass);
+}