diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java index ed52e41..375c2fe 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java @@ -174,11 +174,10 @@ public int doRollbacks(JdbcMigrationContext context, int[] rollbackLevel, boolea throws SQLException, MigrationException { PatchInfoStore patchTable = createPatchStore(context); - + int executedPatchCount = 0; lockPatchStore(context); // Now apply the patches - int executedPatchCount = 0; try { @@ -206,7 +205,7 @@ public int doRollbacks(JdbcMigrationContext context, int[] rollbackLevel, boolea { // If there was any kind of error, we don't want to eat it, but we do // want to unlock the patch store. So do that, then re-throw. - patchTable.unlockPatchStore(); + unlockPatchStore(patchTable); throw me; } @@ -220,7 +219,7 @@ public int doRollbacks(JdbcMigrationContext context, int[] rollbackLevel, boolea { try { - patchTable.unlockPatchStore(); + unlockPatchStore(patchTable); } catch (MigrationException e) { @@ -473,11 +472,10 @@ public LinkedHashMap getContexts() protected int doMigrations(JdbcMigrationContext context) throws SQLException, MigrationException { PatchInfoStore patchTable = createPatchStore(context); - + int executedPatchCount = 0; lockPatchStore(context); // Now apply the patches - int executedPatchCount = 0; try { @@ -506,7 +504,7 @@ protected int doMigrations(JdbcMigrationContext context) throws SQLException, Mi { // If there was any kind of error, we don't want to eat it, but we do // want to unlock the patch store. So do that, then re-throw. - patchTable.unlockPatchStore(); + unlockPatchStore(patchTable); throw me; } @@ -520,7 +518,7 @@ protected int doMigrations(JdbcMigrationContext context) throws SQLException, Mi { try { - patchTable.unlockPatchStore(); + unlockPatchStore(patchTable); } catch (MigrationException e) { @@ -538,6 +536,11 @@ protected int doMigrations(JdbcMigrationContext context) throws SQLException, Mi */ private void lockPatchStore(JdbcMigrationContext context) throws MigrationException { + // Do not issue a lock and write to the database in ReadOnly mode + if (isReadOnly()) { + return; + } + // Patch locks ensure that only one system sharing a patch store will patch // it at the same time. boolean lockObtained = false; @@ -561,6 +564,21 @@ private void lockPatchStore(JdbcMigrationContext context) throws MigrationExcept } } + /** + * Unlock the patch store. + * + * @param patchInfoStore PatchInfoStore to unlock + */ + private void unlockPatchStore(PatchInfoStore patchInfoStore) throws MigrationException + { + // Do not issue an unlock and write to the database in ReadOnly mode + if (isReadOnly()) { + return; + } + + patchInfoStore.unlockPatchStore(); + } + /** * create a patch table object for use in migrations * @@ -590,7 +608,7 @@ private void waitForFreeLock(JdbcMigrationContext context) throws MigrationExcep if ((getLockPollRetries() != -1) && (i >= getLockPollRetries())) { log.info("Reached maximum lock poll retries (" + getLockPollRetries() + "), overriding patch lock"); - piStore.unlockPatchStore(); + unlockPatchStore(piStore); } else { diff --git a/src/test/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherTest.java b/src/test/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherTest.java index 1ee0673..47a82c4 100644 --- a/src/test/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherTest.java +++ b/src/test/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherTest.java @@ -34,6 +34,7 @@ import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; import static org.easymock.classextension.EasyMock.createControl; import static org.easymock.classextension.EasyMock.createStrictControl; @@ -66,6 +67,8 @@ public class JdbcMigrationLauncherTest extends MigrationListenerTestBase { private static final int[] ROLLBACK_LEVELS = new int[]{ROLLBACK_LEVEL}; private static final int ROLLBACK_EXPECTED = 5; private static final boolean FORCE_ROLLBACK = false; + private static final String MIGRATION = "migration"; + private static final String ROLLBACK = "rollback"; /** * constructor that takes a name @@ -124,6 +127,7 @@ protected void setUp() throws Exception { Connection connectionMock = rollbackMocksControl.createMock(Connection.class); //Dependency Interactions + expect(rollbackMigrationProcessMock.isReadOnly()).andReturn(false).anyTimes(); expect(patchInfoStoreMock.isPatchStoreLocked()).andReturn(false); expect(patchInfoStoreMock.getPatchLevel()).andReturn(3); patchInfoStoreMock.lockPatchStore(); @@ -487,4 +491,45 @@ public void testDoRollbacksActionWithoutForceRollbackParameter() throws Migratio rollbackMocksControl.verify(); } + private void runReadOnlyTest(String mode) throws MigrationException{ + IMocksControl mockControl = createControl(); + TestJdbcMigrationLauncher testLauncher = new TestJdbcMigrationLauncher(); + + PatchInfoStore patchStore = mockControl.createMock(PatchInfoStore.class); + expect(patchStore.getPatchLevel()).andReturn(ROLLBACK_LEVEL).anyTimes(); + MigrationProcess migrationProcess = mockControl.createMock(MigrationProcess.class); + expect(migrationProcess.doMigrations(patchStore, context)).andReturn(0).anyTimes(); + expect(migrationProcess.doRollbacks(patchStore, ROLLBACK_LEVELS, context, false)).andReturn(0).anyTimes(); + expect(migrationProcess.doPostPatchMigrations(context)).andReturn(0).anyTimes(); + migrationProcess.addListener(testLauncher); + expectLastCall().anyTimes(); + migrationProcess.addMigrationTaskSource((MigrationTaskSource) EasyMock.anyObject()); + expectLastCall().anyTimes(); + migrationProcess.setReadOnly(true); + expectLastCall(); + expect(migrationProcess.isReadOnly()).andReturn(true).anyTimes(); + mockControl.replay(); + + testLauncher.setMigrationProcess(migrationProcess); + testLauncher.addContext(context); + testLauncher.setPatchStore(patchStore); + testLauncher.getMigrationProcess().setReadOnly(true); + + if (mode.equals(MIGRATION)) { + testLauncher.doMigrations(); + } else if (mode.equals(ROLLBACK)) { + testLauncher.doRollbacks(ROLLBACK_LEVELS); + } else { + throw new MigrationException("Unsupported mode: " + mode); + } + mockControl.verify(); + } + + public void testDoMigrationsDoesNotUpdatePatchStoreInReadOnlyMode() throws MigrationException { + runReadOnlyTest(MIGRATION); + } + + public void testDoRollbacksDoesNotUpdatePatchStoreInReadOnlyMode() throws MigrationException { + runReadOnlyTest(ROLLBACK); + } }