├── .travis.yml ├── src ├── test │ ├── resources │ │ ├── com │ │ │ └── tacitknowledge │ │ │ │ └── util │ │ │ │ └── migration │ │ │ │ └── jdbc │ │ │ │ └── test │ │ │ │ ├── patch0003_third_patch.sql │ │ │ │ ├── patch0003-rollback_third_patch.sql │ │ │ │ ├── patch0001-rollback.sql │ │ │ │ ├── patch0002_second_patch.sql │ │ │ │ ├── patch0001.sql │ │ │ │ └── sybase_tsql.sql │ │ ├── log4j.properties │ │ └── migration.properties │ └── java │ │ └── com │ │ └── tacitknowledge │ │ └── util │ │ └── migration │ │ ├── tasks │ │ ├── normal │ │ │ ├── TestMigrationTask1.java │ │ │ ├── TestMigrationTask4.java │ │ │ ├── TestMigrationTask3.java │ │ │ └── TestMigrationTask2.java │ │ ├── post │ │ │ ├── TestPostMigrationTask1.java │ │ │ └── TestPostMigrationTask2.java │ │ ├── instantiation │ │ │ └── TestMigrationTaskInstantiationException.java │ │ ├── rollback │ │ │ ├── migrationtasks │ │ │ │ └── TestMigrationTaskRollback1.java │ │ │ ├── TestRollbackableTask3.java │ │ │ ├── TestRollbackableTask4.java │ │ │ ├── TestRollbackableTask5.java │ │ │ ├── TestRollbackableTask1.java │ │ │ ├── TestRollbackableTask2.java │ │ │ └── BaseTestRollbackableMigrationTask.java │ │ └── BaseTestMigrationTask.java │ │ ├── test │ │ └── listeners │ │ │ ├── TestListener2.java │ │ │ └── TestListener1.java │ │ ├── jdbc │ │ ├── TestDataSourceMigrationContext.java │ │ ├── TestAutoPatchService.java │ │ ├── TestJdbcMigrationLauncherFactory.java │ │ ├── TestDistributedAutoPatchService.java │ │ ├── MockDatabaseType.java │ │ ├── TestDistributedJdbcMigrationLauncherFactory.java │ │ ├── util │ │ │ ├── SybaseUtilTest.java │ │ │ ├── MigrationUtilTest.java │ │ │ └── ConnectionWrapperDataSourceTest.java │ │ ├── StandaloneMigrationLauncherTest.java │ │ ├── loader │ │ │ ├── NameParseTest.java │ │ │ └── QueryTest.java │ │ ├── DataSourceMigrationContextTest.java │ │ ├── SqlScriptMigrationTaskSourceTest.java │ │ └── TestDistributedJdbcMigrationLauncher.java │ │ ├── TestMigrationContext.java │ │ ├── ClassMigrationTaskSourceTest.java │ │ ├── builders │ │ └── MockBuilder.java │ │ └── PatchRollbackPredicateTest.java ├── integration-test │ ├── resources │ │ ├── com │ │ │ └── tacitknowledge │ │ │ │ └── util │ │ │ │ └── migration │ │ │ │ └── inttest-tasks │ │ │ │ ├── catalog │ │ │ │ ├── patch00004-rollback_patch04.sql │ │ │ │ └── patch00004_patch04.sql │ │ │ │ ├── missingpatchstrategy │ │ │ │ ├── batch2 │ │ │ │ │ ├── patch00002-rollback_patch02.sql │ │ │ │ │ ├── patch00003-rollback_patch03.sql │ │ │ │ │ ├── patch00003_patch03.sql │ │ │ │ │ └── patch00002_patch02.sql │ │ │ │ └── batch1 │ │ │ │ │ ├── patch00001_patch01.sql │ │ │ │ │ └── patch00004_patch04.sql │ │ │ │ ├── race │ │ │ │ ├── patch00002_insert_into_race.sql │ │ │ │ ├── patch00003_insert_into_race_2.sql │ │ │ │ ├── patch00004_insert_into_race_3.sql │ │ │ │ └── patch00001_create_race_table.sql │ │ │ │ ├── core │ │ │ │ └── patch00002_patch02.sql │ │ │ │ └── order │ │ │ │ ├── patch00001_patch01.sql │ │ │ │ └── patch00003_patch03.sql │ │ ├── multiserver-inttest-migration.properties │ │ ├── log4j.properties │ │ ├── missingpatchstrategybatch3-inttest-migration.properties │ │ ├── missingpatchstrategybatch4-inttest-migration.properties │ │ ├── missingpatchstrategybatch2-inttest-migration.properties │ │ ├── missingpatchstrategybatch1-inttest-migration.properties │ │ ├── inttest-migration.properties │ │ └── node-added-inttest-migration.properties │ └── java │ │ └── com │ │ └── tacitknowledge │ │ └── util │ │ └── migration │ │ ├── listeners │ │ └── WhinyMigrationListener.java │ │ └── MultiServerRaceConditionTest.java └── main │ ├── java │ └── com │ │ └── tacitknowledge │ │ └── util │ │ └── migration │ │ ├── MigrationTaskSource.java │ │ ├── MigrationException.java │ │ ├── MigrationContext.java │ │ ├── MigrationTask.java │ │ ├── AutopatchRegistry.java │ │ ├── MigrationRunnerFactory.java │ │ ├── AbstractMigrationListener.java │ │ ├── RollbackableMigrationTask.java │ │ ├── MigrationRunnerStrategy.java │ │ ├── jdbc │ │ ├── JdbcMigrationContext.java │ │ ├── JdbcMigrationLauncherFactoryLoader.java │ │ ├── loader │ │ │ ├── FileLoadingUtility.java │ │ │ ├── ExcelFileLoader.java │ │ │ └── FlatXmlDataSetTaskSource.java │ │ ├── DistributedJdbcMigrationLauncher.java │ │ ├── util │ │ │ ├── MigrationUtil.java │ │ │ └── ConnectionWrapperDataSource.java │ │ ├── DistributedStandaloneMigrationLauncher.java │ │ └── DistributedMigrationTableUnlock.java │ │ ├── MigrationListener.java │ │ ├── PatchRollbackPredicate.java │ │ ├── RollbackListener.java │ │ ├── MissingPatchMigrationRunnerStrategy.java │ │ ├── ClassMigrationTaskSource.java │ │ ├── PatchInfoStore.java │ │ ├── MigrationTaskSupport.java │ │ └── OrderedMigrationRunnerStrategy.java │ └── resources │ └── com │ └── tacitknowledge │ └── util │ └── migration │ └── jdbc │ ├── hsqldb.properties │ ├── sqlserver.properties │ ├── postgres.properties │ ├── oracle.properties │ ├── mysql.properties │ └── sybase.properties ├── examples ├── distributed-sample │ ├── db │ │ ├── sql │ │ │ ├── app1 │ │ │ │ └── patch00002.sql │ │ │ ├── app2 │ │ │ │ ├── patch00001.sql │ │ │ │ └── patch00003.sql │ │ │ └── app3 │ │ │ │ └── patch00004.sql │ │ └── data │ │ │ └── app1 │ │ │ └── patch00005_initial_data.xml │ ├── lib │ │ └── hsqldb.jar │ ├── var │ │ ├── app1.script │ │ ├── app2.script │ │ ├── app3 │ │ │ ├── system1.script │ │ │ ├── system2.script │ │ │ ├── system1.properties │ │ │ └── system2.properties │ │ ├── orchestrator.script │ │ ├── app1.properties │ │ ├── app2.properties │ │ └── orchestrator.properties │ ├── conf │ │ ├── log4j.properties │ │ └── migration.properties │ └── build.xml └── MSSQL-example.txt ├── .gitignore ├── maven-eclipse.xml ├── .mailmap ├── migration.properties └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | - oraclejdk7 5 | - openjdk6 6 | 7 | -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/patch0003_third_patch.sql: -------------------------------------------------------------------------------- 1 | select * from dual; 2 | select * from dual; -------------------------------------------------------------------------------- /examples/distributed-sample/db/sql/app1/patch00002.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE baz ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); -------------------------------------------------------------------------------- /examples/distributed-sample/db/sql/app2/patch00001.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/patch0003-rollback_third_patch.sql: -------------------------------------------------------------------------------- 1 | select * from dual; 2 | select * from dual; -------------------------------------------------------------------------------- /examples/distributed-sample/lib/hsqldb.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tacitknowledge/autopatch/HEAD/examples/distributed-sample/lib/hsqldb.jar -------------------------------------------------------------------------------- /examples/distributed-sample/var/app1.script: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA PUBLIC AUTHORIZATION DBA 2 | CREATE USER SA PASSWORD "" 3 | GRANT DBA TO SA 4 | SET WRITE_DELAY 10 5 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app2.script: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA PUBLIC AUTHORIZATION DBA 2 | CREATE USER SA PASSWORD "" 3 | GRANT DBA TO SA 4 | SET WRITE_DELAY 10 5 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/catalog/patch00004-rollback_patch04.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM catalog_table_1 where id = 1; -------------------------------------------------------------------------------- /examples/distributed-sample/var/app3/system1.script: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA PUBLIC AUTHORIZATION DBA 2 | CREATE USER SA PASSWORD "" 3 | GRANT DBA TO SA 4 | SET WRITE_DELAY 10 5 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app3/system2.script: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA PUBLIC AUTHORIZATION DBA 2 | CREATE USER SA PASSWORD "" 3 | GRANT DBA TO SA 4 | SET WRITE_DELAY 10 5 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/orchestrator.script: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA PUBLIC AUTHORIZATION DBA 2 | CREATE USER SA PASSWORD "" 3 | GRANT DBA TO SA 4 | SET WRITE_DELAY 10 5 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch2/patch00002-rollback_patch02.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE core_table_1; 2 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch2/patch00003-rollback_patch03.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE order_table_2; 2 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/race/patch00002_insert_into_race.sql: -------------------------------------------------------------------------------- 1 | insert into race (key_name, field_value) values ('count', 'One'); 2 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/race/patch00003_insert_into_race_2.sql: -------------------------------------------------------------------------------- 1 | insert into race (key_name, field_value) values ('count', 'Two'); 2 | -------------------------------------------------------------------------------- /examples/distributed-sample/db/sql/app2/patch00003.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE bar ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO bar(id, value) VALUES (1, 'foo'); -------------------------------------------------------------------------------- /examples/distributed-sample/db/sql/app3/patch00004.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE baz ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO baz(id, value) VALUES (1, 'foo'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/race/patch00004_insert_into_race_3.sql: -------------------------------------------------------------------------------- 1 | insert into race (key_name, field_value) values ('count', 'Three'); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .checkstyle 2 | .project 3 | .clover 4 | .settings 5 | .classpath 6 | bin 7 | build 8 | dist 9 | target 10 | *.iml 11 | *.ipr 12 | *.iws 13 | .idea 14 | *.externalToolBuilders 15 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/core/patch00002_patch02.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE core_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | 7 | INSERT INTO core_table_1 (id, value) VALUES (1, 'core_table_1'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/order/patch00001_patch01.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE order_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO order_table_1 (id, value) VALUES (1, 'order_table_1'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/order/patch00003_patch03.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE order_table_2 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO order_table_2 (id, value) VALUES (1, 'order_table_2'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/catalog/patch00004_patch04.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE catalog_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO catalog_table_1 (id, value) VALUES (1, 'catalog_table_1'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/race/patch00001_create_race_table.sql: -------------------------------------------------------------------------------- 1 | create table race ( 2 | key_name varchar(20) not null, 3 | field_value varchar(40) not null, 4 | constraint race_uk unique (key_name, field_value) 5 | ); 6 | -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/patch0001-rollback.sql: -------------------------------------------------------------------------------- 1 | delete from user_role_assoc where application_user_id = '2'; 2 | delete from user_role_assoc where application_user_id = '3'; 3 | delete from user_role_assoc where application_user_id = '4'; 4 | -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch1/patch00001_patch01.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE order_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO order_table_1 (id, value) VALUES (1, 'order_table_1'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch2/patch00003_patch03.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE order_table_2 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO order_table_2 (id, value) VALUES (1, 'order_table_2'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch2/patch00002_patch02.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE core_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | 7 | INSERT INTO core_table_1 (id, value) VALUES (1, 'core_table_1'); -------------------------------------------------------------------------------- /src/integration-test/resources/com/tacitknowledge/util/migration/inttest-tasks/missingpatchstrategy/batch1/patch00004_patch04.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE catalog_table_1 ( 2 | id INT NOT NULL PRIMARY KEY, 3 | value VARCHAR(256) 4 | ); 5 | 6 | INSERT INTO catalog_table_1 (id, value) VALUES (1, 'catalog_table_1'); -------------------------------------------------------------------------------- /examples/distributed-sample/db/data/app1/patch00005_initial_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/patch0002_second_patch.sql: -------------------------------------------------------------------------------- 1 | -- Note that the end of this statement does *not* have a semi-colon; this should 2 | -- be OK since there's only one script in the file 3 | insert into user_role_assoc (user_role_id, application_user_id, role_code, project_id) 4 | values (nextval('role_id_seq'),2, 'SYSA', 3) -------------------------------------------------------------------------------- /maven-eclipse.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/integration-test/resources/multiserver-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Configure a context named "race" 3 | # 4 | race.jdbc.database.type=hsqldb 5 | race.jdbc.driver=org.hsqldb.jdbcDriver 6 | race.jdbc.url=jdbc:hsqldb:mem:race 7 | race.jdbc.username=sa 8 | race.jdbc.password= 9 | race.patch.path=com.tacitknowledge.util.migration.inttest-tasks.race 10 | race.lockPollMillis=3000 11 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG for lots of output 2 | log4j.rootLogger=INFO, CONSOLE 3 | 4 | # If you would like to have output simply be on the console, set the 5 | # rootLogger to CONSOLE 6 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 7 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.CONSOLE.layout.ConversionPattern=%d %-4r [%t] %-5p %c{1} %x - %m%n 9 | -------------------------------------------------------------------------------- /examples/distributed-sample/conf/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG for lots of output 2 | log4j.rootLogger=DEBUG, CONSOLE 3 | 4 | # If you would like to have output simply be on the console, set the 5 | # rootLogger to CONSOLE 6 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 7 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.CONSOLE.layout.ConversionPattern=%d %-4r [%t] %-5p %c{1} %x - %m%n 9 | -------------------------------------------------------------------------------- /src/integration-test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG for lots of output 2 | log4j.rootLogger=INFO, CONSOLE 3 | 4 | # If you would like to have output simply be on the console, set the 5 | # rootLogger to CONSOLE 6 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 7 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.CONSOLE.layout.ConversionPattern=%d %-4r [%t] %-5p %c{1} %x - %m%n 9 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app1.properties: -------------------------------------------------------------------------------- 1 | #HSQL Database Engine 1.8.0.7 2 | #Wed Feb 21 16:08:47 PST 2007 3 | hsqldb.script_format=0 4 | runtime.gc_interval=0 5 | sql.enforce_strict_size=false 6 | hsqldb.cache_size_scale=8 7 | readonly=false 8 | hsqldb.nio_data_file=true 9 | hsqldb.cache_scale=14 10 | version=1.8.0 11 | hsqldb.default_table_type=memory 12 | hsqldb.cache_file_scale=1 13 | hsqldb.log_size=200 14 | modified=yes 15 | hsqldb.cache_version=1.7.0 16 | hsqldb.original_version=1.8.0 17 | hsqldb.compatible_version=1.8.0 18 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app2.properties: -------------------------------------------------------------------------------- 1 | #HSQL Database Engine 1.8.0.7 2 | #Wed Feb 21 16:08:47 PST 2007 3 | hsqldb.script_format=0 4 | runtime.gc_interval=0 5 | sql.enforce_strict_size=false 6 | hsqldb.cache_size_scale=8 7 | readonly=false 8 | hsqldb.nio_data_file=true 9 | hsqldb.cache_scale=14 10 | version=1.8.0 11 | hsqldb.default_table_type=memory 12 | hsqldb.cache_file_scale=1 13 | hsqldb.log_size=200 14 | modified=yes 15 | hsqldb.cache_version=1.7.0 16 | hsqldb.original_version=1.8.0 17 | hsqldb.compatible_version=1.8.0 18 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app3/system1.properties: -------------------------------------------------------------------------------- 1 | #HSQL Database Engine 1.8.0.7 2 | #Wed Feb 21 16:08:47 PST 2007 3 | hsqldb.script_format=0 4 | runtime.gc_interval=0 5 | sql.enforce_strict_size=false 6 | hsqldb.cache_size_scale=8 7 | readonly=false 8 | hsqldb.nio_data_file=true 9 | hsqldb.cache_scale=14 10 | version=1.8.0 11 | hsqldb.default_table_type=memory 12 | hsqldb.cache_file_scale=1 13 | hsqldb.log_size=200 14 | modified=yes 15 | hsqldb.cache_version=1.7.0 16 | hsqldb.original_version=1.8.0 17 | hsqldb.compatible_version=1.8.0 18 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/app3/system2.properties: -------------------------------------------------------------------------------- 1 | #HSQL Database Engine 1.8.0.7 2 | #Wed Feb 21 16:08:47 PST 2007 3 | hsqldb.script_format=0 4 | runtime.gc_interval=0 5 | sql.enforce_strict_size=false 6 | hsqldb.cache_size_scale=8 7 | readonly=false 8 | hsqldb.nio_data_file=true 9 | hsqldb.cache_scale=14 10 | version=1.8.0 11 | hsqldb.default_table_type=memory 12 | hsqldb.cache_file_scale=1 13 | hsqldb.log_size=200 14 | modified=yes 15 | hsqldb.cache_version=1.7.0 16 | hsqldb.original_version=1.8.0 17 | hsqldb.compatible_version=1.8.0 18 | -------------------------------------------------------------------------------- /examples/distributed-sample/var/orchestrator.properties: -------------------------------------------------------------------------------- 1 | #HSQL Database Engine 1.8.0.7 2 | #Wed Mar 07 11:47:15 PST 2007 3 | hsqldb.script_format=0 4 | runtime.gc_interval=0 5 | sql.enforce_strict_size=false 6 | hsqldb.cache_size_scale=8 7 | readonly=false 8 | hsqldb.nio_data_file=true 9 | hsqldb.cache_scale=14 10 | version=1.8.0 11 | hsqldb.default_table_type=memory 12 | hsqldb.cache_file_scale=1 13 | hsqldb.log_size=200 14 | modified=yes 15 | hsqldb.cache_version=1.7.0 16 | hsqldb.original_version=1.8.0 17 | hsqldb.compatible_version=1.8.0 18 | -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/patch0001.sql: -------------------------------------------------------------------------------- 1 | 2 | insert into user_role_assoc (user_role_id, application_user_id, role_code, project_id) 3 | values (nextval('role_id_seq'),2, 'SYSA', 3); 4 | 5 | // Testing 6 | insert into user_role_assoc (user_role_id, application_user_id, role_code, project_id) 7 | values (nextval('role_id_seq'),3, 'SYSA', 3); 8 | 9 | -- This is a comment 10 | insert into user_role_assoc (user_role_id, application_user_id, role_code, project_id) 11 | values (nextval('role_--id_seq;'),4, 'SYSA', 3); 12 | 13 | -------------------------------------------------------------------------------- /src/integration-test/resources/missingpatchstrategybatch3-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Orders. 3 | # 4 | migration.strategy=com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy 5 | 6 | 7 | # 8 | # Configure a context named nodes, and make it a multi-node context 9 | # 10 | nodes.context=nodes 11 | nodes.controlled.systems=nodes 12 | nodes.jdbc.database.type=hsqldb 13 | nodes.jdbc.driver=org.hsqldb.jdbcDriver 14 | nodes.jdbc.url=jdbc:hsqldb:mem:nodes 15 | nodes.jdbc.username=sa 16 | nodes.jdbc.password= 17 | nodes.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch1 18 | 19 | nodes.jdbc.systems=jdbcnode3 20 | nodes.jdbcnode3.database.type=hsqldb 21 | nodes.jdbcnode3.driver=org.hsqldb.jdbcDriver 22 | nodes.jdbcnode3.url=jdbc:hsqldb:mem:node3 23 | nodes.jdbcnode3.username=sa 24 | nodes.jdbcnode3.password= 25 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Mike Hardy mikehardy 2 | Mike Hardy mike 3 | Scott Askew Scott Askew 4 | Scott Askew scott 5 | Scott Askew saskew 6 | Alex Soto alex 7 | Alex Soto asoto 8 | Marques Lee marquesfarques 9 | Artie Pesh-Imam apeshimam 10 | Vladimir Pertu vpertu 11 | Vladimir Pertu woffca 12 | Chris Andrasick chrisa 13 | Ian Mortimer imorti 14 | Paddy Atherstone atherstone 15 | Stephanie Blair sblair 16 | Vladislav Gangan vgangantk 17 | John R. Jackson jrjackso 18 | -------------------------------------------------------------------------------- /src/test/resources/com/tacitknowledge/util/migration/jdbc/test/sybase_tsql.sql: -------------------------------------------------------------------------------- 1 | /* just some sane sql at first */ 2 | 3 | 4 | PRINT 'Creating photo table' 5 | go 6 | create table photo 7 | ( 8 | id numeric(14,0) NOT NULL, 9 | ownerid numeric(14,0) NOT NULL, 10 | date_created datetime NOT NULL, 11 | status tinyint NULL 12 | ) 13 | lock ALLPAGES 14 | go 15 | 16 | create clustered index c_ownerid_status_date on 17 | photos(ownerid,status,date_created) 18 | go 19 | 20 | alter table photo add version default 0 21 | go 22 | 23 | create unique nonclustered index unc_id on 24 | photo(id) 25 | go 26 | 27 | create nonclustered index nc_ownerid on 28 | photo(ownerid) 29 | Go 30 | 31 | grant delete,insert,select,update on photo to user 32 | go 33 | 34 | /* will this table name screw up the parser looking for GO delimiter :)? */ 35 | create table gogo 36 | ( 37 | id numeric(14,0) NOT NULL 38 | value varchar(32) 39 | ) 40 | GO -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask1.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.normal; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * Basic test migration task. 22 | * 23 | * @author Scott Askew (scott@tacitknowledge.com) 24 | */ 25 | public class TestMigrationTask1 extends BaseTestMigrationTask 26 | { 27 | /** 28 | * Creates a new TestMigrationTask1. 29 | */ 30 | public TestMigrationTask1() 31 | { 32 | super("TestTask1", 4); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask4.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.normal; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * Basic test migration task. 22 | * 23 | * @author Scott Askew (scott@tacitknowledge.com) 24 | */ 25 | public class TestMigrationTask4 extends BaseTestMigrationTask 26 | { 27 | /** 28 | * Creates a new TestMigrationTask3. 29 | */ 30 | public TestMigrationTask4() 31 | { 32 | super("TestTask4", 7); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/test/listeners/TestListener2.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.test.listeners; 17 | 18 | import java.util.Properties; 19 | 20 | import com.tacitknowledge.util.migration.AbstractMigrationListener; 21 | import com.tacitknowledge.util.migration.MigrationException; 22 | 23 | /** 24 | * @author Alex Soto (apsoto@gmail.com) 25 | */ 26 | public class TestListener2 extends AbstractMigrationListener 27 | { 28 | 29 | public void initialize(String systemName, Properties properties) throws MigrationException 30 | { 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/post/TestPostMigrationTask1.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.post; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * Basic test post migration task. 22 | * 23 | * @author Mike Hardy (mike@tacitknowledge.com) 24 | */ 25 | public class TestPostMigrationTask1 extends BaseTestMigrationTask 26 | { 27 | /** 28 | * Creates a new TestMigrationTask1. 29 | */ 30 | public TestPostMigrationTask1() 31 | { 32 | super("TestPostTask1", 1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/post/TestPostMigrationTask2.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.post; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * Basic test post migration task. 22 | * 23 | * @author Mike Hardy (mike@tacitknowledge.com) 24 | */ 25 | public class TestPostMigrationTask2 extends BaseTestMigrationTask 26 | { 27 | /** 28 | * Creates a new TestMigrationTask2. 29 | */ 30 | public TestPostMigrationTask2() 31 | { 32 | super("TestPostTask2", 2); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/integration-test/resources/missingpatchstrategybatch4-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Orders. 3 | # 4 | migration.strategy=com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy 5 | 6 | # 7 | # Configure a context named nodes, and make it a multi-node context 8 | # 9 | nodes.context=nodes 10 | nodes.controlled.systems=nodes 11 | nodes.jdbc.database.type=hsqldb 12 | nodes.jdbc.driver=org.hsqldb.jdbcDriver 13 | nodes.jdbc.url=jdbc:hsqldb:mem:nodes 14 | nodes.jdbc.username=sa 15 | nodes.jdbc.password= 16 | nodes.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch2 17 | 18 | nodes.jdbc.systems=jdbcnode1,jdbcnode2,jdbcnode3 19 | nodes.jdbcnode1.database.type=hsqldb 20 | nodes.jdbcnode1.driver=org.hsqldb.jdbcDriver 21 | nodes.jdbcnode1.url=jdbc:hsqldb:mem:node1 22 | nodes.jdbcnode1.username=sa 23 | nodes.jdbcnode1.password= 24 | nodes.jdbcnode2.database.type=hsqldb 25 | nodes.jdbcnode2.driver=org.hsqldb.jdbcDriver 26 | nodes.jdbcnode2.url=jdbc:hsqldb:mem:node2 27 | nodes.jdbcnode2.username=sa 28 | nodes.jdbcnode2.password= 29 | nodes.jdbcnode3.database.type=hsqldb 30 | nodes.jdbcnode3.driver=org.hsqldb.jdbcDriver 31 | nodes.jdbcnode3.url=jdbc:hsqldb:mem:node3 32 | nodes.jdbcnode3.username=sa 33 | nodes.jdbcnode3.password= -------------------------------------------------------------------------------- /examples/distributed-sample/conf/migration.properties: -------------------------------------------------------------------------------- 1 | distributed-sample.context=orchestrator 2 | distributed-sample.controlled.systems=app1,app2,app3 3 | 4 | orchestrator.jdbc.database.type=hsqldb 5 | orchestrator.jdbc.driver=org.hsqldb.jdbcDriver 6 | orchestrator.jdbc.url=jdbc:hsqldb:file:./var/orchestrator 7 | orchestrator.jdbc.username=sa 8 | orchestrator.jdbc.password= 9 | orchestrator.patch.path=orchestrator:example.orchestrator.db.patch 10 | 11 | 12 | app1.jdbc.database.type=hsqldb 13 | app1.jdbc.driver=org.hsqldb.jdbcDriver 14 | app1.jdbc.url=jdbc:hsqldb:file:./var/app1 15 | app1.jdbc.username=sa 16 | app1.jdbc.password= 17 | app1.patch.path=app1:example.app1.db.patch 18 | 19 | app2.jdbc.database.type=hsqldb 20 | app2.jdbc.driver=org.hsqldb.jdbcDriver 21 | app2.jdbc.url=jdbc:hsqldb:file:./var/app2 22 | app2.jdbc.username=sa 23 | app2.jdbc.password= 24 | app2.patch.path=app2:example.app2.db.patch 25 | 26 | app3.jdbc.systems=jdbc-system1,jdbc-system2 27 | app3.jdbc-system1.database.type=hsqldb 28 | app3.jdbc-system1.driver=org.hsqldb.jdbcDriver 29 | app3.jdbc-system1.url=jdbc:hsqldb:file:./var/app3/system1 30 | app3.jdbc-system1.username=sa 31 | app3.jdbc-system1.password= 32 | app3.jdbc-system2.database.type=hsqldb 33 | app3.jdbc-system2.driver=org.hsqldb.jdbcDriver 34 | app3.jdbc-system2.url=jdbc:hsqldb:file:./var/app3/system2 35 | app3.jdbc-system2.username=sa 36 | app3.jdbc-system2.password= 37 | app3.patch.path=app3:example.app3.db.patch -------------------------------------------------------------------------------- /migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? 3 | # 4 | orchestration.context=core 5 | orchestration.controlled.systems=core,orders,catalog 6 | orchestration.lockPollRetries=10 7 | # 8 | # Configure a context named "core" 9 | # 10 | core.jdbc.database.type=postgres 11 | core.jdbc.driver=org.postgresql.Driver 12 | core.jdbc.url=jdbc:postgresql://localhost/core 13 | core.jdbc.username=core 14 | core.jdbc.password=password 15 | core.patch.path=patches.core 16 | # 17 | # Configure a context named "orders" 18 | # 19 | orders.jdbc.database.type=postgres 20 | orders.jdbc.driver=org.postgresql.Driver 21 | orders.jdbc.url=jdbc:postgresql://localhost/orders 22 | orders.jdbc.username=orders 23 | orders.jdbc.password=password 24 | orders.patch.path=patches.orders 25 | # 26 | # Configure a context named catalog 27 | # 28 | catalog.jdbc.database.type=postgres 29 | catalog.jdbc.driver=org.postgresql.Driver 30 | catalog.jdbc.url=jdbc:postgresql://localhost/catalog 31 | catalog.jdbc.username=catalog 32 | catalog.jdbc.password=password 33 | catalog.patch.path=patches.catalog 34 | # 35 | # Configure a spikebook context 36 | # 37 | spikebook.jdbc.database.type=postgres 38 | spikebook.jdbc.driver=org.postgresql.Driver 39 | spikebook.jdbc.url=jdbc:postgresql://localhost/spikebook 40 | spikebook.jdbc.username=spikebook 41 | spikebook.jdbc.password=dbspikeb00k 42 | spikebook.patch.path=patches:com.tacitknowledge.spikebook.patches 43 | -------------------------------------------------------------------------------- /src/integration-test/resources/missingpatchstrategybatch2-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Orders. 3 | # 4 | migration.strategy=com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy 5 | 6 | # 7 | # Configure a context named "orders" 8 | # 9 | orders.jdbc.database.type=hsqldb 10 | orders.jdbc.driver=org.hsqldb.jdbcDriver 11 | orders.jdbc.url=jdbc:hsqldb:mem:orders 12 | orders.jdbc.username=sa 13 | orders.jdbc.password= 14 | orders.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch2 15 | 16 | # 17 | # Configure a context named nodes, and make it a multi-node context 18 | # 19 | nodes.context=nodes 20 | nodes.controlled.systems=nodes 21 | nodes.jdbc.database.type=hsqldb 22 | nodes.jdbc.driver=org.hsqldb.jdbcDriver 23 | nodes.jdbc.url=jdbc:hsqldb:mem:nodes 24 | nodes.jdbc.username=sa 25 | nodes.jdbc.password= 26 | nodes.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch2 27 | 28 | nodes.jdbc.systems=jdbcnode1,jdbcnode2 29 | nodes.jdbcnode1.database.type=hsqldb 30 | nodes.jdbcnode1.driver=org.hsqldb.jdbcDriver 31 | nodes.jdbcnode1.url=jdbc:hsqldb:mem:node1 32 | nodes.jdbcnode1.username=sa 33 | nodes.jdbcnode1.password= 34 | nodes.jdbcnode2.database.type=hsqldb 35 | nodes.jdbcnode2.driver=org.hsqldb.jdbcDriver 36 | nodes.jdbcnode2.url=jdbc:hsqldb:mem:node2 37 | nodes.jdbcnode2.username=sa 38 | nodes.jdbcnode2.password= -------------------------------------------------------------------------------- /src/integration-test/resources/missingpatchstrategybatch1-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Orders. 3 | # 4 | migration.strategy=com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy 5 | 6 | # 7 | # Configure a context named "orders" 8 | # 9 | orders.jdbc.database.type=hsqldb 10 | orders.jdbc.driver=org.hsqldb.jdbcDriver 11 | orders.jdbc.url=jdbc:hsqldb:mem:orders 12 | orders.jdbc.username=sa 13 | orders.jdbc.password= 14 | orders.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch1 15 | 16 | # 17 | # Configure a context named nodes, and make it a multi-node context 18 | # 19 | nodes.context=nodes 20 | nodes.controlled.systems=nodes 21 | nodes.jdbc.database.type=hsqldb 22 | nodes.jdbc.driver=org.hsqldb.jdbcDriver 23 | nodes.jdbc.url=jdbc:hsqldb:mem:nodes 24 | nodes.jdbc.username=sa 25 | nodes.jdbc.password= 26 | nodes.patch.path=com.tacitknowledge.util.migration.inttest-tasks.missingpatchstrategy.batch1 27 | 28 | nodes.jdbc.systems=jdbcnode1,jdbcnode2 29 | nodes.jdbcnode1.database.type=hsqldb 30 | nodes.jdbcnode1.driver=org.hsqldb.jdbcDriver 31 | nodes.jdbcnode1.url=jdbc:hsqldb:mem:node1 32 | nodes.jdbcnode1.username=sa 33 | nodes.jdbcnode1.password= 34 | nodes.jdbcnode2.database.type=hsqldb 35 | nodes.jdbcnode2.driver=org.hsqldb.jdbcDriver 36 | nodes.jdbcnode2.url=jdbc:hsqldb:mem:node2 37 | nodes.jdbcnode2.username=sa 38 | nodes.jdbcnode2.password= 39 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestDataSourceMigrationContext.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | /** 24 | * A DataSourceMigrationContext that doesn't actually talk to a database. 25 | * 26 | * @author Mike Hardy (mike@tacitknowledge.com) 27 | */ 28 | public class TestDataSourceMigrationContext extends TestMigrationContext 29 | { 30 | /** 31 | * Always returns null, doesn't talk to a database 32 | * 33 | * @return null every time 34 | * @exception SQLException never throws 35 | */ 36 | public Connection getConnection() throws SQLException 37 | { 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationTaskSource.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * A source of MigrationTasks. 22 | * 23 | * @author Scott Askew (scott@tacitknowledge.com) 24 | */ 25 | public interface MigrationTaskSource 26 | { 27 | /** 28 | * Returns a list of MigrationTasks that are in the given 29 | * package. 30 | * 31 | * @param packageName to package to search for migration tasks 32 | * @return a list of migration tasks; if not tasks were found, then an empty 33 | * list must be returned. 34 | * @throws MigrationException if an unrecoverable error occurs 35 | */ 36 | public List getMigrationTasks(String packageName) throws MigrationException; 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/instantiation/TestMigrationTaskInstantiationException.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.instantiation; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * 22 | * 23 | * @author Mike Hardy (mike@tacitknowledge.com) 24 | */ 25 | public class TestMigrationTaskInstantiationException extends BaseTestMigrationTask 26 | { 27 | /** 28 | * Constructor throws an exception - this task never works 29 | * 30 | * @exception RuntimeException when instantiated 31 | */ 32 | public TestMigrationTaskInstantiationException() throws RuntimeException 33 | { 34 | super("TestMigrationTaskInstantiationException", 1); 35 | throw new RuntimeException("This class always throws exceptions when instantiated"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestAutoPatchService.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | /** 19 | * Overrides methods in the normal launcher factory that make it difficult to test 20 | * 21 | * @author Mike Hardy (mike@tacitknowledge.com) 22 | */ 23 | public class TestAutoPatchService extends AutoPatchService 24 | { 25 | /** 26 | * Returns TestDataSourceMigrationContext 27 | * 28 | * @return TestDataSourceMigrationContext 29 | */ 30 | public DataSourceMigrationContext getDataSourceMigrationContext() 31 | { 32 | return new TestDataSourceMigrationContext(); 33 | } 34 | 35 | /** 36 | * Returns a TestJdbcMigrationLauncher 37 | * 38 | * @return TestJdbcMigrationLauncher 39 | */ 40 | public JdbcMigrationLauncher getJdbcMigrationLauncher() 41 | { 42 | return new TestJdbcMigrationLauncher(); 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/resources/migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Core. 3 | # 4 | orchestration.context=core 5 | orchestration.controlled.systems=core,orders,catalog 6 | # 7 | # Configure a context named "core" 8 | # 9 | core.jdbc.database.type=hsqldb 10 | core.jdbc.driver=org.hsqldb.jdbcDriver 11 | core.jdbc.url=jdbc:hsqldb:mem:core 12 | core.jdbc.username=sa 13 | core.jdbc.password= 14 | core.patch.path=patches.core:com.tacitknowledge.util.migration.jdbc.test 15 | core.lockPollRetries=10 16 | # 17 | # Configure a context named "orders" 18 | # 19 | orders.jdbc.database.type=hsqldb 20 | orders.jdbc.driver=org.hsqldb.jdbcDriver 21 | orders.jdbc.url=jdbc:hsqldb:mem:orders 22 | orders.jdbc.username=sa 23 | orders.jdbc.password= 24 | orders.patch.path=patches.orders:com.tacitknowledge.util.migration.tasks.normal 25 | # 26 | # Configure a context named catalog, and make it a multi-node context 27 | # 28 | catalog.jdbc.systems=jdbccatalog1,jdbccatalog2 29 | catalog.jdbccatalog1.database.type=sybase 30 | catalog.jdbccatalog1.driver=org.hsqldb.jdbcDriver 31 | catalog.jdbccatalog1.url=jdbc:hsqldb:mem:catalog1 32 | catalog.jdbccatalog1.username=sa 33 | catalog.jdbccatalog1.password= 34 | catalog.jdbccatalog2.database.type=postgres 35 | catalog.jdbccatalog2.driver=org.hsqldb.jdbcDriver 36 | catalog.jdbccatalog2.url=jdbc:hsqldb:mem:catalog2 37 | catalog.jdbccatalog2.username=sa 38 | catalog.jdbccatalog2.password= 39 | catalog.patch.path=patches.catalog 40 | 41 | 42 | # 43 | # for databaseType.properties override tests 44 | # 45 | mysql.supportsMultipleStatements=false -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationException.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | /** 19 | * An exception we can use to type things as we send problems up 20 | * 21 | * @author Scott Askew (scott@tacitknowledge.com) 22 | */ 23 | public class MigrationException extends Exception 24 | { 25 | /** 26 | * Make a new MigrationException with the given message 27 | * 28 | * @param message the message to include in the exception 29 | */ 30 | public MigrationException(String message) 31 | { 32 | super(message); 33 | } 34 | 35 | /** 36 | * Make a new MigrationException with the given message and cause 37 | * 38 | * @param message the message to include in the exception 39 | * @param cause the cause of the problem 40 | */ 41 | public MigrationException(String message, Throwable cause) 42 | { 43 | super(message, cause); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/hsqldb.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=false 2 | 3 | patches.create=CREATE TABLE patches ( \ 4 | system_name VARCHAR(30) NOT NULL \ 5 | , patch_level INT NOT NULL \ 6 | , patch_date TIMESTAMP \ 7 | , patch_in_progress CHAR(1) default 'F' NOT NULL \ 8 | , PRIMARY KEY (system_name, patch_level)) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0) 12 | level.table.exists=SELECT patch_level FROM patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ? 14 | level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO patches (patch_level, system_name, patch_date) VALUES ( ?, ?, NOW()) 16 | level.exists=SELECT patch_level FROM patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? ) 24 | lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' 25 | -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/sqlserver.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=false 2 | 3 | patches.create=CREATE TABLE patches ( \ 4 | system_name VARCHAR(30) NOT NULL \ 5 | , patch_level INT NOT NULL \ 6 | , patch_date DATETIME \ 7 | , patch_in_progress CHAR(1) default 'F' NOT NULL \ 8 | , PRIMARY KEY(system_name, patch_level)) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0) 12 | level.table.exists=SELECT patch_level FROM patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ? 14 | level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO patches (patch_level, system_name, patch_date) VALUES ( ?, ?, getDate()) 16 | level.exists=SELECT patch_level FROM patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? ) 24 | lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' 25 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestJdbcMigrationLauncherFactory.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | /** 19 | * Overrides methods in the normal launcher factory that make it difficult to test 20 | * 21 | * @author Mike Hardy (mike@tacitknowledge.com) 22 | */ 23 | public class TestJdbcMigrationLauncherFactory extends JdbcMigrationLauncherFactory 24 | { 25 | /** 26 | * Returns TestDataSourceMigrationContext 27 | * 28 | * @return TestDataSourceMigrationContext 29 | */ 30 | public DataSourceMigrationContext getDataSourceMigrationContext() 31 | { 32 | return new TestDataSourceMigrationContext(); 33 | } 34 | 35 | /** 36 | * Returns a TestJdbcMigrationLauncher 37 | * 38 | * @return TestJdbcMigrationLauncher 39 | */ 40 | public JdbcMigrationLauncher getJdbcMigrationLauncher() 41 | { 42 | return new TestJdbcMigrationLauncher(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/postgres.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=true 2 | 3 | patches.create=CREATE TABLE patches ( \ 4 | system_name VARCHAR(30) NOT NULL \ 5 | , patch_level INT4 NOT NULL \ 6 | , patch_date DATE NOT NULL DEFAULT (now()) \ 7 | , patch_in_progress CHAR(1) NOT NULL DEFAULT ('F') \ 8 | , PRIMARY KEY (system_name, patch_level)) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 ) 12 | level.table.exists=SELECT patch_level FROM patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ? 14 | level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO patches (patch_level, system_name, patch_date) VALUES ( ?, ?, now()) 16 | level.exists=SELECT patch_level FROM patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? ) 24 | lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' 25 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestDistributedAutoPatchService.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | /** 19 | * Overrides methods in the normal launcher factory that make it difficult to test 20 | * 21 | * @author Mike Hardy (mike@tacitknowledge.com) 22 | */ 23 | public class TestDistributedAutoPatchService extends DistributedAutoPatchService 24 | { 25 | /** 26 | * Returns TestDataSourceMigrationContext 27 | * 28 | * @return TestDataSourceMigrationContext 29 | */ 30 | public DataSourceMigrationContext getDataSourceMigrationContext() 31 | { 32 | return new TestDataSourceMigrationContext(); 33 | } 34 | 35 | /** 36 | * Returns a TestJdbcMigrationLauncher 37 | * 38 | * @return TestJdbcMigrationLauncher 39 | */ 40 | public DistributedJdbcMigrationLauncher getDistributedJdbcMigrationLauncher() 41 | { 42 | return new TestDistributedJdbcMigrationLauncher(); 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/oracle.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=false 2 | 3 | patches.create=CREATE TABLE tk_patches (\ 4 | system_name VARCHAR2(30) NOT NULL\ 5 | , patch_level NUMBER NOT NULL\ 6 | , patch_date DATE DEFAULT SYSDATE NOT NULL\ 7 | , patch_in_progress CHAR(1) DEFAULT 'F' NOT NULL\ 8 | , CONSTRAINT tk_patches_pk PRIMARY KEY (system_name, patch_level) ) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO tk_patches (system_name, patch_level) VALUES ( ?, 0 ) 12 | level.table.exists=SELECT patch_level FROM tk_patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM tk_patches WHERE system_name = ? 14 | level.rollback=DELETE FROM tk_patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO tk_patches (patch_level, system_name, patch_date) VALUES ( ?, ?, SYSDATE) 16 | level.exists=SELECT patch_level FROM tk_patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM tk_patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM tk_patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM tk_patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE tk_patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT MAX(patch_level) FROM tk_patches WHERE system_name = ? ) 24 | lock.release=UPDATE tk_patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' 25 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationContext.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | 19 | /** 20 | * Provides system resources to migration tasks. 21 | * 22 | * @author Scott Askew (scott@tacitknowledge.com) 23 | */ 24 | public interface MigrationContext 25 | { 26 | /** 27 | * The name of the migration configuration file 28 | */ 29 | public static final String MIGRATION_CONFIG_FILE = "migration.properties"; 30 | 31 | /** 32 | * Commits the current migration transaction. 33 | * 34 | * @throws MigrationException if there was an unrecoverable error committing 35 | * the transaction 36 | */ 37 | public void commit() throws MigrationException; 38 | 39 | /** 40 | * Rolls back the current migration transaction. 41 | * 42 | * @throws MigrationException if there was an unrecoverable error committing 43 | * the transaction 44 | */ 45 | public void rollback() throws MigrationException; 46 | } -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationTask.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | /** 19 | * A single, idempotent migration task. 20 | * 21 | * @author Scott Askew (scott@tacitknowledge.com) 22 | */ 23 | public interface MigrationTask extends Comparable 24 | { 25 | /** 26 | * Performs a migration 27 | * 28 | * @param context the MigrationContext for this run 29 | * @throws MigrationException if an unexpected error occurred 30 | */ 31 | public void migrate(MigrationContext context) throws MigrationException; 32 | 33 | /** 34 | * Returns the name of this migration task. 35 | * 36 | * @return the name of this migration task 37 | */ 38 | public String getName(); 39 | 40 | /** 41 | * Returns the relative order in which this migration should occur. 42 | * 43 | * @return the relative order in which this migration should occur; may never 44 | * return null 45 | */ 46 | public Integer getLevel(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/mysql.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=true 2 | 3 | patches.create=CREATE TABLE IF NOT EXISTS patches ( \ 4 | system_name VARCHAR(30) NOT NULL \ 5 | , patch_level INT4 NOT NULL \ 6 | , patch_date TIMESTAMP NOT NULL default CURRENT_TIMESTAMP \ 7 | , patch_in_progress CHAR(1) NOT NULL default 'F' \ 8 | , PRIMARY KEY(system_name, patch_level)) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 ) 12 | level.table.exists=SELECT patch_level FROM patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ? 14 | level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO patches (patch_level, system_name, patch_date) VALUES ( ?, ?, CURRENT_TIMESTAMP) 16 | level.exists=SELECT patch_level FROM patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT max_patch_level FROM (SELECT MAX(patch_level) AS max_patch_level FROM patches WHERE system_name = ? ) AS tmptable ) 24 | lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' -------------------------------------------------------------------------------- /src/main/resources/com/tacitknowledge/util/migration/jdbc/sybase.properties: -------------------------------------------------------------------------------- 1 | supportsMultipleStatements=false 2 | 3 | patches.create=CREATE TABLE patches (\ 4 | system_name VARCHAR(30) NOT NULL PRIMARY KEY,\ 5 | patch_level INT NOT NULL,\ 6 | patch_date DATETIME DEFAULT getdate() NOT NULL,\ 7 | patch_in_progress CHAR(1) DEFAULT 'F' NOT NULL,\ 8 | primary key clustered (system_name, patch_level)) 9 | 10 | # Validates that a record exists for a given system 11 | level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 ) 12 | level.table.exists=SELECT patch_level FROM patches WHERE system_name = ? 13 | level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ? 14 | level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ? 15 | level.update=INSERT INTO patches (patch_level, system_name, patch_date) VALUES ( ?, ?, getdate()) 16 | level.exists=SELECT patch_level FROM patches WHERE system_name=? and patch_level=? 17 | 18 | patches.all=SELECT patch_level FROM patches WHERE system_name = ? 19 | 20 | # Since most DBs do not have a boolean type, return 0 or 1 row to determine if 21 | # the system is currently locked. 22 | lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? )) 23 | lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? ) 24 | lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F' 25 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/migrationtasks/TestMigrationTaskRollback1.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback.migrationtasks; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTaskSupport; 21 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 22 | import com.tacitknowledge.util.migration.TestMigrationContext; 23 | 24 | public class TestMigrationTaskRollback1 extends MigrationTaskSupport implements RollbackableMigrationTask 25 | { 26 | 27 | public TestMigrationTaskRollback1() 28 | { 29 | setName("TestMigrationTaskRollback1"); 30 | setLevel(new Integer(13)); 31 | } 32 | 33 | public void migrate(MigrationContext context) throws MigrationException 34 | { 35 | if (context instanceof TestMigrationContext) 36 | { 37 | TestMigrationContext ctx = (TestMigrationContext) context; 38 | ctx.recordExecution(getName()); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/integration-test/resources/inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Core. 3 | # 4 | integration_test.context=core 5 | integration_test.controlled.systems=core,orders,catalog 6 | integration_test.listeners=com.tacitknowledge.util.migration.listeners.WhinyMigrationListener 7 | 8 | # Configure a context named "core" 9 | # 10 | core.jdbc.database.type=hsqldb 11 | core.jdbc.driver=org.hsqldb.jdbcDriver 12 | core.jdbc.url=jdbc:hsqldb:mem:core 13 | core.jdbc.username=sa 14 | core.jdbc.password= 15 | core.patch.path=com.tacitknowledge.util.migration.inttest-tasks.core 16 | # 17 | # Configure a context named "orders" 18 | # 19 | orders.jdbc.database.type=hsqldb 20 | orders.jdbc.driver=org.hsqldb.jdbcDriver 21 | orders.jdbc.url=jdbc:hsqldb:mem:orders 22 | orders.jdbc.username=sa 23 | orders.jdbc.password= 24 | orders.patch.path=com.tacitknowledge.util.migration.inttest-tasks.order 25 | 26 | # 27 | # Configure a context named catalog, and make it a multi-node context 28 | # 29 | catalog.jdbc.systems=jdbccatalog1,jdbccatalog2,jdbccatalog3 30 | catalog.jdbccatalog1.database.type=hsqldb 31 | catalog.jdbccatalog1.driver=org.hsqldb.jdbcDriver 32 | catalog.jdbccatalog1.url=jdbc:hsqldb:mem:catalog1 33 | catalog.jdbccatalog1.username=sa 34 | catalog.jdbccatalog1.password= 35 | catalog.jdbccatalog2.database.type=hsqldb 36 | catalog.jdbccatalog2.driver=org.hsqldb.jdbcDriver 37 | catalog.jdbccatalog2.url=jdbc:hsqldb:mem:catalog2 38 | catalog.jdbccatalog2.username=sa 39 | catalog.jdbccatalog2.password= 40 | catalog.jdbccatalog3.database.type=hsqldb 41 | catalog.jdbccatalog3.driver=org.hsqldb.jdbcDriver 42 | catalog.jdbccatalog3.url=jdbc:hsqldb:mem:catalog3 43 | catalog.jdbccatalog3.username=sa 44 | catalog.jdbccatalog3.password= 45 | catalog.patch.path=com.tacitknowledge.util.migration.inttest-tasks.catalog -------------------------------------------------------------------------------- /examples/distributed-sample/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/MockDatabaseType.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | /** 19 | * MockDatabaseType since DatabaseType is not interface based and 20 | * can't mock it via easymock (without upgrading version to use the 21 | * class extension lib) 22 | * 23 | * @author Alex Soto (apsoto@gmail.com) 24 | */ 25 | class MockDatabaseType extends DatabaseType 26 | { 27 | /** does database type support multipe sql statements per stmt.execute() */ 28 | private boolean multipleStatementsSupported; 29 | 30 | /** 31 | * constructor 32 | * @param databaseType set the type 33 | */ 34 | public MockDatabaseType(String databaseType) 35 | { 36 | super(databaseType); 37 | } 38 | 39 | /** {@inheritDoc} */ 40 | public boolean isMultipleStatementsSupported() 41 | { 42 | return multipleStatementsSupported; 43 | } 44 | 45 | /** 46 | * simple setter 47 | * @param multipleStatementsSupported the value to set 48 | */ 49 | public void setMultipleStatementsSupported(boolean multipleStatementsSupported) 50 | { 51 | this.multipleStatementsSupported = multipleStatementsSupported; 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/TestRollbackableTask3.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | public class TestRollbackableTask3 extends BaseTestRollbackableMigrationTask 24 | implements RollbackableMigrationTask 25 | { 26 | 27 | public TestRollbackableTask3() 28 | { 29 | super("TestRollbackableTask3", 10); 30 | } 31 | 32 | public void down(MigrationContext context) throws MigrationException 33 | { 34 | if (context instanceof TestMigrationContext) 35 | { 36 | TestMigrationContext ctx = (TestMigrationContext) context; 37 | ctx.recordExecution(getName()); 38 | } 39 | } 40 | 41 | public void up(MigrationContext context) throws MigrationException 42 | { 43 | if (context instanceof TestMigrationContext) 44 | { 45 | TestMigrationContext ctx = (TestMigrationContext) context; 46 | ctx.recordExecution(getName()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/TestRollbackableTask4.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | public class TestRollbackableTask4 extends BaseTestRollbackableMigrationTask 24 | implements RollbackableMigrationTask 25 | { 26 | 27 | public TestRollbackableTask4() 28 | { 29 | super("TestRollbackableTask4", 11); 30 | } 31 | 32 | public void down(MigrationContext context) throws MigrationException 33 | { 34 | if (context instanceof TestMigrationContext) 35 | { 36 | TestMigrationContext ctx = (TestMigrationContext) context; 37 | ctx.recordExecution(getName()); 38 | } 39 | } 40 | 41 | public void up(MigrationContext context) throws MigrationException 42 | { 43 | if (context instanceof TestMigrationContext) 44 | { 45 | TestMigrationContext ctx = (TestMigrationContext) context; 46 | ctx.recordExecution(getName()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/TestRollbackableTask5.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | public class TestRollbackableTask5 extends BaseTestRollbackableMigrationTask 24 | implements RollbackableMigrationTask 25 | { 26 | public TestRollbackableTask5() 27 | { 28 | super("TestRollbackableTask5", 12); 29 | } 30 | 31 | public void down(MigrationContext context) throws MigrationException 32 | { 33 | if (context instanceof TestMigrationContext) 34 | { 35 | TestMigrationContext ctx = (TestMigrationContext) context; 36 | ctx.recordExecution(getName()); 37 | } 38 | } 39 | 40 | public void up(MigrationContext context) throws MigrationException 41 | { 42 | if (context instanceof TestMigrationContext) 43 | { 44 | TestMigrationContext ctx = (TestMigrationContext) context; 45 | ctx.recordExecution(getName()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestDistributedJdbcMigrationLauncherFactory.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | /** 19 | * Overrides methods in the normal launcher factory that make it difficult to test 20 | * 21 | * @author Mike Hardy (mike@tacitknowledge.com) 22 | */ 23 | public class TestDistributedJdbcMigrationLauncherFactory 24 | extends DistributedJdbcMigrationLauncherFactory 25 | { 26 | /** 27 | * Returns TestDataSourceMigrationContext 28 | * 29 | * @return TestDataSourceMigrationContext 30 | */ 31 | public DataSourceMigrationContext getDataSourceMigrationContext() 32 | { 33 | return new TestDataSourceMigrationContext(); 34 | } 35 | 36 | /** 37 | * Returns TestDistributedJdbcMigrationLauncher 38 | * 39 | * @return TestDistributedJdbcMigrationLauncher 40 | */ 41 | public JdbcMigrationLauncher getJdbcMigrationLauncher() 42 | { 43 | return new TestDistributedJdbcMigrationLauncher(); 44 | } 45 | 46 | /** 47 | * Returns TestDistributedJdbcMigrationLauncher 48 | * 49 | * @return TestDistributedJdbcMigrationLauncher 50 | */ 51 | public DistributedJdbcMigrationLauncher getDistributedJdbcMigrationLauncher() 52 | { 53 | return new TestDistributedJdbcMigrationLauncher(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/TestRollbackableTask1.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTaskSupport; 21 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 22 | import com.tacitknowledge.util.migration.TestMigrationContext; 23 | 24 | public class TestRollbackableTask1 extends BaseTestRollbackableMigrationTask 25 | implements RollbackableMigrationTask 26 | { 27 | 28 | public TestRollbackableTask1() 29 | { 30 | super("TestRollbackableTask1", 8); 31 | } 32 | 33 | public TestRollbackableTask1(int level) 34 | { 35 | super("TestRollbackableTask1", level); 36 | } 37 | 38 | public boolean isRollbackSupported() 39 | { 40 | return false; 41 | } 42 | 43 | /** 44 | * @see MigrationTaskSupport#migrate(MigrationContext) 45 | */ 46 | public void migrate(MigrationContext context) throws MigrationException 47 | { 48 | if (context instanceof TestMigrationContext) 49 | { 50 | TestMigrationContext ctx = (TestMigrationContext) context; 51 | ctx.recordExecution(getName()); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/util/SybaseUtilTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.util; 17 | 18 | import junit.framework.TestCase; 19 | 20 | /** 21 | * Test class for {@link SybaseUtil}. 22 | * 23 | * @author Alex Soto (apsoto@gmail.com) 24 | */ 25 | public class SybaseUtilTest extends TestCase 26 | { 27 | 28 | /** 29 | * {@inheritDoc} 30 | */ 31 | protected void setUp() throws Exception 32 | { 33 | super.setUp(); 34 | } 35 | 36 | /** 37 | * Test the containsIllegalMultiStatementTransactionCommand method. 38 | * 39 | */ 40 | public void testContainsIllegalMultiStatementTransactionCommand() 41 | { 42 | String simpleSql = "ALTER TABLE foo ADD version DEFAULT 0"; 43 | String multiLineSql = "INSERT INTO TABLE foo(id) VALUES(1)\n" 44 | + "alter table foo ADD VERSION DEFAULT 0\n" 45 | + "INSERT INTO TABLE foo(id, version) VALUES (1, 1)\n"; 46 | String noIllegalSql = "SELECT * FROM foo"; 47 | 48 | assertTrue(SybaseUtil.containsIllegalMultiStatementTransactionCommand(simpleSql)); 49 | assertTrue(SybaseUtil.containsIllegalMultiStatementTransactionCommand(multiLineSql)); 50 | assertFalse(SybaseUtil.containsIllegalMultiStatementTransactionCommand(noIllegalSql)); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/AutopatchRegistry.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 17 | package com.tacitknowledge.util.migration; 18 | 19 | 20 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncherFactoryLoader; 21 | import com.tacitknowledge.util.migration.jdbc.StandaloneMigrationLauncher; 22 | import com.tacitknowledge.util.migration.jdbc.util.MigrationUtil; 23 | import org.picocontainer.DefaultPicoContainer; 24 | import org.picocontainer.MutablePicoContainer; 25 | import org.picocontainer.PicoContainer; 26 | import org.picocontainer.injectors.CompositeInjection; 27 | import org.picocontainer.injectors.ConstructorInjection; 28 | import org.picocontainer.injectors.SetterInjection; 29 | 30 | /** 31 | * AutopatchRegistry using PicoContainer to set all the dependencies of the application, we favor 32 | * constructor injection over other methods of injection. 33 | * 34 | * @author Oscar Gonzalez (oscar@tacitknowledge.com) 35 | */ 36 | public class AutopatchRegistry 37 | { 38 | MutablePicoContainer pico; 39 | 40 | public PicoContainer configureContainer(){ 41 | pico = new DefaultPicoContainer(); 42 | pico.addComponent(StandaloneMigrationLauncher.class); 43 | pico.addComponent(MigrationUtil.class); 44 | pico.start(); 45 | return pico; 46 | } 47 | 48 | public void destroyContainer(){ 49 | pico.stop(); 50 | pico.dispose(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationRunnerFactory.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import org.apache.commons.lang.StringUtils; 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | /** 23 | * Returns the strategy we should we apply to decide if a patch needs to be applied 24 | * or we should rollback 25 | * 26 | * @author Oscar Gonzalez (oscar@tacitknowledge.com) 27 | */ 28 | 29 | public class MigrationRunnerFactory 30 | { 31 | 32 | private static Log log = LogFactory.getLog(MigrationRunnerFactory.class); 33 | public static final String DEFAULT_MIGRATION_STRATEGY = "com.tacitknowledge.util.migration.OrderedMigrationRunnerStrategy"; 34 | 35 | public static MigrationRunnerStrategy getMigrationRunnerStrategy(String strategy) 36 | { 37 | 38 | log.info("Strategy received '" + strategy + "'"); 39 | 40 | if (StringUtils.isBlank(strategy)) 41 | { 42 | return new OrderedMigrationRunnerStrategy(); 43 | 44 | } 45 | 46 | try 47 | { 48 | Class c = Class.forName(strategy.trim()); 49 | MigrationRunnerStrategy runnerStrategy = (MigrationRunnerStrategy) c.newInstance(); 50 | return runnerStrategy; 51 | } 52 | catch (Exception e) 53 | { 54 | throw new IllegalArgumentException("Strategy selected " + strategy + " cannot be instantiated ", e); 55 | } 56 | 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask3.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.normal; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 21 | 22 | /** 23 | * Basic test migration task. 24 | * 25 | * @author Scott Askew (scott@tacitknowledge.com) 26 | */ 27 | public class TestMigrationTask3 extends BaseTestMigrationTask 28 | { 29 | /** 30 | * Determines if the task should simulate a MigrationException 31 | */ 32 | private static boolean fail = false; 33 | 34 | /** 35 | * Creates a new TestMigrationTask3. 36 | */ 37 | public TestMigrationTask3() 38 | { 39 | super("TestTask3", 6); 40 | } 41 | 42 | /** 43 | * @see BaseTestMigrationTask#migrate(MigrationContext) 44 | */ 45 | public void migrate(MigrationContext context) throws MigrationException 46 | { 47 | if (fail) 48 | { 49 | throw new MigrationException("Test exception"); 50 | } 51 | super.migrate(context); 52 | } 53 | 54 | /** 55 | * Determins if the task should simulate a MigrationException 56 | * 57 | * @param f trueif the task should simulate a MigrationException 58 | */ 59 | public static void setFail(boolean f) 60 | { 61 | TestMigrationTask3.fail = f; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask2.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.normal; 17 | 18 | import com.tacitknowledge.util.migration.tasks.BaseTestMigrationTask; 19 | 20 | /** 21 | * Basic test migration task. 22 | * 23 | * @author Scott Askew (scott@tacitknowledge.com) 24 | */ 25 | public class TestMigrationTask2 extends BaseTestMigrationTask 26 | { 27 | /** 28 | * The patch level to use instead of '2' 29 | */ 30 | private static Integer patchLevelOverride = new Integer(5); 31 | 32 | /** 33 | * Creates a new TestMigrationTask3. 34 | */ 35 | public TestMigrationTask2() 36 | { 37 | super("TestTask2", 5); 38 | } 39 | 40 | /** 41 | * @see com.tacitknowledge.util.migration.MigrationTaskSupport#getLevel() 42 | */ 43 | public Integer getLevel() 44 | { 45 | return patchLevelOverride; 46 | } 47 | 48 | /** 49 | * Sets the patch level to use for all instances of this class. 50 | * 51 | * @param level the patch level to use for all instances of this class; if 52 | * null, then the default patch level (2) will be used 53 | */ 54 | public static void setPatchLevelOverride(Integer level) 55 | { 56 | patchLevelOverride = level; 57 | } 58 | 59 | /** 60 | * Resets the task the its default state. 61 | */ 62 | public static void reset() 63 | { 64 | patchLevelOverride = new Integer(5); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/integration-test/resources/node-added-inttest-migration.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Which context do we use for the orchestration patch store? Core. 3 | # 4 | integration_test.context=core 5 | integration_test.controlled.systems=core,orders,catalog 6 | # 7 | # Configure a context named "core" 8 | # 9 | core.jdbc.database.type=hsqldb 10 | core.jdbc.driver=org.hsqldb.jdbcDriver 11 | core.jdbc.url=jdbc:hsqldb:mem:core 12 | core.jdbc.username=sa 13 | core.jdbc.password= 14 | core.patch.path=com.tacitknowledge.util.migration.inttest-tasks.core 15 | # 16 | # Configure a context named "orders" 17 | # 18 | orders.jdbc.database.type=hsqldb 19 | orders.jdbc.driver=org.hsqldb.jdbcDriver 20 | orders.jdbc.url=jdbc:hsqldb:mem:orders 21 | orders.jdbc.username=sa 22 | orders.jdbc.password= 23 | orders.patch.path=com.tacitknowledge.util.migration.inttest-tasks.order 24 | # 25 | # Configure a context named catalog, and make it a multi-node context 26 | # 27 | catalog.jdbc.systems=jdbccatalog1,jdbccatalog2,jdbccatalog3,jdbccatalog4,jdbccatalog5 28 | catalog.jdbccatalog1.database.type=hsqldb 29 | catalog.jdbccatalog1.driver=org.hsqldb.jdbcDriver 30 | catalog.jdbccatalog1.url=jdbc:hsqldb:mem:catalog1 31 | catalog.jdbccatalog1.username=sa 32 | catalog.jdbccatalog1.password= 33 | catalog.jdbccatalog2.database.type=hsqldb 34 | catalog.jdbccatalog2.driver=org.hsqldb.jdbcDriver 35 | catalog.jdbccatalog2.url=jdbc:hsqldb:mem:catalog2 36 | catalog.jdbccatalog2.username=sa 37 | catalog.jdbccatalog2.password= 38 | catalog.jdbccatalog3.database.type=hsqldb 39 | catalog.jdbccatalog3.driver=org.hsqldb.jdbcDriver 40 | catalog.jdbccatalog3.url=jdbc:hsqldb:mem:catalog3 41 | catalog.jdbccatalog3.username=sa 42 | catalog.jdbccatalog3.password= 43 | catalog.jdbccatalog4.database.type=hsqldb 44 | catalog.jdbccatalog4.driver=org.hsqldb.jdbcDriver 45 | catalog.jdbccatalog4.url=jdbc:hsqldb:mem:catalog4 46 | catalog.jdbccatalog4.username=sa 47 | catalog.jdbccatalog4.password= 48 | catalog.jdbccatalog5.database.type=hsqldb 49 | catalog.jdbccatalog5.driver=org.hsqldb.jdbcDriver 50 | catalog.jdbccatalog5.url=jdbc:hsqldb:mem:catalog5 51 | catalog.jdbccatalog5.username=sa 52 | catalog.jdbccatalog5.password= 53 | catalog.patch.path=com.tacitknowledge.util.migration.inttest-tasks.catalog -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/AbstractMigrationListener.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | /** 19 | * Abstract base class for MigrationListener authors that aren't interested in 20 | * implementing all the MigrationListener events. 21 | * 22 | * @author Alex Soto (apsoto@gmail.com) 23 | */ 24 | public abstract class AbstractMigrationListener implements MigrationListener 25 | { 26 | 27 | /** 28 | * @see com.tacitknowledge.util.migration.MigrationListener#migrationFailed(com.tacitknowledge.util.migration.MigrationTask, com.tacitknowledge.util.migration.MigrationContext, com.tacitknowledge.util.migration.MigrationException) 29 | */ 30 | public void migrationFailed(MigrationTask task, MigrationContext context, 31 | MigrationException e) throws MigrationException 32 | { 33 | } 34 | 35 | /** 36 | * @see com.tacitknowledge.util.migration.MigrationListener#migrationStarted(com.tacitknowledge.util.migration.MigrationTask, com.tacitknowledge.util.migration.MigrationContext) 37 | */ 38 | public void migrationStarted(MigrationTask task, MigrationContext context) 39 | throws MigrationException 40 | { 41 | } 42 | 43 | /** 44 | * @see com.tacitknowledge.util.migration.MigrationListener#migrationSuccessful(com.tacitknowledge.util.migration.MigrationTask, com.tacitknowledge.util.migration.MigrationContext) 45 | */ 46 | public void migrationSuccessful(MigrationTask task, MigrationContext context) 47 | throws MigrationException 48 | { 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/RollbackableMigrationTask.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | /** 19 | * A single, idempotent and migration task, which also supports rollbacks. 20 | * 21 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 22 | */ 23 | public interface RollbackableMigrationTask extends MigrationTask 24 | { 25 | 26 | /** 27 | * Performs a migration 28 | * 29 | * @param context the MigrationContext for this run. 30 | * @throws MigrationException if an unexpected error occurs 31 | */ 32 | public void up(MigrationContext context) throws MigrationException; 33 | 34 | /** 35 | * Performs a rollback 36 | * 37 | * @param context the MigrationContext for this run. 38 | * @throws MigrationException if an unexpected error occurrs 39 | */ 40 | public void down(MigrationContext context) throws MigrationException; 41 | 42 | /** 43 | * Returns a boolean indicating if this task can be rolled back. 44 | * 45 | * @return a boolean indicating if the task can be rolled back. 46 | */ 47 | public boolean isRollbackSupported(); 48 | 49 | /** 50 | * Returns the name of this migration task. 51 | * 52 | * @return the name of this migration task 53 | */ 54 | public String getName(); 55 | 56 | /** 57 | * Returns the relative order in which this migration should occur. 58 | * 59 | * @return the relative order in which this migration should occur; may never 60 | * return null 61 | */ 62 | public Integer getLevel(); 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AutoPatch 2 | ========= 3 | 4 | AutoPatch automates the application of changes to persistent storage. 5 | 6 | AutoPatch was born from the needs of using an agile development process 7 | while working on systems that have persistent storage. Without 8 | AutoPatch, developers usually can't afford the maintenance headache of 9 | their own database, and DBAs are required just to apply changes to all 10 | of the various environments a serious development effort requires. 11 | 12 | The very application of database changes becomes an inefficient, 13 | error-prone, expensive process, all conspiring to discourage any 14 | refactoring that touches the model, or being a bottleneck when model 15 | changes are made. 16 | 17 | AutoPatch solves this problem, completely. 18 | 19 | With AutoPatch, an agile development process that requires a database 20 | change looks like this: 21 | 22 | * Developer alters the model, which requires a change to the database 23 | * Developer possibly consults a DBA, and develops a SQL patch against 24 | their personal database that implements the alteration 25 | * Developer commits the patch to source control at the same time as they 26 | commit their dependent code 27 | * Other developers' and environments' databases are automatically updated 28 | by AutoPatch the next time the new source is run 29 | 30 | This represents streamlined environment maintenance, allowing developers 31 | to cheaply have their own databases and all databases to stay in synch 32 | with massively lower costs and no environment skew. 33 | 34 | Requirements 35 | ------------ 36 | 37 | * Java 6. That's it. 38 | 39 | 40 | Where do I get AutoPatch? 41 | ------------------------- 42 | AutoPatch is open source and is hosted at [Github](http://github.com/tacitknowledge/autopatch). 43 | 44 | The documentation for AutoPatch is on the [AutoPatch Wiki](https://github.com/tacitknowledge/autopatch/wiki) 45 | 46 | You can include AutoPatch in your Maven project via: 47 | 48 | 49 | com.tacitknowledge 50 | autopatch 51 | 1.4.2 52 | 53 | 54 | Help 55 | ==== 56 | 57 | If you have a question about AutoPatch feel free to post it up to our 58 | new [AutoPatch Google Group](http://groups.google.com/group/autopatch-users/). 59 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/TestMigrationContext.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.tacitknowledge.util.migration.jdbc.DataSourceMigrationContext; 22 | 23 | /** 24 | * Extends MigrationContext by adding a log of test executions. 25 | * 26 | * @author Scott Askew (scott@tacitknowledge.com) 27 | */ 28 | public class TestMigrationContext extends DataSourceMigrationContext 29 | { 30 | /** 31 | * A record of task executions 32 | */ 33 | private Map executionLog = new HashMap(); 34 | 35 | /** 36 | * Records a successful task execution 37 | * 38 | * @param taskName the name of the task 39 | */ 40 | public void recordExecution(String taskName) 41 | { 42 | executionLog.put(taskName, Boolean.TRUE); 43 | } 44 | 45 | /** 46 | * Determines if the given task has been executed 47 | * 48 | * @param taskName the name of the task to validate 49 | * @return true if the task has successfully executed 50 | */ 51 | public boolean hasExecuted(String taskName) 52 | { 53 | return executionLog.containsKey(taskName); 54 | } 55 | 56 | /** 57 | * @see com.tacitknowledge.util.migration.MigrationContext#commit() 58 | */ 59 | public void commit() throws MigrationException 60 | { 61 | // does nothing 62 | } 63 | 64 | /** 65 | * @see com.tacitknowledge.util.migration.MigrationContext#rollback() 66 | */ 67 | public void rollback() throws MigrationException 68 | { 69 | // does nothing 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/TestRollbackableTask2.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | public class TestRollbackableTask2 extends BaseTestRollbackableMigrationTask 24 | implements RollbackableMigrationTask 25 | { 26 | private static Integer patchLevelOverride = new Integer(9); 27 | 28 | public static void setPatchLevelOverride(Integer i) 29 | { 30 | patchLevelOverride = (i); 31 | } 32 | 33 | public Integer getLevel() 34 | { 35 | return patchLevelOverride; 36 | } 37 | 38 | public static void reset() 39 | { 40 | patchLevelOverride = new Integer(9); 41 | } 42 | 43 | public TestRollbackableTask2() 44 | { 45 | super("TestRollbackableTask2", 9); 46 | } 47 | 48 | public void down(MigrationContext context) throws MigrationException 49 | { 50 | if (context instanceof TestMigrationContext) 51 | { 52 | TestMigrationContext ctx = (TestMigrationContext) context; 53 | ctx.recordExecution(getName()); 54 | } 55 | } 56 | 57 | public void up(MigrationContext context) throws MigrationException 58 | { 59 | if (context instanceof TestMigrationContext) 60 | { 61 | TestMigrationContext ctx = (TestMigrationContext) context; 62 | ctx.recordExecution(getName()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/integration-test/java/com/tacitknowledge/util/migration/listeners/WhinyMigrationListener.java: -------------------------------------------------------------------------------- 1 | package com.tacitknowledge.util.migration.listeners; 2 | 3 | 4 | import com.tacitknowledge.util.migration.MigrationContext; 5 | import com.tacitknowledge.util.migration.MigrationException; 6 | import com.tacitknowledge.util.migration.MigrationListener; 7 | import com.tacitknowledge.util.migration.MigrationTask; 8 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationContext; 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | 12 | import java.util.Properties; 13 | 14 | 15 | /** 16 | * @author Alex Soto (apsoto@gmail.com) 17 | */ 18 | public class WhinyMigrationListener implements MigrationListener 19 | { 20 | /** 21 | * Class logger 22 | */ 23 | private static Log log = LogFactory.getLog(WhinyMigrationListener.class); 24 | 25 | protected String getTaskInfo(MigrationTask task, MigrationContext context) 26 | { 27 | String ctxInfo = ""; 28 | if (context instanceof JdbcMigrationContext) 29 | { 30 | JdbcMigrationContext ctx = (JdbcMigrationContext) context; 31 | ctxInfo += ctx.getSystemName() + " : " + ctx.getDatabaseName(); 32 | } 33 | 34 | return "Task => (" + task.toString() + "), Context => (" + ctxInfo + ")"; 35 | } 36 | 37 | public void migrationFailed(MigrationTask task, MigrationContext context, MigrationException e) throws MigrationException 38 | { 39 | log.debug("MIGRATION FAILED, " + getTaskInfo(task, context) + " WAHHH!!!"); 40 | } 41 | 42 | public void migrationStarted(MigrationTask task, MigrationContext context) throws MigrationException 43 | { 44 | log.debug("MIGRATION STARTED, " + getTaskInfo(task, context) + " WAHHH!!!"); 45 | } 46 | 47 | public void migrationSuccessful(MigrationTask task, MigrationContext context) throws MigrationException 48 | { 49 | log.debug("MIGRATION SUCCEEDED, " + getTaskInfo(task, context) + " WAHHH!!!"); 50 | } 51 | 52 | /** 53 | * @see com.tacitknowledge.util.migration.MigrationListener#initialize(Properties) 54 | */ 55 | public void initialize(String systemName, Properties properties) throws MigrationException 56 | { 57 | log.debug("MIGRATION LISTENER INTIALIZED FOR " + systemName + " SYSTEM, WAHHH!!!"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/rollback/BaseTestRollbackableMigrationTask.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks.rollback; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTaskSupport; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | /** 24 | * Base class for rollback task tests. 25 | * 26 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 27 | */ 28 | public abstract class BaseTestRollbackableMigrationTask extends MigrationTaskSupport 29 | { 30 | /** 31 | * Create a new BaseTestRollbackableMigrationTask. 32 | * 33 | * @param name 34 | * the name of the task 35 | * @param level 36 | * the patch level of the task 37 | */ 38 | protected BaseTestRollbackableMigrationTask(String name, int level) 39 | { 40 | setName(name); 41 | setLevel(new Integer(level)); 42 | } 43 | 44 | /** 45 | * Perform a rollback 46 | * @param context migration context 47 | * @throws MigrationException if an error happens 48 | */ 49 | public void down(MigrationContext context) throws MigrationException 50 | { 51 | if (context instanceof TestMigrationContext) 52 | { 53 | TestMigrationContext ctx = (TestMigrationContext) context; 54 | ctx.recordExecution(getName()); 55 | } 56 | } 57 | 58 | /** 59 | * Return true indicating that rollback is supported 60 | * @return true if rollback is supported. 61 | */ 62 | public boolean isRollbackSupported() 63 | { 64 | return true; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationRunnerStrategy.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * Dictates the methods that different algorithms should run when we 22 | * try to apply patches or get information about the patches that need to be run. 23 | * 24 | * @author Oscar Gonzalez (oscar@tacitknowledge.com) 25 | * @author Hemri Herrera (hemri@tacitknowledge.com) 26 | * @author Ulises Pulido (upulido@tacitknowledge.com) 27 | */ 28 | 29 | public interface MigrationRunnerStrategy 30 | { 31 | /** 32 | * Determines if a MigrationTask is able to run. 33 | * 34 | * @param migrationLevel of the MigrationTask to be check as in int 35 | * @param patchInfoStore object representing patch level information 36 | * @return boolean value telling us if we should run the migration or not. 37 | */ 38 | public boolean shouldMigrationRun(int migrationLevel, PatchInfoStore patchInfoStore) throws MigrationException; 39 | 40 | /** 41 | * Determines if two stores are synchronized to each other. 42 | * 43 | * @param currentPatchInfoStore 44 | * @param patchInfoStore 45 | * @return 46 | * @throws MigrationException 47 | */ 48 | public boolean isSynchronized(PatchInfoStore currentPatchInfoStore, PatchInfoStore patchInfoStore) throws MigrationException; 49 | 50 | /** 51 | * Retrieves all tasks that are candidates for rollback. 52 | * 53 | * @param allMigrationTasks 54 | * @param rollbackLevels 55 | * @param currentPatchInfoStore 56 | * @return 57 | * @throws MigrationException 58 | */ 59 | public List getRollbackCandidates(List allMigrationTasks, int[] rollbackLevels, PatchInfoStore currentPatchInfoStore) throws MigrationException; 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/test/listeners/TestListener1.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.test.listeners; 17 | 18 | import java.util.Properties; 19 | 20 | import org.apache.commons.logging.Log; 21 | import org.apache.commons.logging.LogFactory; 22 | 23 | import com.tacitknowledge.util.migration.MigrationContext; 24 | import com.tacitknowledge.util.migration.MigrationException; 25 | import com.tacitknowledge.util.migration.MigrationListener; 26 | import com.tacitknowledge.util.migration.MigrationTask; 27 | 28 | 29 | /** 30 | * @author Alex Soto 31 | * 32 | */ 33 | public class TestListener1 implements MigrationListener 34 | { 35 | /** Class logger */ 36 | private static Log log = LogFactory.getLog(TestListener1.class); 37 | 38 | /** system name I was configured with */ 39 | protected String systemName = null; 40 | 41 | public void migrationFailed(MigrationTask task, MigrationContext context, MigrationException e) throws MigrationException 42 | { 43 | log.debug("migration failed"); 44 | } 45 | 46 | public void migrationStarted(MigrationTask task, MigrationContext context) throws MigrationException 47 | { 48 | log.debug("migration started"); 49 | } 50 | 51 | public void migrationSuccessful(MigrationTask task, MigrationContext context) throws MigrationException 52 | { 53 | log.debug("migration successful"); 54 | } 55 | 56 | /** 57 | * @see com.tacitknowledge.util.migration.MigrationListener#initialize(Properties) 58 | */ 59 | public void initialize(String systemName, Properties properties) throws MigrationException 60 | { 61 | log.debug("initialized"); 62 | this.systemName = systemName; 63 | } 64 | 65 | public String getSystemName() 66 | { 67 | return this.systemName; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationContext.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | 21 | import java.sql.Connection; 22 | import java.sql.SQLException; 23 | 24 | /** 25 | * Contains the configuration and resources for a database patch run. 26 | * 27 | * @author Scott Askew (scott@tacitknowledge.com) 28 | */ 29 | public interface JdbcMigrationContext extends MigrationContext 30 | { 31 | /** 32 | * Max length for the systemName columne 33 | */ 34 | public static final int MAX_SYSTEMNAME_LENGTH = 30; 35 | 36 | /** 37 | * Returns a database connection to use. The creator 38 | * of the JdbcMigrationContext are responsible for closing the connection. 39 | * 40 | * @return the database connection to use 41 | * @throws SQLException if an unexpected error occurs 42 | */ 43 | public Connection getConnection() throws SQLException; 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | public void commit() throws MigrationException; 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public void rollback() throws MigrationException; 54 | 55 | /** 56 | * @return the name of the system to patch 57 | */ 58 | public String getSystemName(); 59 | 60 | /** 61 | * @return In a federated distributed configuration this should be some 62 | * unique name to identify each node in the system. In other 63 | * configurations where is typically only one node per system, 64 | * the system name should suffice. 65 | */ 66 | public String getDatabaseName(); 67 | 68 | /** 69 | * @return Returns the database type. 70 | */ 71 | public DatabaseType getDatabaseType(); 72 | } -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/tasks/BaseTestMigrationTask.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.tasks; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTaskSupport; 21 | import com.tacitknowledge.util.migration.TestMigrationContext; 22 | 23 | /** 24 | * Base class for migration task tests. 25 | * 26 | * @author Scott Askew (scott@tacitknowledge.com) 27 | */ 28 | public abstract class BaseTestMigrationTask extends MigrationTaskSupport 29 | { 30 | /** 31 | * Create a new BaseTestMigrationTask. 32 | * 33 | * @param name the name of the task 34 | * @param level the patch level of the task 35 | */ 36 | protected BaseTestMigrationTask(String name, int level) 37 | { 38 | setName(name); 39 | setLevel(new Integer(level)); 40 | } 41 | 42 | /** 43 | * @see MigrationTaskSupport#migrate(MigrationContext) 44 | */ 45 | public void migrate(MigrationContext context) throws MigrationException 46 | { 47 | if (context instanceof TestMigrationContext) 48 | { 49 | TestMigrationContext ctx = (TestMigrationContext) context; 50 | ctx.recordExecution(getName()); 51 | } 52 | } 53 | 54 | public void up(MigrationContext context) throws MigrationException 55 | { 56 | if (context instanceof TestMigrationContext) 57 | { 58 | TestMigrationContext ctx = (TestMigrationContext) context; 59 | ctx.recordExecution(getName()); 60 | } 61 | } 62 | 63 | public void down(MigrationContext context) throws MigrationException 64 | { 65 | if (context instanceof TestMigrationContext) 66 | { 67 | TestMigrationContext ctx = (TestMigrationContext) context; 68 | ctx.recordExecution(getName()); 69 | } 70 | } 71 | 72 | public boolean isRollbackSupported() { 73 | return true; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationListener.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.Properties; 19 | 20 | /** 21 | * Receives notifications regarding migration task migrations. 22 | * 23 | * @author Scott Askew (scott@tacitknowledge.com) 24 | */ 25 | public interface MigrationListener 26 | { 27 | /** 28 | * Initialize the migration listener. This provides an opportunity 29 | * for the MigrationListener to initialize itself before patching 30 | * begins. 31 | * 32 | * @param properties The properties loaded from migration.properties 33 | */ 34 | public void initialize(String systemName, Properties properties) throws MigrationException; 35 | 36 | /** 37 | * Notifies the listener that the given task is about to start execution. 38 | * 39 | * @param task the recently finished task 40 | * @param context the migration context 41 | * @throws MigrationException if an unrecoverable error occurs 42 | */ 43 | public void migrationStarted(MigrationTask task, MigrationContext context) 44 | throws MigrationException; 45 | 46 | /** 47 | * Notifies the listener that the given task has completed execution. 48 | * 49 | * @param task the recently finished task 50 | * @param context the migration context 51 | * @throws MigrationException if an unrecoverable error occurs 52 | */ 53 | public void migrationSuccessful(MigrationTask task, MigrationContext context) 54 | throws MigrationException; 55 | 56 | /** 57 | * Notifies the listener that the given task has completed execution. 58 | * 59 | * @param task the recently finished task 60 | * @param context the migration context 61 | * @param e the MigrationException thrown by the task 62 | * @throws MigrationException if an unrecoverable error occurs 63 | */ 64 | public void migrationFailed(MigrationTask task, 65 | MigrationContext context, MigrationException e) throws MigrationException; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherFactoryLoader.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | 21 | /** 22 | * Load a MigrationLauncherFactory. This will default to loading the 23 | * JdbcMigrationLauncherFactory, but will examine the system properties 24 | * for a property called "migration.factory" and load that one if specified 25 | * 26 | * @author Jacques Morel 27 | */ 28 | public class JdbcMigrationLauncherFactoryLoader 29 | { 30 | /** 31 | * Class logger 32 | */ 33 | private static Log log = LogFactory.getLog(JdbcMigrationLauncherFactoryLoader.class); 34 | 35 | 36 | /** 37 | * Create the JdbcMigrationLauncherFactory 38 | * 39 | * @return JdbcMigrationLauncherFactory (or subclass) 40 | */ 41 | public JdbcMigrationLauncherFactory createFactory() 42 | { 43 | // Get the factory name from the system properties if possible 44 | String factoryName = System.getProperties().getProperty("migration.factory"); 45 | if (factoryName == null) 46 | { 47 | factoryName = JdbcMigrationLauncherFactory.class.getName(); 48 | } 49 | log.debug("Creating JdbcMigrationLauncher using " + factoryName); 50 | 51 | // Load the factory 52 | Class factoryClass = null; 53 | try 54 | { 55 | factoryClass = Class.forName(factoryName); 56 | } 57 | catch (ClassNotFoundException e) 58 | { 59 | throw new IllegalArgumentException("Migration factory class '" 60 | + factoryName + "' not found. Aborting."); 61 | } 62 | try 63 | { 64 | return (JdbcMigrationLauncherFactory) factoryClass.newInstance(); 65 | } 66 | catch (Exception e) 67 | { 68 | throw new RuntimeException("Problem while instantiating factory class '" 69 | + factoryName + "'. Aborting.", e); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/ClassMigrationTaskSourceTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.List; 19 | 20 | import junit.framework.TestCase; 21 | 22 | /** 23 | * Exercise the ClassMigrationTaskSource object 24 | * 25 | * @author Mike Hardy (mike@tacitknowledge.com) 26 | */ 27 | public class ClassMigrationTaskSourceTest extends TestCase 28 | { 29 | /** 30 | * Make sure class instantiation fails on null package name 31 | */ 32 | public void testInstantiateTasksNullPackage() 33 | { 34 | ClassMigrationTaskSource source = new ClassMigrationTaskSource(); 35 | try 36 | { 37 | source.getMigrationTasks(null); 38 | fail("We should have gotten an exception for the null package"); 39 | } 40 | catch (MigrationException me) 41 | { 42 | // we expect this 43 | } 44 | } 45 | 46 | /** 47 | * Make sure class instantiation fails on package with no types 48 | */ 49 | public void testInstantiateTasksNoTasks() 50 | { 51 | ClassMigrationTaskSource source = new ClassMigrationTaskSource(); 52 | try 53 | { 54 | List tasks = source.getMigrationTasks("com.tacitknowledge.foo.bar"); 55 | assertEquals(0, tasks.size()); 56 | } 57 | catch (MigrationException me) 58 | { 59 | fail("We should not have gotten an exception"); 60 | } 61 | } 62 | 63 | /** 64 | * Make sure class instantiation fails on package with bad tasks 65 | */ 66 | public void testInstantiateTasksInstantiationException() 67 | { 68 | ClassMigrationTaskSource source = new ClassMigrationTaskSource(); 69 | try 70 | { 71 | source.getMigrationTasks(getClass().getPackage().getName() + ".tasks.instantiation"); 72 | fail("We should have gotten an exception"); 73 | } 74 | catch (MigrationException me) 75 | { 76 | assertTrue(me.getCause() instanceof RuntimeException); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/PatchRollbackPredicate.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import org.apache.commons.collections.Predicate; 19 | 20 | /** 21 | * This class defines a predicate for CollectionUtils which evaluates if a 22 | * given RollbackableMigrationTask should remain in the collection 23 | * 24 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 25 | * @see Predicate 26 | */ 27 | public class PatchRollbackPredicate implements Predicate 28 | { 29 | /* initialize both to max value */ 30 | private int rollbackPatchLevel = Integer.MAX_VALUE; 31 | private int currentPatchLevel = Integer.MAX_VALUE; 32 | 33 | /** 34 | * Constructor for this predicate. The current patch level and the rollback 35 | * patch level are set in this constructor. 36 | * 37 | * @param currentPatchLevel 38 | * @param rollbackPatchLevel 39 | */ 40 | public PatchRollbackPredicate(int currentPatchLevel, int rollbackPatchLevel) 41 | { 42 | this.rollbackPatchLevel = rollbackPatchLevel; 43 | this.currentPatchLevel = currentPatchLevel; 44 | } 45 | 46 | /** 47 | * The evaluate method returns false if the passed object is: 48 | *
    49 | *
  • null
  • 50 | *
  • not an instance of RollbackableMigrationTask
  • 51 | *
  • the level associated with the task is less than the rollback patch level
  • 52 | *
  • the level associated with the task is greater than the current patch level
  • 53 | *
54 | * 55 | * @param obj the Object to be evaluated 56 | * @boolean a boolean indicating if the object falls within the valid range for 57 | * this rollback 58 | */ 59 | public boolean evaluate(Object obj) 60 | { 61 | 62 | if (obj == null) 63 | return false; 64 | 65 | if (!(obj instanceof RollbackableMigrationTask)) 66 | return false; 67 | 68 | RollbackableMigrationTask task = (RollbackableMigrationTask) obj; 69 | final int level = task.getLevel().intValue(); 70 | 71 | return ((level > rollbackPatchLevel) && (level <= currentPatchLevel)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/RollbackListener.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.Properties; 19 | 20 | /** 21 | * Receives notifications regarding task rollbacks. 22 | * 23 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 24 | */ 25 | public interface RollbackListener extends MigrationListener 26 | { 27 | /** 28 | * Initialize the rollback listener. This provides an opportunity 29 | * for the RollbackListener to initialize itself before patching 30 | * begins. 31 | * 32 | * @param properties The properties loaded from migration.properties 33 | */ 34 | public void initialize(String systemName, Properties properties) throws MigrationException; 35 | 36 | /** 37 | * Notifies the listener that the given rollback is about to start execution. 38 | * 39 | * @param task the recently finished task 40 | * @param context the migration context 41 | * @throws MigrationException if an unrecoverable error occurs 42 | */ 43 | public void rollbackStarted(RollbackableMigrationTask task, MigrationContext context) 44 | throws MigrationException; 45 | 46 | /** 47 | * Notifies the listener that the given roolback has completed execution. 48 | * 49 | * @param task the recently finished task 50 | * @param context the migration context 51 | * @throws MigrationException if an unrecoverable error occurs 52 | */ 53 | public void rollbackSuccessful(RollbackableMigrationTask task, int rollbackLevel, 54 | MigrationContext context) 55 | throws MigrationException; 56 | 57 | /** 58 | * Notifies the listener that the given rollback has completed execution. 59 | * 60 | * @param task the recently finished task 61 | * @param context the migration context 62 | * @param e the MigrationException thrown by the task 63 | * @throws MigrationException if an unrecoverable error occurs 64 | */ 65 | public void rollbackFailed(RollbackableMigrationTask task, 66 | MigrationContext context, MigrationException e) throws MigrationException; 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/loader/FileLoadingUtility.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.loader; 17 | 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileNotFoundException; 24 | import java.io.InputStream; 25 | 26 | /** 27 | * This is a very simple utility that looks for a file 28 | * based upon its existence in the classpath or the 29 | * absolute path if provided. 30 | * 31 | * @author Chris A. (chris@tacitknowledge.com) 32 | */ 33 | public class FileLoadingUtility 34 | { 35 | /** 36 | * Class logger 37 | */ 38 | private static Log log = LogFactory.getLog(FileLoadingUtility.class); 39 | 40 | /** 41 | * The name of the file to load 42 | */ 43 | private String fileName = null; 44 | 45 | /** 46 | * Creates a new FileLoadingUtility. 47 | * 48 | * @param fileName the name of the file to load 49 | */ 50 | public FileLoadingUtility(String fileName) 51 | { 52 | this.fileName = fileName; 53 | } 54 | 55 | /** 56 | * Gets an input stream by first checking the current classloader, 57 | * then trying to use the system classloader, and finally, trying 58 | * to access the file on the file system. If the file is not found, 59 | * an IllegalArgumentException will be thrown. 60 | * 61 | * @return the file as an input stream 62 | */ 63 | public InputStream getResourceAsStream() 64 | { 65 | InputStream stream = 66 | Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); 67 | if (stream == null) 68 | { 69 | stream = ClassLoader.getSystemResourceAsStream(fileName); 70 | 71 | } 72 | if (stream == null) 73 | { 74 | File f = new File(fileName); 75 | try 76 | { 77 | stream = new FileInputStream(f); 78 | } 79 | catch (FileNotFoundException e) 80 | { 81 | log.error("The file: " + fileName + " was not found.", e); 82 | throw new IllegalArgumentException("Must have a valid file name."); 83 | } 84 | } 85 | return stream; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/StandaloneMigrationLauncherTest.java: -------------------------------------------------------------------------------- 1 | package com.tacitknowledge.util.migration.jdbc; 2 | 3 | import com.tacitknowledge.util.migration.MigrationException; 4 | import com.tacitknowledge.util.migration.jdbc.util.MigrationUtil; 5 | import junit.framework.TestCase; 6 | import org.easymock.EasyMock; 7 | import org.easymock.classextension.IMocksControl; 8 | 9 | import static org.easymock.EasyMock.eq; 10 | import static org.easymock.classextension.EasyMock.createStrictControl; 11 | 12 | /** 13 | * @author Hemri Herrera hemri@tacitknowledge.com 14 | * @author Ulises Pulido ulises@tacitknowledge.com 15 | */ 16 | 17 | public class StandaloneMigrationLauncherTest extends TestCase 18 | { 19 | 20 | public void testShouldRunMigrationsForcingRollback() throws Exception 21 | { 22 | IMocksControl mockControl = createStrictControl(); 23 | MigrationUtil migrationUtil = mockControl.createMock(MigrationUtil.class); 24 | StandaloneMigrationLauncher migrationLauncher = new StandaloneMigrationLauncher(migrationUtil); 25 | String[] arguments = new String[]{"orders","migration.properties","-force", "-rollback", "1"}; 26 | 27 | 28 | migrationUtil.doRollbacks(eq("orders"), eq("migration.properties"), EasyMock.anyObject(), eq(true)); 29 | mockControl.replay(); 30 | 31 | migrationLauncher.setMigrationUtil(migrationUtil); 32 | migrationLauncher.run(arguments); 33 | 34 | mockControl.verify(); 35 | } 36 | 37 | 38 | public void testShouldRunMigrationsMultipleRollbacks() throws Exception 39 | { 40 | IMocksControl mockControl = createStrictControl(); 41 | MigrationUtil migrationUtil = mockControl.createMock(MigrationUtil.class); 42 | StandaloneMigrationLauncher migrationLauncher = new StandaloneMigrationLauncher(migrationUtil); 43 | String[] arguments = new String[]{"orders","migration.properties","-force", "-rollback", "1,2,3,4,5,6"}; 44 | 45 | migrationUtil.doRollbacks(eq("orders"), eq("migration.properties"), EasyMock.anyObject(), eq(true)); 46 | mockControl.replay(); 47 | migrationLauncher.run(arguments); 48 | 49 | mockControl.verify(); 50 | } 51 | 52 | 53 | public void testShouldRunMigrationsMultipleRollbacksInvalidRollbackLevels() throws Exception 54 | { 55 | IMocksControl mockControl = createStrictControl(); 56 | MigrationUtil migrationUtil = mockControl.createMock(MigrationUtil.class); 57 | StandaloneMigrationLauncher migrationLauncher = new StandaloneMigrationLauncher(migrationUtil); 58 | String[] arguments = new String[]{"orders","migration.properties","-force", "-rollback", "1,2C,3B,4D,5A,600"}; 59 | 60 | try { 61 | migrationLauncher.run(arguments); 62 | fail("Should have thrown migration exception"); 63 | } catch (MigrationException me) { 64 | assertEquals("The rollbacklevels should be integers separated by a comma", me.getMessage()); 65 | } 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /examples/MSSQL-example.txt: -------------------------------------------------------------------------------- 1 | 2 | Simon Kingaby got a working setup with autopatch talking to MS-SQL using ant. 3 | He documented his configuration in the hopes it would help future folks. 4 | 5 | Here's his configuration: 6 | 7 | Project Structure: 8 | ------------------ 9 | 10 | trunk 11 | -->HelloWorldDatabase 12 | (build.xml) 13 | ---->src 14 | ------>main 15 | -------->conf 16 | (log4j.properties) 17 | (migration.properties) 18 | -------->db 19 | ---------->patch 20 | (patch0001_create_table.sql) 21 | -->vendor 22 | ---->build 23 | ------>sqljdbc_1.1 24 | -------->enu 25 | (sqljdbc.jar and other sqljdbc files and folders) 26 | ------>tk-autopatch 27 | (tk-autopatch-1.0.2.jar and other autopatch files and folders) 28 | 29 | 30 | Here is the migration.properties file: 31 | -------------------------------------- 32 | orchestration.context=zabbadoo 33 | orchestration.controlled.systems=zabbadoo 34 | zabbadoo.jdbc.database.type=sqlserver 35 | zabbadoo.jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver 36 | zabbadoo.jdbc.url=jdbc:sqlserver://kobol\\dev;database=zabbadoo 37 | zabbadoo.jdbc.username=zabbadoo 38 | zabbadoo.jdbc.password=password 39 | zabbadoo.patch.path=db.patch 40 | 41 | 42 | Here is the ant build.xml: 43 | -------------------------- 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 77 | 79 | 80 | 81 | 83 | 88 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/util/MigrationUtilTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.util; 17 | 18 | 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncher; 21 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncherFactory; 22 | import junit.framework.TestCase; 23 | import org.easymock.classextension.IMocksControl; 24 | 25 | import static org.easymock.EasyMock.expect; 26 | import static org.easymock.classextension.EasyMock.createStrictControl; 27 | 28 | public class MigrationUtilTest extends TestCase{ 29 | 30 | private static final int PATCHES_APPLIED = 2; 31 | private static final int ROLLBACK_LEVEL = 3; 32 | private static final int[] ROLLBACK_LEVELS = new int[]{ROLLBACK_LEVEL}; 33 | private MigrationUtil migrationUtil; 34 | private IMocksControl mockControl; 35 | private JdbcMigrationLauncherFactory launcherFactoryMock; 36 | private JdbcMigrationLauncher launcherMock; 37 | private static final String MIGRATION_NAME = "mysystem"; 38 | private static final boolean FORCE_ROLLBACK = false; 39 | private static final String MIGRATION_SETTINGS = "mymigrationsettings"; 40 | 41 | protected void setUp() throws Exception { 42 | super.setUp(); 43 | 44 | mockControl = createStrictControl(); 45 | launcherFactoryMock = mockControl.createMock(JdbcMigrationLauncherFactory.class); 46 | launcherMock = mockControl.createMock( JdbcMigrationLauncher.class ); 47 | migrationUtil = new MigrationUtil(); 48 | migrationUtil.setLauncherFactory(launcherFactoryMock); 49 | } 50 | 51 | public void testDoRollbacksActionWithoutMigrationSettings() throws MigrationException { 52 | 53 | expect( launcherFactoryMock.createMigrationLauncher(MIGRATION_NAME)).andReturn(launcherMock); 54 | expect(launcherMock.doRollbacks(ROLLBACK_LEVELS, FORCE_ROLLBACK)).andReturn(PATCHES_APPLIED); 55 | mockControl.replay(); 56 | 57 | 58 | migrationUtil.doRollbacks(MIGRATION_NAME, null, ROLLBACK_LEVELS, FORCE_ROLLBACK); 59 | mockControl.verify(); 60 | 61 | } 62 | 63 | public void testDoRollbackActionWithMigrationSettings() throws MigrationException{ 64 | 65 | expect( launcherFactoryMock.createMigrationLauncher(MIGRATION_NAME, MIGRATION_SETTINGS)).andReturn(launcherMock); 66 | expect( launcherMock.doRollbacks(ROLLBACK_LEVELS, FORCE_ROLLBACK)).andReturn(PATCHES_APPLIED); 67 | mockControl.replay(); 68 | 69 | migrationUtil.doRollbacks(MIGRATION_NAME, MIGRATION_SETTINGS,ROLLBACK_LEVELS,FORCE_ROLLBACK); 70 | mockControl.verify(); 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/builders/MockBuilder.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.builders; 17 | 18 | import com.tacitknowledge.util.migration.MigrationException; 19 | import com.tacitknowledge.util.migration.PatchInfoStore; 20 | import org.easymock.classextension.IMocksControl; 21 | 22 | import java.util.Properties; 23 | import java.util.Set; 24 | 25 | import static org.easymock.EasyMock.expect; 26 | import static org.easymock.classextension.EasyMock.createStrictControl; 27 | 28 | /** 29 | * MockBuilder to retrieve simple objects used in different tests 30 | * 31 | * @author Oscar Gonzalez (oscar@tacitknowledge.com) 32 | */ 33 | public class MockBuilder 34 | { 35 | 36 | public static PatchInfoStore getPatchInfoStore(int patchLevel) throws MigrationException 37 | { 38 | return getPatchInfoStore(patchLevel, null); 39 | } 40 | 41 | public static PatchInfoStore getPatchInfoStore(int patchLevel, Set patchesApplied) 42 | throws MigrationException 43 | { 44 | IMocksControl patchInfoStoreControl = createStrictControl(); 45 | PatchInfoStore patchInfoStoreMock = patchInfoStoreControl.createMock(PatchInfoStore.class); 46 | expect(patchInfoStoreMock.getPatchLevel()).andReturn(patchLevel).anyTimes(); 47 | expect(patchInfoStoreMock.getPatchesApplied()).andReturn(patchesApplied); 48 | patchInfoStoreControl.replay(); 49 | return patchInfoStoreMock; 50 | } 51 | 52 | public static Properties getPropertiesWithSystemConfiguration(String system, String strategy) 53 | { 54 | Properties properties = new Properties(); 55 | properties.setProperty(system + ".jdbc.database.type", "hsqldb"); 56 | properties.setProperty(system + ".patch.path", "systemPath"); 57 | properties.setProperty(system + ".jdbc.driver", "jdbcDriver"); 58 | properties.setProperty(system + ".jdbc.url", "jdbcUrl"); 59 | properties.setProperty(system + ".jdbc.username", "jdbcUsername"); 60 | properties.setProperty(system + ".jdbc.password", "jdbcPassword"); 61 | properties.setProperty(system + ".jdbc.dialect", "hsqldb"); 62 | properties.setProperty("migration.strategy", strategy); 63 | return properties; 64 | } 65 | 66 | public static Properties getPropertiesWithDistributedSystemConfiguration(String system, 67 | String strategy, String subsystems) 68 | { 69 | Properties properties = getPropertiesWithSystemConfiguration(system, strategy); 70 | properties.setProperty(system + ".context", system); 71 | properties.setProperty(system + ".controlled.systems", subsystems); 72 | 73 | return properties; 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/util/ConnectionWrapperDataSourceTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.util; 17 | 18 | import java.sql.Connection; 19 | 20 | import com.mockrunner.jdbc.JDBCTestCaseAdapter; 21 | 22 | /** 23 | * Test our ConnectionWrapperDataSource 24 | * 25 | * @author Mike Hardy (mike@tacitknowledge.com) 26 | */ 27 | public class ConnectionWrapperDataSourceTest extends JDBCTestCaseAdapter 28 | { 29 | /** A connection object we can use as a fixture */ 30 | private Connection conn = null; 31 | 32 | /** 33 | * @see com.mockrunner.jdbc.JDBCTestCaseAdapter#setUp() 34 | */ 35 | public void setUp() throws Exception 36 | { 37 | super.setUp(); 38 | conn = getJDBCMockObjectFactory().getMockConnection(); 39 | } 40 | 41 | /** 42 | * Test connection wrapping to make sure we get and return the same one 43 | * 44 | * @exception Exception if anything goes wrong 45 | */ 46 | public void testConnectionWrapping() throws Exception 47 | { 48 | ConnectionWrapperDataSource ds = new ConnectionWrapperDataSource(conn); 49 | assertEquals(conn, ds.getConnection()); 50 | assertEquals(conn, ds.getConnection("foo", "bar")); 51 | } 52 | 53 | /** 54 | * Make sure our unsuupported operations really are unsupported 55 | */ 56 | public void testUnsupportedOperations() 57 | { 58 | ConnectionWrapperDataSource ds = new ConnectionWrapperDataSource(conn); 59 | 60 | try 61 | { 62 | ds.getLogWriter(); 63 | fail("Should have gotten an unsupported operation exception"); 64 | } 65 | catch (UnsupportedOperationException uoe) 66 | { 67 | // we expect this 68 | } 69 | 70 | try 71 | { 72 | ds.setLogWriter(null); 73 | fail("Should have gotten an unsupported operation exception"); 74 | } 75 | catch (UnsupportedOperationException uoe) 76 | { 77 | // we expect this 78 | } 79 | 80 | try 81 | { 82 | ds.getLoginTimeout(); 83 | fail("Should have gotten an unsupported operation exception"); 84 | } 85 | catch (UnsupportedOperationException uoe) 86 | { 87 | // we expect this 88 | } 89 | 90 | try 91 | { 92 | ds.setLoginTimeout(-1); 93 | fail("Should have gotten an unsupported operation exception"); 94 | } 95 | catch (UnsupportedOperationException uoe) 96 | { 97 | // we expect this 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/loader/NameParseTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.loader; 17 | 18 | import junit.framework.TestCase; 19 | 20 | /** 21 | * Test used to verify that the table name is correctly 22 | * parsed. 23 | * 24 | * @author Chris A. (chris@tacitknowledge.com) 25 | */ 26 | public class NameParseTest extends TestCase 27 | { 28 | /** 29 | * The dummy loader used for testing 30 | */ 31 | private TestLoader loader = null; 32 | 33 | /** 34 | * Constructor that invokes its parent's constructor 35 | * 36 | * @param name the test name 37 | */ 38 | public NameParseTest(String name) 39 | { 40 | super(name); 41 | } 42 | 43 | /** 44 | * @see junit.framework.TestCase#setUp() 45 | */ 46 | public void setUp() throws Exception 47 | { 48 | super.setUp(); 49 | loader = new TestLoader(); 50 | } 51 | 52 | /** 53 | * Tests several combinations of input names to insure things are 54 | * working well. 55 | */ 56 | public void testNameParsing() 57 | { 58 | String longName = DelimitedFileLoader.PATH_SEPARATOR + "parent-dir" 59 | + DelimitedFileLoader.PATH_SEPARATOR + "child-dir" 60 | + DelimitedFileLoader.PATH_SEPARATOR + "table_db20040704.load"; 61 | String medName = DelimitedFileLoader.PATH_SEPARATOR + "table_db20040704.load"; 62 | String[] names = {"table_db.dat", longName, medName}; 63 | for (int i = 0; i < names.length; i++) 64 | { 65 | loader.setName(names[i]); 66 | String tb = loader.getTableFromName(); 67 | assertTrue("table".equals(tb)); 68 | } 69 | } 70 | 71 | /** 72 | * Inner class used to test file loader 73 | */ 74 | private class TestLoader extends DelimitedFileLoader 75 | { 76 | /** 77 | * The name of the file 78 | */ 79 | private String name = null; 80 | 81 | /** 82 | * @see com.tacitknowledge.util.migration.MigrationTask#getName() 83 | */ 84 | public String getName() 85 | { 86 | return name; 87 | } 88 | 89 | /** 90 | * @see com.tacitknowledge.util.migration.MigrationTaskSupport#setName(java.lang.String) 91 | */ 92 | public void setName(String name) 93 | { 94 | this.name = name; 95 | } 96 | 97 | /** 98 | * @see com.tacitknowledge.util.migration.jdbc.loader.DelimitedFileLoader#getDelimiter() 99 | */ 100 | public String getDelimiter() 101 | { 102 | return "|"; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/DataSourceMigrationContextTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | 21 | import junit.framework.TestCase; 22 | 23 | import com.tacitknowledge.util.migration.jdbc.util.SqlUtil; 24 | 25 | /** 26 | * Exercise the data source migration context 27 | * 28 | * @author Mike Hardy (mike@tacitknowledge.com) 29 | */ 30 | public class DataSourceMigrationContextTest extends TestCase 31 | { 32 | /** 33 | * A sample system name to use for testing 34 | */ 35 | public static final String TEST_SYSTEM_NAME = "testdb"; 36 | 37 | /** 38 | * Test the system name setting 39 | */ 40 | public void testSystemName() 41 | { 42 | DataSourceMigrationContext context = new DataSourceMigrationContext(); 43 | 44 | StringBuffer testNameBuffer = new StringBuffer(""); 45 | for (int i = 0; i < JdbcMigrationContext.MAX_SYSTEMNAME_LENGTH; i++) 46 | { 47 | testNameBuffer.append("a"); 48 | } 49 | String testName = testNameBuffer.toString(); 50 | 51 | try 52 | { 53 | context.setSystemName(null); 54 | fail("Should have thrown an exception on a null system name"); 55 | } 56 | catch (IllegalArgumentException iae) 57 | { 58 | // we expect this, assertion only to satisfy checkstyle 59 | // complaint about empty block 60 | assertNotNull(iae); 61 | } 62 | try 63 | { 64 | context.setSystemName(testName + "."); 65 | fail("We should have thrown an exception on a too-long system name"); 66 | } 67 | catch (IllegalArgumentException iae) 68 | { 69 | // we expect this, assertion only to satisfy checkstyle 70 | // complaint about empty block 71 | assertNotNull(iae); 72 | } 73 | context.setSystemName(testName); 74 | } 75 | 76 | /** 77 | * Test getting a connection on an uninitialized context 78 | */ 79 | public void testUseOfUninitializedContext() 80 | { 81 | DataSourceMigrationContext context = new DataSourceMigrationContext(); 82 | Connection conn = null; 83 | try 84 | { 85 | conn = context.getConnection(); 86 | fail("Expected SQLException"); 87 | } 88 | catch (SQLException e) 89 | { 90 | // we expect this, assertion only to satisfy checkstyle 91 | // complaint about empty block 92 | assertNotNull(e); 93 | } 94 | finally 95 | { 96 | SqlUtil.close(conn, null, null); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MissingPatchMigrationRunnerStrategy.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package com.tacitknowledge.util.migration; 16 | 17 | 18 | import org.apache.commons.lang.ArrayUtils; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.Set; 24 | 25 | /* 26 | * @author Hemri Herrera (hemri@tacitknowledge.com) 27 | * @author Ulises Pulido (upulido@tacitknowledge.com) 28 | */ 29 | 30 | public class MissingPatchMigrationRunnerStrategy implements MigrationRunnerStrategy 31 | { 32 | 33 | public boolean shouldMigrationRun(int migrationLevel, PatchInfoStore patchInfoStore) throws MigrationException 34 | { 35 | 36 | if (patchInfoStore == null) 37 | { 38 | throw new IllegalArgumentException("Patch Info Store should not be null"); 39 | } 40 | 41 | return !patchInfoStore.isPatchApplied(migrationLevel); 42 | } 43 | 44 | public boolean isSynchronized(PatchInfoStore currentPatchInfoStore, PatchInfoStore patchInfoStore) throws MigrationException 45 | { 46 | 47 | if (currentPatchInfoStore == null || patchInfoStore == null) 48 | { 49 | throw new IllegalArgumentException("currentPatchInfoStore and patchInfoStore should not be null"); 50 | } 51 | Set currentPatchInfoStorePatchesApplied = currentPatchInfoStore.getPatchesApplied(); 52 | Set patchInfoStorePatchesApplied = patchInfoStore.getPatchesApplied(); 53 | 54 | return currentPatchInfoStorePatchesApplied.equals(patchInfoStorePatchesApplied); 55 | } 56 | 57 | public List getRollbackCandidates(List allMigrationTasks, int[] rollbackLevels, PatchInfoStore currentPatchInfoStore) throws MigrationException 58 | { 59 | 60 | validateRollbackLevels(rollbackLevels); 61 | 62 | List rollbacksLevelList = Arrays.asList(ArrayUtils.toObject(rollbackLevels)); 63 | List rollbackCandidates = new ArrayList(); 64 | 65 | 66 | for (MigrationTask migrationTask : allMigrationTasks) 67 | { 68 | if (rollbacksLevelList.contains(migrationTask.getLevel()) 69 | && currentPatchInfoStore.isPatchApplied(migrationTask.getLevel())) 70 | { 71 | rollbackCandidates.add(migrationTask); 72 | } 73 | } 74 | 75 | return rollbackCandidates; 76 | } 77 | 78 | private void validateRollbackLevels(int[] rollbackLevels) throws MigrationException 79 | { 80 | if (rollbackLevels == null) 81 | { 82 | throw new MigrationException("rollbackLevels should not be null"); 83 | } 84 | 85 | if (rollbackLevels.length == 0) 86 | { 87 | throw new MigrationException("rollbackLevels should not be empty"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/ClassMigrationTaskSource.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import com.tacitknowledge.util.discovery.ClassDiscoveryUtil; 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * Returns a list of all public, concrete classes that implement the 27 | * MigrationTask in a specific package. 28 | * 29 | * @author Scott Askew (scott@tacitknowledge.com) 30 | */ 31 | public class ClassMigrationTaskSource implements MigrationTaskSource 32 | { 33 | /** 34 | * Class logger 35 | */ 36 | private static Log log = LogFactory.getLog(ClassMigrationTaskSource.class); 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public List getMigrationTasks(String packageName) throws MigrationException 42 | { 43 | if (packageName == null) 44 | { 45 | throw new MigrationException("You must specify a package to get tasks for"); 46 | } 47 | 48 | Class[] taskClasses = ClassDiscoveryUtil.getClasses(packageName, MigrationTask.class); 49 | log.debug("Found " + taskClasses.length + " patches in " + packageName); 50 | return instantiateTasks(taskClasses); 51 | } 52 | 53 | /** 54 | * Instantiates the given classes 55 | * 56 | * @param taskClasses the classes instantiate 57 | * @return a list of MigrationTasks 58 | * @throws MigrationException if a class could not be instantiated; this 59 | * is most likely due to the abscense of a default constructor 60 | */ 61 | private List instantiateTasks(Class[] taskClasses) throws MigrationException 62 | { 63 | List tasks = new ArrayList(); 64 | for (int i = 0; i < taskClasses.length; i++) 65 | { 66 | Class taskClass = taskClasses[i]; 67 | try 68 | { 69 | Object o = taskClass.newInstance(); 70 | 71 | // It's not legal to have a null name. 72 | MigrationTask task = (MigrationTask) o; 73 | if (task.getName() != null) 74 | { 75 | tasks.add(task); 76 | } 77 | else 78 | { 79 | log.warn("MigrationTask " + taskClass.getName() 80 | + " had no migration name. Is that intentional? Skipping task."); 81 | } 82 | } 83 | catch (Exception e) 84 | { 85 | throw new MigrationException("Could not instantiate MigrationTask " 86 | + taskClass.getName(), e); 87 | } 88 | } 89 | return tasks; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTaskSourceTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import java.util.Iterator; 19 | import java.util.List; 20 | 21 | import junit.framework.TestCase; 22 | 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import com.tacitknowledge.util.migration.MigrationException; 27 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 28 | 29 | /** 30 | * Exercise the SqlScriptMigrationTaskSource 31 | * 32 | * @author Mike Hardy (mike@tacitknowledge.com) 33 | */ 34 | public class SqlScriptMigrationTaskSourceTest extends TestCase 35 | { 36 | /** Class logger */ 37 | private static Log log = LogFactory.getLog(SqlScriptMigrationTaskSource.class); 38 | 39 | /** 40 | * Test loading up all the scripts in our test package 41 | */ 42 | public void testScriptLoad() 43 | { 44 | SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); 45 | List tasks = null; 46 | try 47 | { 48 | tasks = source.getMigrationTasks(this.getClass().getPackage().getName() + ".test"); 49 | } 50 | catch (MigrationException me) 51 | { 52 | log.info("Unexpectedly caught: "+ me); 53 | fail("There shouldn't have been a problem loading the tasks: "+ me); 54 | } 55 | 56 | // There are 3 scripts in our package of scripts, make sure they are all here 57 | assertEquals(3, tasks.size()); 58 | } 59 | 60 | /** 61 | * Test that a Migration which does not have a rollback script correctly 62 | * returns false for isRollbackSupported. 63 | */ 64 | public void testNonRollbackableScript() 65 | { 66 | SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); 67 | List tasks = null; 68 | RollbackableMigrationTask task = null; 69 | try 70 | { 71 | tasks = source.getMigrationTasks(this.getClass().getPackage().getName() + ".test"); 72 | 73 | for(Iterator i=tasks.iterator(); i.hasNext(); ) 74 | { 75 | //patch with ID 2 has no rollback 76 | task = (RollbackableMigrationTask) i.next(); 77 | if(task.getLevel().equals(Integer.valueOf(2))) 78 | { 79 | assertFalse(task.isRollbackSupported()); 80 | } 81 | else 82 | { 83 | assertTrue(task.isRollbackSupported()); 84 | } 85 | } 86 | 87 | } 88 | catch (MigrationException me) 89 | { 90 | log.info("Unexpectedly caught: "+ me); 91 | fail("There shouldn't have been a problem loading the tasks: "+ me); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/integration-test/java/com/tacitknowledge/util/migration/MultiServerRaceConditionTest.java: -------------------------------------------------------------------------------- 1 | package com.tacitknowledge.util.migration; 2 | 3 | import java.sql.*; 4 | 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | 8 | import junit.framework.TestCase; 9 | 10 | import com.tacitknowledge.util.migration.MigrationContext; 11 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncher; 12 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncherFactory; 13 | 14 | /** 15 | * Pin-points a race condition when the AutoPatcher is run from multiple servers on the same schema. 16 | * 17 | * @author Jeff Kolesky (jeff@kolesky.com) 18 | */ 19 | public class MultiServerRaceConditionTest extends TestCase 20 | { 21 | private static Log log = LogFactory.getLog(MultiServerRaceConditionTest.class); 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param name the name of the test to run 27 | */ 28 | public MultiServerRaceConditionTest(String name) 29 | { 30 | super(name); 31 | } 32 | 33 | public void tearDown() throws Exception 34 | { 35 | super.tearDown(); 36 | Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:race", "sa", ""); 37 | Statement stmt = conn.createStatement(); 38 | stmt.execute("SHUTDOWN"); 39 | } 40 | 41 | /** 42 | * Tests that two servers both booting up the AutoPatcher will not both try to apply the same patches 43 | * 44 | * @exception Exception if anything goes wrong 45 | */ 46 | public void testMultiServerRaceCondition() throws Exception 47 | { 48 | log.debug("Testing multi server race condition"); 49 | MigrationThread a = new MigrationThread("A"); 50 | MigrationThread b = new MigrationThread("B"); 51 | 52 | a.start(); 53 | b.start(); 54 | 55 | boolean success = true; 56 | 57 | success &= finish(a); 58 | success &= finish(b); 59 | 60 | if (!success) 61 | { 62 | fail("shouldn't have thrown any exceptions"); 63 | } 64 | } 65 | 66 | private boolean finish(MigrationThread t) throws Exception 67 | { 68 | t.join(); 69 | Exception error = t.getError(); 70 | if (error != null) 71 | { 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | private static class MigrationThread extends Thread 78 | { 79 | private JdbcMigrationLauncherFactory factory; 80 | private JdbcMigrationLauncher launcher; 81 | private Exception error; 82 | 83 | private MigrationThread(String name) throws Exception 84 | { 85 | super(name); 86 | this.factory = new JdbcMigrationLauncherFactory(); 87 | this.launcher = this.factory.createMigrationLauncher("race", "multiserver-inttest-migration.properties"); 88 | // initialize the patch table *not* in parallel 89 | this.launcher.getDatabasePatchLevel((MigrationContext)this.launcher.getContexts().keySet().iterator().next()); 90 | } 91 | 92 | public Exception getError() 93 | { 94 | return this.error; 95 | } 96 | 97 | public void run() 98 | { 99 | try 100 | { 101 | this.launcher.doMigrations(); 102 | } 103 | catch (Exception e) 104 | { 105 | this.error = e; 106 | log.error("Unexpected error", this.error); 107 | } 108 | } 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/TestDistributedJdbcMigrationLauncher.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | 21 | import com.tacitknowledge.util.migration.MigrationContext; 22 | import com.tacitknowledge.util.migration.MigrationException; 23 | import com.tacitknowledge.util.migration.MigrationTask; 24 | import com.tacitknowledge.util.migration.PatchInfoStore; 25 | import com.tacitknowledge.util.migration.RollbackableMigrationTask; 26 | 27 | /** 28 | * Override select things in the JdbcMigrationLauncher for testing purposes 29 | * 30 | * @author Mike Hardy (mike@tacitknowledge.com) 31 | */ 32 | public class TestDistributedJdbcMigrationLauncher extends DistributedJdbcMigrationLauncher 33 | { 34 | /** Class logger */ 35 | private static Log log = LogFactory.getLog(TestDistributedJdbcMigrationLauncher.class); 36 | 37 | /** The PatchInfoStore to use for migrations FIXME need to store a map of them */ 38 | private PatchInfoStore patchStore = null; 39 | 40 | /** 41 | * Delegates to the superclass 42 | */ 43 | public TestDistributedJdbcMigrationLauncher() 44 | { 45 | super(); 46 | } 47 | 48 | /** 49 | * Delegating constructors 50 | * 51 | * @param context the context to use for migration loading 52 | */ 53 | public TestDistributedJdbcMigrationLauncher(JdbcMigrationContext context) 54 | { 55 | super(context); 56 | } 57 | 58 | /** 59 | * Override the patch store creation to be the patch table we have 60 | * 61 | * @param context the context to use for the patch store 62 | * @return patchStore held internally 63 | * @throws MigrationException if creating the store fails 64 | */ 65 | protected PatchInfoStore createPatchStore(JdbcMigrationContext context) 66 | throws MigrationException 67 | { 68 | if (patchStore != null) 69 | { 70 | return patchStore; 71 | } 72 | 73 | return super.createPatchStore(context); 74 | } 75 | 76 | /** 77 | * Set the PatchInfoStore object to use for migrations 78 | * 79 | * @param patchStore the PatchInfoStore to use 80 | */ 81 | public void setPatchStore(PatchInfoStore patchStore) 82 | { 83 | this.patchStore = patchStore; 84 | } 85 | 86 | /** 87 | * {@inheritDoc} 88 | */ 89 | public void migrationSuccessful(MigrationTask task, MigrationContext ctx) 90 | throws MigrationException 91 | { 92 | log.debug(this + " silently ignoring a migrationSuccessful call"); 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | */ 98 | public void rollbackSuccessful(RollbackableMigrationTask task, int rollbackLevel, 99 | MigrationContext context) throws MigrationException 100 | { 101 | log.debug(this + " silently ignoring a rollbackSuccessful call"); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/PatchRollbackPredicateTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import junit.framework.TestCase; 19 | 20 | import org.apache.commons.collections.Predicate; 21 | 22 | import com.tacitknowledge.util.migration.tasks.rollback.TestRollbackableTask1; 23 | 24 | /** 25 | * this test cases test the PatchRollbackPredicate 26 | * 27 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 28 | */ 29 | public class PatchRollbackPredicateTest extends TestCase { 30 | 31 | public void testPredicateReturnsInRange() { 32 | 33 | //create a new predicate 34 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,1); 35 | TestRollbackableTask1 task = new TestRollbackableTask1(4); 36 | 37 | boolean result = patchRollbackPredicate.evaluate(task); 38 | assertTrue("PatchRollbackPredicate returned false unexpectedly",result); 39 | } 40 | 41 | public void testPredicateReturnsLessThanRollback() { 42 | 43 | //create a new predicate 44 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 45 | TestRollbackableTask1 task = new TestRollbackableTask1(1); 46 | 47 | boolean result = patchRollbackPredicate.evaluate(task); 48 | assertFalse("PatchRollbackPredicate returned true unexpectedly",result); 49 | } 50 | 51 | public void testPredicateReturnsGreaterThanCurrent() { 52 | 53 | //create a new predicate 54 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 55 | TestRollbackableTask1 task = new TestRollbackableTask1(11); 56 | 57 | boolean result = patchRollbackPredicate.evaluate(task); 58 | assertFalse("PatchRollbackPredicate returned true unexpectedly",result); 59 | } 60 | 61 | public void testPredicateReturnsLowerBoundary() { 62 | 63 | //create a new predicate 64 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 65 | TestRollbackableTask1 task = new TestRollbackableTask1(2); 66 | 67 | boolean result = patchRollbackPredicate.evaluate(task); 68 | assertFalse("PatchRollbackPredicate returned true unexpectedly",result); 69 | } 70 | public void testPredicateReturnsUpperBoundary() { 71 | 72 | //create a new predicate 73 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 74 | TestRollbackableTask1 task = new TestRollbackableTask1(10); 75 | 76 | boolean result = patchRollbackPredicate.evaluate(task); 77 | assertTrue("PatchRollbackPredicate returned false unexpectedly",result); 78 | } 79 | public void testPatchRollbackPredicateNull() { 80 | 81 | //create a new predicate 82 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 83 | 84 | boolean result = patchRollbackPredicate.evaluate(null); 85 | assertFalse("PatchRollbackPredicate returned true unexpectedly",result); 86 | } 87 | 88 | public void testPatchRollbackPredicateWrongClass() { 89 | 90 | //create a new predicate 91 | Predicate patchRollbackPredicate = new PatchRollbackPredicate(10,2); 92 | 93 | boolean result = patchRollbackPredicate.evaluate(new Object()); 94 | assertFalse("testPatchRollbackPredicateWrongClass returned true unexpectedly",result); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/PatchInfoStore.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | import java.util.Set; 19 | 20 | /** 21 | * Interface for the persistence of information related to the patch level 22 | * of the system, as well as whether patches are currently being applied 23 | * 24 | * @author Mike Hardy (mike@tacitknowledge.com) 25 | * @author Hemri Herrera (hemri@tacitknowledge.com) 26 | * @author Ulises Pulido (upulido@tacitknowledge.com) 27 | */ 28 | public interface PatchInfoStore 29 | { 30 | /** 31 | * Creates the patch storage area if it has not been done before 32 | * 33 | * @throws MigrationException if creation is unsuccessful 34 | */ 35 | public void createPatchStoreIfNeeded() throws MigrationException; 36 | 37 | /** 38 | * Returns the current patch level of the system 39 | * 40 | * @return the current patch level of the system 41 | * @throws MigrationException if it is not possible to get the patch level 42 | */ 43 | public int getPatchLevel() throws MigrationException; 44 | 45 | /** 46 | * Updates the system patch level to the specified value 47 | * 48 | * @param level the new system patch level 49 | * @throws MigrationException if updating the patch level failed 50 | */ 51 | public void updatePatchLevel(int level) throws MigrationException; 52 | 53 | /** 54 | * Determines if the patch store is already locked 55 | * 56 | * @return true if the patch store is already locked 57 | * @throws MigrationException if checking for the lock fails 58 | */ 59 | public boolean isPatchStoreLocked() throws MigrationException; 60 | 61 | /** 62 | * Places a lock for this system on the patch store 63 | * 64 | * @throws MigrationException if locking the store fails 65 | * @throws IllegalStateException if a lock already exists 66 | */ 67 | public void lockPatchStore() throws MigrationException, IllegalStateException; 68 | 69 | /** 70 | * Removes any locks for this system on the patch store 71 | * 72 | * @throws MigrationException if unlocking the store fails 73 | */ 74 | public void unlockPatchStore() throws MigrationException; 75 | 76 | /** 77 | * Determines if a given patch has been applied in the system 78 | * 79 | * @throws MigrationException if unlocking the store fails 80 | */ 81 | public boolean isPatchApplied(int patchLevel) throws MigrationException; 82 | 83 | /** 84 | * Updates the system patch level to the specified value after rollback 85 | * 86 | * @param rollbackLevel the new system patch level 87 | * @throws MigrationException if updating the patch level failed 88 | */ 89 | public void updatePatchLevelAfterRollBack(int rollbackLevel) throws MigrationException; 90 | 91 | /** 92 | * Obtains all patches applied in the system. 93 | * 94 | * @return a set containing all patches number applied in the system. 95 | * @throws MigrationException if retrieving patches fails. 96 | */ 97 | public Set getPatchesApplied() throws MigrationException; 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/com/tacitknowledge/util/migration/jdbc/loader/QueryTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.loader; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | 21 | import junit.framework.TestCase; 22 | 23 | /** 24 | * Test used to verify that the sql query is correctly 25 | * created 26 | * 27 | * @author Chris A. (chris@tacitknowledge.com) 28 | */ 29 | public class QueryTest extends TestCase 30 | { 31 | /** 32 | * The dummy loader used for testing 33 | */ 34 | private TestLoader loader = null; 35 | 36 | /** 37 | * Constructor that invokes its parent's constructor 38 | * 39 | * @param name the test name 40 | */ 41 | public QueryTest(String name) 42 | { 43 | super(name); 44 | } 45 | 46 | /** 47 | * @see junit.framework.TestCase#setUp() 48 | */ 49 | public void setUp() throws Exception 50 | { 51 | super.setUp(); 52 | loader = new TestLoader(); 53 | } 54 | 55 | /** 56 | * Tests query generation 57 | */ 58 | public void testQuery() 59 | { 60 | loader.setName("mocktable_db.dat"); 61 | String sql = loader.getStatmentSql(); 62 | String answer = "INSERT INTO mocktable (col1, col2, col3, col4) " 63 | + "VALUES (?, ?, ?, ?)"; 64 | assertTrue(answer.equals(sql)); 65 | } 66 | 67 | /** 68 | * Inner class used to test file loader 69 | */ 70 | private class TestLoader extends DelimitedFileLoader 71 | { 72 | /** 73 | * The name of the file 74 | */ 75 | private String name = null; 76 | 77 | /** 78 | * @see com.tacitknowledge.util.migration.MigrationTask#getName() 79 | */ 80 | public String getName() 81 | { 82 | return name; 83 | } 84 | 85 | /** 86 | * @see com.tacitknowledge.util.migration.MigrationTaskSupport#setName(java.lang.String) 87 | */ 88 | public void setName(String name) 89 | { 90 | this.name = name; 91 | } 92 | 93 | /** 94 | * @see com.tacitknowledge.util.migration.jdbc.loader.DelimitedFileLoader#getDelimiter() 95 | */ 96 | public String getDelimiter() 97 | { 98 | return "|"; 99 | } 100 | 101 | /** 102 | * Returns the header (first line) of the file. 103 | * 104 | * @param is the input stream containing the data to load 105 | * @return the first row 106 | * @throws IOException if the input stream could not be read 107 | */ 108 | protected String getHeader(InputStream is) throws IOException 109 | { 110 | return "col1|col2|col3|col4"; 111 | } 112 | 113 | /** 114 | * @see com.tacitknowledge.util.migration.jdbc.SqlLoadMigrationTask#getResourceAsStream() 115 | */ 116 | protected InputStream getResourceAsStream() 117 | { 118 | return null; 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/loader/ExcelFileLoader.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.loader; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTaskSupport; 21 | import com.tacitknowledge.util.migration.jdbc.DataSourceMigrationContext; 22 | import com.tacitknowledge.util.migration.jdbc.util.SqlUtil; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 26 | import org.apache.poi.poifs.filesystem.POIFSFileSystem; 27 | 28 | import java.io.IOException; 29 | import java.sql.Connection; 30 | import java.sql.SQLException; 31 | 32 | /** 33 | * This is a utility class for reading excel files and 34 | * performing a database insert based upon a cell value 35 | * provided. 36 | * 37 | * @author Chris A. (chris@tacitknowledge.com) 38 | */ 39 | public abstract class ExcelFileLoader extends MigrationTaskSupport 40 | { 41 | /** 42 | * Class logger 43 | */ 44 | private static Log log = LogFactory.getLog(ExcelFileLoader.class); 45 | 46 | /** 47 | * Obtains a database connection, reads a file assumed to be in Excel 48 | * format based on the name provided getName(). Calls the 49 | * abstract method processWorkbook() 50 | * 51 | * @param ctx the JdbcMigrationContext 52 | * @throws MigrationException if an unexpected error occurs 53 | */ 54 | public void migrate(MigrationContext ctx) throws MigrationException 55 | { 56 | DataSourceMigrationContext context = (DataSourceMigrationContext) ctx; 57 | FileLoadingUtility utility = new FileLoadingUtility(getName()); 58 | Connection conn = null; 59 | 60 | try 61 | { 62 | conn = context.getConnection(); 63 | POIFSFileSystem fs = new POIFSFileSystem(utility.getResourceAsStream()); 64 | HSSFWorkbook wb = new HSSFWorkbook(fs); 65 | processWorkbook(wb, conn); 66 | context.commit(); 67 | } 68 | catch (IOException e) 69 | { 70 | log.error("An IO Exception occurred while trying to parse the Excel file.", e); 71 | context.rollback(); 72 | throw new MigrationException("Error reading file.", e); 73 | } 74 | catch (SQLException e) 75 | { 76 | log.error("Caught a SQLException when trying to obtain a database connection"); 77 | context.rollback(); 78 | throw new MigrationException("Error obtaining database connection", e); 79 | } 80 | finally 81 | { 82 | SqlUtil.close(conn, null, null); 83 | } 84 | } 85 | 86 | /** 87 | * Process workbook by overwriting this method 88 | * 89 | * @param wb the excel workbook to process 90 | * @param conn the database connection to use for data loading 91 | * @throws MigrationException if something goes wrong 92 | */ 93 | public abstract void processWorkbook(HSSFWorkbook wb, Connection conn) 94 | throws MigrationException; 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/MigrationTaskSupport.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration; 17 | 18 | /** 19 | * Convenience base class for migration tasks. 20 | * 21 | * @author Scott Askew (scott@tacitknowledge.com) 22 | * @author Artie Pesh-Imam (apeshimam@tacitknowledge.com) 23 | */ 24 | public abstract class MigrationTaskSupport implements RollbackableMigrationTask 25 | { 26 | protected boolean isRollbackSupported = false; 27 | 28 | /** 29 | * The name of this migration task 30 | */ 31 | private String name = this.getClass().getName(); 32 | 33 | /** 34 | * The relative order in which this test should run 35 | */ 36 | private Integer level; 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public String getName() 42 | { 43 | return name; 44 | } 45 | 46 | /** 47 | * Sets the name of this migration task. 48 | * 49 | * @param name the name of this migration task 50 | */ 51 | public void setName(String name) 52 | { 53 | this.name = name; 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public Integer getLevel() 60 | { 61 | return level; 62 | } 63 | 64 | /** 65 | * Sets the relative order in which this test should run 66 | * 67 | * @param lvl the relative order in which this test should run 68 | */ 69 | public void setLevel(Integer lvl) 70 | { 71 | this.level = lvl; 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public int compareTo(Object o) 78 | { 79 | MigrationTask task = (MigrationTask) o; 80 | if (task.getLevel() == null) 81 | { 82 | return 1; 83 | } 84 | return getLevel().compareTo(task.getLevel()); 85 | } 86 | 87 | /** 88 | * By default, this method is not supported. 89 | */ 90 | public void down(MigrationContext context) throws MigrationException 91 | { 92 | throw new UnsupportedOperationException("This method is not supported by this task."); 93 | } 94 | 95 | /** 96 | * @return a boolean indicating if rollback is supported 97 | */ 98 | public boolean isRollbackSupported() 99 | { 100 | return isRollbackSupported; 101 | } 102 | 103 | /** 104 | * Sets the isRollbackSupported attribute 105 | * 106 | * @param isRollbackSupported 107 | */ 108 | public void setRollbackSupported(boolean isRollbackSupported) 109 | { 110 | this.isRollbackSupported = isRollbackSupported; 111 | } 112 | 113 | /** 114 | * By default, this method delegates to the up method. 115 | */ 116 | public void migrate(MigrationContext context) throws MigrationException 117 | { 118 | up(context); 119 | } 120 | 121 | /** 122 | * By default, this method is a no-op. If a legacy task extends 123 | * MigrationTaskSupport but does not implement up, it would cause a 124 | * compilation error. This no-op method resolves that issue. 125 | */ 126 | public void up(MigrationContext context) throws MigrationException 127 | { 128 | // no op 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/DistributedJdbcMigrationLauncher.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import com.tacitknowledge.util.migration.DistributedMigrationProcess; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationProcess; 21 | import com.tacitknowledge.util.migration.MigrationRunnerFactory; 22 | 23 | /** 24 | * Core starting point for a distributed database migration run. 25 | * This class obtains a connection to the orchestration database, 26 | * checks its patch level, delegates the actual execution of the 27 | * migration tasks to a MigrationProcess instance, 28 | * and then commits and cleans everything up at the end. 29 | *

30 | * This class is NOT threadsafe. 31 | * 32 | * @author Mike Hardy (mike@tacitknowledge.com) 33 | */ 34 | public class DistributedJdbcMigrationLauncher extends JdbcMigrationLauncher 35 | { 36 | /** 37 | * Create a new MigrationProcess and add a SqlScriptMigrationTaskSource 38 | */ 39 | public DistributedJdbcMigrationLauncher() 40 | { 41 | super(); 42 | } 43 | 44 | /** 45 | * Create a new MigrationLancher. 46 | * 47 | * @param context the JdbcMigrationContext to use. 48 | */ 49 | public DistributedJdbcMigrationLauncher(JdbcMigrationContext context) 50 | { 51 | super(context); 52 | } 53 | 54 | /** 55 | * Override the sub-class so we get a DistributedMigrationProcess instead of the 56 | * normal one 57 | * 58 | * @return DistributedMigrationProcess 59 | */ 60 | public MigrationProcess getNewMigrationProcess() 61 | { 62 | DistributedMigrationProcess migrationProcess = new DistributedMigrationProcess(); 63 | migrationProcess.setMigrationRunnerStrategy 64 | (MigrationRunnerFactory.getMigrationRunnerStrategy(getMigrationStrategy())); 65 | return migrationProcess; 66 | } 67 | 68 | /** 69 | * Starts the application migration process across all configured contexts 70 | * 71 | * @return the number of patches applied 72 | * @throws MigrationException if an unrecoverable error occurs during 73 | * the migration 74 | */ 75 | public int doMigrations() throws MigrationException 76 | { 77 | if (getContexts().size() == 0) 78 | { 79 | throw new MigrationException("You must configure a migration context"); 80 | } 81 | 82 | return super.doMigrations(); 83 | } 84 | 85 | /** 86 | * Starts the application migration process across all configured contexts 87 | * 88 | * @return the number of patches applied 89 | * @throws MigrationException if an unrecoverable error occurs during 90 | * the migration 91 | */ 92 | public int doRollbacks(int rollbackLevel) throws MigrationException 93 | { 94 | if (getContexts().size() == 0) 95 | { 96 | throw new MigrationException("You must configure a migration context"); 97 | } 98 | 99 | return super.doRollbacks(new int[]{rollbackLevel}); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/OrderedMigrationRunnerStrategy.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package com.tacitknowledge.util.migration; 16 | 17 | import org.apache.commons.collections.CollectionUtils; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | /** 24 | * Helps to get information about how the migrations should run based in an ordered stategy, i.e. 25 | * If the level of the MigrationTask is greater than the current level and 26 | * the MigrationTaskhas not yet been applied. Then the information of that migration 27 | * is considered missing. 28 | * 29 | * @author Oscar Gonzalez (oscar@tacitknowledge.com) 30 | * @author Hemri Herrera (hemri@tacitknowledge.com) 31 | * @author Ulises Pulido (upulido@tacitknowledge.com) 32 | */ 33 | public class OrderedMigrationRunnerStrategy implements MigrationRunnerStrategy 34 | { 35 | public boolean shouldMigrationRun(int migrationLevel, PatchInfoStore patchInfoStore) throws MigrationException 36 | { 37 | return migrationLevel > patchInfoStore.getPatchLevel(); 38 | } 39 | 40 | public boolean isSynchronized(PatchInfoStore currentPatchInfoStore, PatchInfoStore patchInfoStore) throws MigrationException 41 | { 42 | 43 | if (currentPatchInfoStore == null || patchInfoStore == null) 44 | { 45 | throw new IllegalArgumentException("currentPatchInfoStore and patchInfoStore should not be null"); 46 | } 47 | 48 | return currentPatchInfoStore.getPatchLevel() == patchInfoStore.getPatchLevel(); 49 | } 50 | 51 | public List getRollbackCandidates(List allMigrationTasks, int[] rollbackLevels, PatchInfoStore currentPatchInfoStore) throws MigrationException 52 | { 53 | validateRollbackLevel(rollbackLevels); 54 | 55 | int rollbackLevel = rollbackLevels[0]; 56 | int currentPatchLevel = currentPatchInfoStore.getPatchLevel(); 57 | 58 | if (currentPatchLevel < rollbackLevel) 59 | { 60 | throw new MigrationException( 61 | "The rollback patch level cannot be greater than the current patch level"); 62 | } 63 | 64 | PatchRollbackPredicate rollbackPredicate = new PatchRollbackPredicate(currentPatchLevel, 65 | rollbackLevel); 66 | List migrationCandidates = new ArrayList(); 67 | migrationCandidates.addAll(allMigrationTasks); 68 | CollectionUtils.filter(migrationCandidates, rollbackPredicate); 69 | Collections.sort(migrationCandidates); 70 | // need to reverse the list do we apply the rollbacks in descending 71 | // order 72 | Collections.reverse(migrationCandidates); 73 | return migrationCandidates; 74 | 75 | } 76 | 77 | private void validateRollbackLevel(int[] rollbackLevels) throws MigrationException 78 | { 79 | if (rollbackLevels == null) 80 | { 81 | throw new MigrationException("rollbackLevels should not be null"); 82 | } 83 | 84 | if (rollbackLevels.length == 0) 85 | { 86 | throw new MigrationException("rollbackLevels should not be empty"); 87 | } 88 | 89 | if (rollbackLevels.length > 1) 90 | { 91 | throw new MigrationException("OrderedMigrationRunnerStrategy only supports one rollbackLevel"); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/util/MigrationUtil.java: -------------------------------------------------------------------------------- 1 | package com.tacitknowledge.util.migration.jdbc.util; 2 | 3 | import com.tacitknowledge.util.migration.MigrationException; 4 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncher; 5 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncherFactory; 6 | import com.tacitknowledge.util.migration.jdbc.JdbcMigrationLauncherFactoryLoader; 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | 10 | import javax.servlet.ServletContextEvent; 11 | 12 | /** 13 | * A utility class for migration initialization needs 14 | * 15 | * @author Petric Coroli (pcoroli@tacitknowledge.com) 16 | */ 17 | public class MigrationUtil 18 | { 19 | 20 | private static Log log = LogFactory.getLog(MigrationUtil.class); 21 | 22 | public JdbcMigrationLauncherFactory getLauncherFactory() 23 | { 24 | return launcherFactory; 25 | } 26 | 27 | public void setLauncherFactory(JdbcMigrationLauncherFactory launcherFactory) 28 | { 29 | this.launcherFactory = launcherFactory; 30 | } 31 | 32 | private JdbcMigrationLauncherFactory launcherFactory; 33 | 34 | 35 | /** 36 | * Helper method to initiate the migration process. 37 | * 38 | * @param sce the ServletContextEvent being handled 39 | * @throws MigrationException 40 | */ 41 | public static void doMigrations(final ServletContextEvent sce) throws MigrationException 42 | { 43 | JdbcMigrationLauncherFactory launcherFactory = 44 | new JdbcMigrationLauncherFactoryLoader().createFactory(); 45 | JdbcMigrationLauncher launcher = launcherFactory.createMigrationLauncher(sce); 46 | launcher.doMigrations(); 47 | } 48 | 49 | /** 50 | * Helper method to initiate the migration process. 51 | * 52 | * @param migrationSystemName the name of the system to migrate 53 | * @param migrationSettings additional properties for migration 54 | * @throws MigrationException 55 | */ 56 | public static void doMigrations(final String migrationSystemName, 57 | final String migrationSettings) throws MigrationException 58 | { 59 | JdbcMigrationLauncherFactory launcherFactory = new JdbcMigrationLauncherFactoryLoader() 60 | .createFactory(); 61 | JdbcMigrationLauncher launcher = null; 62 | 63 | if (migrationSettings == null) 64 | { 65 | log.info("Using migration.properties (default)"); 66 | launcher = launcherFactory.createMigrationLauncher(migrationSystemName); 67 | } 68 | else 69 | { 70 | log.info("Using " + migrationSettings); 71 | launcher = launcherFactory.createMigrationLauncher(migrationSystemName, 72 | migrationSettings); 73 | } 74 | 75 | launcher.doMigrations(); 76 | } 77 | 78 | /** 79 | * Helper method to initiate the migration process. 80 | * 81 | * @param migrationSystemName the name of the system to migrate 82 | * @param migrationSettings additional properties for migration 83 | * @throws MigrationException 84 | */ 85 | public void doRollbacks(final String migrationSystemName, 86 | final String migrationSettings, final int[] rollbackLevel, final boolean forceRollback) 87 | throws MigrationException 88 | { 89 | 90 | JdbcMigrationLauncher launcher = null; 91 | 92 | if (migrationSettings == null) 93 | { 94 | log.info("Using migration.properties (default)"); 95 | launcher = getLauncherFactory().createMigrationLauncher(migrationSystemName); 96 | } 97 | else 98 | { 99 | log.info("Using " + migrationSettings); 100 | launcher = getLauncherFactory().createMigrationLauncher(migrationSystemName, 101 | migrationSettings); 102 | } 103 | 104 | launcher.doRollbacks(rollbackLevel, forceRollback); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/util/ConnectionWrapperDataSource.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.util; 17 | 18 | import javax.sql.DataSource; 19 | import java.io.PrintWriter; 20 | import java.sql.Connection; 21 | import java.sql.SQLException; 22 | import java.sql.SQLFeatureNotSupportedException; 23 | import java.util.logging.Logger; 24 | 25 | /** 26 | * A partial DataSource implementation that simply wraps a single, 27 | * already opened Connection. 28 | *

29 | * Only the two getConnection methods are supported. 30 | * 31 | * @author Scott Askew (scott@tacitknowledge.com) 32 | */ 33 | public class ConnectionWrapperDataSource implements DataSource 34 | { 35 | /** 36 | * The message used in UnsupportedOperationExceptions. 37 | */ 38 | public static final String UNSUPPORTED_OPERATION_EXCEPTION_MSG 39 | = ConnectionWrapperDataSource.class 40 | + " is not a fully functioning DataSource and only" 41 | + " supports the getConnection methods."; 42 | 43 | /** 44 | * The underlying connection 45 | */ 46 | private Connection connection = null; 47 | 48 | /** 49 | * Creates a new ConnectionWrapperDataSource. 50 | * 51 | * @param connection the connection to use for this data source 52 | */ 53 | public ConnectionWrapperDataSource(Connection connection) 54 | { 55 | this.connection = connection; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | public Connection getConnection() throws SQLException 62 | { 63 | return connection; 64 | } 65 | 66 | /** 67 | * {@inheritDoc} 68 | */ 69 | public Connection getConnection(String user, String pass) throws SQLException 70 | { 71 | return connection; 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public PrintWriter getLogWriter() throws UnsupportedOperationException 78 | { 79 | throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MSG); 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | public void setLogWriter(PrintWriter arg0) throws UnsupportedOperationException 86 | { 87 | throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MSG); 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | public int getLoginTimeout() throws UnsupportedOperationException 94 | { 95 | throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MSG); 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 102 | throw new SQLFeatureNotSupportedException(); 103 | } 104 | 105 | /** 106 | * {@inheritDoc} 107 | */ 108 | public void setLoginTimeout(int arg0) throws UnsupportedOperationException 109 | { 110 | throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MSG); 111 | } 112 | 113 | /** 114 | * {@inheritDoc} 115 | */ 116 | public boolean isWrapperFor(Class iface) 117 | { 118 | return connection != null && iface.isAssignableFrom(connection.getClass()); 119 | } 120 | 121 | /** 122 | * {@inheritDoc} 123 | */ 124 | public Object unwrap(Class iface) 125 | { 126 | return connection; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/DistributedStandaloneMigrationLauncher.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import com.tacitknowledge.util.migration.jdbc.util.ConfigurationUtil; 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | 23 | /** 24 | * Launches the migration process as a standalone application. 25 | *

26 | * This class expects the following Java environment parameters: 27 | *

    28 | *
  • migration.systemname - the name of the logical system being migrated
  • 29 | *
30 | *

31 | * Below is an example of how this class can be configured in build.xml: 32 | *

 33 |  *   ...
 34 |  *  <target name="patch.database" description="Runs the migration system">
 35 |  *   <java
 36 |  *       fork="true"
 37 |  *       classpathref="patch.classpath"
 38 |  *       failonerror="true"
 39 |  *       classname=
 40 |  *         "com.tacitknowledge.util.migration.jdbc.DistributedStandaloneMigrationLauncher">
 41 |  *     <sysproperty key="migration.systemname" value="${application.name}"/>
 42 |  *   </java>
 43 |  * </target>
 44 |  *   ...
 45 |  * 
46 | * 47 | * @author Mike Hardy (mike@tacitknowledge.com) 48 | * @see com.tacitknowledge.util.migration.DistributedMigrationProcess 49 | */ 50 | public class DistributedStandaloneMigrationLauncher 51 | { 52 | /** 53 | * Class logger 54 | */ 55 | private static Log log = LogFactory.getLog(DistributedStandaloneMigrationLauncher.class); 56 | 57 | /** 58 | * Private constructor - this object shouldn't be instantiated 59 | */ 60 | private DistributedStandaloneMigrationLauncher() 61 | { 62 | // does nothing 63 | } 64 | 65 | /** 66 | * Run the migrations for the given system name 67 | * 68 | * @param arguments the command line arguments, if any (none are used) 69 | * @throws Exception if anything goes wrong 70 | */ 71 | public static void main(String[] arguments) throws Exception 72 | { 73 | String systemName = ConfigurationUtil.getRequiredParam("migration.systemname", 74 | System.getProperties(), arguments); 75 | 76 | String migrationSettings = ConfigurationUtil.getOptionalParam("migration.settings", 77 | System.getProperties(), arguments, 1); 78 | 79 | // The MigrationLauncher is responsible for handling the interaction 80 | // between the PatchTable and the underlying MigrationTasks; as each 81 | // task is executed, the patch level is incremented, etc. 82 | try 83 | { 84 | DistributedJdbcMigrationLauncherFactory factory = 85 | new DistributedJdbcMigrationLauncherFactory(); 86 | JdbcMigrationLauncher launcher = null; 87 | 88 | if (migrationSettings == null) 89 | { 90 | log.info("Using migration.properties (default)"); 91 | launcher = factory.createMigrationLauncher(systemName); 92 | } 93 | else 94 | { 95 | log.info("Using " + migrationSettings); 96 | launcher = factory.createMigrationLauncher(systemName, migrationSettings); 97 | } 98 | launcher.doMigrations(); 99 | 100 | } 101 | catch (Exception e) 102 | { 103 | log.error(e); 104 | throw e; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/DistributedMigrationTableUnlock.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc; 17 | 18 | import com.tacitknowledge.util.migration.MigrationContext; 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | 22 | import java.util.Map; 23 | 24 | /** 25 | * Allows you to force-unlock a migration table with an orphaned lock. Should 26 | * be used in the same way that DistributedMigrationInformation is used 27 | * 28 | * @author Mike Hardy (mike@tacitknowledge.com) 29 | * @see com.tacitknowledge.util.migration.jdbc.DistributedMigrationInformation 30 | */ 31 | public class DistributedMigrationTableUnlock 32 | { 33 | /** 34 | * Class logger 35 | */ 36 | private static Log log = LogFactory.getLog(DistributedMigrationTableUnlock.class); 37 | 38 | /** 39 | * Get the migration level information for the given system name 40 | * 41 | * @param arguments the command line arguments, if any (none are used) 42 | * @throws Exception if anything goes wrong 43 | */ 44 | public static void main(String[] arguments) throws Exception 45 | { 46 | DistributedMigrationTableUnlock unlock = new DistributedMigrationTableUnlock(); 47 | String migrationName = System.getProperty("migration.systemname"); 48 | if (migrationName == null) 49 | { 50 | if ((arguments != null) && (arguments.length > 0)) 51 | { 52 | migrationName = arguments[0].trim(); 53 | } 54 | else 55 | { 56 | throw new IllegalArgumentException("The migration.systemname " 57 | + "system property is required"); 58 | } 59 | } 60 | unlock.tableUnlock(migrationName); 61 | } 62 | 63 | /** 64 | * unlock the patch table for the given system name 65 | * 66 | * @param systemName the name of the system 67 | * @throws Exception if anything goes wrong 68 | */ 69 | public void tableUnlock(String systemName) throws Exception 70 | { 71 | tableUnlock(systemName, MigrationContext.MIGRATION_CONFIG_FILE); 72 | } 73 | 74 | /** 75 | * unlock the patch table for the given system name 76 | * 77 | * @param systemName the name of the system 78 | * @param migrationSettings migration settings file 79 | * @throws Exception if anything goes wrong 80 | */ 81 | public void tableUnlock(String systemName, String migrationSettings) throws Exception 82 | { 83 | // The MigrationLauncher is responsible for handling the interaction 84 | // between the PatchTable and the underlying MigrationTasks; as each 85 | // task is executed, the patch level is incremented, etc. 86 | try 87 | { 88 | DistributedJdbcMigrationLauncherFactory factory = 89 | new DistributedJdbcMigrationLauncherFactory(); 90 | DistributedJdbcMigrationLauncher launcher 91 | = (DistributedJdbcMigrationLauncher) factory.createMigrationLauncher(systemName, migrationSettings); 92 | 93 | Map contextMap = launcher.getContexts(); 94 | JdbcMigrationContext context = 95 | (JdbcMigrationContext) contextMap.keySet().iterator().next(); 96 | launcher.createPatchStore(context).unlockPatchStore(); 97 | } 98 | catch (Exception e) 99 | { 100 | log.error(e); 101 | throw e; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/tacitknowledge/util/migration/jdbc/loader/FlatXmlDataSetTaskSource.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2004 Tacit Knowledge 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.tacitknowledge.util.migration.jdbc.loader; 17 | 18 | import com.tacitknowledge.util.discovery.ClassDiscoveryUtil; 19 | import com.tacitknowledge.util.migration.MigrationException; 20 | import com.tacitknowledge.util.migration.MigrationTask; 21 | import com.tacitknowledge.util.migration.MigrationTaskSource; 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.regex.Matcher; 29 | import java.util.regex.Pattern; 30 | 31 | /** 32 | * Search a package (directory) for xml files that match a specific pattern 33 | * and returns corresponding {@link FlatXmlDataSetMigrationTask}s. The name 34 | * of each script must follow the pattern of "patch(\d+)(_.+)?\.xml". 35 | * 36 | * @author Alex Soto (apsoto@gmail.com) 37 | */ 38 | public class FlatXmlDataSetTaskSource implements MigrationTaskSource 39 | { 40 | 41 | /** 42 | * Class logger 43 | */ 44 | private static Log log = LogFactory.getLog(FlatXmlDataSetTaskSource.class); 45 | 46 | /** 47 | * The regular expression used to match XML patch files. 48 | */ 49 | private static final String XML_PATCH_REGEX = "^patch(\\d+)(_.+)?\\.xml"; 50 | 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public List getMigrationTasks(String packageName) throws MigrationException 56 | { 57 | String path = packageName.replace('.', '/'); 58 | String[] xmlFiles = ClassDiscoveryUtil.getResources(path, XML_PATCH_REGEX); 59 | 60 | log.debug("Found " + xmlFiles.length + " xml patch(es) in path: " + path); 61 | for (int i = 0; i < xmlFiles.length; i++) 62 | { 63 | log.debug(" -- \"" + xmlFiles[i] + "\""); 64 | } 65 | return createMigrationTasks(xmlFiles); 66 | } 67 | 68 | /** 69 | * Creates a list of {@link FlatXmlDataSetMigrationTask}s based on the array 70 | * of xml files. 71 | * 72 | * @param xmlFiles the classpath-relative array of xml files 73 | * @return a list of {@link FlatXmlDataSetMigrationTask} 74 | * @throws MigrationException in unexpected error occurs 75 | */ 76 | private List createMigrationTasks(String[] xmlFiles) throws MigrationException 77 | { 78 | Pattern p = Pattern.compile(XML_PATCH_REGEX); 79 | List tasks = new ArrayList(); 80 | for (int i = 0; i < xmlFiles.length; i++) 81 | { 82 | String xmlPathname = xmlFiles[i]; 83 | xmlPathname = xmlPathname.replace('\\', '/'); 84 | log.debug("Examining possible xml patch file \"" + xmlPathname + "\""); 85 | 86 | File xmlFile = new File(xmlPathname); 87 | String xmlFilename = xmlFile.getName(); 88 | 89 | // Get the patch number out of the file name 90 | Matcher matcher = p.matcher(xmlFilename); 91 | if (!matcher.matches() || matcher.groupCount() != 2) 92 | { 93 | throw new MigrationException("Invalid XML patch name: " + xmlFilename); 94 | } 95 | 96 | FlatXmlDataSetMigrationTask task = new FlatXmlDataSetMigrationTask(); 97 | task.setLevel(new Integer(Integer.parseInt(matcher.group(1)))); 98 | task.setName(xmlPathname); 99 | tasks.add(task); 100 | 101 | } 102 | return tasks; 103 | } 104 | 105 | } 106 | --------------------------------------------------------------------------------