]> source.dussan.org Git - sonarqube.git/commitdiff
[SONAR-2347] Close a review when its corresponding violation does not
authorFabrice Bellingard <bellingard@gmail.com>
Thu, 14 Apr 2011 10:58:19 +0000 (12:58 +0200)
committerFabrice Bellingard <bellingard@gmail.com>
Wed, 20 Apr 2011 06:57:56 +0000 (08:57 +0200)
exist anymore

plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationPersisterDecorator.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsDecoratorTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ReviewsDecoratorTest/fixture.xml [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/index/ViolationPersister.java
sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java
sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java

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