aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DbTemplate.java194
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java90
-rw-r--r--sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java75
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml6
4 files changed, 365 insertions, 0 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DbTemplate.java b/sonar-core/src/main/java/org/sonar/core/persistence/DbTemplate.java
new file mode 100644
index 00000000000..ec40ffee5eb
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DbTemplate.java
@@ -0,0 +1,194 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.persistence;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.SonarException;
+
+import javax.sql.DataSource;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class DbTemplate implements ServerComponent {
+ private static final Logger LOG = LoggerFactory.getLogger(DbTemplate.class);
+
+ public DbTemplate copyTable(DataSource source, DataSource dest, String table, String query) {
+ LOG.info("Copy table " + table);
+
+ int colCount = getColumnCount(source, table);
+
+ truncate(dest, table);
+
+ Connection sourceConnection = null;
+ Statement sourceStatement = null;
+ ResultSet sourceResultSet = null;
+ Connection destConnection = null;
+ ResultSet destResultSet = null;
+ try {
+ sourceConnection = source.getConnection();
+ sourceStatement = sourceConnection.createStatement();
+ sourceResultSet = sourceStatement.executeQuery(query);
+
+ destConnection = dest.getConnection();
+ destConnection.setAutoCommit(false);
+
+ PreparedStatement destStatement = destConnection.prepareStatement("INSERT INTO " + table + " VALUES(" + StringUtils.repeat("?", ",", colCount) + ")");
+ while (sourceResultSet.next()) {
+ for (int col = 1; col <= colCount; col++) {
+ Object value = sourceResultSet.getObject(col);
+ destStatement.setObject(col, value);
+ }
+ destStatement.addBatch();
+ }
+
+ destStatement.executeBatch();
+ destConnection.commit();
+ destStatement.close();
+ } catch (SQLException e) {
+ throw new SonarException("Fail to copy table " + table, e);
+ } finally {
+ closeQuietly(destResultSet);
+ closeQuietly(destConnection);
+ closeQuietly(sourceResultSet);
+ closeQuietly(sourceStatement);
+ closeQuietly(sourceConnection);
+ }
+
+ return this;
+ }
+
+ public int getColumnCount(DataSource dataSource, String table) {
+ Connection connection = null;
+ ResultSet metaData = null;
+ try {
+ connection = dataSource.getConnection();
+ metaData = connection.getMetaData().getColumns(null, null, table, null);
+
+ int nbColumns = 0;
+ while (metaData.next()) {
+ nbColumns++;
+ }
+
+ return nbColumns;
+ } catch (SQLException e) {
+ throw new SonarException("Fail to get column count for table " + table, e);
+ } finally {
+ closeQuietly(metaData);
+ closeQuietly(connection);
+ }
+ }
+
+ public int getRowCount(BasicDataSource dataSource, String table) {
+ Connection connection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = dataSource.getConnection();
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery("SELECT count(*) from " + table);
+
+ return resultSet.next() ? resultSet.getInt(1) : 0;
+ } catch (SQLException e) {
+ throw new SonarException("Fail to get row count for table " + table, e);
+ } finally {
+ closeQuietly(resultSet);
+ closeQuietly(statement);
+ closeQuietly(connection);
+ }
+ }
+
+ public DbTemplate truncate(DataSource dataSource, String table) {
+ Connection connection = null;
+ Statement statement = null;
+ try {
+ connection = dataSource.getConnection();
+ statement = connection.createStatement();
+ statement.executeUpdate("TRUNCATE TABLE " + table);
+ } catch (SQLException e) {
+ throw new SonarException("Fail to truncate table " + table, e);
+ } finally {
+ closeQuietly(statement);
+ closeQuietly(connection);
+ }
+
+ return this;
+ }
+
+ public BasicDataSource dataSource(String driver, String user, String password, String url) {
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName(driver);
+ dataSource.setUsername(user);
+ dataSource.setPassword(password);
+ dataSource.setUrl(url);
+ return dataSource;
+ }
+
+ public DbTemplate createSchema(DataSource dataSource, String dialect) {
+ Connection connection = null;
+ try {
+ connection = dataSource.getConnection();
+ DdlUtils.createSchema(connection, dialect);
+ } catch (SQLException e) {
+ throw new SonarException("Fail to createSchema local database schema", e);
+ } finally {
+ closeQuietly(connection);
+ }
+
+ return this;
+ }
+
+ private void closeQuietly(Connection connection) {
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ // ignore
+ }
+ }
+ }
+
+ private void closeQuietly(Statement statement) {
+ if (statement != null) {
+ try {
+ statement.close();
+ } catch (SQLException e) {
+ // ignore
+ }
+ }
+ }
+
+ private void closeQuietly(ResultSet resultSet) {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (SQLException e) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java b/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java
new file mode 100644
index 00000000000..94d83bfaf5c
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.persistence;
+
+import com.google.common.io.Files;
+import org.apache.commons.dbcp.BasicDataSource;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.SonarException;
+
+import javax.sql.DataSource;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.SQLException;
+
+public class LocalDatabaseFactory implements ServerComponent {
+ private static final String DIALECT = "h2";
+ private static final String DRIVER = "org.h2.Driver";
+ private static final String URL = "jdbc:h2:";
+ private static final String USER = "sonar";
+ private static final String PASSWORD = "sonar";
+
+ private final Database database;
+
+ public LocalDatabaseFactory(Database database) {
+ this.database = database;
+ }
+
+ public byte[] createDatabaseForLocalMode() {
+ String name = System.getenv("java.io") + System.nanoTime(); // TODO
+
+ try {
+ BasicDataSource destination = create(DIALECT, DRIVER, USER, PASSWORD, URL + name);
+ copy(database.getDataSource(), destination);
+ close(destination);
+
+ return dbFileContent(name);
+ } catch (SQLException e) {
+ throw new SonarException("Unable to create database for local mode", e);
+ }
+ }
+
+ private void copy(DataSource source, DataSource dest) {
+ new DbTemplate().copyTable(source, dest, "PROPERTIES", "SELECT * FROM PROPERTIES WHERE (USER_ID IS NULL) AND (RESOURCE_ID IS NULL) AND NOT (PROP_KEY LIKE '%.secured')")
+ .copyTable(source, dest, "RULES_PROFILES", "SELECT * FROM RULES_PROFILES")
+ .copyTable(source, dest, "RULES", "SELECT * FROM RULES")
+ .copyTable(source, dest, "RULES_PARAMETERS", "SELECT * FROM RULES_PARAMETERS")
+ .copyTable(source, dest, "ACTIVE_RULES", "SELECT * FROM ACTIVE_RULES")
+ .copyTable(source, dest, "ACTIVE_RULE_PARAMETERS", "SELECT * FROM ACTIVE_RULE_PARAMETERS")
+ .copyTable(source, dest, "METRICS", "SELECT * FROM METRICS");
+ }
+
+ private BasicDataSource create(String dialect, String driver, String user, String password, String url) {
+ BasicDataSource dataSource = new DbTemplate().dataSource(driver, user, password, url);
+ new DbTemplate().createSchema(dataSource, dialect);
+ return dataSource;
+ }
+
+ private void close(BasicDataSource dest) throws SQLException {
+ dest.close();
+ }
+
+ private byte[] dbFileContent(String name) {
+ try {
+ File dbFile = new File(name + ".h2.db");
+ byte[] content = Files.toByteArray(dbFile);
+ dbFile.delete();
+ return content;
+ } catch (IOException e) {
+ throw new SonarException("Unable to read h2 database file", e);
+ }
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java
new file mode 100644
index 00000000000..37f48eb2110
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java
@@ -0,0 +1,75 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.persistence;
+
+import com.google.common.io.Files;
+import org.apache.commons.dbcp.BasicDataSource;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.SQLException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class LocalDatabaseFactoryTest extends AbstractDaoTestCase {
+ private LocalDatabaseFactory localDatabaseFactory;
+ private BasicDataSource dataSource;
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() {
+ localDatabaseFactory = new LocalDatabaseFactory(getDatabase());
+ }
+
+ @After
+ public void closeDatabase() throws SQLException {
+ if (dataSource != null) {
+ dataSource.close();
+ }
+ }
+
+ @Test
+ public void should_create_database() throws IOException {
+ setupData("should_create_database");
+
+ byte[] database = localDatabaseFactory.createDatabaseForLocalMode();
+ dataSource = createDatabase(database);
+
+ assertThat(rowCount("PROPERTIES")).isEqualTo(1);
+ assertThat(rowCount("PROJECTS")).isZero();
+ }
+
+ private BasicDataSource createDatabase(byte[] db) throws IOException {
+ File file = temporaryFolder.newFile("db.h2.db");
+ Files.write(db, file);
+ return new DbTemplate().dataSource("org.h2.Driver", "sonar", "sonar", "jdbc:h2:" + file.getAbsolutePath().replaceAll(".h2.db", ""));
+ }
+
+ private int rowCount(String table) {
+ return new DbTemplate().getRowCount(dataSource, table);
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml
new file mode 100644
index 00000000000..281958780db
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml
@@ -0,0 +1,6 @@
+<dataset>
+ <properties id="1" prop_key="resourceProperty" text_value="value1" resource_id="1" user_id="[null]"/>
+ <properties id="2" prop_key="globalProperty" text_value="value2" resource_id="[null]" user_id="[null]"/>
+ <properties id="3" prop_key="userProperty" text_value="value3" resource_id="[null]" user_id="1"/>
+ <properties id="4" prop_key="property.secured" text_value="value4" resource_id="[null]" user_id="[null]"/>
+</dataset> \ No newline at end of file