import org.sonar.batch.ServerMetadata;
import org.sonar.batch.config.BatchSettings;
import org.sonar.batch.config.BatchSettingsEnhancer;
+import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.jpa.session.DatabaseSessionProvider;
import org.sonar.jpa.session.DefaultDatabaseConnector;
import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
// set as the current context classloader for hibernate, else it does not find the JDBC driver.
Thread.currentThread().setContextClassLoader(bootstrapClassLoader);
+ // mybatis
addCoreSingleton(BatchDatabase.class);
addCoreSingleton(MyBatis.class);
- addCoreSingleton(DefaultDatabaseConnector.class);
- addCoreSingleton(ThreadLocalDatabaseSessionFactory.class);
+ addCoreSingleton(DatabaseVersion.class);
+ addCoreSingleton(DatabaseBatchCompatibility.class);
for (Class daoClass : DaoUtils.getDaoClasses()) {
addCoreSingleton(daoClass);
}
+
+ // hibernate
+ addCoreSingleton(DefaultDatabaseConnector.class);
+ addCoreSingleton(ThreadLocalDatabaseSessionFactory.class);
addAdapter(new DatabaseSessionProvider());
+
for (Object component : boostrapperComponents) {
addCoreSingleton(component);
}
--- /dev/null
+/*
+ * 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.batch.bootstrap;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.platform.Server;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+/**
+ * Detects if database is not up-to-date with the version required by the batch.
+ */
+public class DatabaseBatchCompatibility implements BatchComponent {
+
+ private DatabaseVersion version;
+ private Server server;
+
+ public DatabaseBatchCompatibility(DatabaseVersion version, Server server) {
+ this.version = version;
+ this.server = server;
+ }
+
+ public void start() {
+ DatabaseVersion.Status status = version.getStatus();
+ if (status == DatabaseVersion.Status.REQUIRES_DOWNGRADE) {
+ throw new BadDatabaseVersion("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
+ }
+ if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) {
+ throw new BadDatabaseVersion("Database must be upgraded. Please browse " + server.getURL() + "/setup");
+ }
+ if (status != DatabaseVersion.Status.UP_TO_DATE) {
+ // Support other future values
+ throw new BadDatabaseVersion("Unknown database status: " + status);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.batch.bootstrap;
+
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.Server;
+import org.sonar.batch.ServerMetadata;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DatabaseBatchCompatibilityTest {
+
+ private Server server = new ServerMetadata(new Settings());
+
+ @Test(expected = BadDatabaseVersion.class)
+ public void shouldFailIfRequiresDowngrade() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
+ new DatabaseBatchCompatibility(version, server).start();
+ }
+
+ @Test(expected = BadDatabaseVersion.class)
+ public void shouldFailIfRequiresUpgrade() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
+ new DatabaseBatchCompatibility(version, server).start();
+ }
+
+ @Test
+ public void shouldDoNothingIfUpToDate() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE);
+ new DatabaseBatchCompatibility(version, server).start();
+ // no error
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+public final class BadDatabaseVersion extends RuntimeException {
+ public BadDatabaseVersion(String s) {
+ super(s);
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
import java.sql.Connection;
/**
- * Restore schema by executing DDL scripts. Only Derby database is supported. Other databases are created by Ruby on Rails migrations.
+ * Restore schema by executing DDL scripts. Only Derby database is supported.
+ * Other databases are created by Ruby on Rails migrations.
*
* @since 2.12
*/
--- /dev/null
+/*
+ * 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.annotations.VisibleForTesting;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @since 2.15
+ */
+public class DatabaseVersion implements BatchComponent, ServerComponent {
+
+ public static final int LAST_VERSION = 263;
+
+ public static enum Status {
+ UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE
+ }
+
+ private MyBatis mybatis;
+
+ public DatabaseVersion(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public Integer getVersion() {
+ SqlSession session = mybatis.openSession();
+ try {
+ List<Integer> versions = session.getMapper(SchemaMigrationMapper.class).selectVersions();
+ if (!versions.isEmpty()) {
+ Collections.sort(versions);
+ return versions.get(versions.size() - 1);
+ }
+ return null;
+ } catch (Exception e) {
+ // The table SCHEMA_MIGRATIONS does not exist.
+ // Ignore this exception -> it will be created by Ruby on Rails migrations.
+ return null;
+
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public Status getStatus() {
+ return getStatus(getVersion(), LAST_VERSION);
+ }
+
+ @VisibleForTesting
+ static Status getStatus(Integer currentVersion, int lastVersion) {
+ Status status = Status.REQUIRES_UPGRADE;
+ if (currentVersion != null) {
+ if (currentVersion == lastVersion) {
+ status = Status.UP_TO_DATE;
+ } else if (currentVersion > lastVersion) {
+ status = Status.REQUIRES_DOWNGRADE;
+ }
+ }
+ return status;
+ }
+}
loadAlias(conf, "ResourceIndex", ResourceIndexDto.class);
loadAlias(conf, "Rule", RuleDto.class);
loadAlias(conf, "Snapshot", SnapshotDto.class);
+ loadAlias(conf, "SchemaMigration", SchemaMigrationDto.class);
loadAlias(conf, "Widget", WidgetDto.class);
loadAlias(conf, "WidgetProperty", WidgetPropertyDto.class);
loadMapper(conf, ReviewMapper.class);
loadMapper(conf, ResourceIndexerMapper.class);
loadMapper(conf, RuleMapper.class);
+ loadMapper(conf, SchemaMigrationMapper.class);
loadMapper(conf, WidgetMapper.class);
loadMapper(conf, WidgetPropertyMapper.class);
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Maps the table SCHEMA_MIGRATIONS that is fed by Ruby on Rails Migrations
+ * @since 2.15
+ */
+public class SchemaMigrationDto {
+ private String version;//NOSONAR this field is assigned by MyBatis
+
+ public String getVersion() {
+ return version;
+ }
+}
--- /dev/null
+/*
+ * 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 java.util.List;
+
+public interface SchemaMigrationMapper {
+ List<Integer> selectVersions();
+}
import javax.persistence.*;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-
@Entity
-@Table(name = SchemaMigration.TABLE_NAME, uniqueConstraints = {@UniqueConstraint(columnNames = {"version"})})
+@Table(name = "schema_migrations", uniqueConstraints = {@UniqueConstraint(columnNames = {"version"})})
public class SchemaMigration {
- public static final int VERSION_UNKNOWN = -1;
-
- public static final int LAST_VERSION = 263;
- public static final int VERSION_2_13 = 241;
-
- public static final String TABLE_NAME = "schema_migrations";
-
@Id
@Column(name = "version", updatable = true)
private String version;
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 ???
- }
- }
- }
-
@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.utils.Logs;
import org.sonar.core.persistence.Database;
+import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.core.persistence.dialect.Dialect;
-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.Map;
import java.util.Properties;
protected Database database;
private EntityManagerFactory factory = null;
- private int databaseVersion = SchemaMigration.VERSION_UNKNOWN;
- private boolean operational = false;
- private boolean started = false;
protected AbstractDatabaseConnector(Database database) {
this.database = database;
}
- public String getDialectId() {
- return database.getDialect().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;
- }
-
public void start() {
- if (!started) {
- testConnection();
- started = true;
- }
- if (!operational) {
- boolean upToDate = upToDateSchemaVersion();
- if (upToDate) {
- Logs.INFO.info("Initializing Hibernate");
- factory = createEntityManagerFactory();
- operational = true;
- }
- }
+ LOG.info("Initializing Hibernate");
+ factory = createEntityManagerFactory();
+
}
public void stop() {
factory.close();
factory = null;
}
- operational = false;
- started = false;
database = null;
}
return factory;
}
- protected void setEntityManagerFactory(EntityManagerFactory factory) {
- this.factory = factory;
- }
-
protected EntityManagerFactory createEntityManagerFactory() {
// other settings are stored into /META-INF/persistence.xml
Properties props = database.getHibernateProperties();
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;
- }
-
public final int getDatabaseVersion() {
- return databaseVersion;
+ throw new UnsupportedOperationException("Moved to " + DatabaseVersion.class.getCanonicalName());
}
public final Dialect getDialect() {
import org.sonar.api.utils.SonarException;
import org.sonar.core.persistence.Database;
-import org.sonar.jpa.entity.SchemaMigration;
import java.sql.Connection;
import java.sql.SQLException;
super(database);
}
- @Override
- public boolean isOperational() {
- if (isStarted() && getDatabaseVersion() != SchemaMigration.LAST_VERSION) {
- // connector was started and connection OK but schema version was not OK
- // call start again to check if this is now ok (schema created by rails)
- start();
- }
- return super.isOperational();
- }
-
@Override
public void start() {
- if (!isStarted()) {
- createDatasource();
- }
- if (!super.isOperational()) {
- super.start();
- }
+ createDatasource();
+ super.start();
}
private void createDatasource() {
package org.sonar.jpa.session;
import org.sonar.core.persistence.Database;
+import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.jpa.entity.SchemaMigration;
import javax.persistence.EntityManager;
public MemoryDatabaseConnector(Database database) {
super(database);
- version = SchemaMigration.LAST_VERSION;
- }
-
- public MemoryDatabaseConnector(Database database, int version) {
- this(database);
- this.version = version;
+ version = DatabaseVersion.LAST_VERSION;
}
@Override
public void start() {
- try {
- super.start();
- } catch (DatabaseException ex) {
- if (!isStarted()) {
- throw ex;
- }
- }
- setEntityManagerFactory(createEntityManagerFactory());
+ super.start();
setupSchemaVersion(version);
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.persistence.SchemaMigrationMapper">
+
+ <select id="selectVersions" resultType="int">
+ select * from schema_migrations
+ </select>
+
+</mapper>
+
--- /dev/null
+/*
+ * 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.hamcrest.core.Is;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class DatabaseVersionTest extends DaoTestCase {
+ @Test
+ public void getVersion() {
+ setupData("getVersion");
+
+ Integer version = new DatabaseVersion(getMyBatis()).getVersion();
+
+ assertThat(version, Is.is(123));
+ }
+
+ @Test
+ public void getVersion_no_rows() {
+ setupData("getVersion_no_rows");
+
+ Integer version = new DatabaseVersion(getMyBatis()).getVersion();
+
+ assertThat(version, nullValue());
+ }
+
+ @Test
+ public void getStatus() {
+ assertThat(DatabaseVersion.getStatus(null, 150), is(DatabaseVersion.Status.REQUIRES_UPGRADE));
+ assertThat(DatabaseVersion.getStatus(123, 150), is(DatabaseVersion.Status.REQUIRES_UPGRADE));
+ assertThat(DatabaseVersion.getStatus(150, 150), is(DatabaseVersion.Status.UP_TO_DATE));
+ assertThat(DatabaseVersion.getStatus(200, 150), is(DatabaseVersion.Status.REQUIRES_DOWNGRADE));
+ }
+}
+++ /dev/null
-/*
- * 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.jpa.entity;
-
-import org.junit.Test;
-import org.sonar.core.persistence.Database;
-import org.sonar.core.persistence.InMemoryDatabase;
-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 {
- Database database = new InMemoryDatabase();
- database.start();
-
- MemoryDatabaseConnector connector = new MemoryDatabaseConnector(database, SchemaMigration.VERSION_UNKNOWN);
- connector.start();
-
- Connection connection = null;
- try {
- connection = connector.getConnection();
- assertEquals(SchemaMigration.VERSION_UNKNOWN, SchemaMigration.getCurrentVersion(connection));
- } finally {
- if (connection != null) {
- connection.close();
- }
- }
- connector.stop();
- database.stop();
- }
-
- @Test
- public void versionShouldBeLoadedFromSchemaMigrationsTable() throws Exception {
- Database database = new InMemoryDatabase();
- database.start();
- MemoryDatabaseConnector connector = new MemoryDatabaseConnector(database, 30);
- connector.start();
-
- Connection connection = null;
- try {
- connection = connector.getConnection();
- assertEquals(30, SchemaMigration.getCurrentVersion(connection));
-
- } finally {
- if (connection != null) {
- connection.close();
- }
- }
- connector.stop();
- database.stop();
- }
-}
--- /dev/null
+<dataset>
+
+ <schema_migrations version="1"/>
+ <schema_migrations version="2"/>
+ <schema_migrations version="4"/>
+ <schema_migrations version="123"/>
+ <schema_migrations version="50"/>
+</dataset>
--- /dev/null
+<dataset></dataset>
\ No newline at end of file
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.database.DatabaseSession;
-import org.sonar.jpa.entity.SchemaMigration;
+import org.sonar.core.persistence.DatabaseVersion;
import javax.annotation.Nullable;
import java.io.IOException;
* Utils methods
*/
protected int getVersion() {
- return SchemaMigration.LAST_VERSION;
+ return DatabaseVersion.LAST_VERSION;
}
protected Date getCurrentDate() {
--- /dev/null
+/*
+ * 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.server.platform;
+
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerComponent;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+public class DatabaseServerCompatibility implements ServerComponent {
+
+ private DatabaseVersion version;
+
+ public DatabaseServerCompatibility(DatabaseVersion version) {
+ this.version = version;
+ }
+
+ public void start() {
+ DatabaseVersion.Status status = version.getStatus();
+ if (status== DatabaseVersion.Status.REQUIRES_DOWNGRADE) {
+ throw new BadDatabaseVersion("Database relates to a more recent version of sonar. Please check your settings.");
+ }
+ if (status== DatabaseVersion.Status.REQUIRES_UPGRADE) {
+ LoggerFactory.getLogger(DatabaseServerCompatibility.class).info("Database must be upgraded. Please browse /setup");
+ }
+ }
+
+}
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.platform.ServerUpgradeStatus;
-import org.sonar.jpa.entity.SchemaMigration;
-import org.sonar.jpa.session.DatabaseConnector;
+import org.sonar.core.persistence.DatabaseVersion;
/**
* @since 2.5
public final class DefaultServerUpgradeStatus implements ServerUpgradeStatus {
private int initialDbVersion;
- private DatabaseConnector dbConnector;
+ private DatabaseVersion dbVersion;
- public DefaultServerUpgradeStatus(DatabaseConnector dbConnector) {
- this.dbConnector = dbConnector;
+ public DefaultServerUpgradeStatus(DatabaseVersion dbVersion) {
+ this.dbVersion = dbVersion;
}
public void start() {
- this.initialDbVersion = dbConnector.getDatabaseVersion();
+ Integer v = dbVersion.getVersion();
+ this.initialDbVersion = (v != null ? v : -1);
}
public boolean isUpgraded() {
- return !isFreshInstall() &&(initialDbVersion < SchemaMigration.LAST_VERSION);
+ return !isFreshInstall() && (initialDbVersion < DatabaseVersion.LAST_VERSION);
}
public boolean isFreshInstall() {
- return initialDbVersion <= 0;
+ return initialDbVersion<0;
}
public int getInitialDbVersion() {
import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.core.metric.DefaultMetricFinder;
import org.sonar.core.notification.DefaultNotificationManager;
-import org.sonar.core.persistence.DaoUtils;
-import org.sonar.core.persistence.DatabaseMigrator;
-import org.sonar.core.persistence.DefaultDatabase;
-import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.persistence.*;
import org.sonar.core.qualitymodel.DefaultModelFinder;
import org.sonar.core.rule.DefaultRuleFinder;
import org.sonar.core.user.DefaultUserFinder;
}
public void start() {
- if (!started && isUpToDateDatabase()) {
+ if (!started && getDatabaseStatus()== DatabaseVersion.Status.UP_TO_DATE) {
try {
- TimeProfiler profiler = new TimeProfiler().start("Start services");
+ TimeProfiler profiler = new TimeProfiler().start("Start components");
startCoreComponents();
startServiceComponents();
executeStartupTasks();
rootContainer.addSingleton(EmbeddedDatabaseFactory.class);
rootContainer.addSingleton(DefaultDatabase.class);
rootContainer.addSingleton(MyBatis.class);
- rootContainer.addSingleton(DefaultDatabaseConnector.class);
rootContainer.addSingleton(DefaultServerUpgradeStatus.class);
+ rootContainer.addSingleton(DatabaseServerCompatibility.class);
rootContainer.addSingleton(DatabaseMigrator.class);
+ rootContainer.addSingleton(DatabaseVersion.class);
for (Class daoClass : DaoUtils.getDaoClasses()) {
rootContainer.addSingleton(daoClass);
}
rootContainer.startComponents();
}
- private boolean isUpToDateDatabase() {
- DefaultDatabaseConnector databaseConnector = getContainer().getComponentByType(DefaultDatabaseConnector.class);
- return databaseConnector.isOperational();
+ private DatabaseVersion.Status getDatabaseStatus() {
+ DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class);
+ return version.getStatus();
}
private void startCoreComponents() {
coreContainer = rootContainer.createChild();
+ coreContainer.addSingleton(DefaultDatabaseConnector.class);
coreContainer.addSingleton(PluginDeployer.class);
coreContainer.addSingleton(DefaultServerPluginRepository.class);
coreContainer.addSingleton(ServerExtensionInstaller.class);
+ sonar-core/src/main/resources/org/sonar/core/persistence/schema-derby.ddl
+ sonar-core/src/main/resources/org/sonar/core/persistence/rows-derby.sql :
- add "INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('<THE MIGRATION ID>')"
-* Update the migration id defined in sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
+* Update the migration id defined in sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
* If a table is added or removed, then edit sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java
--- /dev/null
+/*
+ * 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.server.platform;
+
+import org.junit.Test;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DatabaseServerCompatibilityTest {
+
+ @Test(expected = BadDatabaseVersion.class)
+ public void shouldFailIfRequiresDowngrade() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
+ new DatabaseServerCompatibility(version).start();
+ }
+
+ @Test
+ public void shouldLogWarningIfRequiresUpgrade() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
+ new DatabaseServerCompatibility(version).start();
+ // oh well... how to simply test logging ?
+ // Let's assume that this test verifies that no error is raised.
+ }
+
+ @Test
+ public void shouldDoNothingIfUpToDate() {
+ DatabaseVersion version = mock(DatabaseVersion.class);
+ when(version.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE);
+ new DatabaseServerCompatibility(version).start();
+ // no error
+ }
+}
package org.sonar.server.platform;
import org.junit.Test;
-import org.sonar.jpa.entity.SchemaMigration;
-import org.sonar.jpa.session.DatabaseConnector;
+import org.sonar.core.persistence.DatabaseVersion;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
@Test
public void shouldBeFreshInstallation() {
- DatabaseConnector connector = mock(DatabaseConnector.class);
- when(connector.getDatabaseVersion()).thenReturn(-1);
+ DatabaseVersion dbVersion = mock(DatabaseVersion.class);
+ when(dbVersion.getVersion()).thenReturn(null);
- DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(connector);
+ DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(dbVersion);
status.start();
assertThat(status.isFreshInstall(), is(true));
@Test
public void shouldBeUpgraded() {
- DatabaseConnector connector = mock(DatabaseConnector.class);
- when(connector.getDatabaseVersion()).thenReturn(50);
+ DatabaseVersion dbVersion = mock(DatabaseVersion.class);
+ when(dbVersion.getVersion()).thenReturn(50);
- DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(connector);
+ DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(dbVersion);
status.start();
assertThat(status.isFreshInstall(), is(false));
@Test
public void shouldNotBeUpgraded() {
- DatabaseConnector connector = mock(DatabaseConnector.class);
- when(connector.getDatabaseVersion()).thenReturn(SchemaMigration.LAST_VERSION);
+ DatabaseVersion dbVersion = mock(DatabaseVersion.class);
+ when(dbVersion.getVersion()).thenReturn(DatabaseVersion.LAST_VERSION);
- DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(connector);
+ DefaultServerUpgradeStatus status = new DefaultServerUpgradeStatus(dbVersion);
status.start();
-
+
assertThat(status.isFreshInstall(), is(false));
assertThat(status.isUpgraded(), is(false));
- assertThat(status.getInitialDbVersion(), is(SchemaMigration.LAST_VERSION));
+ assertThat(status.getInitialDbVersion(), is(DatabaseVersion.LAST_VERSION));
}
}
import org.junit.Test;
import org.sonar.api.database.model.MeasureModel;
import org.sonar.api.platform.ServerUpgradeStatus;
-import org.sonar.jpa.entity.SchemaMigration;
+import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.jpa.test.AbstractDbUnitTestCase;
import java.sql.SQLException;
public void shouldNotDoPurgeOnStandardStartup() {
ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class);
when(upgradeStatus.isUpgraded()).thenReturn(false);
- when(upgradeStatus.getInitialDbVersion()).thenReturn(SchemaMigration.LAST_VERSION);
+ when(upgradeStatus.getInitialDbVersion()).thenReturn(DatabaseVersion.LAST_VERSION);
final DeleteDeprecatedMeasures purge = new DeleteDeprecatedMeasures(getSessionFactory(), upgradeStatus);
assertThat(purge.mustDoPurge(), is(false));