aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
committersimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
commitaeadc1f9129274949daaa57738c7c4550bdfbc7b (patch)
tree08dadf5ef7474fc41d1d48f74648f1ba8b55f34d /sonar-core
downloadsonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.tar.gz
sonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.zip
SONAR-236 remove deprecated code from checkstyle plugin + display default value of rule parameters in Q profile console
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/pom.xml90
-rw-r--r--sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java68
-rw-r--r--sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java149
-rw-r--r--sonar-core/src/main/java/org/sonar/api/database/configuration/ResourceDatabaseConfiguration.java88
-rw-r--r--sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java249
-rw-r--r--sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginDao.java79
-rw-r--r--sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginFile.java74
-rw-r--r--sonar-core/src/main/java/org/sonar/core/purge/AbstractPurge.java117
-rw-r--r--sonar-core/src/main/java/org/sonar/core/purge/DefaultPurgeContext.java86
-rw-r--r--sonar-core/src/main/java/org/sonar/core/qualitymodel/DefaultModelProvider.java129
-rw-r--r--sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java75
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresDao.java196
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresService.java130
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/BaseDao.java38
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/DaoFacade.java53
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java120
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/ProfilesDao.java66
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dao/RulesDao.java138
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java73
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java50
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/DialectRepository.java93
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java46
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java68
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java59
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java63
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/OracleSequenceGenerator.java55
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSQLSequenceGenerator.java64
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java60
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java95
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseBatch.java46
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java241
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DatabaseBatch.java27
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DatabaseConnector.java39
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DatabaseException.java41
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionFactory.java29
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionProvider.java31
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java152
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java222
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java101
-rw-r--r--sonar-core/src/main/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactory.java35
-rw-r--r--sonar-core/src/main/resources/META-INF/persistence.xml52
-rw-r--r--sonar-core/src/main/resources/ehcache.xml76
-rw-r--r--sonar-core/src/test/java/org/sonar/api/database/configuration/DatabaseConfigurationTest.java40
-rw-r--r--sonar-core/src/test/java/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest.java41
-rw-r--r--sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginDaoTest.java120
-rw-r--r--sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginTest.java42
-rw-r--r--sonar-core/src/test/java/org/sonar/core/purge/AbstractPurgeTest.java39
-rw-r--r--sonar-core/src/test/java/org/sonar/core/qualitymodel/DefaultModelProviderTest.java121
-rw-r--r--sonar-core/src/test/java/org/sonar/core/qualitymodel/ModelTest.java106
-rw-r--r--sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java83
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresDaoTest.java165
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresServiceTest.java153
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dao/MeasuresDaoTest.java107
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dao/ProfilesDaoTest.java68
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dao/RulesDaoTest.java131
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/DerbyTest.java32
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java70
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/HsqlDbTest.java32
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/MsSqlTest.java37
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/MySqlTest.java37
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/OracleSequenceGeneratorTest.java43
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/OracleTest.java32
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSQLSequenceGeneratorTest.java43
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSqlTest.java33
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/entity/PropertyTest.java39
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/entity/SchemaMigrationTest.java65
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java114
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/session/DatabaseSessionTest.java120
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java92
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactoryTest.java49
-rw-r--r--sonar-core/src/test/java/org/sonar/jpa/test/AbstractDbUnitTestCase.java247
-rw-r--r--sonar-core/src/test/resources/logback-test.xml27
-rw-r--r--sonar-core/src/test/resources/org/sonar/api/database/configuration/DatabaseConfigurationTest/some-properties.xml6
-rw-r--r--sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldNotLoadGlobalProperties.xml16
-rw-r--r--sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldOverrideGlobalPropertiesBySpecificResourceProperties.xml16
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml6
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml12
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml15
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml7
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots-result.xml111
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots.xml111
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/noDefinitionsToRegister-result.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/registerOnlyNewDefinitions-result.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/reset-result.xml15
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/shared.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/saveModelAndCharacteristics.xml4
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testGraphOfCharacteristics.xml8
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testTreeOfCharacteristics.xml5
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/rule/DefaultRuleProviderTest/shared.xml20
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/sharedFixture.xml23
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure-result.xml22
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure.xml26
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots-result.xml11
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots.xml11
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetAsyncMeasureSnapshotsFromSnapshotId.xml26
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetLastAsyncMeasureSnapshot.xml12
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshot.xml11
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshotsUntilDate.xml50
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextSnapshotsUntilDate.xml35
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousAsyncMeasureSnapshots.xml18
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousSnapshot.xml24
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot-result.xml28
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot.xml24
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure-result.xml28
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure.xml24
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric-result.xml37
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric.xml33
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot-result.xml42
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot.xml36
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot-result.xml45
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot.xml43
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric-result.xml59
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric.xml49
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot-result.xml24
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot.xml24
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots-result.xml44
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots.xml36
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot-result.xml43
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot.xml42
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot-result.xml47
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot.xml42
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure-result.xml30
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure.xml32
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure-result.xml41
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure.xml39
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/sharedFixture.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/ExtensionDaoTest/shared.xml18
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/ProfilesDaoTest/shouldGetProfiles.xml7
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile-result.xml19
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldCountNumberOfRulesOfACategoryForGivenPlugins.xml17
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter-result.xml22
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter.xml21
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules-result.xml19
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules.xml23
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetActiveRules.xml19
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleParams.xml19
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleWithRuleKeyAndPluginKey.xml8
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRules.xml15
-rw-r--r--sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldSynchronizeRuleOfActiveRule.xml13
140 files changed, 7883 insertions, 0 deletions
diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml
new file mode 100644
index 00000000000..2874417e059
--- /dev/null
+++ b/sonar-core/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>2.3-SNAPSHOT</version>
+ </parent>
+ <artifactId>sonar-core</artifactId>
+ <name>Sonar :: Core</name>
+ <description>Core components shared to batch and server</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-commons-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geronimo-spec</groupId>
+ <artifactId>geronimo-spec-jta</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-ehcache</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.dbunit</groupId>
+ <artifactId>dbunit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java b/sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java
new file mode 100644
index 00000000000..cd945c5425b
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java
@@ -0,0 +1,68 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import java.util.List;
+
+/**
+ * IMPORTANT : This class can't be moved to org.sonar.jpa.dao for backward-compatibility reasons.
+ * This class is still used in some plugins.
+ *
+ * @since 1.10
+ */
+public class DatabaseConfiguration extends BaseConfiguration {
+ private DatabaseSessionFactory sessionFactory;
+ private DatabaseSession session;
+
+ public DatabaseConfiguration(DatabaseSessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ load();
+ }
+
+ public DatabaseConfiguration(DatabaseSession session) {
+ this.session = session;
+ load();
+ }
+
+ public void load() {
+ clear();
+
+ List<Property> properties = getSession()
+ .createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId is null and p.userId is null")
+ .getResultList();
+
+ if (properties != null) {
+ for (Property property : properties) {
+ setProperty(property.getKey(), property.getValue());
+ }
+ }
+ }
+
+ private DatabaseSession getSession() {
+ if (session != null) {
+ return session;
+ }
+ return sessionFactory.getSession();
+ }
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java b/sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java
new file mode 100644
index 00000000000..efafbcc508d
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java
@@ -0,0 +1,149 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+
+/**
+ * IMPORTANT : This class can't be moved to org.sonar.jpa.dao for backward-compatibility reasons.
+ * This class is still used in some plugins.
+ *
+ * @since 1.10
+ */
+@Entity
+@Table(name = "properties")
+public class Property extends BaseIdentifiable {
+
+ @Column(name = "prop_key", updatable = true, nullable = true)
+ private String key;
+
+ @Column(name = "text_value", updatable = true, nullable = true, length = 167772150)
+ @Lob
+ private char[] value;
+
+ @Column(name = "resource_id", updatable = true, nullable = true)
+ private Integer resourceId;
+
+ @Column(name = "user_id", updatable = true, nullable = true)
+ private Integer userId;
+
+ public Property(String key, String value) {
+ this(key, value, null);
+ }
+
+ public Property(String key, String value, Integer resourceId) {
+ this.key = key;
+ if (value != null) {
+ this.value = value.toCharArray();
+
+ } else {
+ this.value = null;
+ }
+ this.resourceId = resourceId;
+ }
+
+ public Property() {
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ if (value != null) {
+ return new String(value);
+ }
+ return null;
+ }
+
+ public void setValue(String value) {
+ if (value != null) {
+ this.value = value.toCharArray();
+ } else {
+ this.value = null;
+ }
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(Integer resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public Integer getUserId() {
+ return userId;
+ }
+
+ public Property setUserId(Integer userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Property property = (Property) o;
+ if (!key.equals(property.key)) {
+ return false;
+ }
+ if (resourceId != null ? !resourceId.equals(property.resourceId) : property.resourceId != null) {
+ return false;
+ }
+ return !(userId != null ? !userId.equals(property.userId) : property.userId != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key.hashCode();
+ result = 31 * result + (resourceId != null ? resourceId.hashCode() : 0);
+ result = 31 * result + (userId != null ? userId.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("key", key)
+ .append("resource", resourceId)
+ .append("user", userId)
+ .append("value", value)
+ .toString();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/api/database/configuration/ResourceDatabaseConfiguration.java b/sonar-core/src/main/java/org/sonar/api/database/configuration/ResourceDatabaseConfiguration.java
new file mode 100644
index 00000000000..4cead063e31
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/api/database/configuration/ResourceDatabaseConfiguration.java
@@ -0,0 +1,88 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+import org.sonar.api.database.model.ResourceModel;
+
+import java.util.List;
+
+/**
+ *
+ * IMPORTANT : This class can't be moved to org.sonar.jpa.dao for backward-compatibility reasons.
+ * This class is still used in some plugins.
+ *
+ * @since 1.10
+ */
+public class ResourceDatabaseConfiguration extends BaseConfiguration {
+ private final DatabaseSessionFactory sessionFactory;
+ private Integer resourceId = null;
+
+ public ResourceDatabaseConfiguration(DatabaseSessionFactory sessionFactory, ResourceModel resource) {
+ this.sessionFactory = sessionFactory;
+ if (resource != null) {
+ this.resourceId = resource.getId();
+ }
+ load();
+ }
+
+ public ResourceDatabaseConfiguration(DatabaseSessionFactory sessionFactory, Integer resourceId) {
+ this.sessionFactory = sessionFactory;
+ this.resourceId = resourceId;
+ load();
+ }
+
+ public ResourceDatabaseConfiguration(DatabaseSessionFactory sessionFactory, String resourceKey) {
+ this.sessionFactory = sessionFactory;
+
+ ResourceModel resource = sessionFactory.getSession().getSingleResult(ResourceModel.class, "key", resourceKey);
+ if (resource != null) {
+ this.resourceId = resource.getId();
+ }
+ load();
+ }
+
+ public void load() {
+ clear();
+
+ loadResourceProperties();
+ }
+
+ private void loadResourceProperties() {
+ if (resourceId != null) {
+ List<Property> properties = sessionFactory.getSession()
+ .createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId=:resourceId and p.userId is null")
+ .setParameter("resourceId", resourceId)
+ .getResultList();
+
+ registerProperties(properties);
+ }
+ }
+
+ private void registerProperties(List<Property> properties) {
+ if (properties != null) {
+ for (Property property : properties) {
+ setProperty(property.getKey(), property.getValue());
+ }
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
new file mode 100644
index 00000000000..c178f59acaa
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPlugin.java
@@ -0,0 +1,249 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.plugin;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.hibernate.annotations.Cascade;
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.*;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Installed plugins
+ *
+ * @since 2.2
+ */
+@Entity
+@Table(name = "plugins")
+public class JpaPlugin extends BaseIdentifiable {
+
+ @Column(name = "plugin_key", updatable = true, nullable = false, length = 100)
+ private String key;
+
+ @Column(name = "version", updatable = true, nullable = true, length = 100)
+ private String version;
+
+ @Column(name = "name", updatable = true, nullable = true, length = 100)
+ private String name;
+
+ @Column(name = "description", updatable = true, nullable = true, length = 3000)
+ private String description;
+
+ @Column(name = "organization", updatable = true, nullable = true, length = 100)
+ private String organization;
+
+ @Column(name = "organization_url", updatable = true, nullable = true, length = 500)
+ private String organizationUrl;
+
+ @Column(name = "license", updatable = true, nullable = true, length = 50)
+ private String license;
+
+ @Column(name = "installation_date", updatable = true, nullable = true)
+ private Date installationDate;
+
+ @Column(name = "plugin_class", updatable = true, nullable = true, length = 100)
+ private String pluginClass;
+
+ @Column(name = "homepage", updatable = true, nullable = true, length = 500)
+ private String homepage;
+
+ @Column(name = "core", updatable = true, nullable = true)
+ private Boolean core;
+
+ @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
+ org.hibernate.annotations.CascadeType.DELETE,
+ org.hibernate.annotations.CascadeType.MERGE,
+ org.hibernate.annotations.CascadeType.PERSIST,
+ org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
+ @OneToMany(mappedBy = "plugin", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
+ private List<JpaPluginFile> files = new ArrayList<JpaPluginFile>();
+
+ public JpaPlugin() {
+ }
+
+ public JpaPlugin(String pluginKey) {
+ if (StringUtils.isBlank(pluginKey)) {
+ throw new IllegalArgumentException("LocalExtension.pluginKey can not be blank");
+ }
+ this.key = pluginKey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public JpaPlugin setKey(String s) {
+ this.key = s;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public JpaPlugin setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public JpaPlugin setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public String getOrganization() {
+ return organization;
+ }
+
+ public JpaPlugin setOrganization(String organization) {
+ this.organization = organization;
+ return this;
+ }
+
+ public String getOrganizationUrl() {
+ return organizationUrl;
+ }
+
+ public JpaPlugin setOrganizationUrl(URI uri) {
+ this.organizationUrl = (uri != null ? uri.toString() : null);
+ return this;
+ }
+
+ public JpaPlugin setOrganizationUrl(String s) {
+ this.organizationUrl = s;
+ return this;
+ }
+
+ public String getLicense() {
+ return license;
+ }
+
+ public JpaPlugin setLicense(String license) {
+ this.license = license;
+ return this;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public JpaPlugin setVersion(String s) {
+ this.version = s;
+ return this;
+ }
+
+ public Date getInstallationDate() {
+ return installationDate;
+ }
+
+ public JpaPlugin setInstallationDate(Date installationDate) {
+ this.installationDate = installationDate;
+ return this;
+ }
+
+ public String getPluginClass() {
+ return pluginClass;
+ }
+
+ public JpaPlugin setPluginClass(String s) {
+ this.pluginClass = s;
+ return this;
+ }
+
+ public String getHomepage() {
+ return homepage;
+ }
+
+ public JpaPlugin setHomepage(URI uri) {
+ this.homepage = (uri != null ? uri.toString() : null);
+ return this;
+ }
+
+ public JpaPlugin setHomepage(String s) {
+ this.homepage = s;
+ return this;
+ }
+
+ public Boolean isCore() {
+ return core;
+ }
+
+ public JpaPlugin setCore(Boolean b) {
+ this.core = b;
+ return this;
+ }
+
+ public void createFile(String filename) {
+ JpaPluginFile file = new JpaPluginFile(this, filename);
+ this.files.add(file);
+ }
+
+ public List<JpaPluginFile> getFiles() {
+ return files;
+ }
+
+ public void removeFiles() {
+ files.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ JpaPlugin other = (JpaPlugin) o;
+ return key.equals(other.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("id", getId())
+ .append("key", key)
+ .append("version", version)
+ .append("homepage", homepage)
+ .append("installationDate", installationDate)
+ .toString();
+ }
+
+
+ public static JpaPlugin create(String pluginKey) {
+ return new JpaPlugin(pluginKey);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginDao.java b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginDao.java
new file mode 100644
index 00000000000..1fe44ab9e7b
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginDao.java
@@ -0,0 +1,79 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.plugin;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import javax.persistence.Query;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @since 2.2
+ */
+public class JpaPluginDao implements BatchComponent, ServerComponent {
+
+ private DatabaseSessionFactory sessionFactory;
+
+ public JpaPluginDao(DatabaseSessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ public List<JpaPlugin> getPlugins() {
+ DatabaseSession session = sessionFactory.getSession();
+ Query query = session.createQuery("FROM " + JpaPlugin.class.getSimpleName());
+ return (List<JpaPlugin>) query.getResultList();
+ }
+
+ public List<JpaPluginFile> getPluginFiles() {
+ DatabaseSession session = sessionFactory.getSession();
+ Query query = session.createQuery("FROM " + JpaPluginFile.class.getSimpleName());
+ return (List<JpaPluginFile>) query.getResultList();
+ }
+
+ public void register(List<JpaPlugin> plugins) {
+ DatabaseSession session = sessionFactory.getSession();
+ List<Integer> ids = new ArrayList<Integer>();
+ for (JpaPlugin plugin : plugins) {
+ session.saveWithoutFlush(plugin);
+ ids.add(plugin.getId());
+ }
+ session.commit();
+
+ if (ids.isEmpty()) {
+ session.createQuery("DELETE " + JpaPluginFile.class.getSimpleName()).executeUpdate();
+ session.createQuery("DELETE " + JpaPlugin.class.getSimpleName()).executeUpdate();
+
+ } else {
+ Query query = session.createQuery("DELETE " + JpaPluginFile.class.getSimpleName() + " WHERE plugin.id NOT IN (:ids)");
+ query.setParameter("ids", ids);
+ query.executeUpdate();
+
+ query = session.createQuery("DELETE " + JpaPlugin.class.getSimpleName() + " WHERE id NOT IN (:ids)");
+ query.setParameter("ids", ids);
+ query.executeUpdate();
+
+ }
+ session.commit();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginFile.java b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginFile.java
new file mode 100644
index 00000000000..03c4934e4aa
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/plugin/JpaPluginFile.java
@@ -0,0 +1,74 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.plugin;
+
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.*;
+
+/**
+ * @since 2.2
+ */
+@Entity
+@Table(name = "plugin_files")
+public class JpaPluginFile extends BaseIdentifiable {
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "plugin_id")
+ private JpaPlugin plugin;
+
+ @Column(name = "filename", updatable = true, nullable = false, length = 100)
+ private String filename;
+
+ public JpaPluginFile() {
+ }
+
+ public JpaPluginFile(JpaPlugin plugin, String filename) {
+ this.plugin = plugin;
+ this.filename = filename;
+ }
+
+ public JpaPlugin getPlugin() {
+ return plugin;
+ }
+
+ public String getPluginKey() {
+ return plugin.getKey();
+ }
+
+ public void setPlugin(JpaPlugin plugin) {
+ this.plugin = plugin;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public String getPath() {
+ return new StringBuilder()
+ .append(plugin.getKey())
+ .append("/")
+ .append(filename).toString();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/purge/AbstractPurge.java b/sonar-core/src/main/java/org/sonar/core/purge/AbstractPurge.java
new file mode 100644
index 00000000000..fe1d9bf40b9
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/purge/AbstractPurge.java
@@ -0,0 +1,117 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.purge;
+
+import org.sonar.api.batch.Purge;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.*;
+import org.sonar.api.design.DependencyDto;
+
+import javax.persistence.Query;
+import java.util.List;
+
+public abstract class AbstractPurge implements Purge {
+
+ private static final int MAX_IN_ELEMENTS = 950;
+
+ private int sqlInPageSize = MAX_IN_ELEMENTS;
+ private DatabaseSession session;
+
+ public AbstractPurge(DatabaseSession session) {
+ this.session = session;
+ }
+
+ protected DatabaseSession getSession() {
+ return session;
+ }
+
+ /**
+ * Delete SNAPSHOTS and all dependent tables (MEASURES, ...)
+ */
+ protected void deleteSnapshotData(List<Integer> snapshotIds) {
+ deleteMeasuresBySnapshotId(snapshotIds);
+ deleteSources(snapshotIds);
+ deleteViolations(snapshotIds);
+ deleteDependencies(snapshotIds);
+ deleteSnapshots(snapshotIds);
+ }
+
+ protected void deleteDependencies(List<Integer> snapshotIds) {
+ executeQuery(snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.fromSnapshotId in (:ids)");
+ executeQuery(snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.toSnapshotId in (:ids)");
+ }
+
+ /**
+ * Delete all measures, including MEASURE_DATA
+ */
+ protected void deleteMeasuresBySnapshotId(List<Integer> snapshotIds) {
+ executeQuery(snapshotIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.snapshotId in (:ids)");
+ executeQuery(snapshotIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.snapshotId in (:ids)");
+ }
+
+ /**
+ * Delete all measures, including MEASURE_DATA
+ */
+ protected void deleteMeasuresById(List<Integer> measureIds) {
+ executeQuery(measureIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.measure.id in (:ids)");
+ executeQuery(measureIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.id in (:ids)");
+ }
+
+ /**
+ * Delete SNAPSHOT_SOURCES table
+ */
+ protected void deleteSources(List<Integer> snapshotIds) {
+ executeQuery(snapshotIds, "delete from " + SnapshotSource.class.getSimpleName() + " e where e.snapshotId in (:ids)");
+ }
+
+ /**
+ * Delete violations (RULE_FAILURES table)
+ */
+ protected void deleteViolations(List<Integer> snapshotIds) {
+ executeQuery(snapshotIds, "delete from " + RuleFailureModel.class.getSimpleName() + " e where e.snapshotId in (:ids)");
+ }
+
+ /**
+ * Delete SNAPSHOTS table
+ */
+ protected void deleteSnapshots(List<Integer> snapshotIds) {
+ executeQuery(snapshotIds, "delete from " + Snapshot.class.getSimpleName() + " s where s.id in (:ids)");
+ }
+
+
+ /**
+ * Paginate execution of SQL requests to avoid exceeding size of rollback segment
+ */
+ protected void executeQuery(List<Integer> ids, String hql) {
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+
+ int index = 0;
+ while (index < ids.size()) {
+ Query query = session.createQuery(hql);
+ List<Integer> paginedSids = ids.subList(index, Math.min(ids.size(), index + sqlInPageSize));
+ query.setParameter("ids", paginedSids);
+ query.executeUpdate();
+ index += sqlInPageSize;
+ session.commit();
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/purge/DefaultPurgeContext.java b/sonar-core/src/main/java/org/sonar/core/purge/DefaultPurgeContext.java
new file mode 100644
index 00000000000..f2106a6bd58
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/purge/DefaultPurgeContext.java
@@ -0,0 +1,86 @@
+package org.sonar.core.purge;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.model.Snapshot;
+
+public class DefaultPurgeContext implements PurgeContext {
+
+ private Integer currentSid;
+ private Integer lastSid;
+
+ public DefaultPurgeContext() {
+ }
+
+ public DefaultPurgeContext(Snapshot currentSnapshot) {
+ this(currentSnapshot, null);
+ }
+
+ public DefaultPurgeContext(Snapshot currentSnapshot, Snapshot lastSnapshot) {
+ if (currentSnapshot != null) {
+ currentSid = currentSnapshot.getId();
+ }
+ if (lastSnapshot != null) {
+ lastSid = lastSnapshot.getId();
+ }
+ }
+
+ public DefaultPurgeContext(Integer currentSid, Integer lastSid) {
+ this.currentSid = currentSid;
+ this.lastSid = lastSid;
+ }
+
+ public DefaultPurgeContext setLastSnapshotId(Integer lastSid) {
+ this.lastSid = lastSid;
+ return this;
+ }
+
+ public DefaultPurgeContext setCurrentSnapshotId(Integer currentSid) {
+ this.currentSid = currentSid;
+ return this;
+ }
+
+ public Integer getPreviousSnapshotId() {
+ return lastSid;
+ }
+
+ public Integer getLastSnapshotId() {
+ return currentSid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultPurgeContext context = (DefaultPurgeContext) o;
+
+ if (!currentSid.equals(context.currentSid)) {
+ return false;
+ }
+ if (lastSid != null ? !lastSid.equals(context.lastSid) : context.lastSid != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = lastSid != null ? lastSid.hashCode() : 0;
+ result = 31 * result + currentSid.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("currentSid", currentSid)
+ .append("lastSid", lastSid)
+ .toString();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/qualitymodel/DefaultModelProvider.java b/sonar-core/src/main/java/org/sonar/core/qualitymodel/DefaultModelProvider.java
new file mode 100644
index 00000000000..7f8f561bc97
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/qualitymodel/DefaultModelProvider.java
@@ -0,0 +1,129 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.qualitymodel;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.qualitymodel.ModelDefinition;
+import org.sonar.api.qualitymodel.ModelProvider;
+import org.sonar.api.utils.Logs;
+import org.sonar.api.utils.SonarException;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import javax.persistence.Query;
+
+public class DefaultModelProvider implements ModelProvider {
+
+ private ModelDefinition[] definitions;
+ private DatabaseSessionFactory sessionFactory;
+
+ public DefaultModelProvider(DatabaseSessionFactory sessionFactory, ModelDefinition[] definitions) {
+ this.sessionFactory = sessionFactory;
+ this.definitions = definitions;
+ }
+
+ /**
+ * this constructor is used when there are no templates
+ */
+ public DefaultModelProvider(DatabaseSessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ this.definitions = new ModelDefinition[0];
+ }
+
+ /**
+ * Executed when the server starts
+ */
+ public void registerDefinitions() {
+ DatabaseSession session = sessionFactory.getSession();
+ for (ModelDefinition definition : definitions) {
+ if (StringUtils.isNotBlank(definition.getName()) && !exists(session, definition.getName())) {
+ Logs.INFO.info("Register quality model: " + definition.getName());
+ Model model = definition.create();
+ if (StringUtils.isBlank(model.getName())) {
+ model.setName(definition.getName());
+ }
+ insert(session, model);
+ session.commit();
+ }
+ }
+ }
+
+ public Model reset(String name) {
+ ModelDefinition definition = findDefinitionByName(name);
+ if (definition == null) {
+ throw new SonarException("Can not reset quality model. Definition not found: " + name);
+ }
+
+ LoggerFactory.getLogger(getClass()).info("Reset quality model: " + name);
+ Model model = definition.create();
+ return reset(model);
+ }
+
+
+
+ Model reset(Model model) {
+ DatabaseSession session = sessionFactory.getSession();
+ try {
+ delete(session, model.getName());
+ model = insert(session, model);
+ session.commit();
+ return model;
+
+ } catch (RuntimeException e) {
+ session.rollback();
+ throw e;
+ }
+ }
+
+ public ModelDefinition findDefinitionByName(String name) {
+ for (ModelDefinition definition : definitions) {
+ if (StringUtils.equals(name, definition.getName())) {
+ return definition;
+ }
+ }
+ return null;
+ }
+
+ public Model findByName(String name) {
+ DatabaseSession session = sessionFactory.getSession();
+ return session.getSingleResult(Model.class, "name", name);
+ }
+
+ public static void delete(DatabaseSession session, String name) {
+ Model model = session.getSingleResult(Model.class, "name", name);
+ if (model != null) {
+ session.removeWithoutFlush(model);
+ session.commit();
+ }
+ }
+
+ public static Model insert(DatabaseSession session, Model model) {
+ return (Model) session.saveWithoutFlush(model);
+ }
+
+ public static boolean exists(DatabaseSession session, String name) {
+ Query query = session.getEntityManager().createQuery("SELECT COUNT(qm) FROM " + Model.class.getSimpleName() + " qm WHERE qm.name=:name");
+ query.setParameter("name", name);
+ Number count = (Number) query.getSingleResult();
+ return count.intValue() > 0;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java
new file mode 100644
index 00000000000..faac5971355
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.rule;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleProvider;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import javax.persistence.Query;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultRuleProvider implements RuleProvider {
+
+ private DatabaseSessionFactory sessionFactory;
+
+ public DefaultRuleProvider(DatabaseSessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ public Rule findByKey(String repositoryKey, String key) {
+ return sessionFactory.getSession().getSingleResult(Rule.class, "pluginName", repositoryKey, "key", key, "enabled", true);
+ }
+
+ public Rule find(RuleQuery query) {
+ return (Rule) createHqlQuery(query).getSingleResult();
+ }
+
+ public Collection<Rule> findAll(RuleQuery query) {
+ return createHqlQuery(query).getResultList();
+ }
+
+ private Query createHqlQuery(RuleQuery query) {
+ StringBuilder hql = new StringBuilder().append("from ").append(Rule.class.getSimpleName()).append(" where enabled=true ");
+ Map<String,Object> params = new HashMap<String,Object>();
+ if (StringUtils.isNotBlank(query.getRepositoryKey())) {
+ hql.append("AND pluginName=:repositoryKey");
+ params.put("repositoryKey", query.getRepositoryKey());
+ }
+ if (StringUtils.isNotBlank(query.getKey())) {
+ hql.append("AND key=:key");
+ params.put("key", query.getKey());
+ }
+ if (StringUtils.isNotBlank(query.getConfigKey())) {
+ hql.append("AND configKey=:configKey");
+ params.put("configKey", query.getConfigKey());
+ }
+
+ Query hqlQuery = sessionFactory.getSession().createQuery(hql.toString());
+ for (Map.Entry<String, Object> entry : params.entrySet()) {
+ hqlQuery.setParameter(entry.getKey(), entry.getValue());
+ }
+ return hqlQuery;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresDao.java
new file mode 100644
index 00000000000..4e2ece885ab
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresDao.java
@@ -0,0 +1,196 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.AsyncMeasureSnapshot;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import java.util.Date;
+import java.util.List;
+
+public class AsyncMeasuresDao extends BaseDao {
+
+ public AsyncMeasuresDao(DatabaseSession session) {
+ super(session);
+ }
+
+ public MeasureModel getAsyncMeasure(Long asyncMeasureId) {
+ return getSession().getEntityManager().find(MeasureModel.class, asyncMeasureId);
+ }
+
+ public void deleteAsyncMeasure(MeasureModel asyncMeasure) {
+ deleteAsyncMeasureSnapshots(asyncMeasure.getId());
+ getSession().remove(asyncMeasure);
+ }
+
+ public Snapshot getPreviousSnapshot(Snapshot s) {
+ try {
+ return (Snapshot) getSession().createQuery(
+ "SELECT s FROM Snapshot s " +
+ "WHERE s.createdAt<:date " +
+ "AND s.scope=:scope " +
+ "AND s.resourceId=:resourceId " +
+ "ORDER BY s.createdAt DESC")
+ .setParameter("date", s.getCreatedAt())
+ .setParameter("scope", s.getScope())
+ .setParameter("resourceId", s.getResourceId())
+ .setMaxResults(1)
+ .getSingleResult();
+ } catch (NoResultException ex) {
+ return null;
+ }
+ }
+
+ public List<Snapshot> getNextSnapshotsUntilDate(MeasureModel measure, Date date) {
+ Query query = getSession().createQuery(
+ "SELECT s FROM Snapshot s " +
+ "WHERE s.resourceId=:projectId " +
+ "AND s.createdAt>=:beginDate " +
+ (date != null ? "AND s.createdAt<:endDate " : "") +
+ "AND s.scope=:scope " +
+ "ORDER BY s.createdAt ASC ")
+ .setParameter("projectId", measure.getProjectId())
+ .setParameter("beginDate", measure.getMeasureDate())
+ .setParameter("scope", ResourceModel.SCOPE_PROJECT);
+ if (date != null) {
+ query.setParameter("endDate", date);
+ }
+ return query.getResultList();
+ }
+
+ public AsyncMeasureSnapshot createAsyncMeasureSnapshot(Long asyncMeasureId, Integer snapshotId, Date AsyncMeasureDate, Date snapshotDate, Integer metricId, Integer projectId) {
+ AsyncMeasureSnapshot asyncMeasureSnapshot = new AsyncMeasureSnapshot(asyncMeasureId, snapshotId, AsyncMeasureDate, snapshotDate, metricId, projectId);
+ getSession().save(asyncMeasureSnapshot);
+ return asyncMeasureSnapshot;
+ }
+
+ public void updateAsyncMeasureSnapshot(AsyncMeasureSnapshot asyncMeasureSnapshot, Snapshot snapshot) {
+ if (snapshot != null) {
+ asyncMeasureSnapshot.setSnapshotId(snapshot.getId());
+ asyncMeasureSnapshot.setSnapshotDate(snapshot.getCreatedAt());
+ } else {
+ asyncMeasureSnapshot.setSnapshotId(null);
+ asyncMeasureSnapshot.setSnapshotDate(null);
+ }
+ getSession().merge(asyncMeasureSnapshot);
+ }
+
+ public void removeSnapshotFromAsyncMeasureSnapshot(AsyncMeasureSnapshot asyncMeasureSnapshot) {
+ asyncMeasureSnapshot.setSnapshotId(null);
+ asyncMeasureSnapshot.setSnapshotDate(null);
+ getSession().merge(asyncMeasureSnapshot);
+ }
+
+
+ public AsyncMeasureSnapshot getNextAsyncMeasureSnapshot(Integer projetcId, Integer metricId, Date date) {
+ try {
+ return (AsyncMeasureSnapshot) getSession().createQuery(
+ "SELECT ams FROM AsyncMeasureSnapshot ams " +
+ "WHERE ams.projectId=:projectId " +
+ "AND ams.metricId=:metricId " +
+ "AND ams.measureDate>:date " +
+ "ORDER BY ams.measureDate ASC")
+ .setParameter("projectId", projetcId)
+ .setParameter("metricId", metricId)
+ .setParameter("date", date)
+ .setMaxResults(1)
+ .getSingleResult();
+ } catch (NoResultException ex) {
+ return null;
+ }
+ }
+
+ public List<AsyncMeasureSnapshot> getNextAsyncMeasureSnapshotsUntilDate(MeasureModel asyncMeasure, Date endDate) {
+ Query query = getSession().createQuery(
+ "SELECT ams FROM AsyncMeasureSnapshot ams " +
+ "WHERE ams.projectId=:projectId " +
+ "AND ams.metricId=:metricId " +
+ (endDate != null ? "AND ams.measureDate<:endDate " : "") +
+ "AND ams.snapshotDate>=:measureDate " +
+ "ORDER BY ams.snapshotDate ASC ")
+ .setParameter("projectId", asyncMeasure.getProjectId())
+ .setParameter("metricId", asyncMeasure.getMetric().getId())
+ .setParameter("measureDate", asyncMeasure.getMeasureDate());
+ if (endDate != null) {
+ query.setParameter("endDate", endDate);
+ }
+ return query.getResultList();
+ }
+
+ public List<AsyncMeasureSnapshot> getPreviousAsyncMeasureSnapshots(Integer projectId, Date beginDate, Date endDate) {
+ Query query = getSession().createQuery(
+ "SELECT ams FROM AsyncMeasureSnapshot ams " +
+ "WHERE ams.projectId=:projectId " +
+ "AND ams.measureDate<=:endDate " +
+ (beginDate != null ? "AND ams.measureDate>:beginDate " : "") +
+ "AND ams.snapshotId IS NULL " +
+ "ORDER BY ams.measureDate ASC")
+ .setParameter("projectId", projectId)
+ .setParameter("endDate", endDate);
+ if (beginDate != null) {
+ query.setParameter("beginDate", beginDate);
+ }
+ return query.getResultList();
+ }
+
+ public List<AsyncMeasureSnapshot> getAsyncMeasureSnapshotsFromSnapshotId(Integer snapshotId, List<Integer> metricIdsToExclude) {
+ Query query = getSession().createQuery(
+ "SELECT ams FROM AsyncMeasureSnapshot ams " +
+ "WHERE ams.snapshotId=:snapshotId " +
+ (!metricIdsToExclude.isEmpty() ? "AND ams.metricId NOT IN (:metricIdsToExclude) " : "") +
+ "ORDER BY ams.measureDate ASC")
+ .setParameter("snapshotId", snapshotId);
+ if (!metricIdsToExclude.isEmpty()) {
+ query.setParameter("metricIdsToExclude", metricIdsToExclude);
+ }
+ return query.getResultList();
+ }
+
+ public AsyncMeasureSnapshot getLastAsyncMeasureSnapshot(Integer projetcId, Integer metricId, Date date) {
+ try {
+ return (AsyncMeasureSnapshot) getSession().createQuery(
+ "SELECT ams FROM AsyncMeasureSnapshot ams " +
+ "WHERE ams.projectId=:projectId " +
+ "AND ams.metricId=:metricId " +
+ "AND ams.measureDate<:date " +
+ "ORDER BY ams.measureDate DESC")
+ .setParameter("projectId", projetcId)
+ .setParameter("metricId", metricId)
+ .setParameter("date", date)
+ .setMaxResults(1)
+ .getSingleResult();
+ } catch (NoResultException ex) {
+ return null;
+ }
+ }
+
+ public void deleteAsyncMeasureSnapshots(Long asyncMeasureId) {
+ getSession().createQuery(
+ "DELETE FROM AsyncMeasureSnapshot ams WHERE ams.measureId=:measureId")
+ .setParameter("measureId", asyncMeasureId)
+ .executeUpdate();
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresService.java b/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresService.java
new file mode 100644
index 00000000000..ae296d1e356
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/AsyncMeasuresService.java
@@ -0,0 +1,130 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.AsyncMeasureSnapshot;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.Snapshot;
+
+import java.util.*;
+
+public class AsyncMeasuresService {
+ private final DatabaseSession session;
+
+ public AsyncMeasuresService(DatabaseSession session) {
+ this.session = session;
+ }
+
+ public void refresh(Snapshot snapshot) {
+ AsyncMeasuresDao dao = new AsyncMeasuresDao(session);
+ Snapshot previousSnapshot = dao.getPreviousSnapshot(snapshot);
+ Date datePreviousSnapshot = (previousSnapshot != null ? previousSnapshot.getCreatedAt() : null);
+
+ List<AsyncMeasureSnapshot> previousAsyncMeasureSnapshots = dao.getPreviousAsyncMeasureSnapshots(
+ snapshot.getResourceId(), datePreviousSnapshot, snapshot.getCreatedAt());
+ if (previousSnapshot != null) {
+ previousAsyncMeasureSnapshots.addAll(dao.getAsyncMeasureSnapshotsFromSnapshotId(
+ previousSnapshot.getId(), getMetricIds(previousAsyncMeasureSnapshots)));
+ }
+
+ for (AsyncMeasureSnapshot asyncMeasureSnapshot : purge(previousAsyncMeasureSnapshots)) {
+ if (asyncMeasureSnapshot.getSnapshotId() == null) {
+ dao.updateAsyncMeasureSnapshot(asyncMeasureSnapshot, snapshot);
+ } else {
+ dao.createAsyncMeasureSnapshot(
+ asyncMeasureSnapshot.getMeasureId(), snapshot.getId(), asyncMeasureSnapshot.getMeasureDate(),
+ snapshot.getCreatedAt(), asyncMeasureSnapshot.getMetricId(), asyncMeasureSnapshot.getProjectId());
+ }
+ }
+ session.commit();
+ }
+
+ public void registerMeasure(Long id) {
+ AsyncMeasuresDao dao = new AsyncMeasuresDao(session);
+ registerMeasure(dao.getAsyncMeasure(id), dao);
+ }
+
+ private List<Integer> getMetricIds(List<AsyncMeasureSnapshot> list) {
+ List<Integer> ids = new ArrayList<Integer>();
+ for (AsyncMeasureSnapshot ams : list) {
+ ids.add(ams.getMetricId());
+ }
+ return ids;
+ }
+
+ private Collection<AsyncMeasureSnapshot> purge(List<AsyncMeasureSnapshot> list) {
+ Map<Integer, AsyncMeasureSnapshot> measuresById = new LinkedHashMap<Integer, AsyncMeasureSnapshot>();
+ for (AsyncMeasureSnapshot currentAsyncMeasureSnapshot : list) {
+ AsyncMeasureSnapshot asyncMeasureSnapshotFromMap = measuresById.get(currentAsyncMeasureSnapshot.getMetricId());
+ if (asyncMeasureSnapshotFromMap != null) {
+ if (asyncMeasureSnapshotFromMap.getMeasureDate().before(currentAsyncMeasureSnapshot.getMeasureDate())) {
+ measuresById.put(currentAsyncMeasureSnapshot.getMetricId(), currentAsyncMeasureSnapshot);
+ }
+ } else {
+ measuresById.put(currentAsyncMeasureSnapshot.getMetricId(), currentAsyncMeasureSnapshot);
+ }
+ }
+ return measuresById.values();
+ }
+
+
+ public void deleteMeasure(Long id) {
+ AsyncMeasuresDao dao = new AsyncMeasuresDao(session);
+ MeasureModel measure = dao.getAsyncMeasure(id);
+ AsyncMeasureSnapshot pastAsyncMeasureSnapshot = dao.getLastAsyncMeasureSnapshot(measure.getProjectId(),
+ measure.getMetric().getId(), measure.getMeasureDate());
+ dao.deleteAsyncMeasure(measure);
+ if (pastAsyncMeasureSnapshot != null) {
+ MeasureModel pastAsyncMeasure = dao.getAsyncMeasure(pastAsyncMeasureSnapshot.getMeasureId());
+ dao.deleteAsyncMeasureSnapshots(pastAsyncMeasureSnapshot.getMeasureId());
+ registerMeasure(pastAsyncMeasure, dao);
+ }
+ session.commit();
+ }
+
+ private void registerMeasure(MeasureModel measure, AsyncMeasuresDao dao) {
+ AsyncMeasureSnapshot nextAsyncMeasureSnapshot = dao.getNextAsyncMeasureSnapshot(
+ measure.getProjectId(), measure.getMetric().getId(), measure.getMeasureDate());
+ Date dateNextAsyncMeasure = (nextAsyncMeasureSnapshot != null) ? nextAsyncMeasureSnapshot.getMeasureDate() : null;
+
+ List<AsyncMeasureSnapshot> nextAsyncMeasureSnapshots = dao.getNextAsyncMeasureSnapshotsUntilDate(
+ measure, dateNextAsyncMeasure);
+ if (!nextAsyncMeasureSnapshots.isEmpty()) {
+ for (AsyncMeasureSnapshot asyncMeasureSnapshot : nextAsyncMeasureSnapshots) {
+ dao.createAsyncMeasureSnapshot(measure.getId(), asyncMeasureSnapshot.getSnapshotId(), measure.getMeasureDate(),
+ asyncMeasureSnapshot.getSnapshotDate(), measure.getMetric().getId(), measure.getProjectId());
+ dao.removeSnapshotFromAsyncMeasureSnapshot(asyncMeasureSnapshot);
+ }
+ } else {
+ List<Snapshot> nextSnapshotsUntilDate = dao.getNextSnapshotsUntilDate(measure, dateNextAsyncMeasure);
+ if (!nextSnapshotsUntilDate.isEmpty()) {
+ for (Snapshot nextSnapshot : nextSnapshotsUntilDate) {
+ dao.createAsyncMeasureSnapshot(measure.getId(), nextSnapshot.getId(), measure.getMeasureDate(),
+ nextSnapshot.getCreatedAt(), measure.getMetric().getId(), measure.getProjectId());
+ }
+ } else {
+ dao.createAsyncMeasureSnapshot(measure.getId(), null, measure.getMeasureDate(),
+ null, measure.getMetric().getId(), measure.getProjectId());
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/BaseDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/BaseDao.java
new file mode 100644
index 00000000000..3c5f441bf43
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/BaseDao.java
@@ -0,0 +1,38 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.sonar.api.database.DatabaseSession;
+
+public class BaseDao {
+
+ private final DatabaseSession session;
+
+ public BaseDao(DatabaseSession session) {
+ super();
+ this.session = session;
+ }
+
+ public DatabaseSession getSession() {
+ return session;
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/DaoFacade.java b/sonar-core/src/main/java/org/sonar/jpa/dao/DaoFacade.java
new file mode 100644
index 00000000000..078b5c65396
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/DaoFacade.java
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+public class DaoFacade {
+
+ private final RulesDao rulesDao;
+ private final MeasuresDao measuresDao;
+ private final AsyncMeasuresDao asyncMeasureDao;
+ private final ProfilesDao profilesDao;
+
+ public DaoFacade(ProfilesDao profilesDao, RulesDao rulesDao, MeasuresDao measuresDao, AsyncMeasuresDao asyncMeasureDao) {
+ super();
+ this.rulesDao = rulesDao;
+ this.measuresDao = measuresDao;
+ this.asyncMeasureDao = asyncMeasureDao;
+ this.profilesDao = profilesDao;
+ }
+
+ public RulesDao getRulesDao() {
+ return rulesDao;
+ }
+
+ public ProfilesDao getProfilesDao() {
+ return profilesDao;
+ }
+
+ public MeasuresDao getMeasuresDao() {
+ return measuresDao;
+ }
+
+ public AsyncMeasuresDao getAsyncMeasureDao() {
+ return asyncMeasureDao;
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java
new file mode 100644
index 00000000000..77303c1c9ba
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java
@@ -0,0 +1,120 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Predicate;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.measures.Metric;
+
+import java.util.*;
+
+public class MeasuresDao extends BaseDao {
+
+ private final Map<String, Metric> metricsByName = new HashMap<String, Metric>();
+
+ public MeasuresDao(DatabaseSession session) {
+ super(session);
+ }
+
+ public Metric getMetric(Metric metric) {
+ return getMetricsByName().get(metric.getKey());
+ }
+
+ public List<Metric> getMetrics(List<Metric> metrics) {
+ List<Metric> result = new ArrayList<Metric>();
+ for (Metric metric : metrics) {
+ result.add(getMetric(metric));
+ }
+ return result;
+ }
+
+ public Metric getMetric(String metricName) {
+ return getMetricsByName().get(metricName);
+ }
+
+ public Collection<Metric> getMetrics() {
+ return getMetricsByName().values();
+ }
+
+ public Collection<Metric> getEnabledMetrics() {
+ return CollectionUtils.select(getMetricsByName().values(), new Predicate() {
+ public boolean evaluate(Object o) {
+ return ((Metric) o).getEnabled();
+ }
+ });
+ }
+
+ public Collection<Metric> getUserDefinedMetrics() {
+ return CollectionUtils.select(getMetricsByName().values(), new Predicate() {
+ public boolean evaluate(Object o) {
+ Metric m = (Metric) o;
+ return (m.getEnabled() && m.getOrigin() != Metric.Origin.JAV);
+ }
+ });
+ }
+
+ public void disableAutomaticMetrics() {
+ getSession().createQuery("update " + Metric.class.getSimpleName() + " m set m.enabled=false where m.userManaged=false").executeUpdate();
+ getSession().commit();
+ metricsByName.clear();
+ }
+
+ public void registerMetrics(Collection<Metric> metrics) {
+ if (metrics != null) {
+ for (Metric metric : metrics) {
+ metric.setEnabled(Boolean.TRUE);
+ persistMetric(metric);
+ }
+ getSession().commit();
+ }
+ }
+
+ public void persistMetric(Metric metric) {
+ Metric dbMetric = getMetric(metric);
+ if (dbMetric != null) {
+ dbMetric.merge(metric);
+ getSession().getEntityManager().merge(dbMetric);
+
+ } else {
+ getSession().getEntityManager().persist(metric);
+ }
+
+ metricsByName.clear();
+ }
+
+ public void disabledMetrics(Collection<Metric> metrics) {
+ for (Metric metric : metrics) {
+ metric.setEnabled(Boolean.FALSE);
+ getSession().getEntityManager().persist(metric);
+ metricsByName.put(metric.getName(), metric);
+ }
+ }
+
+ private Map<String, Metric> getMetricsByName() {
+ if (metricsByName.isEmpty()) {
+ List<Metric> metrics = getSession().getResults(Metric.class);
+ for (Metric metric : metrics) {
+ metricsByName.put(metric.getKey(), metric);
+ }
+ }
+ return metricsByName;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/ProfilesDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/ProfilesDao.java
new file mode 100644
index 00000000000..65944ab720b
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/ProfilesDao.java
@@ -0,0 +1,66 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.profiles.RulesProfile;
+
+import java.util.List;
+
+public class ProfilesDao extends BaseDao {
+
+ public ProfilesDao(DatabaseSession session) {
+ super(session);
+ }
+
+ public List<RulesProfile> getActiveProfiles() {
+ return getSession().getResults(RulesProfile.class, "defaultProfile", true);
+ }
+
+ public RulesProfile getActiveProfile(String languageKey, String projectResourceKey) {
+ ResourceModel projectResource = getSession().getSingleResult(ResourceModel.class, "key", projectResourceKey, "scope", ResourceModel.SCOPE_PROJECT);
+ if (projectResource != null && projectResource.getRulesProfile() != null) {
+ return projectResource.getRulesProfile();
+ }
+ return getSession().getSingleResult(RulesProfile.class, "defaultProfile", true, "language", languageKey);
+ }
+
+ public List<RulesProfile> getProfiles(String languageKey) {
+ return getSession().getResults(RulesProfile.class, "language", languageKey);
+ }
+
+ public List<RulesProfile> getProfiles() {
+ return getSession().getResults(RulesProfile.class);
+ }
+
+ public List<RulesProfile> getProvidedProfiles() {
+ return getSession().getResults(RulesProfile.class, "provided", true);
+ }
+
+ public RulesProfile getProfile(String languageKey, String profileName) {
+ return getSession().getSingleResult(RulesProfile.class, "language", languageKey, "name", profileName);
+ }
+
+ public RulesProfile getProfileById(int profileId) {
+ return getSession().getEntityManager().getReference(RulesProfile.class, profileId);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/RulesDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/RulesDao.java
new file mode 100644
index 00000000000..3f5788bae6c
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dao/RulesDao.java
@@ -0,0 +1,138 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.RuleFailureModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class RulesDao extends BaseDao {
+
+ private List<RulesCategory> rulesCategories;
+
+ public RulesDao(DatabaseSession session) {
+ super(session);
+ }
+
+ public List<Rule> getRules() {
+ return getSession().getResults(Rule.class, "enabled", true);
+ }
+
+ public List<Rule> getRulesByPlugin(String pluginKey) {
+ return getSession().getResults(Rule.class, "pluginName", pluginKey, "enabled", true);
+ }
+
+ public List<Rule> getRulesByCategory(RulesCategory categ) {
+ List<Rule> result = new ArrayList<Rule>();
+ for (Rule rule : getRules()) {
+ if (rule.getRulesCategory().equals(categ)) {
+ result.add(rule);
+ }
+ }
+ return result;
+ }
+
+ public Rule getRuleByKey(String pluginKey, String ruleKey) {
+ return getSession().getSingleResult(Rule.class, "key", ruleKey, "pluginName", pluginKey, "enabled", true);
+ }
+
+ public Long countRules(List<String> plugins, String categoryName) {
+ return (Long) getSession().createQuery(
+ "SELECT COUNT(r) FROM Rule r WHERE r.pluginName IN (:pluginNames) AND r.rulesCategory=:rulesCategory AND r.enabled=true").
+ setParameter("pluginNames", plugins).
+ setParameter("rulesCategory", getCategory(categoryName)).
+ getSingleResult();
+ }
+
+ public List<RulesCategory> getCategories() {
+ if (rulesCategories == null) {
+ rulesCategories = getSession().getResults(RulesCategory.class);
+ }
+ return rulesCategories;
+ }
+
+ public RulesCategory getCategory(String key) {
+ return getSession().getSingleResult(RulesCategory.class, "name", key);
+ }
+
+
+ public List<RuleParam> getRuleParams() {
+ return getSession().getResults(RuleParam.class);
+ }
+
+ public RuleParam getRuleParam(Rule rule, String paramKey) {
+ return getSession().getSingleResult(RuleParam.class, "rule", rule, "key", paramKey);
+ }
+
+ public void addActiveRulesToProfile(List<ActiveRule> activeRules, int profileId, String pluginKey) {
+ RulesProfile rulesProfile = getProfileById(profileId);
+ for (ActiveRule activeRule : activeRules) {
+ synchronizeRuleOfActiveRule(activeRule, pluginKey);
+ activeRule.setRulesProfile(rulesProfile);
+ getSession().save(activeRule);
+ }
+ }
+
+ public void deleteActiveRuleParameters(RuleParam ruleParam) {
+ getSession().createQuery(
+ "DELETE FROM ActiveRuleParam arp WHERE ruleParam=:param")
+ .setParameter("param", ruleParam)
+ .executeUpdate();
+ }
+
+ public List<RuleFailureModel> getViolations(Snapshot snapshot) {
+ return getSession().getResults(RuleFailureModel.class, "snapshotId", snapshot.getId());
+ }
+
+ public void synchronizeRuleOfActiveRule(ActiveRule activeRule, String pluginKey) {
+ Rule rule = activeRule.getRule();
+ Rule ruleFromDataBase = getRuleByKey(pluginKey, rule.getKey());
+ activeRule.setRule(ruleFromDataBase);
+ List<RuleParam> ruleParamsFromDataBase = getRuleParams();
+ for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) {
+ boolean found = false;
+ Iterator<RuleParam> iterator = ruleParamsFromDataBase.iterator();
+ while (iterator.hasNext() && !found) {
+ RuleParam ruleParamFromDataBase = iterator.next();
+ if (isRuleParamEqual(activeRuleParam.getRuleParam(), ruleParamFromDataBase, rule.getKey(), pluginKey)) {
+ activeRuleParam.setRuleParam(ruleParamFromDataBase);
+ found = true;
+ }
+ }
+ }
+ }
+
+ public boolean isRuleParamEqual(RuleParam ruleParam, RuleParam ruleParamFromDatabase, String ruleKey, String pluginKey) {
+ return ruleParam.getKey().equals(ruleParamFromDatabase.getKey()) &&
+ ruleKey.equals(ruleParamFromDatabase.getRule().getKey()) &&
+ ruleParamFromDatabase.getRule().getPluginName().equals(pluginKey);
+ }
+
+ public RulesProfile getProfileById(int profileId) {
+ return getSession().getEntityManager().getReference(RulesProfile.class, profileId);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java
new file mode 100644
index 00000000000..ec54f8f059a
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java
@@ -0,0 +1,73 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.id.IdentityGenerator;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.Types;
+
+/**
+ * @since 1.12
+ */
+public class Derby implements Dialect {
+
+ public String getId() {
+ return "derby";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "derby";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return DerbyWithDecimalDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:derby:");
+ }
+
+ public static class DerbyWithDecimalDialect extends DerbyDialect {
+ public DerbyWithDecimalDialect() {
+ super();
+ registerColumnType(Types.DOUBLE, "decimal");
+ registerColumnType(Types.VARCHAR, DatabaseProperties.MAX_TEXT_SIZE, "clob");
+ registerColumnType(Types.VARBINARY, "blob");
+
+ // Not possible to do alter column types in Derby
+ registerColumnType(Types.BIGINT, "integer");
+ }
+
+ /**
+ * To be compliant with Oracle, we define on each model (ch.hortis.sonar.model classes)
+ * a sequence generator. It works on mySQL because strategy = GenerationType.AUTO, so
+ * it equals GenerationType.IDENTITY.
+ * But on derby, AUTO becomes TABLE instead of IDENTITY. So we explicitly change this behavior.
+ */
+ @Override
+ public Class getNativeIdentifierGeneratorClass() {
+ return IdentityGenerator.class;
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java
new file mode 100644
index 00000000000..0f860ae4e32
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java
@@ -0,0 +1,50 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+/**
+ * @since 1.12
+ */
+public interface Dialect {
+
+ /**
+ * @return the sonar dialect Id to be matched with the sonar.jdbc.dialect property when provided
+ */
+ String getId();
+
+ /**
+ * @return the hibernate dialect class to be used
+ */
+ Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass();
+
+ /**
+ * @return the activerecord dialect to be used
+ */
+ String getActiveRecordDialectCode();
+
+ /**
+ * Used to autodetect a dialect for a given driver URL
+ *
+ * @param jdbcConnectionURL a jdbc driver url such as jdbc:mysql://localhost:3306/sonar
+ * @return true if the dialect supports surch url
+ */
+ boolean matchesJdbcURL(String jdbcConnectionURL);
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/DialectRepository.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/DialectRepository.java
new file mode 100644
index 00000000000..e05c3b92329
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/DialectRepository.java
@@ -0,0 +1,93 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterators;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.SonarException;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * @since 1.12
+ */
+public final class DialectRepository {
+
+ private DialectRepository() {
+ }
+
+ private static List<Dialect> builtInDialects = getSupportedDialects();
+
+ public static Dialect find(final String dialectId, final String jdbcConnectionUrl) {
+ Dialect match = StringUtils.isNotEmpty(dialectId) ? findById(dialectId) : findByJdbcUrl(jdbcConnectionUrl);
+ if (match == null) {
+ throw new SonarException("Unable to determine database dialect to use within sonar with dialect " + dialectId + " jdbc url " + jdbcConnectionUrl);
+ }
+ return match;
+ }
+
+ private static Dialect findByJdbcUrl(final String jdbcConnectionUrl) {
+ Dialect match = findDialect(builtInDialects, new Predicate<Dialect>() {
+ public boolean apply(Dialect dialect) {
+ return dialect.matchesJdbcURL(StringUtils.trimToEmpty(jdbcConnectionUrl));
+ }
+ });
+ return match;
+ }
+
+ private static Dialect findById(final String dialectId) {
+ Dialect match = findDialect(builtInDialects, new Predicate<Dialect>() {
+ public boolean apply(Dialect dialect) {
+ return dialect.getId().equals(dialectId);
+ }
+ });
+ // maybe a class name if no match
+ match = match == null ? getDialectByClassname(dialectId) : match;
+ return match;
+ }
+
+ private static Dialect findDialect(Collection<Dialect> dialects, Predicate<Dialect> predicate) {
+ try {
+ return Iterators.find(dialects.iterator(), predicate);
+ } catch (NoSuchElementException ex) {
+ return null;
+ }
+ }
+
+ private static Dialect getDialectByClassname(String dialectId) {
+ try {
+ Class<? extends Dialect> dialectClass = (Class<? extends Dialect>) DialectRepository.class.getClassLoader().loadClass(dialectId);
+ return dialectClass.newInstance();
+ } catch (ClassNotFoundException e) {
+ // dialectId was not a class name :)
+ } catch (Exception e) {
+ throw new SonarException("Unable to instanciate dialect class", e);
+ }
+ return null;
+ }
+
+ private static List<Dialect> getSupportedDialects() {
+ return Arrays.asList(new Derby(), new HsqlDb(), new MySql(), new Oracle(), new PostgreSql(), new MsSql());
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java
new file mode 100644
index 00000000000..b6bb3a68392
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java
@@ -0,0 +1,46 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.dialect.HSQLDialect;
+
+/**
+ * @since 1.12
+ */
+public class HsqlDb implements Dialect {
+
+ public String getId() {
+ return "hsqldb";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "hsqldb";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return HSQLDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:hsqldb:");
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java
new file mode 100644
index 00000000000..f59c2ee4240
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java
@@ -0,0 +1,68 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.SQLServerDialect;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.Types;
+
+public class MsSql implements Dialect {
+
+ public String getId() {
+ return "mssql";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "sqlserver";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return MsSqlDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:microsoft:sqlserver:")
+ || StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:jtds:sqlserver:");
+ }
+
+ public static class MsSqlDialect extends SQLServerDialect {
+ public MsSqlDialect() {
+ super();
+ registerColumnType(Types.DOUBLE, "decimal");
+ registerColumnType(Types.VARCHAR, 255, "nvarchar($l)");
+ registerColumnType(Types.VARCHAR, DatabaseProperties.MAX_TEXT_SIZE, "nvarchar(max)");
+ registerColumnType(Types.CHAR, "nchar(1)");
+ registerColumnType(Types.CLOB, "nvarchar(max)");
+ }
+
+ public String getTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ if (code != 2005) {
+ return super.getTypeName(code, length, precision, scale);
+ } else {
+ return "ntext";
+ }
+ }
+ }
+
+}
+
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java
new file mode 100644
index 00000000000..180dec0b1ee
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java
@@ -0,0 +1,59 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.dialect.MySQLDialect;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.Types;
+
+/**
+ * @since 1.12
+ */
+public class MySql implements Dialect {
+
+ public String getId() {
+ return "mysql";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "mysql";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return MySqlWithDecimalDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:mysql:");
+ }
+
+ public static class MySqlWithDecimalDialect extends MySQLDialect {
+ public MySqlWithDecimalDialect() {
+ super();
+ registerColumnType(Types.DOUBLE, "decimal precision");
+ registerColumnType(Types.VARCHAR, DatabaseProperties.MAX_TEXT_SIZE, "mediumtext");
+ registerColumnType(Types.CLOB, "mediumtext");
+ registerColumnType(Types.BLOB, "blob");
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java
new file mode 100644
index 00000000000..ebec6753619
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.dialect.Oracle10gDialect;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.Types;
+
+/**
+ * @since 1.12
+ */
+public class Oracle implements Dialect {
+
+ public String getId() {
+ return "oracle";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "oracle";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return Oracle10gWithDecimalDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:oracle:");
+ }
+
+ public static class Oracle10gWithDecimalDialect extends Oracle10gDialect {
+ public Oracle10gWithDecimalDialect() {
+ super();
+ registerColumnType(Types.DOUBLE, "number($p,$s)");
+ registerColumnType(Types.VARCHAR, DatabaseProperties.MAX_TEXT_SIZE, "clob");
+ registerColumnType(Types.VARBINARY, "blob");
+ }
+
+ @Override
+ public Class getNativeIdentifierGeneratorClass() {
+ return OracleSequenceGenerator.class;
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/OracleSequenceGenerator.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/OracleSequenceGenerator.java
new file mode 100644
index 00000000000..7fa1abfa2d1
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/OracleSequenceGenerator.java
@@ -0,0 +1,55 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.id.SequenceGenerator;
+import org.hibernate.type.Type;
+
+import java.util.Properties;
+
+/**
+ * @since 1.10
+ */
+public class OracleSequenceGenerator extends SequenceGenerator {
+
+ public static final String SEQUENCE_NAME_SUFFIX = "_SEQ";
+
+ @Override
+ public void configure(Type type, Properties params, Dialect dialect)
+ throws MappingException {
+
+ String tableName = params.getProperty(PersistentIdentifierGenerator.TABLE);
+
+ if (tableName != null) {
+ StringBuilder sequenceNameBuilder = new StringBuilder();
+
+ sequenceNameBuilder.append(tableName);
+ sequenceNameBuilder.append(SEQUENCE_NAME_SUFFIX);
+
+ params.setProperty(SEQUENCE, sequenceNameBuilder.toString().toUpperCase());
+ }
+
+ super.configure(type, params, dialect);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSQLSequenceGenerator.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSQLSequenceGenerator.java
new file mode 100644
index 00000000000..12094ba0745
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSQLSequenceGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.id.SequenceGenerator;
+import org.hibernate.type.Type;
+
+import java.util.Properties;
+
+/**
+ * if the underlying database is PostgreSQL, the sequence
+ * naming convention is different and includes the primary key
+ * column name
+ *
+ * @since 1.10
+ */
+public class PostgreSQLSequenceGenerator extends SequenceGenerator {
+
+ public static final String SEQUENCE_NAME_SEPARATOR = "_";
+ public static final String SEQUENCE_NAME_SUFFIX = "seq";
+
+ @Override
+ public void configure(Type type, Properties params, Dialect dialect)
+ throws MappingException {
+
+ String tableName = params.getProperty(PersistentIdentifierGenerator.TABLE);
+ String columnName = params.getProperty(PersistentIdentifierGenerator.PK);
+
+ if (tableName != null && columnName != null) {
+ StringBuilder sequenceNameBuilder = new StringBuilder();
+
+ sequenceNameBuilder.append(tableName);
+ sequenceNameBuilder.append(SEQUENCE_NAME_SEPARATOR);
+ sequenceNameBuilder.append(columnName);
+ sequenceNameBuilder.append(SEQUENCE_NAME_SEPARATOR);
+ sequenceNameBuilder.append(SEQUENCE_NAME_SUFFIX);
+
+ params.setProperty(SEQUENCE, sequenceNameBuilder.toString());
+ }
+
+ super.configure(type, params, dialect);
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java b/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java
new file mode 100644
index 00000000000..7d7318e51eb
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java
@@ -0,0 +1,60 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.dialect.PostgreSQLDialect;
+
+import java.sql.Types;
+
+/**
+ * @since 1.12
+ */
+public class PostgreSql implements Dialect {
+
+ public String getId() {
+ return "postgresql";
+ }
+
+ public String getActiveRecordDialectCode() {
+ return "postgre";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return PostgreSQLWithDecimalDialect.class;
+ }
+
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:postgresql:");
+ }
+
+ public static class PostgreSQLWithDecimalDialect extends PostgreSQLDialect {
+ public PostgreSQLWithDecimalDialect() {
+ super();
+ registerColumnType(Types.DOUBLE, "numeric($p,$s)");
+ }
+
+ @Override
+ public Class getNativeIdentifierGeneratorClass() {
+ return PostgreSQLSequenceGenerator.class;
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
new file mode 100644
index 00000000000..5549aeff502
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
@@ -0,0 +1,95 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.entity;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import javax.persistence.*;
+
+@Entity
+@Table(name = SchemaMigration.TABLE_NAME, uniqueConstraints = {@UniqueConstraint(columnNames = {"version"})})
+public class SchemaMigration {
+
+ public final static int VERSION_UNKNOWN = -1;
+ public static final int LAST_VERSION = 137;
+
+ public final static String TABLE_NAME = "schema_migrations";
+
+ @Id
+ @Column(name = "version", updatable = true)
+ private String version;
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String s) {
+ this.version = s;
+ }
+
+ public void setVersion(int i) {
+ this.version = String.valueOf(i);
+ }
+
+ public static int getCurrentVersion(Connection connection) {
+ Statement stmt = null;
+ ResultSet rs = null;
+ int version = VERSION_UNKNOWN;
+ try {
+ stmt = connection.createStatement();
+ rs = stmt.executeQuery("SELECT version FROM " + SchemaMigration.TABLE_NAME);
+ while (rs.next()) {
+ int i = Integer.parseInt(rs.getString(1));
+ if (i > version) {
+ version = i;
+ }
+ }
+ } catch (SQLException e) {
+ // ignore
+ } finally {
+ close(rs);
+ close(stmt);
+ }
+
+ return version;
+ }
+
+ private static void close(ResultSet rs) {
+ if (rs != null) {
+ try {
+ rs.close();
+ } catch (SQLException e) {
+ // why does close() throw a checked-exception ???
+ }
+ }
+ }
+
+ private static void close(Statement st) {
+ if (st != null) {
+ try {
+ st.close();
+ } catch (SQLException e) {
+ // why does close() throw a checked-exception ???
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseBatch.java b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseBatch.java
new file mode 100644
index 00000000000..6f90114d5a8
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseBatch.java
@@ -0,0 +1,46 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.picocontainer.MutablePicoContainer;
+import org.sonar.api.database.DatabaseSession;
+
+public abstract class AbstractDatabaseBatch implements DatabaseBatch {
+ private MutablePicoContainer container;
+
+ public void startIn(MutablePicoContainer container) {
+ this.container = container;
+ doStart();
+ }
+
+ protected abstract void doStart();
+
+ protected DatabaseSession getSession() {
+ return container.getComponent(DatabaseSession.class);
+ }
+
+ protected <T> T getComponent(Class<T> clazz) {
+ return container.getComponent(clazz);
+ }
+
+ protected MutablePicoContainer getContainer() {
+ return container;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java
new file mode 100644
index 00000000000..ac778560a18
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java
@@ -0,0 +1,241 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.jpa.dialect.Dialect;
+import org.sonar.jpa.dialect.DialectRepository;
+import org.sonar.jpa.entity.SchemaMigration;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+public abstract class AbstractDatabaseConnector implements DatabaseConnector {
+ protected static final Logger LOG_SQL = LoggerFactory.getLogger("org.hibernate.SQL");
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractDatabaseConnector.class);
+
+ private Configuration configuration = null;
+ private EntityManagerFactory factory = null;
+ private int databaseVersion = SchemaMigration.VERSION_UNKNOWN;
+ private boolean operational = false;
+ private boolean started = false;
+ private boolean startsFailIfSchemaOutdated;
+ private Integer transactionIsolation = null;
+ private Dialect dialect = null;
+
+ protected AbstractDatabaseConnector(Configuration configuration, boolean startsFailIfSchemaOutdated) {
+ this.configuration = configuration;
+ this.startsFailIfSchemaOutdated = startsFailIfSchemaOutdated;
+ }
+
+ protected AbstractDatabaseConnector() {
+ }
+
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ public String getDialectId() {
+ return dialect.getId();
+ }
+ /**
+ * Indicates if the connector is operational : database connection OK and schema version OK
+ */
+ public boolean isOperational() {
+ return operational;
+ }
+
+ /**
+ * Indicates if the connector is started : database connection OK and schema version OK or KO
+ */
+ protected boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Get the JDBC transaction isolation defined by the configuration
+ *
+ * @return JDBC transaction isolation
+ */
+ public Integer getTransactionIsolation() {
+ return transactionIsolation;
+ }
+
+ public void start() {
+ if (!started) {
+ transactionIsolation = configuration.getInteger(DatabaseProperties.PROP_ISOLATION, null /* use driver default setting */);
+ String jdbcConnectionUrl = testConnection();
+ dialect = DialectRepository.find(configuration.getString("sonar.jdbc.dialect"), jdbcConnectionUrl);
+ LoggerFactory.getLogger("org.sonar.INFO").info("Database dialect class " + dialect.getClass().getName());
+ started = true;
+ }
+ if (!operational) {
+ boolean upToDate = upToDateSchemaVersion();
+ if (!upToDate && startsFailIfSchemaOutdated) {
+ throw new DatabaseException(databaseVersion, SchemaMigration.LAST_VERSION);
+ }
+ if (upToDate) {
+ factory = createEntityManagerFactory();
+ operational = true;
+ }
+ }
+ }
+
+ public void stop() {
+ if (factory != null && factory.isOpen()) {
+ factory.close();
+ factory = null;
+ }
+ operational = false;
+ started = false;
+ }
+
+ public abstract void setupEntityManagerFactory(Properties factoryProps);
+
+ public EntityManagerFactory getEntityManagerFactory() {
+ return factory;
+ }
+
+ protected void setEntityManagerFactory(EntityManagerFactory factory) {
+ this.factory = factory;
+ }
+
+ protected EntityManagerFactory createEntityManagerFactory() {
+ // other settings are stored into /META-INF/persistence.xml
+ Properties props = getHibernateProperties();
+ logHibernateSettings(props);
+ return Persistence.createEntityManagerFactory("sonar", props);
+ }
+
+ private void logHibernateSettings(Properties props) {
+ if (LOG.isDebugEnabled()) {
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ LOG.debug(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+
+ protected Properties getHibernateProperties() {
+ Properties props = new Properties();
+ if (transactionIsolation != null) {
+ props.put("hibernate.connection.isolation", Integer.toString(transactionIsolation));
+ }
+ props.put("hibernate.hbm2ddl.auto", getConfiguration().getString(DatabaseProperties.PROP_HIBERNATE_HBM2DLL, "validate"));
+ props.put("hibernate.dialect", getDialectClass());
+
+ props.put("hibernate.generate_statistics", getConfiguration().getBoolean(DatabaseProperties.PROP_HIBERNATE_GENERATE_STATISTICS, false));
+ props.put("hibernate.show_sql", Boolean.valueOf(LOG_SQL.isInfoEnabled()).toString());
+
+ Configuration subset = getConfiguration().subset("sonar.hibernate");
+ for (Iterator keys = subset.getKeys(); keys.hasNext();) {
+ String key = (String) keys.next();
+ if (StringUtils.isNotBlank((String)subset.getProperty(key))) {
+ props.put("hibernate." + key, subset.getProperty(key));
+ }
+ }
+
+ // custom impl setup
+ setupEntityManagerFactory(props);
+
+
+ return props;
+ }
+
+ public EntityManager createEntityManager() {
+ return factory.createEntityManager();
+ }
+
+ private String testConnection() throws DatabaseException {
+ Connection connection = null;
+ try {
+ connection = getConnection();
+ return connection.getMetaData().getURL();
+
+ } catch (SQLException e) {
+ throw new DatabaseException("Cannot open connection to database: " + e.getMessage(), e);
+
+ } finally {
+ close(connection);
+ }
+ }
+
+ protected int loadVersion() {
+ Connection connection = null;
+ try {
+ connection = getConnection();
+ return SchemaMigration.getCurrentVersion(connection);
+
+ } catch (SQLException e) {
+ // schema not created
+ return 0;
+ } finally {
+ close(connection);
+ }
+ }
+
+ private void close(Connection connection) {
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ // why does close() throw a checked-exception ???
+ }
+ }
+ }
+
+ protected boolean upToDateSchemaVersion() {
+ if (databaseVersion == SchemaMigration.LAST_VERSION) {
+ return true;
+ }
+ databaseVersion = loadVersion();
+ return databaseVersion == SchemaMigration.LAST_VERSION;
+ }
+
+ protected int getDatabaseVersion() {
+ return databaseVersion;
+ }
+
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ public String getDialectClass() {
+ String dialectClass = configuration.getString(DatabaseProperties.PROP_DIALECT_CLASS);
+ if (dialectClass == null && dialect != null) {
+ dialectClass = dialect.getHibernateDialectClass().getName();
+ }
+ return dialectClass;
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseBatch.java b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseBatch.java
new file mode 100644
index 00000000000..8612b5c3f03
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseBatch.java
@@ -0,0 +1,27 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.picocontainer.MutablePicoContainer;
+
+public interface DatabaseBatch {
+ void startIn(MutablePicoContainer container);
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseConnector.java
new file mode 100644
index 00000000000..952c2529a33
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseConnector.java
@@ -0,0 +1,39 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.sonar.jpa.dialect.Dialect;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public interface DatabaseConnector {
+
+ Dialect getDialect();
+
+ Connection getConnection() throws SQLException;
+
+ EntityManagerFactory getEntityManagerFactory();
+
+ EntityManager createEntityManager();
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseException.java b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseException.java
new file mode 100644
index 00000000000..46ec6d5cf26
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseException.java
@@ -0,0 +1,41 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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 javax.persistence.PersistenceException;
+
+public class DatabaseException extends PersistenceException {
+
+ public DatabaseException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public DatabaseException(String message) {
+ super(message);
+ }
+
+ public DatabaseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DatabaseException(int version, int requiredVersion) {
+ super("Database schema must be updated [version/required=" + version + "/" + requiredVersion + "]. Please browse to your sonar homepage.");
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionFactory.java b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionFactory.java
new file mode 100644
index 00000000000..8257f4c5cb2
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.sonar.api.database.DatabaseSession;
+
+public interface DatabaseSessionFactory {
+
+ DatabaseSession getSession();
+
+ void clear();
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionProvider.java b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionProvider.java
new file mode 100644
index 00000000000..8ea912e1b79
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DatabaseSessionProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.database.DatabaseSession;
+
+public class DatabaseSessionProvider extends ProviderAdapter {
+
+ public DatabaseSession provide(DatabaseSessionFactory factory) {
+ return factory.getSession();
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java
new file mode 100644
index 00000000000..7f24d8cabea
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java
@@ -0,0 +1,152 @@
+package org.sonar.jpa.session;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+public class DriverDatabaseConnector extends AbstractDatabaseConnector {
+
+ private ClassLoader classloader;
+
+ public DriverDatabaseConnector(Configuration configuration) {
+ super(configuration, true);
+ this.classloader = getClass().getClassLoader();
+ }
+
+ public DriverDatabaseConnector(Configuration configuration, ClassLoader classloader) {
+ super(configuration, true);
+ this.classloader = classloader;
+ }
+
+ public String getDriver() {
+ String driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER);
+ if (driver == null) {
+ driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER_DEPRECATED);
+ }
+ if (driver == null) {
+ driver = DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE;
+ }
+ return driver;
+ }
+
+ public String getUrl() {
+ return getConfiguration().getString(DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE);
+ }
+
+ public String getUsername() {
+ String username = getConfiguration().getString(DatabaseProperties.PROP_USER);
+ if (username == null) {
+ username = getConfiguration().getString(DatabaseProperties.PROP_USER_DEPRECATED);
+ }
+ if (username == null) {
+ username = DatabaseProperties.PROP_USER_DEFAULT_VALUE;
+ }
+ return username;
+ }
+
+ public String getPassword() {
+ return getConfiguration().getString(DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE);
+ }
+
+ public Connection getConnection() throws SQLException {
+ try {
+ /*
+ The sonar batch downloads the JDBC driver in a separated classloader.
+ This is a well-know problem of java.sql.DriverManager. The workaround
+ is to use a proxy.
+ See http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location
+ */
+ Driver driver = (Driver)classloader.loadClass(getDriver()).newInstance();
+ DriverManager.registerDriver(new DriverProxy(driver));
+
+ } catch (Exception e) {
+ SQLException ex = new SQLException("SQL driver not found " + getDriver());
+ ex.initCause(e);
+ throw ex;
+ }
+ return DriverManager.getConnection(getUrl(), getUsername(), getPassword());
+ }
+
+ @Override
+ public void setupEntityManagerFactory(Properties factoryProps) {
+ factoryProps.put("hibernate.connection.url", getUrl());
+ factoryProps.put("hibernate.connection.driver_class", getDriver());
+ factoryProps.put("hibernate.connection.username", getUsername());
+ if (StringUtils.isNotEmpty(getPassword())) {
+ factoryProps.put("hibernate.connection.password", getPassword());
+ }
+ }
+}
+
+/**
+ * A Driver that stands in for another Driver.
+ * This is necessary because java.sql.DriverManager
+ * examines the Driver class class loader.
+ */
+final class DriverProxy implements Driver {
+ private final Driver target;
+
+ DriverProxy(Driver target) {
+ if (target == null) {
+ throw new NullPointerException();
+ }
+ this.target = target;
+ }
+
+ public Driver getTarget() {
+ return target;
+ }
+
+ public boolean acceptsURL(String url) throws SQLException {
+ return target.acceptsURL(url);
+ }
+
+ public Connection connect(
+ String url, Properties info
+ ) throws SQLException {
+ return target.connect(url, info);
+ }
+
+ public int getMajorVersion() {
+ return target.getMajorVersion();
+ }
+
+ public int getMinorVersion() {
+ return target.getMinorVersion();
+ }
+
+ public java.sql.DriverPropertyInfo[] getPropertyInfo(
+ String url, Properties info
+ ) throws SQLException {
+ return target.getPropertyInfo(url, info);
+ }
+
+ public boolean jdbcCompliant() {
+ return target.jdbcCompliant();
+ }
+
+ @Override
+ public String toString() {
+ return "Proxy: " + target;
+ }
+
+ @Override
+ public int hashCode() {
+ return target.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof org.sonar.jpa.session.DriverProxy)) {
+ return false;
+ }
+ org.sonar.jpa.session.DriverProxy other = (org.sonar.jpa.session.DriverProxy) obj;
+ return this.target.equals(other.target);
+ }
+} \ No newline at end of file
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
new file mode 100644
index 00000000000..8390dd8fd6b
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/JpaDatabaseSession.java
@@ -0,0 +1,222 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseSession;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.Query;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+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;
+ }
+
+ public EntityManager getEntityManager() {
+ return entityManager;
+ }
+
+ public void start() {
+ entityManager = connector.createEntityManager();
+ index = 0;
+ }
+
+ public void stop() {
+ commit();
+ if (entityManager != null && entityManager.isOpen()) {
+ entityManager.clear();
+ 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();
+ }
+ }
+ inTransaction = false;
+ index = 0;
+ }
+ }
+
+ public void rollback() {
+ if (entityManager != null && inTransaction) {
+ entityManager.getTransaction().rollback();
+ inTransaction = false;
+ index = 0;
+ }
+ }
+
+ 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) {
+ entityManager.persist(model);
+ if (flushIfNeeded && (++index % BATCH_SIZE == 0)) {
+ flush();
+ }
+ }
+
+ public Object merge(Object model) {
+ startTransaction();
+ return entityManager.merge(model);
+ }
+
+ public void remove(Object model) {
+ startTransaction();
+ entityManager.remove(model);
+ if (++index % BATCH_SIZE == 0) {
+ flush();
+ }
+ }
+
+ 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;
+ }
+ }
+
+ public void flush() {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ public Query createQuery(String hql) {
+ startTransaction();
+ return entityManager.createQuery(hql);
+ }
+
+ public <T> T getSingleResult(Query query, T defaultValue) {
+ try {
+ return (T) query.getSingleResult();
+ } catch (NoResultException ex) {
+ return defaultValue;
+ }
+ }
+
+ public <T> T getEntity(Class<T> entityClass, Object id) {
+ startTransaction();
+ return getEntityManager().find(entityClass, id);
+ }
+
+ public <T> T getSingleResult(Class<T> entityClass, Object... criterias) {
+ try {
+ return getSingleResult(getQueryForCriterias(entityClass, true, criterias), (T) null);
+
+ } catch (NonUniqueResultException ex) {
+ LoggerFactory.getLogger(JpaDatabaseSession.class).warn("NonUniqueResultException on entity {} with criterias : {}",
+ entityClass.getSimpleName(), StringUtils.join(criterias, ","));
+ throw ex;
+ }
+ }
+
+ 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-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java
new file mode 100644
index 00000000000..5d00ddb02df
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java
@@ -0,0 +1,101 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.jpa.entity.SchemaMigration;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import java.sql.Connection;
+
+public class MemoryDatabaseConnector extends DriverDatabaseConnector {
+ public static final String DRIVER = "org.hsqldb.jdbcDriver";
+ public static final String URL = "jdbc:hsqldb:mem:sonar";
+ public static final String USER = "sa";
+ public static final String PASSWORD = "";
+ public static final int ISOLATION = Connection.TRANSACTION_READ_UNCOMMITTED;
+
+ private int version;
+
+ public MemoryDatabaseConnector(Configuration config) {
+ super(config);
+ version = SchemaMigration.LAST_VERSION;
+ }
+
+ public MemoryDatabaseConnector() {
+ this(getInMemoryConfiguration(true));
+ }
+
+ public MemoryDatabaseConnector(int version) {
+ this(getInMemoryConfiguration(true));
+ this.version = version;
+ }
+
+ protected static Configuration getInMemoryConfiguration(boolean createSchema) {
+ PropertiesConfiguration conf = new PropertiesConfiguration();
+ conf.setProperty(DatabaseProperties.PROP_URL, URL);
+ conf.setProperty(DatabaseProperties.PROP_DRIVER, DRIVER);
+ conf.setProperty(DatabaseProperties.PROP_USER, USER);
+ conf.setProperty(DatabaseProperties.PROP_PASSWORD, PASSWORD);
+ conf.setProperty(DatabaseProperties.PROP_ISOLATION, ISOLATION);
+ if (createSchema) {
+ conf.setProperty(DatabaseProperties.PROP_HIBERNATE_HBM2DLL, "create-drop");
+ }
+ return conf;
+ }
+
+ @Override
+ public void start() {
+ try {
+ super.start();
+ } catch (DatabaseException ex) {
+ if (!isStarted()) {
+ throw ex;
+ }
+ setEntityManagerFactory(createEntityManagerFactory());
+ setupSchemaVersion(version);
+ }
+ }
+
+ @Override
+ protected EntityManagerFactory createEntityManagerFactory() {
+ return super.createEntityManagerFactory();
+ }
+
+ protected void setupSchemaVersion(int version) {
+ SchemaMigration migration = new SchemaMigration();
+ migration.setVersion(version);
+ EntityManager manager = null;
+ try {
+ manager = createEntityManager();
+ manager.getTransaction().begin();
+ manager.persist(migration);
+ manager.getTransaction().commit();
+
+ } finally {
+ if (manager != null) {
+ manager.close();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactory.java b/sonar-core/src/main/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactory.java
new file mode 100644
index 00000000000..75ef5f9de3f
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactory.java
@@ -0,0 +1,35 @@
+package org.sonar.jpa.session;
+
+import org.sonar.api.database.DatabaseSession;
+
+public class ThreadLocalDatabaseSessionFactory implements DatabaseSessionFactory {
+
+ private final ThreadLocal<JpaDatabaseSession> threadSession = new ThreadLocal<JpaDatabaseSession>();
+ private final DatabaseConnector connector;
+
+ public ThreadLocalDatabaseSessionFactory(DatabaseConnector connector) {
+ this.connector = connector;
+ }
+
+ public DatabaseSession getSession() {
+ JpaDatabaseSession session = threadSession.get();
+ if (session == null) {
+ session = new JpaDatabaseSession(connector);
+ session.start();
+ threadSession.set(session);
+ }
+ return session;
+ }
+
+ public void clear() {
+ JpaDatabaseSession session = threadSession.get();
+ if (session != null) {
+ session.stop();
+ }
+ threadSession.set(null);
+ }
+
+ public void stop() {
+ clear();
+ }
+}
diff --git a/sonar-core/src/main/resources/META-INF/persistence.xml b/sonar-core/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 00000000000..2d3f56e0cbb
--- /dev/null
+++ b/sonar-core/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+ version="1.0">
+ <persistence-unit name="sonar" transaction-type="RESOURCE_LOCAL">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+ <class>org.sonar.jpa.entity.SchemaMigration</class>
+ <class>org.sonar.api.database.configuration.Property</class>
+ <class>org.sonar.api.qualitymodel.Model</class>
+ <class>org.sonar.api.qualitymodel.Characteristic</class>
+ <class>org.sonar.core.plugin.JpaPlugin</class>
+ <class>org.sonar.core.plugin.JpaPluginFile</class>
+
+ <class>org.sonar.api.database.model.User</class>
+ <class>org.sonar.api.database.model.Snapshot</class>
+ <class>org.sonar.api.database.model.MeasureModel</class>
+ <class>org.sonar.api.database.model.MeasureData</class>
+ <class>org.sonar.api.design.DependencyDto</class>
+ <class>org.sonar.api.measures.Metric</class>
+ <class>org.sonar.api.database.model.ResourceModel</class>
+ <class>org.sonar.api.database.model.SnapshotSource</class>
+ <class>org.sonar.api.database.model.RuleFailureModel</class>
+ <class>org.sonar.api.security.UserRole</class>
+ <class>org.sonar.api.security.GroupRole</class>
+ <class>org.sonar.api.rules.Rule</class>
+ <class>org.sonar.api.rules.RuleParam</class>
+ <class>org.sonar.api.rules.RulesCategory</class>
+ <class>org.sonar.api.resources.ProjectLink</class>
+ <class>org.sonar.api.profiles.RulesProfile</class>
+ <class>org.sonar.api.rules.ActiveRule</class>
+ <class>org.sonar.api.rules.ActiveRuleParam</class>
+ <class>org.sonar.api.database.model.AsyncMeasureSnapshot</class>
+ <class>org.sonar.api.batch.Event</class>
+ <class>org.sonar.api.profiles.Alert</class>
+
+ <properties>
+ <property name="hibernate.current_session_context_class" value="thread"/>
+ <property name="hibernate.connection.release_mode" value="after_transaction"/>
+ <property name="hibernate.bytecode.use_reflection_optimizer" value="true"/>
+ <property name="hibernate.query.factory_class" value="org.hibernate.hql.ast.ASTQueryTranslatorFactory"/>
+ <property name="hibernate.jdbc.batch_size" value="30"/>
+ <property name="hibernate.connection.useUnicode" value="true"/>
+ <property name="hibernate.connection.charSet" value="UTF-8"/>
+ <property name="hibernate.connection.characterEncoding" value="UTF-8"/>
+ <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
+ <property name="hibernate.cache.use_second_level_cache" value="false"/>
+ <property name="hibernate.cache.use_query_cache" value="false"/>
+ </properties>
+ </persistence-unit>
+</persistence> \ No newline at end of file
diff --git a/sonar-core/src/main/resources/ehcache.xml b/sonar-core/src/main/resources/ehcache.xml
new file mode 100644
index 00000000000..99e2597c98e
--- /dev/null
+++ b/sonar-core/src/main/resources/ehcache.xml
@@ -0,0 +1,76 @@
+<ehcache>
+ <!--ehcache config ref
+
+ name:
+ Sets the name of the cache. This is used to identify the cache. It must be unique.
+
+ maxElementsInMemory:
+ Sets the maximum number of objects that will be created in memory
+
+ maxElementsOnDisk:
+ Sets the maximum number of objects that will be maintained in the DiskStore
+ The default value is zero, meaning unlimited.
+
+ eternal:
+ Sets whether elements are eternal. If eternal, timeouts are ignored and the
+ element is never expired.
+
+ overflowToDisk:
+ Sets whether elements can overflow to disk when the memory store
+ has reached the maxInMemory limit.
+
+ The following attributes are optional.
+
+ timeToIdleSeconds:
+ Sets the time to idle for an element before it expires.
+ i.e. The maximum amount of time between accesses before an element expires
+ Is only used if the element is not eternal.
+ Optional attribute. A value of 0 means that an Element can idle for infinity.
+ The default value is 0.
+
+ timeToLiveSeconds:
+ Sets the time to live for an element before it expires.
+ i.e. The maximum time between creation time and when an element expires.
+ Is only used if the element is not eternal.
+ Optional attribute. A value of 0 means that and Element can live for infinity.
+ The default value is 0.
+
+ diskPersistent:
+ Whether the disk store persists between restarts of the Virtual Machine.
+ The default value is false.
+
+ diskExpiryThreadIntervalSeconds:
+ The number of seconds between runs of the disk expiry thread. The default value
+ is 120 seconds.
+
+ memoryStoreEvictionPolicy:
+ Policy would be enforced upon reaching the maxElementsInMemory limit. Default
+ policy is Least Recently Used (specified as LRU). Other policies available -
+ First In First Out (specified as FIFO) and Less Frequently Used
+ (specified as LFU)
+ -->
+
+ <defaultCache
+ maxElementsInMemory="4096"
+ eternal="false"
+ timeToIdleSeconds="300"
+ timeToLiveSeconds="1200"
+ overflowToDisk="false"
+ memoryStoreEvictionPolicy="LRU"/>
+
+ <cache
+ name="org.sonar.api.measures.Metric"
+ maxElementsInMemory="256"
+ eternal="false"
+ overflowToDisk="false"/>
+ <cache
+ name="org.sonar.api.rules.Rule"
+ maxElementsInMemory="4096"
+ eternal="false"
+ overflowToDisk="false"/>
+ <cache
+ name="org.sonar.api.rules.RulesCategory"
+ maxElementsInMemory="32"
+ eternal="true"
+ overflowToDisk="false"/>
+</ehcache> \ No newline at end of file
diff --git a/sonar-core/src/test/java/org/sonar/api/database/configuration/DatabaseConfigurationTest.java b/sonar-core/src/test/java/org/sonar/api/database/configuration/DatabaseConfigurationTest.java
new file mode 100644
index 00000000000..26a54faedff
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/api/database/configuration/DatabaseConfigurationTest.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class DatabaseConfigurationTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void shouldLoadPropertiesFromDatabase() {
+ setupData("some-properties");
+
+ DatabaseConfiguration configuration = new DatabaseConfiguration(getSessionFactory());
+
+ assertEquals("value1", configuration.getString("key1"));
+ assertEquals("value2", configuration.getString("key2"));
+ assertNull(configuration.getString("keyxxxx"));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest.java b/sonar-core/src/test/java/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest.java
new file mode 100644
index 00000000000..a856d0729fd
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest.java
@@ -0,0 +1,41 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class ResourceDatabaseConfigurationTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void shouldNotLoadGlobalProperties() {
+ setupData("shouldNotLoadGlobalProperties");
+
+ ResourceDatabaseConfiguration conf = new ResourceDatabaseConfiguration(getSessionFactory(), 100);
+ assertEquals(2, CollectionUtils.size(conf.getKeys()));
+ assertEquals("project_value1", conf.getString("key1"));
+ assertNull(conf.getString("key2"));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginDaoTest.java b/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginDaoTest.java
new file mode 100644
index 00000000000..ba4e26a322b
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginDaoTest.java
@@ -0,0 +1,120 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.plugin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class JpaPluginDaoTest extends AbstractDbUnitTestCase {
+
+ private JpaPluginDao dao;
+
+ @Before
+ public void before() {
+ dao = new JpaPluginDao(getSessionFactory());
+ }
+
+ @Test
+ public void getPlugins() {
+ setupData("shared");
+
+ List<JpaPlugin> plugins = dao.getPlugins();
+
+ assertEquals(1, plugins.size());
+ assertEquals("checkstyle", plugins.get(0).getKey());
+ assertEquals(2, plugins.get(0).getFiles().size());
+ }
+
+
+ @Test
+ public void savePluginAndFiles() {
+ setupData("shared");
+ JpaPlugin pmd = JpaPlugin.create("pmd");
+ pmd.setCore(false);
+ pmd.setName("PMD");
+ pmd.setVersion("2.2");
+ pmd.setPluginClass("org.sonar.pmd.Main");
+
+ pmd.createFile("sonar-pmd-plugin-2.2.jar");
+ pmd.createFile("pmd-extension.jar");
+ pmd.createFile("pmd-extension2.jar");
+
+ getSession().saveWithoutFlush(pmd);
+ checkTables("savePluginAndFiles", "plugins", "plugin_files");
+ }
+
+ @Test
+ public void saveDeprecatedPlugin() {
+ setupData("shared");
+ JpaPlugin pmd = JpaPlugin.create("pmd");
+ pmd.setCore(false);
+ pmd.setName("PMD");
+ pmd.setPluginClass("org.sonar.pmd.Main");
+
+ pmd.createFile("sonar-pmd-plugin-2.2.jar");
+
+ getSession().saveWithoutFlush(pmd);
+ checkTables("saveDeprecatedPlugin", "plugins", "plugin_files");
+ }
+
+ @Test
+ public void removePreviousFilesWhenRegisteringPlugin() {
+ setupData("shared");
+
+ List<JpaPlugin> plugins = dao.getPlugins();
+ plugins.get(0).removeFiles();
+ plugins.get(0).createFile("newfile.jar");
+
+ dao.register(plugins);
+
+ checkTables("removePreviousFilesWhenRegisteringPlugin", "plugins", "plugin_files");
+ }
+
+ @Test
+ public void registerManyPlugins() {
+ setupData("shared");
+
+ List<JpaPlugin> plugins = createManyPlugins();
+ dao.register(plugins);
+
+ assertThat(dao.getPlugins().size(), is(150));
+ assertThat(dao.getPluginFiles().size(), is(150 * 20)); // initial plugin "checkstyle" has been deleted
+ }
+
+ private List<JpaPlugin> createManyPlugins() {
+ List<JpaPlugin> plugins = new ArrayList<JpaPlugin>();
+ for (int i=0 ; i<150 ; i++) {
+ JpaPlugin plugin = JpaPlugin.create("plugin-" + i);
+ for (int j=0 ; j<20 ; j++) {
+ plugin.createFile("file-" + i + "-" + j + ".jar");
+ }
+ plugins.add(plugin);
+ }
+ return plugins;
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginTest.java b/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginTest.java
new file mode 100644
index 00000000000..6a6d3cad2ca
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/plugin/JpaPluginTest.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.plugin;
+
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class JpaPluginTest {
+
+ @Test
+ public void createPlugin() {
+ JpaPlugin plugin = JpaPlugin.create("foo");
+ assertThat(plugin.getKey(), is("foo"));
+
+ assertEquals(plugin, plugin);
+ assertEquals(plugin, JpaPlugin.create("foo"));
+ assertFalse(plugin.equals(JpaPlugin.create("bar")));
+ }
+
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/purge/AbstractPurgeTest.java b/sonar-core/src/test/java/org/sonar/core/purge/AbstractPurgeTest.java
new file mode 100644
index 00000000000..5211d037cd3
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/purge/AbstractPurgeTest.java
@@ -0,0 +1,39 @@
+package org.sonar.core.purge;
+
+import org.junit.Test;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: SimonBrandhof
+ * Date: Jul 20, 2010
+ * Time: 10:47:13 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class AbstractPurgeTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeSnapshots() throws SQLException {
+ setupData("purgeSnapshots");
+
+ final FakePurge purge = new FakePurge(getSession());
+ purge.purge(null);
+
+ checkTables("purgeSnapshots", "snapshots", "project_measures", "measure_data", "rule_failures", "snapshot_sources", "dependencies");
+ }
+}
+
+class FakePurge extends AbstractPurge {
+ public FakePurge(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ deleteSnapshotData(Arrays.asList(3, 4));
+ }
+} \ No newline at end of file
diff --git a/sonar-core/src/test/java/org/sonar/core/qualitymodel/DefaultModelProviderTest.java b/sonar-core/src/test/java/org/sonar/core/qualitymodel/DefaultModelProviderTest.java
new file mode 100644
index 00000000000..beb59782327
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/qualitymodel/DefaultModelProviderTest.java
@@ -0,0 +1,121 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.qualitymodel;
+
+import org.junit.Test;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.qualitymodel.ModelDefinition;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+public class DefaultModelProviderTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void reset() {
+ setupData("shared");
+ DefaultModelProvider provider = new DefaultModelProvider(getSessionFactory());
+
+ Model model = Model.createByName("M1");
+ Characteristic c1 = model.createCharacteristicByName("NEWM1C1");
+ Characteristic c1a = model.createCharacteristicByName("NEWM1C1A");
+ c1.addChild(c1a);
+
+ model.createCharacteristicByName("NEWM1C2");
+ model = provider.reset(model);
+
+ model = getSession().getSingleResult(Model.class, "name", "M1");
+ assertNotNull(model);
+ assertThat(model.getCharacteristics().size(), is(3));
+ assertThat(model.getCharacteristicByName("NEWM1C1A").getParents().size(), is(1));
+ assertNotNull(model.getCharacteristicByName("NEWM1C1A").getParent("NEWM1C1"));
+ }
+
+ @Test
+ public void findByName() {
+ setupData("shared");
+ DefaultModelProvider provider = new DefaultModelProvider(getSessionFactory());
+ Model model = provider.findByName("M1");
+ assertNotNull(model);
+ assertNotNull(model.getCharacteristicByName("M1C1"));
+ }
+
+ @Test
+ public void findByNameNotFound() {
+ setupData("shared");
+ DefaultModelProvider provider = new DefaultModelProvider(getSessionFactory());
+ assertNull(provider.findByName("UNKNOWN"));
+ }
+
+ @Test
+ public void noDefinitionsToRegister() {
+ setupData("shared");
+ DefaultModelProvider provider = new DefaultModelProvider(getSessionFactory());
+ provider.registerDefinitions();
+
+ // same state
+ List<Model> models = getSession().getResults(Model.class);
+ assertThat(models.size(), is(2));
+ }
+
+ @Test
+ public void registerOnlyNewDefinitions() {
+ setupData("shared");
+
+ ModelDefinition existingDefinition = new FakeDefinition("M1");
+ ModelDefinition newDefinition = new FakeDefinition("NEWMODEL");
+
+ ModelDefinition[] definitions = new ModelDefinition[]{existingDefinition, newDefinition};
+ DefaultModelProvider provider = new DefaultModelProvider(getSessionFactory(), definitions);
+ provider.registerDefinitions();
+
+ List<Model> models = getSession().getResults(Model.class);
+ assertThat(models.size(), is(3)); // 2 existing + one new
+ }
+
+ @Test
+ public void exists() {
+ setupData("shared");
+ assertTrue(DefaultModelProvider.exists(getSession(), "M1"));
+ }
+
+ @Test
+ public void notExists() {
+ setupData("shared");
+ assertFalse(DefaultModelProvider.exists(getSession(), "UNKNOWN"));
+ }
+}
+
+class FakeDefinition extends ModelDefinition {
+
+ public FakeDefinition(String name) {
+ super(name);
+ }
+
+ @Override
+ public Model create() {
+ return Model.create();
+ }
+
+} \ No newline at end of file
diff --git a/sonar-core/src/test/java/org/sonar/core/qualitymodel/ModelTest.java b/sonar-core/src/test/java/org/sonar/core/qualitymodel/ModelTest.java
new file mode 100644
index 00000000000..a9793912a13
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/qualitymodel/ModelTest.java
@@ -0,0 +1,106 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.qualitymodel;
+
+import org.junit.Test;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
+
+public class ModelTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void saveModelAndCharacteristics() {
+ setupData("saveModelAndCharacteristics");
+ Model model = Model.createByName("fake");
+ model.createCharacteristicByName("Efficiency");
+ model.createCharacteristicByName("Usability");
+ getSession().save(model);
+ getSession().commit();
+
+ model = getSession().getSingleResult(Model.class, "name", "fake");
+ assertThat(model.getName(), is("fake"));
+ assertThat(model.getCharacteristics().size(), is(2));
+ assertThat(model.getRootCharacteristics().size(), is(2));
+ assertNotNull(model.getCharacteristicByName("Efficiency"));
+ }
+
+ /**
+ * max one parent by characteristic
+ */
+ @Test
+ public void saveTreeOfCharacteristics() {
+ setupData("testTreeOfCharacteristics");
+ Model model = Model.createByName("fake");
+
+ Characteristic efficiency = model.createCharacteristicByName("Efficiency");
+ Characteristic usability = model.createCharacteristicByName("Usability");
+ Characteristic cpuEfficiency = model.createCharacteristicByName("CPU Efficiency");
+ Characteristic ramEfficiency = model.createCharacteristicByName("RAM Efficiency");
+
+ efficiency.addChildren(cpuEfficiency, ramEfficiency);
+
+ getSession().save(model);
+ getSession().commit();
+
+ model = getSession().getSingleResult(Model.class, "name", "fake");
+ assertThat(model.getCharacteristics().size(), is(4));
+ assertThat(model.getCharacteristics(), hasItems(efficiency, usability, ramEfficiency, cpuEfficiency));
+ assertThat(efficiency.getChildren(), hasItems(ramEfficiency, cpuEfficiency));
+ assertTrue(ramEfficiency.getChildren().isEmpty());
+ assertThat(ramEfficiency.getParents(), hasItems(efficiency));
+ }
+
+ /**
+ * many-to-many relation between characteristics
+ */
+ @Test
+ public void testGraphOfCharacteristics() {
+ setupData("testGraphOfCharacteristics");
+ Model model = Model.createByName("fake");
+
+ Characteristic level1a = model.createCharacteristicByName("level1a");
+ Characteristic level1b = model.createCharacteristicByName("level1b");
+ Characteristic level2a = model.createCharacteristicByName("level2a");
+ Characteristic level2b = model.createCharacteristicByName("level2b");
+
+ level1a.addChildren(level2a, level2b);
+ level1b.addChildren(level2a, level2b);
+
+ getSession().save(model);
+ getSession().commit();
+
+ Model persistedModel = getSession().getSingleResult(Model.class, "name", "fake");
+ assertThat(persistedModel.getCharacteristics().size(), is(4));
+ assertThat(persistedModel.getRootCharacteristics().size(), is(2));
+
+ assertThat(persistedModel.getCharacteristicByName("level1a").getChildren().size(), is(2));
+ assertThat(persistedModel.getCharacteristicByName("level1b").getChildren().size(), is(2));
+
+ assertThat(persistedModel.getCharacteristicByName("level2a").getParents().size(), is(2));
+ assertThat(persistedModel.getCharacteristicByName("level2b").getParents().size(), is(2));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java
new file mode 100644
index 00000000000..10830c4256f
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java
@@ -0,0 +1,83 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.core.rule;
+
+import org.junit.Test;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.Collection;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+public class DefaultRuleProviderTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void findByKey() {
+ setupData("shared");
+ DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory());
+ Rule rule = provider.findByKey("checkstyle", "com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck");
+ assertNotNull(rule);
+ assertThat(rule.getKey(), is("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"));
+ assertThat(rule.isEnabled(), is(true));
+ }
+
+ @Test
+ public void findRepositoryRules() {
+ setupData("shared");
+ DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory());
+ Collection<Rule> rules = provider.findAll(RuleQuery.create().withRepositoryKey("checkstyle"));
+ assertNotNull(rules);
+ assertThat(rules.size(), is(2)); // only enabled checkstyle rules
+ }
+
+ @Test
+ public void findAllEnabled() {
+ setupData("shared");
+ DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory());
+ Collection<Rule> rules = provider.findAll(RuleQuery.create());
+ assertNotNull(rules);
+ assertThat(rules.size(), is(3)); // only enabled checkstyle+pmd rules
+ for (Rule rule : rules) {
+ assertThat(rule.getId(), anyOf(is(1), is(3), is(4)));
+ }
+ }
+
+ @Test
+ public void doNotFindDisabledRules() {
+ setupData("shared");
+ DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory());
+ Rule rule = provider.findByKey("checkstyle", "DisabledCheck");
+ assertNull(rule);
+ }
+
+ @Test
+ public void doNotFindUnknownRules() {
+ setupData("shared");
+ DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory());
+ Collection<Rule> rules = provider.findAll(RuleQuery.create().withRepositoryKey("unknown_repository"));
+ assertThat(rules.size(), is(0));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresDaoTest.java b/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresDaoTest.java
new file mode 100644
index 00000000000..3c70c3fd62c
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresDaoTest.java
@@ -0,0 +1,165 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.junit.Test;
+import org.sonar.api.database.model.AsyncMeasureSnapshot;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class AsyncMeasuresDaoTest extends AbstractDbUnitTestCase {
+
+ private static final int PROJECT_ID = 1;
+ private static final int METRIC_ID = 1;
+
+ @Test
+ public void testGetNextAsyncMeasureSnapshot() {
+ setupData("sharedFixture", "testGetNextAsyncMeasureSnapshot");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ AsyncMeasureSnapshot asyncMeasure = asyncMeasuresDao.getNextAsyncMeasureSnapshot(
+ PROJECT_ID, METRIC_ID, stringToDate("2008-12-04 08:00:00.00"));
+
+ assertThat(asyncMeasure.getId(), is(3));
+ }
+
+ @Test
+ public void testGetNextSnapshotsUntilDate() {
+ setupData("sharedFixture", "testGetNextSnapshotsUntilDate");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ MeasureModel asyncMeasure = getSession().getEntityManager().find(MeasureModel.class, 1l);
+ List<Snapshot> snapshotIds = asyncMeasuresDao.getNextSnapshotsUntilDate(
+ asyncMeasure, stringToDate("2008-12-06 12:00:00.00"));
+
+ assertThat(snapshotIds.size(), is(2));
+ assertThat(snapshotIds.get(0).getId(), is(2));
+ assertThat(snapshotIds.get(1).getId(), is(4));
+ }
+
+ @Test
+ public void testGetPreviousSnapshot() {
+ setupData("sharedFixture", "testGetPreviousSnapshot");
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ Snapshot s = new Snapshot();
+ s.setCreatedAt(stringToDate("2008-12-04 08:00:00.00"));
+ s.setScope(ResourceModel.SCOPE_PROJECT);
+ ResourceModel resource1 = getSession().getEntity(ResourceModel.class, 1);
+ ResourceModel resource2 = getSession().getEntity(ResourceModel.class, 2);
+
+ s.setResource(resource1);
+ assertThat(asyncMeasuresDao.getPreviousSnapshot(s).getId(), is(1));
+
+ s.setResource(resource2);
+ assertThat(asyncMeasuresDao.getPreviousSnapshot(s).getId(), is(5));
+ }
+
+ @Test
+ public void testGetNextAsyncMeasureSnapshotsUntilDate() {
+ setupData("sharedFixture", "testGetNextAsyncMeasureSnapshotsUntilDate");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ MeasureModel asyncMeasure = getSession().getEntityManager().find(MeasureModel.class, 3l);
+ List<AsyncMeasureSnapshot> asyncMeasureSnapshots = asyncMeasuresDao.getNextAsyncMeasureSnapshotsUntilDate(
+ asyncMeasure, stringToDate("2008-12-06 08:00:00.00"));
+
+ assertThat(asyncMeasureSnapshots.size(), is(2));
+ assertThat(asyncMeasureSnapshots.get(0).getId(), is(2));
+ assertThat(asyncMeasureSnapshots.get(1).getId(), is(3));
+ }
+
+ @Test
+ public void testDeleteAsyncMeasure() {
+ setupData("sharedFixture", "testDeleteAsyncMeasure");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ MeasureModel asyncMeasure = getSession().getEntityManager().find(MeasureModel.class, 1l);
+ asyncMeasuresDao.deleteAsyncMeasure(asyncMeasure);
+
+ getSession().commit();
+ checkTables("testDeleteAsyncMeasure", "project_measures", "async_measure_snapshots");
+ }
+
+ @Test
+ public void testGetAsyncMeasureSnapshotsFromSnapshotId() {
+ setupData("sharedFixture", "testGetAsyncMeasureSnapshotsFromSnapshotId");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ Integer snapshotId = 1;
+ List<AsyncMeasureSnapshot> asyncMeasureSnapshots = asyncMeasuresDao.getAsyncMeasureSnapshotsFromSnapshotId(
+ snapshotId, Arrays.asList(1));
+ assertThat(asyncMeasureSnapshots.size(), is(1));
+ assertThat(asyncMeasureSnapshots.get(0).getId(), is(2));
+ }
+
+ @Test
+ public void testGetLastAsyncMeasureSnapshot() {
+ setupData("sharedFixture", "testGetLastAsyncMeasureSnapshot");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ AsyncMeasureSnapshot asyncMeasureSnapshot = asyncMeasuresDao.getLastAsyncMeasureSnapshot(
+ PROJECT_ID, METRIC_ID, stringToDate("2008-12-04 12:00:00.00"));
+ assertThat(asyncMeasureSnapshot.getId(), is(2));
+ }
+
+ @Test
+ public void testDeleteAsyncMeasureSnapshots() {
+ setupData("sharedFixture", "testDeleteAsyncMeasureSnapshots");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ asyncMeasuresDao.deleteAsyncMeasureSnapshots(1l);
+
+ checkTables("testDeleteAsyncMeasureSnapshots", "async_measure_snapshots");
+ }
+
+ @Test
+ public void testGetPreviousAsyncMeasureSnapshots() {
+ setupData("sharedFixture", "testGetPreviousAsyncMeasureSnapshots");
+
+ AsyncMeasuresDao asyncMeasuresDao = new AsyncMeasuresDao(getSession());
+ List<AsyncMeasureSnapshot> asyncMeasureSnapshots = asyncMeasuresDao.getPreviousAsyncMeasureSnapshots(
+ PROJECT_ID, stringToDate("2008-12-04 08:00:00.00"), stringToDate("2008-12-08 08:00:00.00"));
+ assertThat(asyncMeasureSnapshots.size(), is(2));
+ assertThat(asyncMeasureSnapshots.get(0).getId(), is(5));
+ assertThat(asyncMeasureSnapshots.get(1).getId(), is(6));
+ }
+
+
+ private static Date stringToDate(String sDate) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS");
+ try {
+ return sdf.parse(sDate);
+ } catch (ParseException e) {
+ throw new RuntimeException("Bad date format.");
+ }
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresServiceTest.java b/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresServiceTest.java
new file mode 100644
index 00000000000..9ef3ad41034
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dao/AsyncMeasuresServiceTest.java
@@ -0,0 +1,153 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.junit.Test;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+public class AsyncMeasuresServiceTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void assignLatestMeasuresToLastSnapshot() {
+ setupData("sharedFixture", "assignLatestMeasuresToLastSnapshot");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ Snapshot snapshot = getSession().getEntityManager().find(Snapshot.class, 2);
+ asyncMeasuresService.refresh(snapshot);
+
+ checkTables("assignLatestMeasuresToLastSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignNewMeasuresToLastSnapshot() {
+ setupData("sharedFixture", "assignNewMeasuresToLastSnapshot");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ Snapshot snapshot = getSession().getEntityManager().find(Snapshot.class, 2);
+ asyncMeasuresService.refresh(snapshot);
+
+ checkTables("assignNewMeasuresToLastSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignMeasuresWhenNoPreviousSnapshot() {
+ setupData("sharedFixture", "assignMeasuresWhenNoPreviousSnapshot");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ Snapshot snapshot = getSession().getEntityManager().find(Snapshot.class, 1);
+ asyncMeasuresService.refresh(snapshot);
+
+ checkTables("assignMeasuresWhenNoPreviousSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignLatestMeasuresWhenNoPreviousSnapshot() {
+ setupData("sharedFixture", "assignLatestMeasuresWhenNoPreviousSnapshot");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ Snapshot snapshot = getSession().getEntityManager().find(Snapshot.class, 1);
+ asyncMeasuresService.refresh(snapshot);
+
+ checkTables("assignLatestMeasuresWhenNoPreviousSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignPastMeasuresToPastSnapshot() {
+ setupData("sharedFixture", "assignPastMeasuresToPastSnapshot");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ Snapshot snapshot = getSession().getEntityManager().find(Snapshot.class, 3);
+ asyncMeasuresService.refresh(snapshot);
+
+ checkTables("assignPastMeasuresToPastSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignNewMeasureToFutureSnapshots() {
+ setupData("sharedFixture", "assignNewMeasureToFutureSnapshots");
+
+ AsyncMeasuresService asyncMeasuresService = new AsyncMeasuresService(getSession());
+ asyncMeasuresService.registerMeasure(2l);
+
+ checkTables("assignNewMeasureToFutureSnapshots", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignMeasureToFutureSnapshotsWithDifferentMetric() {
+ setupData("sharedFixture", "assignMeasureToFutureSnapshotsWithDifferentMetric");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.registerMeasure(3l);
+
+ checkTables("assignMeasureToFutureSnapshotsWithDifferentMetric", "async_measure_snapshots");
+ }
+
+ @Test
+ public void assignAPastMeasureToNextSnapshotsWithDifferentMetric() {
+ setupData("sharedFixture", "assignAPastMeasureToNextSnapshotsWithDifferentMetric");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.registerMeasure(2l);
+
+ checkTables("assignAPastMeasureToNextSnapshotsWithDifferentMetric", "async_measure_snapshots");
+ }
+
+ @Test
+ public void addFutureSnapshot() {
+ setupData("sharedFixture", "addFutureSnapshot");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.registerMeasure(2l);
+
+ checkTables("addFutureSnapshot", "async_measure_snapshots");
+ }
+
+ @Test
+ public void addInvisibleMeasure() {
+ setupData("sharedFixture", "addInvisibleMeasure");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.registerMeasure(2l);
+
+ checkTables("addInvisibleMeasure", "async_measure_snapshots");
+ }
+
+ @Test
+ public void deleteMeasure() {
+ setupData("sharedFixture", "deleteMeasure");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.deleteMeasure(2l);
+
+ checkTables("deleteMeasure", "async_measure_snapshots");
+ }
+
+ @Test
+ public void deleteLastMeasure() {
+ setupData("sharedFixture", "deleteLastMeasure");
+
+ AsyncMeasuresService asyncMeasureService = new AsyncMeasuresService(getSession());
+ asyncMeasureService.deleteMeasure(1l);
+
+ checkTables("deleteLastMeasure", "async_measure_snapshots");
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dao/MeasuresDaoTest.java b/sonar-core/src/test/java/org/sonar/jpa/dao/MeasuresDaoTest.java
new file mode 100644
index 00000000000..5502905e5e6
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dao/MeasuresDaoTest.java
@@ -0,0 +1,107 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.measures.Metric;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+public class MeasuresDaoTest extends AbstractDbUnitTestCase {
+
+ private MeasuresDao service;
+ private ResourceModel project;
+
+ @Before
+ public void before() throws Exception {
+ service = new MeasuresDao(getSession());
+ project = new ResourceModel(ResourceModel.SCOPE_PROJECT, "foo:bar", ResourceModel.QUALIFIER_PROJECT_TRUNK, null, "Foo");
+ project.setName("project name");
+ getSession().save(project);
+ }
+
+ @Test
+ public void shouldReturnUserDefinedMetrics() {
+ for (Metric metric : createMetrics()) {
+ getSession().save(metric);
+ }
+
+ Collection<Metric> metrics = service.getUserDefinedMetrics();
+ assertThat(metrics.size(), is(2));
+ for (Metric metric : metrics) {
+ assertThat(metric.getOrigin(), not(Metric.Origin.JAV));
+ }
+ }
+
+ @Test
+ public void shouldRegisterMetrics() {
+ Collection<Metric> newMetrics = createMetrics();
+ service.registerMetrics(newMetrics);
+
+ Collection<Metric> metrics = service.getEnabledMetrics();
+ assertThat(metrics.size(), is(newMetrics.size()));
+ }
+
+ @Test
+ public void shouldDisabledMetrics() {
+ Collection<Metric> newMetrics = createMetrics();
+
+ service.disabledMetrics(newMetrics);
+
+ Collection<Metric> allMetrics = service.getMetrics();
+ assertThat(allMetrics.size(), is(newMetrics.size()));
+
+ Collection<Metric> disabledMetrics = service.getEnabledMetrics();
+ assertThat(disabledMetrics.size(), is(0));
+ }
+
+
+ private Collection<Metric> createMetrics() {
+ Metric m1 = new Metric("metric1");
+ m1.setEnabled(false);
+ m1.setOrigin(Metric.Origin.JAV);
+
+ Metric m2 = new Metric("metric2");
+ m2.setEnabled(true);
+ m2.setOrigin(Metric.Origin.JAV);
+
+ Metric m3 = new Metric("metric3");
+ m3.setEnabled(false);
+ m3.setOrigin(Metric.Origin.GUI);
+
+ Metric m4 = new Metric("metric4");
+ m4.setEnabled(true);
+ m4.setOrigin(Metric.Origin.GUI);
+
+ Metric m5 = new Metric("metric5");
+ m5.setEnabled(true);
+ m5.setOrigin(Metric.Origin.WS);
+
+ return Arrays.asList(m1, m2, m3, m4, m5);
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dao/ProfilesDaoTest.java b/sonar-core/src/test/java/org/sonar/jpa/dao/ProfilesDaoTest.java
new file mode 100644
index 00000000000..796435c5dab
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dao/ProfilesDaoTest.java
@@ -0,0 +1,68 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+public class ProfilesDaoTest extends AbstractDbUnitTestCase {
+
+ private ProfilesDao profilesDao;
+
+ @Before
+ public void setup() {
+ profilesDao = new ProfilesDao(getSession());
+ }
+
+ @Test
+ public void shouldGetProfiles() {
+ setupData("shouldGetProfiles");
+
+ List<RulesProfile> profiles = profilesDao.getProfiles("java");
+
+ assertThat(profiles.size(), is(2));
+ }
+
+ @Test
+ public void testGetActiveProfile() {
+ RulesProfile testDefaultProfile = new RulesProfile("default", "java", true, true);
+ RulesProfile testProfile = new RulesProfile("not default", "java", false, false);
+ getSession().save(testDefaultProfile, testProfile);
+
+ ResourceModel testResourceWithProfile = new ResourceModel(ResourceModel.SCOPE_PROJECT, "withProfile", "qual", null, "test");
+ testResourceWithProfile.setRulesProfile(testProfile);
+ ResourceModel testResourceWithNoProfile = new ResourceModel(ResourceModel.SCOPE_PROJECT, "withoutProfile", "qual", null, "test");
+ getSession().save(testResourceWithProfile, testResourceWithNoProfile);
+
+ assertNull(profilesDao.getActiveProfile("wrongLanguage", "withoutProfile"));
+ assertEquals(testDefaultProfile.getId(), profilesDao.getActiveProfile("java", "wrongKey").getId());
+ assertEquals(testDefaultProfile.getId(), profilesDao.getActiveProfile("java", "withoutProfile").getId());
+ assertEquals(testProfile.getId(), profilesDao.getActiveProfile("java", "withProfile").getId());
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dao/RulesDaoTest.java b/sonar-core/src/test/java/org/sonar/jpa/dao/RulesDaoTest.java
new file mode 100644
index 00000000000..30dc9fc0602
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dao/RulesDaoTest.java
@@ -0,0 +1,131 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dao;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.*;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class RulesDaoTest extends AbstractDbUnitTestCase {
+
+ private RulesDao rulesDao;
+
+ @Before
+ public void setup() {
+ rulesDao = new RulesDao(getSession());
+ }
+
+ @Test
+ public void shouldGetRules() {
+ setupData("shouldGetRules");
+
+ List<Rule> rules = rulesDao.getRules();
+ assertThat(rules, notNullValue());
+ assertThat(rules.size(), is(2));
+
+ assertEquals("rule_one", rules.get(0).getKey());
+ assertEquals(1, rules.get(0).getParams().size());
+ assertEquals("category one", rules.get(0).getRulesCategory().getName());
+ }
+
+ @Test
+ public void shouldGetRuleWithRuleKeyAndPluginKey() {
+ setupData("shouldGetRuleWithRuleKeyAndPluginKey");
+
+ Rule rule = rulesDao.getRuleByKey("plugin", "checkstyle.rule1");
+ assertThat(rule, notNullValue());
+ assertThat(rule.getId(), notNullValue());
+
+ Rule rule2 = rulesDao.getRuleByKey("plugin", "key not found");
+ assertThat(rule2, nullValue());
+ }
+
+ @Test
+ public void shouldCountNumberOfRulesOfACategoryForGivenPlugins() {
+ setupData("shouldCountNumberOfRulesOfACategoryForGivenPlugins");
+
+ Long result = rulesDao.countRules(Arrays.asList("plugin1", "plugin2"), "category one");
+ assertThat(result, is(3L));
+
+ result = rulesDao.countRules(Arrays.asList("plugin1", "plugin2"), "category two");
+ assertThat(result, is(1L));
+ }
+
+ @Test
+ public void shouldGetRuleParams() {
+ setupData("shouldGetRuleParams");
+
+ List<RuleParam> ruleParams = rulesDao.getRuleParams();
+
+ assertThat(ruleParams.size(), is(3));
+ }
+
+ @Test
+ public void shouldSynchronizeRuleOfActiveRule() {
+ setupData("shouldSynchronizeRuleOfActiveRule");
+ Rule rule = new Rule(null, "other key", (String) null, null, null);
+ RuleParam ruleParam = new RuleParam(null, "rule1_param1", null, null);
+ rule.setParams(Arrays.asList(ruleParam));
+ ActiveRule activeRule = new ActiveRule(null, rule, null);
+ ActiveRuleParam activeRuleParam = new ActiveRuleParam(activeRule, ruleParam, null);
+ activeRule.setActiveRuleParams(Arrays.asList(activeRuleParam));
+
+ rulesDao.synchronizeRuleOfActiveRule(activeRule, "plugin");
+
+ assertThat(activeRule.getRule().getId(), notNullValue());
+ assertThat(activeRule.getActiveRuleParams().size(), is(1));
+ }
+
+ @Test
+ public void shouldGetRulesProfileById() {
+ RulesProfile rulesProfile = new RulesProfile("profil", "java", true, true);
+ getSession().save(rulesProfile);
+
+ RulesProfile rulesProfileExpected = rulesDao.getProfileById(rulesProfile.getId());
+
+ assertThat(rulesProfileExpected, is(rulesProfile));
+ }
+
+ @Test
+ public void shouldAddActiveRulesToProfile() {
+ setupData("shouldAddActiveRulesToProfile");
+
+ Rule rule = new Rule("rule1", "key1", "config1", new RulesCategory("test"), null);
+ RuleParam ruleParam = new RuleParam(null, "param1", null, null);
+ rule.setParams(Arrays.asList(ruleParam));
+
+ ActiveRule activeRule = new ActiveRule(null, rule, RulePriority.MAJOR);
+ ActiveRuleParam activeRuleParam = new ActiveRuleParam(activeRule, ruleParam, "20");
+ activeRule.setActiveRuleParams(Arrays.asList(activeRuleParam));
+ rulesDao.addActiveRulesToProfile(Arrays.asList(activeRule), 1, "plugin");
+
+ checkTables("shouldAddActiveRulesToProfile", "rules", "active_rules", "rules_profiles");
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/DerbyTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/DerbyTest.java
new file mode 100644
index 00000000000..298f4775765
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/DerbyTest.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class DerbyTest {
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new Derby().matchesJdbcURL("jdbc:derby:foo"), is(true));
+ assertThat(new Derby().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java
new file mode 100644
index 00000000000..0f7db36bd7a
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java
@@ -0,0 +1,70 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.api.utils.SonarException;
+
+public class DialectRepositoryTest {
+
+ @Test
+ public void testFindById() {
+ Dialect d = DialectRepository.find(DatabaseProperties.DIALECT_MYSQL, null);
+ assertEquals(MySql.class, d.getClass());
+ }
+
+ @Test
+ public void testFindByJdbcUrl() {
+ Dialect d = DialectRepository.find(null, "jdbc:mysql:foo:bar");
+ assertEquals(MySql.class, d.getClass());
+ }
+
+ @Test
+ public void testFindClassName() {
+ Dialect d = DialectRepository.find(TestDialect.class.getName(), null);
+ assertEquals(TestDialect.class, d.getClass());
+ }
+
+ @Test(expected=SonarException.class)
+ public void testFindNoMatch() {
+ DialectRepository.find("foo", "bar");
+ }
+
+ public static class TestDialect implements Dialect {
+ public boolean matchesJdbcURL(String jdbcConnectionURL) {
+ return false;
+ }
+
+ public String getId() {
+ return "testDialect";
+ }
+
+ public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
+ return null;
+ }
+
+ public String getActiveRecordDialectCode() {
+ return null;
+ }
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/HsqlDbTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/HsqlDbTest.java
new file mode 100644
index 00000000000..20cebfd9eb7
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/HsqlDbTest.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class HsqlDbTest {
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new HsqlDb().matchesJdbcURL("jdbc:hsqldb:hsql:foo"), is(true));
+ assertThat(new HsqlDb().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/MsSqlTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/MsSqlTest.java
new file mode 100644
index 00000000000..5d2a9a53043
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/MsSqlTest.java
@@ -0,0 +1,37 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class MsSqlTest {
+
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new MsSql().matchesJdbcURL("jdbc:jtds:sqlserver://localhost;databaseName=SONAR;SelectMethod=Cursor"), is(true));
+ assertThat(new MsSql().matchesJdbcURL("jdbc:microsoft:sqlserver://localhost:1433;databasename=sonar"), is(true));
+
+ assertThat(new MsSql().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ assertThat(new MsSql().matchesJdbcURL("jdbc:mysql:foo"), is(false));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/MySqlTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/MySqlTest.java
new file mode 100644
index 00000000000..ad60fa7e0a1
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/MySqlTest.java
@@ -0,0 +1,37 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class MySqlTest {
+
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new MySql().matchesJdbcURL("jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8"), is(true));
+ assertThat(new MySql().matchesJdbcURL("JDBC:MYSQL://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8"), is(true));
+
+ assertThat(new MySql().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ assertThat(new MySql().matchesJdbcURL("jdbc:oracle:foo"), is(false));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleSequenceGeneratorTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleSequenceGeneratorTest.java
new file mode 100644
index 00000000000..ba15aa47854
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleSequenceGeneratorTest.java
@@ -0,0 +1,43 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class OracleSequenceGeneratorTest {
+
+ @Test
+ public void sequenceNameShouldFollowRailsConventions() {
+ Properties props = new Properties();
+ props.setProperty(PersistentIdentifierGenerator.TABLE, "my_table");
+ props.setProperty(PersistentIdentifierGenerator.PK, "id");
+
+ OracleSequenceGenerator generator = new OracleSequenceGenerator();
+ generator.configure(null, props, new Oracle.Oracle10gWithDecimalDialect());
+ assertThat(generator.getSequenceName(), is("MY_TABLE_SEQ"));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleTest.java
new file mode 100644
index 00000000000..89145bf7305
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/OracleTest.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class OracleTest {
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new Oracle().matchesJdbcURL("jdbc:oracle:thin:@localhost/XE"), is(true));
+ assertThat(new Oracle().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSQLSequenceGeneratorTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSQLSequenceGeneratorTest.java
new file mode 100644
index 00000000000..3738d30f56c
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSQLSequenceGeneratorTest.java
@@ -0,0 +1,43 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class PostgreSQLSequenceGeneratorTest {
+
+ @Test
+ public void sequenceNameShouldFollowRailsConventions() {
+ Properties props = new Properties();
+ props.setProperty(PersistentIdentifierGenerator.TABLE, "my_table");
+ props.setProperty(PersistentIdentifierGenerator.PK, "id");
+
+ PostgreSQLSequenceGenerator generator = new PostgreSQLSequenceGenerator();
+ generator.configure(null, props, new PostgreSql.PostgreSQLWithDecimalDialect());
+ assertThat(generator.getSequenceName(), is("my_table_id_seq"));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSqlTest.java b/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSqlTest.java
new file mode 100644
index 00000000000..b16d92c5e4d
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSqlTest.java
@@ -0,0 +1,33 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.dialect;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class PostgreSqlTest {
+ @Test
+ public void matchesJdbcURL() {
+ assertThat(new PostgreSql().matchesJdbcURL("jdbc:postgresql://localhost/sonar"), is(true));
+ assertThat(new PostgreSql().matchesJdbcURL("jdbc:hsql:foo"), is(false));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/entity/PropertyTest.java b/sonar-core/src/test/java/org/sonar/jpa/entity/PropertyTest.java
new file mode 100644
index 00000000000..c948023d82a
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/entity/PropertyTest.java
@@ -0,0 +1,39 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.entity;
+
+import org.junit.Test;
+import org.sonar.api.database.configuration.Property;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class PropertyTest {
+
+ @Test
+ public void encodeBlob() {
+ Property prop = new Property("key1", "value1");
+ assertThat(prop.getValue(), is("value1"));
+
+ prop.setValue(null);
+ assertThat(prop.getValue(), nullValue());
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/entity/SchemaMigrationTest.java b/sonar-core/src/test/java/org/sonar/jpa/entity/SchemaMigrationTest.java
new file mode 100644
index 00000000000..8be589fb5b2
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/entity/SchemaMigrationTest.java
@@ -0,0 +1,65 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.entity;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.jpa.session.MemoryDatabaseConnector;
+
+import java.sql.Connection;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class SchemaMigrationTest {
+
+ @Test
+ public void currentVersionShouldBeUnknownWhenSchemaIsEmpty() throws Exception {
+ MemoryDatabaseConnector connector = new MemoryDatabaseConnector(SchemaMigration.VERSION_UNKNOWN);
+ connector.start();
+
+ Connection connection = Mockito.mock(Connection.class);
+ try {
+ connection = connector.getConnection();
+ assertEquals(SchemaMigration.VERSION_UNKNOWN, SchemaMigration.getCurrentVersion(connection));
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ }
+
+ @Test
+ public void versionShouldBeLoadedFromSchemaMigrationsTable() throws Exception {
+ MemoryDatabaseConnector connector = new MemoryDatabaseConnector(30);
+ connector.start();
+
+ Connection connection = null;
+ try {
+ connection = connector.getConnection();
+ assertEquals(30, SchemaMigration.getCurrentVersion(connection));
+
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java
new file mode 100644
index 00000000000..5d5cc9cfc83
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.jpa.dialect.HsqlDb;
+import org.sonar.jpa.dialect.Oracle;
+
+import javax.persistence.EntityManagerFactory;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class AbstractDatabaseConnectorTest {
+
+ @Test
+ public void autodetectDialectWhenNotExcplicitlyDefined() {
+ MemoryDatabaseConnector connector = new MemoryDatabaseConnector();
+ connector.start();
+ assertEquals(HsqlDb.class, connector.getDialect().getClass());
+ connector.stop();
+ }
+
+ @Test
+ public void useConfiguredDialectByDefault() {
+ Configuration conf = MemoryDatabaseConnector.getInMemoryConfiguration(false);
+ conf.setProperty(DatabaseProperties.PROP_DIALECT, DatabaseProperties.DIALECT_ORACLE);
+
+ TestDatabaseConnector connector = new TestDatabaseConnector(conf);
+ connector.start();
+ assertEquals(Oracle.class, connector.getDialect().getClass());
+ connector.stop();
+ }
+
+ @Test
+ public void getHibernateProperties() {
+ PropertiesConfiguration conf = new PropertiesConfiguration();
+ conf.setProperty("sonar.foo", "foo value");
+
+ // all properties prefixed by sonar.hibernate are propagated to hibernate configuration (the prefix "sonar." is removed)
+ conf.setProperty("sonar.hibernate.foo", "hibernate.foo value");
+
+ // hardcoded property. Should be replaced by sonar.hibernate.hbm2ddl.auto
+ conf.setProperty("sonar.jdbc.hibernate.hbm2ddl", "hibernate.hbm2ddl value");
+
+ // the dialect is mandatory if the JDBC url is not set
+ conf.setProperty("sonar.jdbc.dialect", DatabaseProperties.DIALECT_ORACLE);
+
+ AbstractDatabaseConnector connector = new TestDatabaseConnector(conf);
+ connector.start();
+
+ Properties hibernateProps = connector.getHibernateProperties();
+ assertThat(hibernateProps.getProperty("sonar.foo"), Matchers.nullValue()); // not an hibernate property
+ assertThat(hibernateProps.getProperty("hibernate.foo"), Matchers.is("hibernate.foo value"));
+ assertThat(hibernateProps.getProperty("hibernate.hbm2ddl.auto"), Matchers.is("hibernate.hbm2ddl value"));
+ assertThat(hibernateProps.getProperty("hibernate.dialect"), Matchers.is(Oracle.Oracle10gWithDecimalDialect.class.getName()));
+ }
+
+ private class TestDatabaseConnector extends AbstractDatabaseConnector {
+
+ public TestDatabaseConnector(Configuration configuration) {
+ super(configuration, false);
+ }
+
+ @Override
+ public void setupEntityManagerFactory(Properties factoryProps) {
+ }
+
+ @Override
+ protected boolean upToDateSchemaVersion() {
+ return true;
+ }
+
+ @Override
+ public EntityManagerFactory createEntityManagerFactory() {
+ return null;
+ }
+
+ public Connection getConnection() throws SQLException {
+ Connection c = Mockito.mock(Connection.class);
+ DatabaseMetaData m = Mockito.mock(DatabaseMetaData.class);
+ Mockito.when(c.getMetaData()).thenReturn(m);
+ return c;
+ }
+ }
+
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/DatabaseSessionTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/DatabaseSessionTest.java
new file mode 100644
index 00000000000..6cc891f6cf8
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/session/DatabaseSessionTest.java
@@ -0,0 +1,120 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.internal.matchers.IsCollectionContaining;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.measures.Metric;
+import org.sonar.jpa.dao.MeasuresDao;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import javax.persistence.NonUniqueResultException;
+import java.sql.Date;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class DatabaseSessionTest extends AbstractDbUnitTestCase {
+ private static final Long NB_INSERTS = 20000l;
+
+ private ResourceModel project1;
+ private ResourceModel project2;
+
+ @Before
+ public void setup() {
+ project1 = new ResourceModel(ResourceModel.SCOPE_PROJECT, "mygroup:myartifact", "JAV", null, "my name");
+ project2 = new ResourceModel(ResourceModel.SCOPE_PROJECT, "mygroup:myartifact1", "JAV", null, "my name 2");
+ }
+
+ @Test
+ public void performaceTestOnBatchInserts() throws Exception {
+
+ Snapshot snapshot = new Snapshot(project1, true, "", new Date(1));
+ getSession().save(project1, snapshot);
+ getSession().commit();
+
+ Metric metric = new MeasuresDao(getSession()).getMetric("classes_count");
+ for (int i = 0; i < NB_INSERTS; i++) {
+ MeasureModel pm = new MeasureModel(metric, 1.0).setSnapshotId(snapshot.getId());
+ getSession().save(pm);
+ }
+
+ getSession().commit();
+ assertEquals(NB_INSERTS, getHQLCount(MeasureModel.class));
+
+ }
+
+ @Test
+ public void testGetSingleResultWithNoResults() {
+ assertNull(getSession().getSingleResult(ResourceModel.class, "name", "test"));
+ }
+
+ @Test
+ public void testGetSingleResultWithNoCriterias() {
+ try {
+ assertNull(getSession().getSingleResult(ResourceModel.class, (Object[]) null));
+ fail("No IllegalStateException raised");
+ } catch (IllegalStateException ex) {
+ // error raised correctly
+ }
+ }
+
+ @Test
+ public void testGetSingleResultWithOneResult() {
+ getSession().save(project1);
+ ResourceModel hit = getSession().getSingleResult(ResourceModel.class, "name", "my name");
+ assertNotNull(hit);
+ assertEquals(project1, hit);
+ }
+
+ @Test
+ public void testGetSingleResultWithTwoResults() {
+ getSession().save(project1, project2);
+ try {
+ getSession().getSingleResult(ResourceModel.class, "qualifier", "JAV");
+ fail("No NonUniqueResultException raised");
+ } catch (NonUniqueResultException ex) {
+ // error raised correctly
+ }
+ }
+
+ @Test
+ public void testGetResultsWithNoResults() {
+ List<ResourceModel> hits = getSession().getResults(ResourceModel.class, "name", "foo");
+ assertTrue(hits.isEmpty());
+ }
+
+ @Test
+ public void testGetResultsWithMultipleResults() {
+ ResourceModel project3 = new ResourceModel(ResourceModel.SCOPE_PROJECT, "mygroup:myartifact3", "TEST", null, "my name 3");
+ getSession().save(project1, project2, project3);
+
+ List<ResourceModel> hits = getSession().getResults(ResourceModel.class, "qualifier", "JAV");
+ assertFalse(hits.isEmpty());
+ assertThat(hits, IsCollectionContaining.hasItems(project1, project2));
+ assertThat(hits, Matchers.not(IsCollectionContaining.hasItem(project3)));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java
new file mode 100644
index 00000000000..f133296d10f
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java
@@ -0,0 +1,92 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.configuration.PropertiesConfiguration;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Test;
+import org.sonar.api.database.DatabaseProperties;
+
+import java.sql.SQLException;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+public class DriverDatabaseConnectorTest {
+
+ private DriverDatabaseConnector connector = null;
+
+ @After
+ public void stop() {
+ if (connector != null) {
+ connector.stop();
+ }
+ }
+
+ @Test(expected = DatabaseException.class)
+ public void failsIfUnvalidConfiguration() throws SQLException {
+ PropertiesConfiguration conf = new PropertiesConfiguration();
+ conf.setProperty(DatabaseProperties.PROP_URL, "jdbc:foo:bar//xxx");
+ conf.setProperty(DatabaseProperties.PROP_DRIVER, MemoryDatabaseConnector.DRIVER);
+ conf.setProperty(DatabaseProperties.PROP_USER, "sa");
+ conf.setProperty(DatabaseProperties.PROP_PASSWORD, null);
+ connector = new DriverDatabaseConnector(conf);
+ try {
+ connector.start();
+ } finally {
+ assertFalse(connector.isStarted());
+ assertFalse(connector.isOperational());
+ }
+ }
+
+ @Test(expected = DatabaseException.class)
+ public void failsIfSchemaIsNotCreated() {
+ connector = new DriverDatabaseConnector(MemoryDatabaseConnector.getInMemoryConfiguration(false));
+ try {
+ connector.start();
+ } finally {
+ assertTrue(connector.isStarted());
+ assertFalse(connector.isOperational());
+ }
+ }
+
+ @Test(expected = DatabaseException.class)
+ public void failsIfUpToDateSchema() {
+ connector = new DriverDatabaseConnector(MemoryDatabaseConnector.getInMemoryConfiguration(true));
+ try {
+ connector.start();
+ } finally {
+ assertTrue(connector.isStarted());
+ assertFalse(connector.isOperational());
+ }
+ }
+
+ @Test
+ public void deprecatedParametersAreStillValid() {
+ PropertiesConfiguration conf = new PropertiesConfiguration();
+ conf.setProperty(DatabaseProperties.PROP_DRIVER_DEPRECATED, MemoryDatabaseConnector.DRIVER);
+ conf.setProperty(DatabaseProperties.PROP_USER_DEPRECATED, "freddy");
+ connector = new DriverDatabaseConnector(conf);
+
+ assertThat(connector.getDriver(), is(MemoryDatabaseConnector.DRIVER));
+ assertThat(connector.getUsername(), Matchers.is("freddy"));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactoryTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactoryTest.java
new file mode 100644
index 00000000000..7f1c2eb5c3b
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/session/ThreadLocalDatabaseSessionFactoryTest.java
@@ -0,0 +1,49 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.junit.Test;
+import org.sonar.api.database.DatabaseSession;
+
+import static org.junit.Assert.assertTrue;
+
+public class ThreadLocalDatabaseSessionFactoryTest {
+
+
+ @Test
+ public void shouldCreateOneSessionPerThread() {
+ final MemoryDatabaseConnector connector = new MemoryDatabaseConnector();
+ connector.start();
+ final DatabaseSessionFactory factory = new ThreadLocalDatabaseSessionFactory(connector);
+
+ final DatabaseSession junitThreadSession = factory.getSession();
+ assertTrue(junitThreadSession == factory.getSession());
+
+ new Thread() {
+ @Override
+ public void run() {
+ DatabaseSession threadSession = factory.getSession();
+ assertTrue(threadSession != junitThreadSession);
+ }
+
+ }.start();
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/jpa/test/AbstractDbUnitTestCase.java b/sonar-core/src/test/java/org/sonar/jpa/test/AbstractDbUnitTestCase.java
new file mode 100644
index 00000000000..0f4eb82793d
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/jpa/test/AbstractDbUnitTestCase.java
@@ -0,0 +1,247 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.test;
+
+import org.apache.commons.io.IOUtils;
+import org.dbunit.Assertion;
+import org.dbunit.DatabaseUnitException;
+import org.dbunit.IDatabaseTester;
+import org.dbunit.JdbcDatabaseTester;
+import org.dbunit.database.DatabaseConfig;
+import org.dbunit.database.IDatabaseConnection;
+import org.dbunit.dataset.CompositeDataSet;
+import org.dbunit.dataset.DataSetException;
+import org.dbunit.dataset.IDataSet;
+import org.dbunit.dataset.ReplacementDataSet;
+import org.dbunit.dataset.xml.FlatXmlDataSet;
+import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
+import org.dbunit.operation.DatabaseOperation;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+import org.sonar.jpa.dao.*;
+import org.sonar.jpa.session.JpaDatabaseSession;
+import org.sonar.jpa.session.MemoryDatabaseConnector;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.sql.SQLException;
+
+import static org.junit.Assert.fail;
+
+public abstract class AbstractDbUnitTestCase {
+
+ private MemoryDatabaseConnector dbConnector;
+ private JpaDatabaseSession session;
+ private DaoFacade dao;
+ protected IDatabaseTester databaseTester;
+ protected IDatabaseConnection connection;
+
+ @Before
+ public final void startDatabase() throws Exception {
+ if (dbConnector == null) {
+ dbConnector = new MemoryDatabaseConnector();
+ dbConnector.start();
+ session = new JpaDatabaseSession(dbConnector);
+ session.start();
+ }
+
+ databaseTester = new JdbcDatabaseTester(MemoryDatabaseConnector.DRIVER, MemoryDatabaseConnector.URL, MemoryDatabaseConnector.USER,
+ MemoryDatabaseConnector.PASSWORD);
+ databaseTester.onTearDown();
+ }
+
+ @After
+ public final void stopDatabase() {
+ if (dbConnector != null) {
+ dbConnector.stop();
+ session.stop();
+ }
+ }
+
+ public DaoFacade getDao() {
+ if (dao == null) {
+ dao = new DaoFacade(new ProfilesDao(session), new RulesDao(session), new MeasuresDao(session), new AsyncMeasuresDao(session));
+ }
+ return dao;
+ }
+
+ public DatabaseSession getSession() {
+ return session;
+ }
+
+ public DatabaseSessionFactory getSessionFactory() {
+ return new DatabaseSessionFactory() {
+
+ public DatabaseSession getSession() {
+ return session;
+ }
+
+ public void clear() {
+ }
+ };
+ }
+
+ protected final void setupData(String... testNames) {
+ InputStream[] streams = new InputStream[testNames.length];
+ try {
+ for (int i = 0; i < testNames.length; i++) {
+ String className = getClass().getName();
+ className = String.format("/%s/%s.xml", className.replace(".", "/"), testNames[i]);
+ streams[i] = getClass().getResourceAsStream(className);
+ if (streams[i] == null) {
+ throw new RuntimeException("Test not found :" + className);
+ }
+ }
+
+ setupData(streams);
+
+ } finally {
+ for (InputStream stream : streams) {
+ IOUtils.closeQuietly(stream);
+ }
+ }
+ }
+
+ protected final void setupData(InputStream... dataSetStream) {
+ try {
+ IDataSet[] dataSets = new IDataSet[dataSetStream.length];
+ for (int i = 0; i < dataSetStream.length; i++) {
+ ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(dataSetStream[i]));
+ dataSet.addReplacementObject("[null]", null);
+ dataSets[i] = dataSet;
+ }
+ CompositeDataSet compositeDataSet = new CompositeDataSet(dataSets);
+
+ databaseTester.setDataSet(compositeDataSet);
+ connection = databaseTester.getConnection();
+ connection.getConnection().prepareStatement("set referential_integrity FALSE").execute(); // HSQL DB
+ DatabaseConfig config = connection.getConfig();
+ config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
+
+ DatabaseOperation.CLEAN_INSERT.execute(connection, databaseTester.getDataSet());
+
+ connection.getConnection().prepareStatement("set referential_integrity TRUE").execute(); // HSQL DB
+ } catch (Exception e) {
+ throw translateException("Could not setup DBUnit data", e);
+ }
+ }
+
+ protected final void checkTables(String testName, String... tables) {
+ getSession().commit();
+ try {
+ IDataSet dataSet = getCurrentDataSet();
+ IDataSet expectedDataSet = getExpectedData(testName);
+ for (String table : tables) {
+ Assertion.assertEquals(expectedDataSet.getTable(table), dataSet.getTable(table));
+ }
+ } catch (DataSetException e) {
+ throw translateException("Error while checking results", e);
+ } catch (DatabaseUnitException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ protected final void assertEmptyTables(String... emptyTables) {
+ for (String table : emptyTables) {
+ try {
+ Assert.assertEquals(0, getCurrentDataSet().getTable(table).getRowCount());
+ } catch (DataSetException e) {
+ throw translateException("Error while checking results", e);
+ }
+ }
+ }
+
+ protected final IDataSet getExpectedData(String testName) {
+ String className = getClass().getName();
+ className = String.format("/%s/%s-result.xml", className.replace(".", "/"), testName);
+
+ InputStream in = getClass().getResourceAsStream(className);
+ try {
+ return getData(in);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ protected final IDataSet getData(InputStream stream) {
+ try {
+ ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(stream));
+ dataSet.addReplacementObject("[null]", null);
+ return dataSet;
+ } catch (Exception e) {
+ throw translateException("Could not read the dataset stream", e);
+ }
+ }
+
+ protected final IDataSet getCurrentDataSet() {
+ try {
+ return connection.createDataSet();
+ } catch (SQLException e) {
+ throw translateException("Could not create the current dataset", e);
+ }
+ }
+
+ protected String getCurrentDataSetAsXML() {
+ return getDataSetAsXML(getCurrentDataSet());
+ }
+
+ protected String getDataSetAsXML(IDataSet dataset) {
+ try {
+ StringWriter writer = new StringWriter();
+ FlatXmlDataSet.write(dataset, writer);
+ return writer.getBuffer().toString();
+ } catch (Exception e) {
+ throw translateException("Could not build XML from dataset", e);
+ }
+ }
+
+ private static RuntimeException translateException(String msg, Exception cause) {
+ RuntimeException runtimeException = new RuntimeException(String.format("%s: [%s] %s", msg, cause.getClass().getName(), cause.getMessage()));
+ runtimeException.setStackTrace(cause.getStackTrace());
+ return runtimeException;
+ }
+
+ /*public static class HsqlDataTypeFactory extends DefaultDataTypeFactory {
+
+ public DataType createDataType(int sqlType, String sqlTypeName) throws DataTypeException {
+ if (sqlType == Types.BOOLEAN) {
+ return DataType.BOOLEAN;
+ }
+ return super.createDataType(sqlType, sqlTypeName);
+ }
+ }*/
+
+ protected Long getHQLCount(final Class<?> hqlClass) {
+ String hqlCount = "SELECT count(o) from " + hqlClass.getSimpleName() + " o";
+ return (Long) getSession().createQuery(hqlCount).getSingleResult();
+ }
+
+ protected IDatabaseConnection getConnection() {
+ return connection;
+ }
+
+ protected IDatabaseTester getDatabaseTester() {
+ return databaseTester;
+ }
+
+}
diff --git a/sonar-core/src/test/resources/logback-test.xml b/sonar-core/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..d42db6a18b6
--- /dev/null
+++ b/sonar-core/src/test/resources/logback-test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<configuration>
+
+ <appender name="STDOUT"
+ class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <pattern>
+ %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
+ </pattern>
+ </layout>
+ </appender>
+
+ <!-- logger name="org.sonar.DBSTATISTICS">
+ <level value="INFO"/>
+ </logger>
+-->
+ <!--<logger name="org.hibernate.SQL">-->
+ <!--<level value="INFO"/>-->
+ <!--</logger>-->
+
+ <root>
+ <level value="WARN"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/api/database/configuration/DatabaseConfigurationTest/some-properties.xml b/sonar-core/src/test/resources/org/sonar/api/database/configuration/DatabaseConfigurationTest/some-properties.xml
new file mode 100644
index 00000000000..64c38fb55ea
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/api/database/configuration/DatabaseConfigurationTest/some-properties.xml
@@ -0,0 +1,6 @@
+<dataset>
+
+ <properties prop_key="key1" resource_id="[null]" text_value="value1" user_id="[null]"/>
+ <properties prop_key="key2" resource_id="[null]" text_value="value2" user_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldNotLoadGlobalProperties.xml b/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldNotLoadGlobalProperties.xml
new file mode 100644
index 00000000000..5e53464e6e4
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldNotLoadGlobalProperties.xml
@@ -0,0 +1,16 @@
+<dataset>
+
+ <projects long_name="[null]" id="100" scope="PRJ" qualifier="TRK" kee="myproject" name="[null]" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- global properties -->
+ <properties prop_key="key1" resource_id="[null]" text_value="value1" user_id="[null]"/>
+ <properties prop_key="key2" resource_id="[null]" text_value="value2" user_id="[null]"/>
+
+ <!-- specific properties -->
+ <properties prop_key="key1" resource_id="100" text_value="project_value1" user_id="[null]"/>
+ <properties prop_key="key3" resource_id="100" text_value="project_value3" user_id="[null]"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldOverrideGlobalPropertiesBySpecificResourceProperties.xml b/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldOverrideGlobalPropertiesBySpecificResourceProperties.xml
new file mode 100644
index 00000000000..3f29bfb26bb
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/api/database/configuration/ResourceDatabaseConfigurationTest/shouldOverrideGlobalPropertiesBySpecificResourceProperties.xml
@@ -0,0 +1,16 @@
+<dataset>
+
+ <projects long_name="[null]" id="100" scope="PRJ" qualifier="TRK" kee="myproject" name="[null]" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- global properties -->
+ <properties prop_key="key1" resource_id="[null]" text_value="value1" user_id="[null]"/>
+ <properties prop_key="key2" resource_id="[null]" text_value="value2" user_id="[null]"/>
+
+ <!-- specific properties -->
+ <properties prop_key="key1" resource_id="100" text_value="overriden value1" user_id="[null]"/>
+ <properties prop_key="key3" resource_id="100" text_value="value3" user_id="[null]"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
new file mode 100644
index 00000000000..e7f3523119d
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/removePreviousFilesWhenRegisteringPlugin-result.xml
@@ -0,0 +1,6 @@
+<dataset>
+ <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2" />
+
+ <plugin_files id="3" plugin_id="1" filename="newfile.jar"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
new file mode 100644
index 00000000000..aa6e51ec2cd
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/saveDeprecatedPlugin-result.xml
@@ -0,0 +1,12 @@
+<dataset>
+ <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2"/>
+
+ <plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" version="[null]" />
+
+ <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
+ <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
+
+ <plugin_files id="3" plugin_id="2" filename="sonar-pmd-plugin-2.2.jar"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
new file mode 100644
index 00000000000..6c215921357
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/savePluginAndFiles-result.xml
@@ -0,0 +1,15 @@
+<dataset>
+ <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2"/>
+
+ <plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" version="2.2" />
+
+ <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
+ <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
+
+ <plugin_files id="3" plugin_id="2" filename="sonar-pmd-plugin-2.2.jar"/>
+ <plugin_files id="4" plugin_id="2" filename="pmd-extension.jar"/>
+ <plugin_files id="5" plugin_id="2" filename="pmd-extension2.jar"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
new file mode 100644
index 00000000000..00861080067
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/plugin/JpaPluginDaoTest/shared.xml
@@ -0,0 +1,7 @@
+<dataset>
+ <plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]"
+ description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2" />
+
+ <plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/>
+ <plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots-result.xml
new file mode 100644
index 00000000000..3f4962d5509
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots-result.xml
@@ -0,0 +1,111 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]" />
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- files -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class2"
+ name="class" root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!--<snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <!--<snapshots depth="[null]" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="4"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="[null]"/>-->
+
+
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="30" DATA="some sources"/>
+ <!--<SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="some sources"/>-->
+
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <!--<RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>-->
+ <!--<RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>-->
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <!--<measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>-->
+ <!--<measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>-->
+
+ <dependencies id="1" from_resource_id="1" from_snapshot_id="1" to_resource_id="30" to_snapshot_id="30"
+ parent_dependency_id="[null]" project_snapshot_id="1"
+ dep_usage="USES" dep_weight="1" from_scope="PRJ" to_scope="LIB"/>
+
+ <!--<dependencies id="2" from_resource_id="3" from_snapshot_id="3" to_resource_id="40" to_snapshot_id="40"-->
+ <!--parent_dependency_id="[null]" project_snapshot_id="1"-->
+ <!--dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL"/>-->
+
+ <!--<dependencies id="3" from_resource_id="50" from_snapshot_id="50" to_resource_id="3" to_snapshot_id="3"-->
+ <!--parent_dependency_id="[null]" project_snapshot_id="1"-->
+ <!--dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL"/>-->
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots.xml b/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots.xml
new file mode 100644
index 00000000000..e6bc3164e30
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/AbstractPurgeTest/purgeSnapshots.xml
@@ -0,0 +1,111 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- files -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class2"
+ name="class" root_id="1"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="30" DATA="some sources"/>
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="some sources"/>
+
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+ <dependencies id="1" from_resource_id="1" from_snapshot_id="1" to_resource_id="30" to_snapshot_id="30"
+ parent_dependency_id="[null]" project_snapshot_id="1"
+ dep_usage="USES" dep_weight="1" from_scope="PRJ" to_scope="LIB"/>
+
+ <dependencies id="2" from_resource_id="3" from_snapshot_id="3" to_resource_id="40" to_snapshot_id="40"
+ parent_dependency_id="[null]" project_snapshot_id="1"
+ dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL"/>
+
+ <dependencies id="3" from_resource_id="50" from_snapshot_id="50" to_resource_id="3" to_snapshot_id="3"
+ parent_dependency_id="[null]" project_snapshot_id="1"
+ dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL" />
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/noDefinitionsToRegister-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/noDefinitionsToRegister-result.xml
new file mode 100644
index 00000000000..7635d06bd99
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/noDefinitionsToRegister-result.xml
@@ -0,0 +1,10 @@
+<dataset>
+ <quality_models id="1" name="M1" />
+ <quality_models id="2" name="M2" />
+
+ <characteristics id="1" kee="M1C1" name="M1C1" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />
+ <characteristics id="2" kee="M1C2" name="M1C2" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="2" />
+ <characteristics id="3" kee="M2C1" name="M2C1" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1"/>
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/registerOnlyNewDefinitions-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/registerOnlyNewDefinitions-result.xml
new file mode 100644
index 00000000000..0477a531971
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/registerOnlyNewDefinitions-result.xml
@@ -0,0 +1,13 @@
+<dataset>
+ <quality_models id="1" name="M1" />
+ <quality_models id="2" name="M2" />
+
+ <!-- NEW MODEL -->
+ <quality_models id="3" name="NEWMODEL" />
+
+ <characteristics id="1" kee="M1C1" name="M1C1" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />
+ <characteristics id="2" kee="M1C2" name="M1C2" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="2" />
+ <characteristics id="3" kee="M2C1" name="M2C1" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1"/>
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/reset-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/reset-result.xml
new file mode 100644
index 00000000000..91c5773b8ff
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/reset-result.xml
@@ -0,0 +1,15 @@
+<dataset>
+ <!--<quality_models id="1" name="M1" />-->
+ <quality_models id="2" name="M2" />
+ <quality_models id="3" name="M1" />
+
+ <!--<characteristics id="1" kee="M1C1" name="M1C1" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />-->
+ <!--<characteristics id="2" kee="M1C2" name="M1C2" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="2" />-->
+ <characteristics id="3" kee="M2C1" name="M2C1" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1" />
+ <characteristics id="4" kee="NEWM1C1A" name="NEWM1C1A" quality_model_id="3" rule_id="[null]" characteristic_order="1" depth="2"/>
+ <characteristics id="5" kee="NEWM1C1" name="NEWM1C1" quality_model_id="3" rule_id="[null]" characteristic_order="1" depth="1"/>
+ <characteristics id="6" kee="NEWM1C2" name="NEWM1C2" quality_model_id="3" rule_id="[null]" characteristic_order="2" depth="1"/>
+
+ <!--<characteristic_edges child_id="2" parent_id="1"/>-->
+ <characteristic_edges child_id="4" parent_id="5"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/shared.xml
new file mode 100644
index 00000000000..7635d06bd99
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/DefaultModelProviderTest/shared.xml
@@ -0,0 +1,10 @@
+<dataset>
+ <quality_models id="1" name="M1" />
+ <quality_models id="2" name="M2" />
+
+ <characteristics id="1" kee="M1C1" name="M1C1" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />
+ <characteristics id="2" kee="M1C2" name="M1C2" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="2" />
+ <characteristics id="3" kee="M2C1" name="M2C1" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1"/>
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/saveModelAndCharacteristics.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/saveModelAndCharacteristics.xml
new file mode 100644
index 00000000000..693d4448b8d
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/saveModelAndCharacteristics.xml
@@ -0,0 +1,4 @@
+<dataset>
+ <quality_models id="1" name="initial" />
+ <characteristics id="1" kee="FAKE" name="fake" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testGraphOfCharacteristics.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testGraphOfCharacteristics.xml
new file mode 100644
index 00000000000..70a69a6a1ba
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testGraphOfCharacteristics.xml
@@ -0,0 +1,8 @@
+<dataset>
+ <quality_models id="1" name="initial" />
+
+ <characteristics id="1" kee="FAKE1" name="fake1" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1"/>
+ <characteristics id="2" kee="FAKE2" name="fake2" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="1" />
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testTreeOfCharacteristics.xml b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testTreeOfCharacteristics.xml
new file mode 100644
index 00000000000..6dcd2a78d63
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/qualitymodel/ModelTest/testTreeOfCharacteristics.xml
@@ -0,0 +1,5 @@
+<dataset>
+ <quality_models id="1" name="initial" />
+ <characteristics id="1" kee="FAKE" name="fake" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" />
+ <characteristic_edges child_id="1" parent_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/core/rule/DefaultRuleProviderTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/rule/DefaultRuleProviderTest/shared.xml
new file mode 100644
index 00000000000..f6adafee0da
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/rule/DefaultRuleProviderTest/shared.xml
@@ -0,0 +1,20 @@
+<dataset>
+ <rules_categories id="6" name="Efficiency" description="[null]" />
+
+ <!-- CHECKSTYLE -->
+
+ <rules id="1" name="Check Header" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
+ plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <!-- disabled rule -->
+ <rules id="2" name="Disabled checked" rules_category_id="6" plugin_rule_key="DisabledCheck"
+ plugin_config_key="Checker/Treewalker/DisabledCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="false" cardinality="SINGLE" parent_id="[null]" />
+
+ <rules id="3" name="Check Annotation" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck"
+ plugin_config_key="Checker/Treewalker/AnnotationUseStyleCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="true" cardinality="SINGLE" parent_id="[null]" />
+
+
+ <!-- PMD -->
+ <rules id="4" name="Call Super First" rules_category_id="6" plugin_rule_key="CallSuperFirst"
+ plugin_config_key="rulesets/android.xml/CallSuperFirst" plugin_name="pmd" description="[null]" priority="2" enabled="true" cardinality="SINGLE" parent_id="[null]" />
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/sharedFixture.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/sharedFixture.xml
new file mode 100644
index 00000000000..4fbd9f7d094
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/sharedFixture.xml
@@ -0,0 +1,23 @@
+<dataset>
+
+ <metrics id="1" name="foo" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false"
+ enabled="false" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+ <metrics id="2" name="bar" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false"
+ enabled="false" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+ <metrics id="3" name="boo" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false"
+ enabled="false" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="[null]"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="mygroup2:myartifact" name="[null]"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure-result.xml
new file mode 100644
index 00000000000..4af92259aaa
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure-result.xml
@@ -0,0 +1,22 @@
+<dataset>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]" rule_priority="[null]" alert_text="[null]" id="1" project_id="1" metric_id="1" value="1" measure_date="2008-12-03 08:00:00.00" rule_id="[null]"-->
+ <!--snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-06 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!--<async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>-->
+ <!--<async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>-->
+ <!--<async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>-->
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure.xml
new file mode 100644
index 00000000000..f48a0da88bc
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasure.xml
@@ -0,0 +1,26 @@
+<dataset>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ alert_status="[null]" description="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-06 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots-result.xml
new file mode 100644
index 00000000000..ae9389c25b2
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots-result.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <!--<async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="[null]" metric_id="1" project_id="1"/>-->
+ <!--<async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="[null]" metric_id="1" project_id="1"/>-->
+ <!--<async_measure_snapshots id="3" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="[null]" metric_id="1" project_id="1"/>-->
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots.xml
new file mode 100644
index 00000000000..55b7e7ecc0f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testDeleteAsyncMeasureSnapshots.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetAsyncMeasureSnapshotsFromSnapshotId.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetAsyncMeasureSnapshotsFromSnapshotId.xml
new file mode 100644
index 00000000000..75fdf0a8bcb
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetAsyncMeasureSnapshotsFromSnapshotId.xml
@@ -0,0 +1,26 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 18:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="2" value="2"
+ measure_date="2008-12-04 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 18:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="1" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetLastAsyncMeasureSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetLastAsyncMeasureSnapshot.xml
new file mode 100644
index 00000000000..7adc403b439
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetLastAsyncMeasureSnapshot.xml
@@ -0,0 +1,12 @@
+<dataset>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshot.xml
new file mode 100644
index 00000000000..705cebd5ef7
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshot.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshotsUntilDate.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshotsUntilDate.xml
new file mode 100644
index 00000000000..a7f814154d8
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextAsyncMeasureSnapshotsUntilDate.xml
@@ -0,0 +1,50 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-06 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-06 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+ <!-- measure inserted on snapshot 2 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextSnapshotsUntilDate.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextSnapshotsUntilDate.xml
new file mode 100644
index 00000000000..bbd2c20759f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetNextSnapshotsUntilDate.xml
@@ -0,0 +1,35 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="5" scope="FIL" qualifier="CLA" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="4" root_project_id="[null]" root_snapshot_id="4" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="6" scope="PRJ" qualifier="TRK" created_at="2008-12-06 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"
+ description="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousAsyncMeasureSnapshots.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousAsyncMeasureSnapshots.xml
new file mode 100644
index 00000000000..b5a5b9dc96d
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousAsyncMeasureSnapshots.xml
@@ -0,0 +1,18 @@
+<dataset>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="2" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="2" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="4" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <async_measure_snapshots id="5" project_measure_id="5" snapshot_id="[null]" measure_date="2008-12-07 08:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+ <async_measure_snapshots id="6" project_measure_id="6" snapshot_id="[null]" measure_date="2008-12-08 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="7" project_measure_id="7" snapshot_id="[null]" measure_date="2008-12-09 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousSnapshot.xml
new file mode 100644
index 00000000000..21fa947d845
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresDaoTest/testGetPreviousSnapshot.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot-result.xml
new file mode 100644
index 00000000000..2f9fd27ff54
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot-result.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure, after last snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- No snapshot attached -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-04 12:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot.xml
new file mode 100644
index 00000000000..a89dcda8aa6
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addFutureSnapshot.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure, after last snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure-result.xml
new file mode 100644
index 00000000000..758305c84ed
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure-result.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure, just before first measure -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="50"
+ measure_date="2008-12-02 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- No snapshot attached -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-02 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure.xml
new file mode 100644
index 00000000000..9cb2a52dc62
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/addInvisibleMeasure.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure, just before first measure -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="50"
+ measure_date="2008-12-02 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric-result.xml
new file mode 100644
index 00000000000..a56e5efb96b
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric-result.xml
@@ -0,0 +1,37 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to snapshot 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-05 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- Past measure inserted, have to be assigned only to snapshot 1 and 2 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="2" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric.xml
new file mode 100644
index 00000000000..6b1e191cddd
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignAPastMeasureToNextSnapshotsWithDifferentMetric.xml
@@ -0,0 +1,33 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to snapshot 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-05 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- Past measure inserted, have to be assigned only to snapshot 1 and 2 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot-result.xml
new file mode 100644
index 00000000000..6e32c3a4b49
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot-result.xml
@@ -0,0 +1,42 @@
+<dataset>
+
+ <!-- Previous snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- New snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-02 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-03 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- Previous async_measure_snapshots, attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="2" snapshot_id="1" measure_date="2008-12-02 10:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="3" snapshot_id="1" measure_date="2008-12-03 10:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="2" project_id="1"/>
+
+ <!--async_measure_snapshots from previous snapshot created for last snapshot -->
+ <async_measure_snapshots id="3" project_measure_id="2" snapshot_id="2" measure_date="2008-12-02 10:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="3" snapshot_id="2" measure_date="2008-12-03 10:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot.xml
new file mode 100644
index 00000000000..b9d192b0f8c
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresToLastSnapshot.xml
@@ -0,0 +1,36 @@
+<dataset>
+
+ <!-- Previous snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- New snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-02 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-03 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- Previous async_measure_snapshots, attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="2" snapshot_id="1" measure_date="2008-12-02 10:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="3" snapshot_id="1" measure_date="2008-12-03 10:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot-result.xml
new file mode 100644
index 00000000000..311388ad4d9
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot-result.xml
@@ -0,0 +1,45 @@
+<dataset>
+
+ <!-- new snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!--latest-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-03 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="6"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!--latest-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="4" project_id="1" metric_id="2" value="7"
+ measure_date="2008-12-03 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <!-- attached -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="1" measure_date="2008-12-03 12:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="[null]" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+ <!-- attached -->
+ <async_measure_snapshots id="4" project_measure_id="4" snapshot_id="1" measure_date="2008-12-03 12:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot.xml
new file mode 100644
index 00000000000..6d721271a79
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignLatestMeasuresWhenNoPreviousSnapshot.xml
@@ -0,0 +1,43 @@
+<dataset>
+
+ <!-- new snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!--latest-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-03 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="6"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!--latest-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="4" project_id="1" metric_id="2" value="7"
+ measure_date="2008-12-03 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 12:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="[null]" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="4" snapshot_id="[null]" measure_date="2008-12-03 12:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric-result.xml
new file mode 100644
index 00000000000..c6c5e2df71f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric-result.xml
@@ -0,0 +1,59 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-06 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to snapshot 1, 2 and 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- Assigned to snapshot 4 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-06 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- To be assigned only to snapshot 2 and 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <!-- Now assigned to no snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <!-- Now assigned to no snapshot -->
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- Now assigned to review 3 -->
+ <async_measure_snapshots id="5" project_measure_id="3" snapshot_id="2" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <!-- Now assigned to review 3 -->
+ <async_measure_snapshots id="6" project_measure_id="3" snapshot_id="3" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric.xml
new file mode 100644
index 00000000000..a883add73f4
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasureToFutureSnapshotsWithDifferentMetric.xml
@@ -0,0 +1,49 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-06 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to snapshot 1, 2 and 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- Assigned to snapshot 4 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-06 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- To be assigned only to snapshot 2 and 3 -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="4" measure_date="2008-12-06 08:00:00.00"
+ snapshot_date="2008-12-06 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot-result.xml
new file mode 100644
index 00000000000..70469bf5308
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot-result.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-03 14:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-03 15:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 14:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="1" measure_date="2008-12-03 15:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot.xml
new file mode 100644
index 00000000000..a5aeb9f7731
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignMeasuresWhenNoPreviousSnapshot.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-03 14:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-03 15:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 14:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 15:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots-result.xml
new file mode 100644
index 00000000000..98a68c44271
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots-result.xml
@@ -0,0 +1,44 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="10"
+ measure_date="2008-12-03 8:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 8:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- Now assigned to no snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="[null]" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+
+ <!-- Now assigned to snapshot 2 and 3 -->
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="2" measure_date="2008-12-04 8:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="5" project_measure_id="2" snapshot_id="3" measure_date="2008-12-04 8:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots.xml
new file mode 100644
index 00000000000..1fc5a2d0295
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasureToFutureSnapshots.xml
@@ -0,0 +1,36 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="10"
+ measure_date="2008-12-03 8:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- New measure -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 8:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="2" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="3" measure_date="2008-12-03 8:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot-result.xml
new file mode 100644
index 00000000000..87a13cbde66
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot-result.xml
@@ -0,0 +1,43 @@
+<dataset>
+
+ <!-- Previous snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- New snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- Assigmed to no snapshot, because its new reviews -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-03 18:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-04 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- async_measure_snapshots attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- async_measure_snapshots now attached to last snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="2" measure_date="2008-12-03 18:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="2" measure_date="2008-12-04 10:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot.xml
new file mode 100644
index 00000000000..4bbfcb29c27
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignNewMeasuresToLastSnapshot.xml
@@ -0,0 +1,42 @@
+<dataset>
+
+ <!-- Previous snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- New snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"/>
+
+ <!-- Assigned to first sanspshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-02 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- Assigmed to no snapshot, because its new reviews -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-03 18:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="3" project_id="1" metric_id="2" value="5"
+ measure_date="2008-12-04 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- async_measure_snapshots attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-02 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+ <!-- async_measure_snapshots attached to no snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="[null]" measure_date="2008-12-03 18:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="3" snapshot_id="[null]" measure_date="2008-12-04 10:00:00.00"
+ snapshot_date="[null]" metric_id="2" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot-result.xml
new file mode 100644
index 00000000000..49a10949faa
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot-result.xml
@@ -0,0 +1,47 @@
+<dataset>
+
+ <!-- First snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- Last snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- New snapshot, inserted between last two -->
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- reviews attached to first snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-01 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- reviews attached to last snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <!-- async_measure_snapshots attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-01 12:00:00.00"
+ snapshot_date="2008-12-02 12:00:00.00" metric_id="1" project_id="1"/>
+ <!-- async_measure_snapshots attached to last snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="2" measure_date="2008-12-04 12:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+
+ <!-- async_measure_snapshots created for the new snapshot -->
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="3" measure_date="2008-12-01 12:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot.xml
new file mode 100644
index 00000000000..e55b0965ae8
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/assignPastMeasuresToPastSnapshot.xml
@@ -0,0 +1,42 @@
+<dataset>
+
+ <!-- First snapshot -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <!-- Last snapshot -->
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- New snapshot, inserted between last two -->
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- reviews attached to first snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-01 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- reviews attached to last snapshot -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="5"
+ measure_date="2008-12-04 12:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <!-- async_measure_snapshots attached to first snapshot -->
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="1" measure_date="2008-12-01 12:00:00.00"
+ snapshot_date="2008-12-02 12:00:00.00" metric_id="1" project_id="1"/>
+ <!-- async_measure_snapshots attached to last snapshot -->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="2" measure_date="2008-12-04 12:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure-result.xml
new file mode 100644
index 00000000000..9f297f684fb
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure-result.xml
@@ -0,0 +1,30 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]" rule_priority="[null]" alert_text="[null]" id="1" project_id="1" metric_id="1" value="12" measure_date="2008-12-03 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>-->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+
+ <!--<async_measure_snapshots id="1" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 10:00:00.00"-->
+ <!--snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>-->
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="3" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure.xml
new file mode 100644
index 00000000000..9c2e690dbde
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteLastMeasure.xml
@@ -0,0 +1,32 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="12"
+ measure_date="2008-12-03 10:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="8"
+ measure_date="2008-12-04 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 10:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="2" project_measure_id="2" snapshot_id="3" measure_date="2008-12-04 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure-result.xml
new file mode 100644
index 00000000000..bbb77395adc
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure-result.xml
@@ -0,0 +1,41 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- async measure to be deleted -->
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]" rule_priority="[null]" alert_text="[null]" id="2" project_id="1" metric_id="1" value="2" measure_date="2008-12-05 08:00:00.00" rule_id="[null]"-->
+ <!--snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>-->
+
+ <!-- old async measure snapshots -->
+ <!--<async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="[null]" metric_id="1" project_id="1" />-->
+
+ <!--<async_measure_snapshots id="2" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1" />-->
+ <!--<async_measure_snapshots id="3" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"-->
+ <!--snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1" />-->
+ <!--<async_measure_snapshots id="4" project_measure_id="2" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"-->
+ <!--snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1" />-->
+
+ <async_measure_snapshots id="5" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="6" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="7" project_measure_id="1" snapshot_id="3" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure.xml
new file mode 100644
index 00000000000..1a86562878e
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/deleteMeasure.xml
@@ -0,0 +1,39 @@
+<dataset>
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-03 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="2" scope="PRJ" qualifier="TRK" created_at="2008-12-04 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-05 12:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="1" project_id="1" metric_id="1" value="1"
+ measure_date="2008-12-03 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+ <!-- async measure to be deleted -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" id="2" project_id="1" metric_id="1" value="2"
+ measure_date="2008-12-05 08:00:00.00" rule_id="[null]"
+ snapshot_id="[null]" rules_category_id="[null]" text_value="[null]" tendency="[null]"/>
+
+ <!-- old async measure snapshots -->
+ <async_measure_snapshots id="1" project_measure_id="1" snapshot_id="[null]" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="[null]" metric_id="1" project_id="1"/>
+
+ <async_measure_snapshots id="2" project_measure_id="1" snapshot_id="1" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-03 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="3" project_measure_id="1" snapshot_id="2" measure_date="2008-12-03 08:00:00.00"
+ snapshot_date="2008-12-04 12:00:00.00" metric_id="1" project_id="1"/>
+ <async_measure_snapshots id="4" project_measure_id="2" snapshot_id="3" measure_date="2008-12-05 08:00:00.00"
+ snapshot_date="2008-12-05 12:00:00.00" metric_id="1" project_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/sharedFixture.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/sharedFixture.xml
new file mode 100644
index 00000000000..d1a6b09be2f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/AsyncMeasuresServiceTest/sharedFixture.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <metrics id="1" name="foo" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="false" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+ <metrics id="2" name="bar" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="false" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="[null]"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/ExtensionDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/ExtensionDaoTest/shared.xml
new file mode 100644
index 00000000000..a369c467f68
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/ExtensionDaoTest/shared.xml
@@ -0,0 +1,18 @@
+<dataset>
+
+ <!-- plugins -->
+ <extensions id="1" plugin_key="checkstyle" extension_type="PLUGIN" version="1.0"
+ name="Checkstyle" filename="sonar-checkstyle-plugin.jar" plugin_class="[null]"
+ description="Checkstyle Plugin" organization="SonarSource" organization_url="[null]" license="LGPL" installation_date="[null]"
+ core="true" />
+
+ <extensions id="2" plugin_key="pmd" extension_type="PLUGIN" version="1.1"
+ name="PMD" filename="sonar-pmd-plugin.jar" installation_date="[null]" plugin_class="[null]"
+ description="PMD Plugin" organization="SonarSource" organization_url="[null]" license="LGPL"
+ core="true" />
+
+ <!-- plugin extensions -->
+ <extensions id="3" plugin_key="checkstyle" extension_type="PLUGIN_EXTENSION" version="[null]"
+ name="my_checkstyle_rules.jar" filename="my_checkstyle_rules.jar" installation_date="[null]" plugin_class="[null]"
+ description="[null]" organization="[null]" organization_url="[null]" license="[null]" core="[null]" />
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/ProfilesDaoTest/shouldGetProfiles.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/ProfilesDaoTest/shouldGetProfiles.xml
new file mode 100644
index 00000000000..f1e87d6dcb5
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/ProfilesDaoTest/shouldGetProfiles.xml
@@ -0,0 +1,7 @@
+<dataset>
+
+ <rules_profiles id="1" provided="true" name="profile one" default_profile="0" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile two" default_profile="0" language="java"/>
+ <rules_profiles id="3" provided="true" name="profile three" default_profile="0" language="plsql"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile-result.xml
new file mode 100644
index 00000000000..c7059375406
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile-result.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <rules_profiles id="1" provided="true" name="profile" default_profile="1" language="java"/>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="rule1" rules_category_id="1" description="desc" plugin_config_key="config1"
+ plugin_rule_key="key1" plugin_name="plugin" priority="1" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="param1" description="foo" param_type="r"/>
+
+ <!-- Active rule created -->
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+
+ <!-- Active rule param created -->
+ <active_rule_parameters id="1" active_rule_id="1" rules_parameter_id="1" value="20"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile.xml
new file mode 100644
index 00000000000..f30fb8d64d9
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldAddActiveRulesToProfile.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <rules_profiles id="1" provided="true" name="profile" default_profile="1" language="java"/>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="rule1" rules_category_id="1" description="desc" plugin_config_key="config1"
+ plugin_rule_key="key1" plugin_name="plugin" priority="1" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="param1" description="foo" param_type="r"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldCountNumberOfRulesOfACategoryForGivenPlugins.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldCountNumberOfRulesOfACategoryForGivenPlugins.xml
new file mode 100644
index 00000000000..edb6c8d9271
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldCountNumberOfRulesOfACategoryForGivenPlugins.xml
@@ -0,0 +1,17 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules_categories id="2" name="category two" description="[null]"/>
+
+ <rules id="1" name="rule one" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="rule_one" plugin_name="plugin1" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules id="2" name="rule two" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="rule_two" plugin_name="plugin2" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="3" name="rule three" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="rule_three" plugin_name="plugin2" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules id="4" name="rule 4" rules_category_id="2" description="desc" plugin_config_key="config"
+ plugin_rule_key="rule_4" plugin_name="plugin2" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter-result.xml
new file mode 100644
index 00000000000..71c8345bb80
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter-result.xml
@@ -0,0 +1,22 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="rule1" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="key1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="param1" description="[null]" param_type="r"/>
+ <rules_parameters id="2" rule_id="1" name="param2" description="[null]" param_type="r"/>
+
+ <rules_profiles id="1" provided="true" name="profile1" default_profile="1" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile2" default_profile="1" language="OTHER"/>
+
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+ <active_rules id="2" profile_id="2" rule_id="1" failure_level="2"/>
+
+ <!-- deleted -->
+ <!--<active_rule_parameters id="1" active_rule_id="1" rules_parameter_id="1" value="20"/>-->
+ <!--<active_rule_parameters id="2" active_rule_id="2" rules_parameter_id="1" value="15"/>-->
+ <active_rule_parameters id="3" active_rule_id="2" rules_parameter_id="2" value="15"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter.xml
new file mode 100644
index 00000000000..90456d43522
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRuleParametersFromARuleParameter.xml
@@ -0,0 +1,21 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="rule1" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="key1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="param1" description="foo" param_type="r"/>
+ <rules_parameters id="2" rule_id="1" name="param2" description="foo" param_type="r"/>
+
+ <rules_profiles id="1" provided="true" name="profile1" default_profile="1" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile2" default_profile="1" language="OTHER"/>
+
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+ <active_rules id="2" profile_id="2" rule_id="1" failure_level="2"/>
+
+ <active_rule_parameters id="1" active_rule_id="1" rules_parameter_id="1" value="20"/>
+ <active_rule_parameters id="2" active_rule_id="2" rules_parameter_id="1" value="15"/>
+ <active_rule_parameters id="3" active_rule_id="2" rules_parameter_id="2" value="15"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules-result.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules-result.xml
new file mode 100644
index 00000000000..58dc223fc1f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules-result.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules_profiles id="1" provided="true" name="profile one" default_profile="1" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile two" default_profile="0" language="java"/>
+
+ <rules id="1" name="foo" rules_category_id="1" description="test" plugin_config_key="checker/foo"
+ plugin_rule_key="checkstyle.rule1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="bar" rules_category_id="1" description="test" plugin_config_key="checker/bar"
+ plugin_rule_key="checkstyle.rule2" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="3" name="baz" rules_category_id="1" description="test" plugin_config_key="checker/baz"
+ plugin_rule_key="checkstyle.rule3" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+ <active_rules id="2" profile_id="1" rule_id="2" failure_level="2"/>
+ <active_rules id="3" profile_id="1" rule_id="3" failure_level="2"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules.xml
new file mode 100644
index 00000000000..ffd0336d51f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldDeleteActiveRules.xml
@@ -0,0 +1,23 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules_profiles id="1" provided="true" name="profile one" default_profile="1" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile two" default_profile="0" language="java"/>
+
+ <rules id="1" name="foo" rules_category_id="1" description="test" plugin_config_key="checker/foo"
+ plugin_rule_key="checkstyle.rule1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="bar" rules_category_id="1" description="test" plugin_config_key="checker/bar"
+ plugin_rule_key="checkstyle.rule2" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="3" name="baz" rules_category_id="1" description="test" plugin_config_key="checker/baz"
+ plugin_rule_key="checkstyle.rule3" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+ <active_rules id="2" profile_id="1" rule_id="2" failure_level="2"/>
+ <active_rules id="3" profile_id="1" rule_id="3" failure_level="2"/>
+
+ <active_rules id="4" profile_id="2" rule_id="1" failure_level="2"/>
+ <active_rules id="5" profile_id="2" rule_id="2" failure_level="2"/>
+ <active_rules id="6" profile_id="2" rule_id="3" failure_level="2"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetActiveRules.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetActiveRules.xml
new file mode 100644
index 00000000000..58dc223fc1f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetActiveRules.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules_profiles id="1" provided="true" name="profile one" default_profile="1" language="java"/>
+ <rules_profiles id="2" provided="true" name="profile two" default_profile="0" language="java"/>
+
+ <rules id="1" name="foo" rules_category_id="1" description="test" plugin_config_key="checker/foo"
+ plugin_rule_key="checkstyle.rule1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="bar" rules_category_id="1" description="test" plugin_config_key="checker/bar"
+ plugin_rule_key="checkstyle.rule2" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="3" name="baz" rules_category_id="1" description="test" plugin_config_key="checker/baz"
+ plugin_rule_key="checkstyle.rule3" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <active_rules id="1" profile_id="1" rule_id="1" failure_level="2"/>
+ <active_rules id="2" profile_id="1" rule_id="2" failure_level="2"/>
+ <active_rules id="3" profile_id="1" rule_id="3" failure_level="2"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleParams.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleParams.xml
new file mode 100644
index 00000000000..2eded82f21f
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleParams.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <!-- Rules -->
+ <rules id="1" name="new1" rules_category_id="1" description="test1" plugin_config_key="checker/new1"
+ plugin_rule_key="checkstyle.new1" plugin_name="PLUGIN_KEY" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="new2" rules_category_id="1" description="test2" plugin_config_key="checker/new2"
+ plugin_rule_key="checkstyle.new2" plugin_name="PLUGIN_KEY" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <!-- Rules parameters -->
+ <rules_parameters id="1" rule_id="1" name="rule1_param1" description="rule1_desc1" param_type="r"
+ />
+ <rules_parameters id="2" rule_id="1" name="rule1_param2" description="rule1_desc2" param_type="r"
+ />
+ <rules_parameters id="3" rule_id="2" name="rule2_param1" description="rule2_desc1" param_type="r"
+ />
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleWithRuleKeyAndPluginKey.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleWithRuleKeyAndPluginKey.xml
new file mode 100644
index 00000000000..00de02226cf
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRuleWithRuleKeyAndPluginKey.xml
@@ -0,0 +1,8 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="foo" rules_category_id="1" description="test" plugin_config_key="checker/foo"
+ plugin_rule_key="checkstyle.rule1" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRules.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRules.xml
new file mode 100644
index 00000000000..9190f5bcdab
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldGetRules.xml
@@ -0,0 +1,15 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="rule one" rules_category_id="1" description="desc" plugin_config_key="other config"
+ plugin_rule_key="rule_one" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="rule two" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="rule_two" plugin_name="other plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="rule1_param1" description="rule1_desc1" param_type="r"
+ />
+ <rules_parameters id="2" rule_id="2" name="rule2_param1" description="rule2_desc1" param_type="r"
+ />
+
+</dataset> \ No newline at end of file
diff --git a/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldSynchronizeRuleOfActiveRule.xml b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldSynchronizeRuleOfActiveRule.xml
new file mode 100644
index 00000000000..3de538103e6
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/jpa/dao/RulesDaoTest/shouldSynchronizeRuleOfActiveRule.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <rules_categories id="1" name="category one" description="[null]"/>
+
+ <rules id="1" name="other rule" rules_category_id="1" description="desc" plugin_config_key="other config"
+ plugin_rule_key="other key" plugin_name="plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+ <rules id="2" name="rule" rules_category_id="1" description="desc" plugin_config_key="config"
+ plugin_rule_key="key" plugin_name="other plugin" enabled="true" cardinality="SINGLE" parent_id="[null]"/>
+
+ <rules_parameters id="1" rule_id="1" name="rule1_param1" description="rule1_desc1" param_type="r" />
+ <rules_parameters id="2" rule_id="2" name="rule2_param1" description="rule2_desc1" param_type="r" />
+
+</dataset> \ No newline at end of file