From 53ba957d41791ba6b44b9f4bdb6cf8d4a5875dd9 Mon Sep 17 00:00:00 2001 From: izabel <izabel> Date: Mon, 21 Jul 2008 22:55:16 +0000 Subject: [PATCH] [LMS-520] Allow pre- and post-methods for db migration written in Java SVN: 7373 --- .../cisd/dbmigration/DBMigrationEngine.java | 29 +++ .../cisd/dbmigration/IDAOFactory.java | 5 + .../IJavaMigrationStepExecutor.java | 29 +++ .../cisd/dbmigration/IMigrationStep.java | 44 ++++ .../JavaMigrationStepExecutor.java | 186 +++++++++++++++++ .../cisd/dbmigration/h2/H2DAOFactory.java | 14 +- .../postgresql/PostgreSQLDAOFactory.java | 14 +- .../dbmigration/DBMigrationEngineTest.java | 193 +++++++++++++++++- .../JavaMigrationStepExecutorTest.java | 114 +++++++++++ .../MigrationStepFrom001To002.java | 40 ++++ .../MigrationStepFrom002To003.java | 40 ++++ 11 files changed, 700 insertions(+), 8 deletions(-) create mode 100644 dbmigration/source/java/ch/systemsx/cisd/dbmigration/IJavaMigrationStepExecutor.java create mode 100644 dbmigration/source/java/ch/systemsx/cisd/dbmigration/IMigrationStep.java create mode 100644 dbmigration/source/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutor.java create mode 100644 dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutorTest.java create mode 100644 dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom001To002.java create mode 100644 dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom002To003.java diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java index c0dfddb4006..8485a5e4167 100644 --- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java @@ -24,6 +24,7 @@ import ch.systemsx.cisd.common.Script; import ch.systemsx.cisd.common.db.ISqlScriptExecutor; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.Status; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; @@ -71,6 +72,8 @@ public final class DBMigrationEngine private final ISqlScriptExecutor scriptExecutor; + private final IJavaMigrationStepExecutor javaMigrationStepExecutor; + /** * Creates an instance for the specified DAO factory and SQL script provider. * @@ -83,6 +86,7 @@ public final class DBMigrationEngine adminDAO = daoFactory.getDatabaseDAO(); logDAO = daoFactory.getDatabaseVersionLogDAO(); scriptExecutor = daoFactory.getSqlScriptExecutor(); + javaMigrationStepExecutor = daoFactory.getJavaMigrationStepExecutor(); this.scriptProvider = scriptProvider; this.shouldCreateFromScratch = shouldCreateFromScratch; } @@ -251,7 +255,13 @@ public final class DBMigrationEngine throw new EnvironmentFailureException(message); } final long time = System.currentTimeMillis(); + final Status preMigrationStatusOrNull = + javaMigrationStepExecutor.tryPerformPreMigration(migrationScript); + checkErrors(preMigrationStatusOrNull, "pre"); scriptExecutor.execute(migrationScript, true, logDAO); + final Status postMigrationStatusOrNull = + javaMigrationStepExecutor.tryPerformPostMigration(migrationScript); + checkErrors(postMigrationStatusOrNull, "post"); if (operationLog.isInfoEnabled()) { operationLog.info("Successfully migrated from version " + version + " to " @@ -261,6 +271,25 @@ public final class DBMigrationEngine } while (version.equals(toVersion) == false); } + private void checkErrors(final Status migrationStatusOrNull, final String prefix) + { + if (migrationStatusOrNull != null) + { + if (migrationStatusOrNull.isError()) + { + final String message = + String.format("Java %s-migration finnished with an error status ('%s')", + prefix, migrationStatusOrNull.tryGetErrorMessage()); + operationLog.error(message); + throw new EnvironmentFailureException(message); + } else if (operationLog.isInfoEnabled()) + { + operationLog.info(String.format("Java %s-migration succesfull.", prefix)); + } + + } + } + @Private final static String increment(final String version) { diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IDAOFactory.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IDAOFactory.java index 72488db1ac8..18757a584f7 100644 --- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IDAOFactory.java +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IDAOFactory.java @@ -44,4 +44,9 @@ public interface IDAOFactory * Returns the mass uploader used for initial data uploading. */ public IMassUploader getMassUploader(); + + /** + * FIXME: + */ + public IJavaMigrationStepExecutor getJavaMigrationStepExecutor(); } diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IJavaMigrationStepExecutor.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IJavaMigrationStepExecutor.java new file mode 100644 index 00000000000..66925f204d7 --- /dev/null +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IJavaMigrationStepExecutor.java @@ -0,0 +1,29 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import ch.systemsx.cisd.common.Script; +import ch.systemsx.cisd.common.exceptions.Status; + + +public interface IJavaMigrationStepExecutor +{ + + public Status tryPerformPostMigration(final Script sqlScript); + + public Status tryPerformPreMigration(final Script sqlScript); +} diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IMigrationStep.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IMigrationStep.java new file mode 100644 index 00000000000..5ad82aad285 --- /dev/null +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/IMigrationStep.java @@ -0,0 +1,44 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import org.springframework.jdbc.core.JdbcTemplate; + +import ch.systemsx.cisd.common.exceptions.Status; + +/** + * An interface which must be implemented by all classes providing Java code that performs migration + * steps prior (pre) or after (post) the SQL migration script ran. Canonical name of class + * implementing this interface (preceded by <code>-- JAVA </code>) may be included in the first + * line of SQL migration script. <Example: + * <code> -- JAVA ch.systemsx.cisd.openbis.db.migration.MigrationStepFrom022To023</code> + * + * @author Izabela Adamczyk + */ +public interface IMigrationStep +{ + /** + * Called before the SQL migration is performed. + */ + public Status performPreMigration(JdbcTemplate jdbcTemplate); + + /** + * Called after the SQL migration has been performed. + */ + public Status performPostMigration(JdbcTemplate jdbcTemplate); + +} diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutor.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutor.java new file mode 100644 index 00000000000..ac35f7f331f --- /dev/null +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutor.java @@ -0,0 +1,186 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import java.util.Arrays; +import java.util.List; + +import javax.sql.DataSource; + +import org.apache.commons.lang.StringUtils; +import org.springframework.jdbc.core.support.JdbcDaoSupport; + +import ch.systemsx.cisd.common.Script; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.Status; + +/** + * Allows to extract {@link IMigrationStep} class from migration script and run the pre- and post- + * migration java steps. Example of the script containing Java Migration Step definition: + * + * <pre> + * -- JAVA ch.systemsx.cisd.openbis.db.migration.MigrationStepFrom022To023 + * </pre> + * + * @author Izabela Adamczyk + */ +public class JavaMigrationStepExecutor extends JdbcDaoSupport implements IJavaMigrationStepExecutor +{ + private static final String JAVA_MIGRATION_STEP_PREFIX = "--JAVA"; + + public JavaMigrationStepExecutor(final DataSource dataSource) + { + setDataSource(dataSource); + } + + private IMigrationStep tryExtractMigrationStep(final Script sqlScript) + + { + if (sqlScript == null || StringUtils.isBlank(sqlScript.getCode())) + { + return null; + } + final String code = sqlScript.getCode(); + final List<String> lines = Arrays.asList(code.split("\n")); + IMigrationStep extratedMigrationStepOrNull = null; + boolean nonEmptyLineFound = false; + for (final String line : lines) + { + // blank lines are allowed at the beginning of the script + if (StringUtils.isBlank(line)) + { + continue; + } + // only the first non-blank line is supposed to contain Java Migration Step + if (nonEmptyLineFound == false) + { + extratedMigrationStepOrNull = tryExtractMigrationStepFromLine(line); + nonEmptyLineFound = true; + } else + { + checkIfCurrentLineConsistentWithAlredyProcessed(sqlScript, + extratedMigrationStepOrNull, line); + } + } + return extratedMigrationStepOrNull; + } + + private void checkIfCurrentLineConsistentWithAlredyProcessed(final Script sqlScript, + final IMigrationStep extratedMigrationStepOrNull, final String line) + { + if (tryExtractMigrationStepFromLine(line) != null) + { + final String msg; + if (extratedMigrationStepOrNull != null) + { + + msg = + String + .format( + "Migration script '%s' contains more than one Java Migration Steps.", + sqlScript.getName()); + + } else + { + msg = + String + .format( + "Java Migration Step should be defined in the first non-blank line of the migration script '%s'.", + sqlScript.getName()); + + } + + throw new EnvironmentFailureException(msg); + } + } + + @SuppressWarnings("unchecked") + private IMigrationStep tryExtractMigrationStepFromLine(final String lineToProcess) + { + final String line = StringUtils.deleteWhitespace(lineToProcess); + if (line != null && line.startsWith(JAVA_MIGRATION_STEP_PREFIX)) + { + final String className = StringUtils.removeStart(line, JAVA_MIGRATION_STEP_PREFIX); + String msg; + try + { + final Class clazz = Class.forName(className); + final Object object = clazz.newInstance(); + return (IMigrationStep) object; + } catch (final ClassNotFoundException ex) + { + msg = String.format("Class '%s' not found.", className); + + } catch (final InstantiationException ex) + { + msg = + String.format( + "Cannot instantiate class '%s' (EnvironmentFailureException).", + className); + } catch (final IllegalAccessException ex) + { + msg = + String.format("Cannot instantiate class '%s' (IllegalAccessException).", + className); + } + throw new EnvironmentFailureException(msg); + } else + { + return null; + } + } + + /** + * Returns null if MigrationStep has not been found and status returned by + * {@link IMigrationStep#performPreMigration(org.springframework.jdbc.core.JdbcTemplate)} + * otherwise. + */ + public Status tryPerformPreMigration(final Script sqlScript) + + { + final IMigrationStep migrationStep = tryExtractMigrationStep(sqlScript); + if (migrationStep != null) + { + + final Status preMigrationStatus = migrationStep.performPreMigration(getJdbcTemplate()); + return preMigrationStatus; + } else + { + return null; + } + } + + /** + * Returns null if MigrationStep has not been found and status returned by + * {@link IMigrationStep#performPostMigration(org.springframework.jdbc.core.JdbcTemplate)} + * otherwise. + */ + public Status tryPerformPostMigration(final Script sqlScript) + { + final IMigrationStep migrationStep = tryExtractMigrationStep(sqlScript); + if (migrationStep != null) + { + final Status postMigrationStatus = + migrationStep.performPostMigration(getJdbcTemplate()); + return postMigrationStatus; + } else + { + return null; + } + } + +} diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/h2/H2DAOFactory.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/h2/H2DAOFactory.java index 78b7dc09fba..27863e57017 100644 --- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/h2/H2DAOFactory.java +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/h2/H2DAOFactory.java @@ -27,7 +27,9 @@ import ch.systemsx.cisd.dbmigration.DatabaseVersionLogDAO; import ch.systemsx.cisd.dbmigration.IDAOFactory; import ch.systemsx.cisd.dbmigration.IDatabaseAdminDAO; import ch.systemsx.cisd.dbmigration.IDatabaseVersionLogDAO; +import ch.systemsx.cisd.dbmigration.IJavaMigrationStepExecutor; import ch.systemsx.cisd.dbmigration.IMassUploader; +import ch.systemsx.cisd.dbmigration.JavaMigrationStepExecutor; import ch.systemsx.cisd.dbmigration.SqlScriptExecutor; /** @@ -45,18 +47,21 @@ public class H2DAOFactory implements IDAOFactory private final IMassUploader massUploader; + private final IJavaMigrationStepExecutor javaMigrationStepExecutor; + /** * Creates an instance based on the specified configuration context. */ - public H2DAOFactory(DatabaseConfigurationContext context) + public H2DAOFactory(final DatabaseConfigurationContext context) { final DataSource dataSource = context.getDataSource(); sqlScriptExecutor = new SqlScriptExecutor(dataSource, context.isScriptSingleStepMode()); + javaMigrationStepExecutor = new JavaMigrationStepExecutor(dataSource); databaseVersionLogDAO = new DatabaseVersionLogDAO(dataSource, context.getLobHandler()); try { massUploader = new H2MassUploader(dataSource, context.getSequenceNameMapper()); - } catch (SQLException ex) + } catch (final SQLException ex) { throw new CheckedExceptionTunnel(ex); } @@ -85,4 +90,9 @@ public class H2DAOFactory implements IDAOFactory return massUploader; } + public IJavaMigrationStepExecutor getJavaMigrationStepExecutor() + { + return javaMigrationStepExecutor; + } + } diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/postgresql/PostgreSQLDAOFactory.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/postgresql/PostgreSQLDAOFactory.java index 98b73de749f..60e5da6698b 100644 --- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/postgresql/PostgreSQLDAOFactory.java +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/postgresql/PostgreSQLDAOFactory.java @@ -27,7 +27,9 @@ import ch.systemsx.cisd.dbmigration.DatabaseVersionLogDAO; import ch.systemsx.cisd.dbmigration.IDAOFactory; import ch.systemsx.cisd.dbmigration.IDatabaseAdminDAO; import ch.systemsx.cisd.dbmigration.IDatabaseVersionLogDAO; +import ch.systemsx.cisd.dbmigration.IJavaMigrationStepExecutor; import ch.systemsx.cisd.dbmigration.IMassUploader; +import ch.systemsx.cisd.dbmigration.JavaMigrationStepExecutor; import ch.systemsx.cisd.dbmigration.SqlScriptExecutor; /** @@ -45,18 +47,21 @@ public class PostgreSQLDAOFactory implements IDAOFactory private final IMassUploader massUploader; + private final IJavaMigrationStepExecutor javaMigrationStepExecutor; + /** * Creates an instance based on the specified configuration context. */ - public PostgreSQLDAOFactory(DatabaseConfigurationContext context) + public PostgreSQLDAOFactory(final DatabaseConfigurationContext context) { final DataSource dataSource = context.getDataSource(); sqlScriptExecutor = new SqlScriptExecutor(dataSource, context.isScriptSingleStepMode()); + javaMigrationStepExecutor = new JavaMigrationStepExecutor(dataSource); databaseVersionLogDAO = new DatabaseVersionLogDAO(dataSource, context.getLobHandler()); try { massUploader = new PostgreSQLMassUploader(dataSource, context.getSequenceNameMapper()); - } catch (SQLException ex) + } catch (final SQLException ex) { throw new CheckedExceptionTunnel(ex); } @@ -86,4 +91,9 @@ public class PostgreSQLDAOFactory implements IDAOFactory return massUploader; } + public IJavaMigrationStepExecutor getJavaMigrationStepExecutor() + { + return javaMigrationStepExecutor; + } + } diff --git a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java index 7ecede9a4c2..63e7537ad13 100644 --- a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java +++ b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java @@ -18,6 +18,7 @@ package ch.systemsx.cisd.dbmigration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertSame; +import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import java.io.File; @@ -37,6 +38,7 @@ import ch.systemsx.cisd.common.Script; import ch.systemsx.cisd.common.db.ISqlScriptExecutor; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.Status; import ch.systemsx.cisd.common.logging.BufferedAppender; import ch.systemsx.cisd.common.utilities.OSUtilities; @@ -55,6 +57,18 @@ public class DBMigrationEngineTest { will(returnValue(script)); one(scriptExecutor).execute(script, honorSingleStepMode, logDAO); + + } + + protected void expectSuccessfulScriptExecutionWithMigrationSteps(final Script script, + final boolean honorSingleStepMode) + { + will(returnValue(script)); + one(javaMigrationStepExecutor).tryPerformPreMigration(script); + will(returnValue(Status.OK)); + one(scriptExecutor).execute(script, honorSingleStepMode, logDAO); + one(javaMigrationStepExecutor).tryPerformPostMigration(script); + will(returnValue(Status.OK)); } } @@ -70,6 +84,8 @@ public class DBMigrationEngineTest private ISqlScriptExecutor scriptExecutor; + private IJavaMigrationStepExecutor javaMigrationStepExecutor; + private BufferedAppender logRecorder; @BeforeMethod @@ -81,6 +97,7 @@ public class DBMigrationEngineTest adminDAO = context.mock(IDatabaseAdminDAO.class); logDAO = context.mock(IDatabaseVersionLogDAO.class); scriptExecutor = context.mock(ISqlScriptExecutor.class); + javaMigrationStepExecutor = context.mock(IJavaMigrationStepExecutor.class); logRecorder = new BufferedAppender("%-5p %c - %m%n", Level.DEBUG); } @@ -115,6 +132,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); @@ -165,6 +184,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); @@ -214,6 +235,9 @@ public class DBMigrationEngineTest one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); + one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); will(returnValue(false)); @@ -255,6 +279,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(false)); @@ -300,6 +326,9 @@ public class DBMigrationEngineTest one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); + one(logDAO).canConnectToDatabase(); will(returnValue(false)); one(adminDAO).getDatabaseName(); @@ -345,6 +374,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(false)); @@ -390,6 +421,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(true)); @@ -432,6 +465,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(true)); @@ -445,13 +480,15 @@ public class DBMigrationEngineTest one(adminDAO).getDatabaseURL(); will(returnValue("my database URL")); one(scriptProvider).tryGetMigrationScript(fromVersion, "100"); - expectSuccessfulExecution(new Script("m-099-100", "code 099 100", toVersion), - true); + final Script script = new Script("m-099-100", "code 099 100", toVersion); + expectSuccessfulScriptExecutionWithMigrationSteps(script, true); + one(scriptProvider).tryGetMigrationScript("100", toVersion); - expectSuccessfulExecution(new Script("m-100-101", "code 100 101", toVersion), - true); + expectSuccessfulScriptExecutionWithMigrationSteps(new Script("m-100-101", + "code 100 101", toVersion), true); one(adminDAO).getDatabaseName(); will(returnValue("my 2. database")); + } }); final DBMigrationEngine migrationEngine = @@ -464,9 +501,17 @@ public class DBMigrationEngineTest "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + "Trying to migrate database 'my 1. database' from version 099 to 101." + OSUtilities.LINE_SEPARATOR + + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java pre-migration succesfull." + + OSUtilities.LINE_SEPARATOR + + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java post-migration succesfull." + + OSUtilities.LINE_SEPARATOR + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + "Successfully migrated from version 099 to 100 in 0 msec" + OSUtilities.LINE_SEPARATOR + + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java pre-migration succesfull." + + OSUtilities.LINE_SEPARATOR + + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java post-migration succesfull." + + OSUtilities.LINE_SEPARATOR + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + "Successfully migrated from version 100 to 101 in 0 msec" + OSUtilities.LINE_SEPARATOR @@ -479,6 +524,132 @@ public class DBMigrationEngineTest context.assertIsSatisfied(); } + @Test + public void testMigrationStepsFailPostMigrationStep() + { + final String fromVersion = "099"; + final String toVersion = "101"; + context.checking(new MyExpectations() + { + { + one(daoFactory).getDatabaseDAO(); + will(returnValue(adminDAO)); + one(daoFactory).getDatabaseVersionLogDAO(); + will(returnValue(logDAO)); + one(daoFactory).getSqlScriptExecutor(); + will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); + + one(logDAO).canConnectToDatabase(); + will(returnValue(true)); + one(logDAO).getLastEntry(); + final LogEntry logEntry = new LogEntry(); + logEntry.setRunStatus(LogEntry.RunStatus.SUCCESS); + logEntry.setVersion(fromVersion); + will(returnValue(logEntry)); + one(adminDAO).getDatabaseName(); + will(returnValue("my 1. database")); + one(scriptProvider).tryGetMigrationScript(fromVersion, "100"); + final Script script = new Script("m-099-100", "code 099 100", toVersion); + + will(returnValue(script)); + + one(javaMigrationStepExecutor).tryPerformPreMigration(script); + will(returnValue(Status.OK)); + one(scriptExecutor).execute(script, true, logDAO); + + one(javaMigrationStepExecutor).tryPerformPostMigration(script); + will(returnValue(Status.createError("Bad system user code."))); + } + }); + final DBMigrationEngine migrationEngine = + new DBMigrationEngine(daoFactory, scriptProvider, false); + + boolean exceptionThrown = false; + try + { + migrationEngine.migrateTo(toVersion); + } catch (final EnvironmentFailureException e) + { + exceptionThrown = true; + assertEquals( + "Java post-migration finnished with an error status ('Bad system user code.')", + e.getMessage()); + } + assertTrue(exceptionThrown); + String logContent = logRecorder.getLogContent(); + logContent = logContent.replaceAll("\\d+ msec", "0 msec"); + assertEquals( + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + + "Trying to migrate database 'my 1. database' from version 099 to 101." + + OSUtilities.LINE_SEPARATOR + + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java pre-migration succesfull." + + OSUtilities.LINE_SEPARATOR + + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java post-migration finnished with an error status ('Bad system user code.')", + logContent); + + context.assertIsSatisfied(); + } + + @Test + public void testMigrationStepsFailPreMigration() + { + final String fromVersion = "099"; + final String toVersion = "101"; + context.checking(new MyExpectations() + { + { + one(daoFactory).getDatabaseDAO(); + will(returnValue(adminDAO)); + one(daoFactory).getDatabaseVersionLogDAO(); + will(returnValue(logDAO)); + one(daoFactory).getSqlScriptExecutor(); + will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); + + one(logDAO).canConnectToDatabase(); + will(returnValue(true)); + one(logDAO).getLastEntry(); + final LogEntry logEntry = new LogEntry(); + logEntry.setRunStatus(LogEntry.RunStatus.SUCCESS); + logEntry.setVersion(fromVersion); + will(returnValue(logEntry)); + one(adminDAO).getDatabaseName(); + will(returnValue("my 1. database")); + one(scriptProvider).tryGetMigrationScript(fromVersion, "100"); + final Script script = new Script("m-099-100", "code 099 100", toVersion); + will(returnValue(script)); + one(javaMigrationStepExecutor).tryPerformPreMigration(script); + will(returnValue(Status.createError("Bad system user code."))); + } + }); + final DBMigrationEngine migrationEngine = + new DBMigrationEngine(daoFactory, scriptProvider, false); + boolean exceptionThrown = false; + try + { + migrationEngine.migrateTo(toVersion); + } catch (final EnvironmentFailureException e) + { + exceptionThrown = true; + assertEquals( + "Java pre-migration finnished with an error status ('Bad system user code.')", + e.getMessage()); + } + assertTrue(exceptionThrown); + String logContent = logRecorder.getLogContent(); + logContent = logContent.replaceAll("\\d+ msec", "0 msec"); + assertEquals( + "INFO OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Trying to migrate database 'my 1. database' from version 099 to 101." + + OSUtilities.LINE_SEPARATOR + + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Java pre-migration finnished with an error status ('Bad system user code.')", + logContent); + + context.assertIsSatisfied(); + } + @Test public void testMigrationScriptNotFound() { @@ -493,6 +664,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(true)); @@ -545,6 +718,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(true)); @@ -588,6 +763,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); @@ -633,6 +810,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); @@ -682,6 +861,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(adminDAO).dropDatabase(); one(logDAO).canConnectToDatabase(); @@ -728,6 +909,8 @@ public class DBMigrationEngineTest will(returnValue(logDAO)); one(daoFactory).getSqlScriptExecutor(); will(returnValue(scriptExecutor)); + one(daoFactory).getJavaMigrationStepExecutor(); + will(returnValue(javaMigrationStepExecutor)); one(logDAO).canConnectToDatabase(); will(returnValue(true)); @@ -741,6 +924,8 @@ public class DBMigrationEngineTest one(scriptProvider).tryGetMigrationScript(fromVersion, toVersion); final Script script = new Script("m-1-2", "code", toVersion); will(returnValue(script)); + + one(javaMigrationStepExecutor).tryPerformPreMigration(script); one(scriptExecutor).execute(script, true, logDAO); will(throwException(exception)); } diff --git a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutorTest.java b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutorTest.java new file mode 100644 index 00000000000..a1f5a64dac3 --- /dev/null +++ b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/JavaMigrationStepExecutorTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import javax.sql.DataSource; + +import org.jmock.Mockery; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.Script; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.StatusFlag; +import ch.systemsx.cisd.common.logging.LogInitializer; + +/** + * Test cases for the {@link JavaMigrationStepExecutor}. + * + * @author Izabela Adamczyk + */ +public class JavaMigrationStepExecutorTest +{ + private Mockery context; + + DataSource dataSource; + + @BeforeMethod + public void setUp() + { + context = new Mockery(); + dataSource = context.mock(DataSource.class); + } + + @BeforeClass(alwaysRun = true) + public void beforeClass() throws Exception + { + LogInitializer.init(); + } + + @Test + public void testHappyCase() + { + final JavaMigrationStepExecutor javaMigrationStepExecutor = + new JavaMigrationStepExecutor(dataSource); + final Script script = + new Script("001To002.sql", + "-- JAVA ch.systemsx.cisd.dbmigration.MigrationStepFrom001To002"); + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPreMigration(script).getFlag() + .equals(StatusFlag.OK)); + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPostMigration(script).getFlag() + .equals(StatusFlag.OK)); + + } + + @Test + public void testUnhappyCase() + { + final JavaMigrationStepExecutor javaMigrationStepExecutor = + new JavaMigrationStepExecutor(dataSource); + final Script script = + new Script("002To003.sql", + "-- JAVA ch.systemsx.cisd.dbmigration.MigrationStepFrom002To003"); + + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPreMigration(script).getFlag() + .equals(StatusFlag.ERROR)); + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPreMigration(script) + .tryGetErrorMessage().equals("bad pre")); + + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPostMigration(script).getFlag() + .equals(StatusFlag.ERROR)); + Assert.assertTrue(javaMigrationStepExecutor.tryPerformPostMigration(script) + .tryGetErrorMessage().equals("bad post")); + + } + + @Test + public void testClassNotFound() + { + final JavaMigrationStepExecutor javaMigrationStepExecutor = + new JavaMigrationStepExecutor(dataSource); + final Script script = + new Script("003To004.sql", + "-- JAVA ch.systemsx.cisd.dbmigration.MigrationStepFrom003To003"); + boolean exceptionThrown = false; + try + { + javaMigrationStepExecutor.tryPerformPreMigration(script); + } catch (final EnvironmentFailureException e) + { + exceptionThrown = true; + Assert.assertEquals(e.getMessage(), + "Class 'ch.systemsx.cisd.dbmigration.MigrationStepFrom003To003' not found."); + } + Assert.assertTrue(exceptionThrown); + + } + +} diff --git a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom001To002.java b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom001To002.java new file mode 100644 index 00000000000..f3d4f6051f3 --- /dev/null +++ b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom001To002.java @@ -0,0 +1,40 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import org.springframework.jdbc.core.JdbcTemplate; + +import ch.systemsx.cisd.common.exceptions.Status; + +/** + * @author Izabela Adamczyk + */ +public class MigrationStepFrom001To002 implements IMigrationStep +{ + + public Status performPostMigration(final JdbcTemplate jdbcTemplate) + { + + return Status.OK; + } + + public Status performPreMigration(final JdbcTemplate jdbcTemplate) + { + return Status.OK; + } + +} diff --git a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom002To003.java b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom002To003.java new file mode 100644 index 00000000000..0bb37c79bef --- /dev/null +++ b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/MigrationStepFrom002To003.java @@ -0,0 +1,40 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.dbmigration; + +import org.springframework.jdbc.core.JdbcTemplate; + +import ch.systemsx.cisd.common.exceptions.Status; + +/** + * @author Izabela Adamczyk + */ +public class MigrationStepFrom002To003 implements IMigrationStep +{ + + public Status performPostMigration(final JdbcTemplate jdbcTemplate) + { + + return Status.createError("bad post"); + } + + public Status performPreMigration(final JdbcTemplate jdbcTemplate) + { + return Status.createError("bad pre"); + } + +} -- GitLab