diff options
author | David Gageot <david@gageot.net> | 2012-10-23 20:36:54 +0200 |
---|---|---|
committer | David Gageot <david@gageot.net> | 2012-10-23 22:19:10 +0200 |
commit | 525b465edfbfe72f0e0959b75112e08d9420983e (patch) | |
tree | ffa0f1c673f9fc43838e695d33a1ba1f4d06abe3 /sonar-core | |
parent | 7d66c01396a407628fda7ab99f80ede0af8a9dab (diff) | |
download | sonarqube-525b465edfbfe72f0e0959b75112e08d9420983e.tar.gz sonarqube-525b465edfbfe72f0e0959b75112e08d9420983e.zip |
SONAR-3895 Better Local mode
Diffstat (limited to 'sonar-core')
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 |