This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository topia. See http://git.nuiton.org/topia.git commit 61f31da24421a4de7a42921265dfb14f0b7be673 Author: Brendan Le Ny <bleny@codelutin.com> Date: Wed May 6 13:03:06 2015 +0200 Introduce TopiaConfiguration#isValidateSchema and implement it --- .../topia/persistence/BeanTopiaConfiguration.java | 11 ++++ .../SchemaValidationTopiaException.java | 49 ++++++++++++++++++ .../topia/persistence/TopiaConfiguration.java | 9 ++++ .../persistence/TopiaConfigurationBuilder.java | 60 +++++++++++++++++----- .../persistence/internal/HibernateProvider.java | 6 ++- .../nuiton/topia/persistence/util/TopiaUtil.java | 11 +++- .../persistence/TopiaConfigurationBuilderTest.java | 1 + .../topia/migration/TopiaMigrationEngineTest.java | 7 ++- 8 files changed, 133 insertions(+), 21 deletions(-) diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/BeanTopiaConfiguration.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/BeanTopiaConfiguration.java index a1c306b..ec9c3eb 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/BeanTopiaConfiguration.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/BeanTopiaConfiguration.java @@ -38,6 +38,8 @@ public class BeanTopiaConfiguration extends BeanJdbcConfiguration implements Top protected boolean initSchema = true; + protected boolean validateSchema = true; + protected Map<String, Class<? extends TopiaService>> declaredServices = new LinkedHashMap<String, Class<? extends TopiaService>>(); @@ -116,6 +118,15 @@ public class BeanTopiaConfiguration extends BeanJdbcConfiguration implements Top } @Override + public boolean isValidateSchema() { + return validateSchema; + } + + public void setValidateSchema(boolean validateSchema) { + this.validateSchema = validateSchema; + } + + @Override public Map<String, Class<? extends TopiaService>> getDeclaredServices() { return declaredServices; } diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/SchemaValidationTopiaException.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/SchemaValidationTopiaException.java new file mode 100644 index 0000000..832df52 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/SchemaValidationTopiaException.java @@ -0,0 +1,49 @@ +package org.nuiton.topia.persistence; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.hibernate.HibernateException; +import org.hibernate.tool.hbm2ddl.SchemaValidator; + +import java.util.Arrays; + +/** + * This exception is throwed when the database schema is not suitable for the + * current entities model. It means that a table or a column is missing + * + * @since 3.0 + */ +public class SchemaValidationTopiaException extends TopiaException { + + /** + * We can know that an {@link HibernateException} is about schema validation + * if one of the stack trace element validate this predicate. + */ + protected static final Predicate<StackTraceElement> IS_STACK_TRACE_ELEMENT_ABOUT_SCHEMA_VALIDATION = + new Predicate<StackTraceElement>() { + + @Override + public boolean apply(StackTraceElement input) { + return input.getClassName().equals(SchemaValidator.class.getName()) + && input.getMethodName().equals("validate"); + } + }; + + public SchemaValidationTopiaException(String message, HibernateException e) { + super(message, e); + } + + /** + * If given {@link HibernateException} is about schema validation, throw a SchemaValidationTopiaException. + */ + public static void throwIfHibernateExceptionIsAboutSchemaValidation(HibernateException hibernateException) { + // XXX brendan 06/05/15 dirty hack to know if e is about schema validation since Hibernate exception management sucks + boolean stackTraceIsAboutSchemaValidation = Iterables.any( + Arrays.asList(hibernateException.getStackTrace()), + IS_STACK_TRACE_ELEMENT_ABOUT_SCHEMA_VALIDATION); + if (stackTraceIsAboutSchemaValidation) { + throw new SchemaValidationTopiaException(hibernateException.getMessage(), hibernateException); + } + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfiguration.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfiguration.java index 4b109b0..b2eb019 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfiguration.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfiguration.java @@ -48,6 +48,15 @@ public interface TopiaConfiguration extends JdbcConfiguration { boolean isInitSchema(); /** + * If true, ToPIA will validate schema against model upon starting. ToPIA will raise a + * {@link SchemaValidationTopiaException} if the schema is not suitable for ToPIA to run + * fine. + * + * @since 3.0 + */ + boolean isValidateSchema(); + + /** * Configuration directive to change topia Ids generation strategy. * * @since 3.0 diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfigurationBuilder.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfigurationBuilder.java index 1b92a59..99ba014 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfigurationBuilder.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaConfigurationBuilder.java @@ -22,7 +22,6 @@ package org.nuiton.topia.persistence; * #L% */ -import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; @@ -30,8 +29,6 @@ import com.google.common.collect.ImmutableSet; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.cfg.AvailableSettings; -import org.nuiton.topia.persistence.internal.DefaultTopiaIdFactory; -import org.nuiton.topia.persistence.internal.FullyQualifiedNamePlusUuidTopiaIdFactory; import org.nuiton.topia.persistence.jdbc.JdbcConfiguration; import org.nuiton.topia.persistence.jdbc.JdbcConfigurationBuilder; import org.nuiton.util.beans.Binder; @@ -73,6 +70,9 @@ public class TopiaConfigurationBuilder { protected static final String CONFIG_PERSISTENCE_INIT_SCHEMA = "topia.persistence.initSchema"; + protected static final String CONFIG_PERSISTENCE_VALIDATE_SCHEMA = + "topia.persistence.validateSchema"; + /** * Configuration that must not be in {@link TopiaConfiguration#getHibernateExtraConfiguration()}. */ @@ -154,6 +154,11 @@ public class TopiaConfigurationBuilder { boolean initSchema = StringUtils.isBlank(initSchemaConfigValue) || Boolean.parseBoolean(initSchemaConfigValue); result.setInitSchema(initSchema); + // Schema validation + String validateSchemaConfigValue = configuration.get(CONFIG_PERSISTENCE_VALIDATE_SCHEMA); + boolean validateSchema = StringUtils.isBlank(validateSchemaConfigValue) || Boolean.parseBoolean(validateSchemaConfigValue); + result.setValidateSchema(validateSchema); + // others String topiaIdFactoryClassName = configuration.get(CONFIG_PERSISTENCE_TOPIA_ID_FACTORY_CLASS_NAME); if (!Strings.isNullOrEmpty(topiaIdFactoryClassName)) { @@ -225,6 +230,11 @@ public class TopiaConfigurationBuilder { if ( ! topiaConfiguration.isInitSchema()) { map.put(CONFIG_PERSISTENCE_INIT_SCHEMA, String.valueOf(topiaConfiguration.isInitSchema())); } + + // Schema validation + if ( ! topiaConfiguration.isInitSchema()) { + map.put(CONFIG_PERSISTENCE_VALIDATE_SCHEMA, String.valueOf(topiaConfiguration.isValidateSchema())); + } // others map.put(CONFIG_PERSISTENCE_TOPIA_ID_FACTORY_CLASS_NAME, topiaConfiguration.getTopiaIdFactory().getClass().getName()); @@ -266,7 +276,7 @@ public class TopiaConfigurationBuilder { public BeanTopiaConfiguration forTest(Class<?> testClass, String methodName) { JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forTestDatabase(testClass, methodName); - BeanTopiaConfiguration configuration = forDatabase(jdbcConfiguration).onlyCreateSchemaIfDatabaseIsEmpty().build(); + BeanTopiaConfiguration configuration = forDatabase(jdbcConfiguration).onlyCreateSchemaIfDatabaseIsEmpty().validateSchemaOnStartup().build(); return configuration; } @@ -285,31 +295,55 @@ public class TopiaConfigurationBuilder { this.beanTopiaConfiguration = beanTopiaConfiguration; } - public BuildStep onlyCreateSchemaIfDatabaseIsEmpty() { + public ConfigureValidateSchemaStep onlyCreateSchemaIfDatabaseIsEmpty() { beanTopiaConfiguration.setInitSchema(true); - return new BuildStep(beanTopiaConfiguration); + return nextStep(); } - public BuildStep useAlreadyExistingDatabaseAsIs() { + public ConfigureValidateSchemaStep useAlreadyExistingDatabaseAsIs() { beanTopiaConfiguration.setInitSchema(false); - return new BuildStep(beanTopiaConfiguration); + return nextStep(); } - public BuildStep useHibernateUpdate() { + public ConfigureValidateSchemaStep useHibernateUpdate() { beanTopiaConfiguration.setInitSchema(true); beanTopiaConfiguration.addDeclaredService("migration", org.nuiton.topia.persistence.HibernateTopiaMigrationService.class, Collections.<String, String>emptyMap()); - return new BuildStep(beanTopiaConfiguration); + return nextStep(); } - public BuildStep useFlyway() { + public ConfigureValidateSchemaStep useFlyway() { beanTopiaConfiguration.setInitSchema(true); beanTopiaConfiguration.addDeclaredService("migration", "org.nuiton.topia.flyway.TopiaFlywayServiceImpl", Collections.<String, String>emptyMap()); - return new BuildStep(beanTopiaConfiguration); + return nextStep(); } - public BuildStep useLiquibase() { + public ConfigureValidateSchemaStep useLiquibase() { beanTopiaConfiguration.setInitSchema(true); beanTopiaConfiguration.addDeclaredService("migration", "org.nuiton.topia.flyway.TopiaLiquibaseServiceImpl", Collections.<String, String>emptyMap()); + return nextStep(); + } + + protected ConfigureValidateSchemaStep nextStep() { + return new ConfigureValidateSchemaStep(beanTopiaConfiguration); + } + + } + + public class ConfigureValidateSchemaStep { + + protected BeanTopiaConfiguration beanTopiaConfiguration; + + public ConfigureValidateSchemaStep(BeanTopiaConfiguration beanTopiaConfiguration) { + this.beanTopiaConfiguration = beanTopiaConfiguration; + } + + public BuildStep validateSchemaOnStartup() { + beanTopiaConfiguration.setValidateSchema(true); + return new BuildStep(beanTopiaConfiguration); + } + + public BuildStep doNotValidateSchemaOnStartup() { + beanTopiaConfiguration.setValidateSchema(false); return new BuildStep(beanTopiaConfiguration); } diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/HibernateProvider.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/HibernateProvider.java index f864975..bcabf44 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/HibernateProvider.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/HibernateProvider.java @@ -136,8 +136,10 @@ public class HibernateProvider { properties.put(AvailableSettings.C3P0_TIMEOUT, 1800); properties.put(AvailableSettings.C3P0_MAX_STATEMENTS, 50); - // always validate schema - properties.put(AvailableSettings.HBM2DDL_AUTO, "validate"); + // schema validation + if (topiaConfiguration.isValidateSchema()) { + properties.put(AvailableSettings.HBM2DDL_AUTO, "validate"); + } properties.put(HibernateAvailableSettings.NAMING_STRATEGY, org.hibernate.cfg.ImprovedNamingStrategy.class.getName()); properties.put(AvailableSettings.FORMAT_SQL, true); diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaUtil.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaUtil.java index 27f86a0..958b13b 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaUtil.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaUtil.java @@ -30,6 +30,7 @@ import com.google.common.base.Supplier; import com.google.common.collect.ImmutableSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; @@ -42,6 +43,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Table; import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.TableMetadata; +import org.nuiton.topia.persistence.SchemaValidationTopiaException; import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.TopiaException; import org.nuiton.topia.persistence.TopiaNotFoundException; @@ -582,8 +584,13 @@ public class TopiaUtil { StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); StandardServiceRegistry standardServiceRegistry = builder.applySettings(properties).build(); - SessionFactory result = hibernateConfiguration.buildSessionFactory(standardServiceRegistry); - + SessionFactory result; + try { + result = hibernateConfiguration.buildSessionFactory(standardServiceRegistry); + } catch (HibernateException e) { + SchemaValidationTopiaException.throwIfHibernateExceptionIsAboutSchemaValidation(e); + throw e; + } return result; } diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaConfigurationBuilderTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaConfigurationBuilderTest.java index 3c32cda..781bdb1 100644 --- a/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaConfigurationBuilderTest.java +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaConfigurationBuilderTest.java @@ -68,6 +68,7 @@ public class TopiaConfigurationBuilderTest { TopiaConfiguration topiaConfiguration = new TopiaConfigurationBuilder().forTestDatabase(getClass(), "build") .useHibernateUpdate() + .validateSchemaOnStartup() .build(); Assert.assertTrue("any generated topia configuration must have initSchema to true", topiaConfiguration.isInitSchema()); diff --git a/topia-service-migration/src/test/java/org/nuiton/topia/migration/TopiaMigrationEngineTest.java b/topia-service-migration/src/test/java/org/nuiton/topia/migration/TopiaMigrationEngineTest.java index 0f50f2c..6ef173f 100644 --- a/topia-service-migration/src/test/java/org/nuiton/topia/migration/TopiaMigrationEngineTest.java +++ b/topia-service-migration/src/test/java/org/nuiton/topia/migration/TopiaMigrationEngineTest.java @@ -25,13 +25,12 @@ package org.nuiton.topia.migration; */ import com.google.common.collect.ImmutableMap; -import org.hibernate.HibernateException; import org.junit.Assert; import org.junit.Test; import org.nuiton.topia.it.mapping.TopiaItMappingTopiaApplicationContext; import org.nuiton.topia.it.mapping.TopiaItMappingTopiaPersistenceContext; import org.nuiton.topia.persistence.BeanTopiaConfiguration; -import org.nuiton.topia.persistence.TopiaConfiguration; +import org.nuiton.topia.persistence.SchemaValidationTopiaException; import org.nuiton.topia.persistence.TopiaConfigurationBuilder; import org.nuiton.topia.persistence.jdbc.JdbcConfiguration; import org.nuiton.topia.persistence.jdbc.JdbcH2Helper; @@ -153,8 +152,8 @@ public class TopiaMigrationEngineTest { TopiaItMappingTopiaPersistenceContext persistenceContext = applicationContext.newPersistenceContext(); persistenceContext.close(); Assert.fail("Validation should have failed"); - } catch (HibernateException he) { - Assert.assertEquals("Missing column: name in H2.PUBLIC.B71", he.getMessage()); + } catch (SchemaValidationTopiaException e) { + Assert.assertEquals("Missing column: name in H2.PUBLIC.B71", e.getMessage()); } applicationContext.close(); -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.