diff --git a/pom.xml b/pom.xml
index adb177e6d..6ff89a8e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,12 @@
2.1.1
+
+ org.avaje
+ avaje-dbmigration
+ 1.1.1
+
+
org.slf4j
slf4j-api
diff --git a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
index ffa07076a..abfa940ea 100644
--- a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
+++ b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
@@ -3,6 +3,8 @@ package com.avaje.ebean.config;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.dbplatform.DbPlatformName;
import com.avaje.ebean.dbmigration.DbMigration;
+import org.avaje.dbmigration.MigrationConfig;
+import org.avaje.dbmigration.MigrationRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -494,4 +496,20 @@ public class DbMigrationConfig {
return val == null || val.trim().isEmpty();
}
+ /**
+ * Create the MigrationRunner to run migrations if necessary.
+ */
+ public MigrationRunner createRunner(ClassLoader classLoader) {
+
+ MigrationConfig runnerConfig = new MigrationConfig();
+ runnerConfig.setMetaTable(metaTable);
+ runnerConfig.setApplySuffix(applySuffix);
+ runnerConfig.setMigrationPath(migrationPath);
+ runnerConfig.setRunPlaceholderMap(runPlaceholderMap);
+ runnerConfig.setRunPlaceholders(runPlaceholders);
+ runnerConfig.setDbUsername(getDbUser());
+ runnerConfig.setDbPassword(getDbPassword());
+ runnerConfig.setClassLoader(classLoader);
+ return new MigrationRunner(runnerConfig);
+ }
}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/DdlGenerator.java b/src/main/java/com/avaje/ebean/dbmigration/DdlGenerator.java
index 58cee566f..4b9e12073 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/DdlGenerator.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/DdlGenerator.java
@@ -5,6 +5,7 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.model.CurrentModel;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
+import org.avaje.dbmigration.ddl.DdlRunner;
import javax.persistence.PersistenceException;
import java.io.File;
diff --git a/src/main/java/com/avaje/ebean/dbmigration/DdlParser.java b/src/main/java/com/avaje/ebean/dbmigration/DdlParser.java
deleted file mode 100644
index c0f787d7e..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/DdlParser.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.avaje.ebean.dbmigration;
-
-import javax.persistence.PersistenceException;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Parses string content into separate SQL/DDL statements.
- */
-public class DdlParser {
-
- /**
- * Break up the sql in reader into a list of statements using the semi-colon and $$ delimiters;
- */
- public List parse(StringReader reader) {
-
- try {
- BufferedReader br = new BufferedReader(reader);
- StatementsSeparator statements = new StatementsSeparator();
-
- String s;
- while ((s = br.readLine()) != null) {
- s = s.trim();
- statements.nextLine(s);
- }
-
- return statements.statements;
-
- } catch (IOException e) {
- throw new PersistenceException(e);
- }
- }
-
-
- /**
- * Local utility used to detect the end of statements / separate statements.
- * This is often just the semicolon character but for trigger/procedures this
- * detects the $$ demarcation used in the history DDL generation for MySql and
- * Postgres.
- */
- static class StatementsSeparator {
-
- ArrayList statements = new ArrayList();
-
- boolean trimDelimiter;
-
- boolean inDbProcedure;
-
- StringBuilder sb = new StringBuilder();
-
- void lineContainsDollars(String line) {
- if (inDbProcedure) {
- if (trimDelimiter) {
- line = line.replace("$$","");
- }
- endOfStatement(line);
- } else {
- // MySql style delimiter needs to be trimmed/removed
- trimDelimiter = line.equals("delimiter $$");
- if (!trimDelimiter) {
- sb.append(line).append(" ");
- }
- }
- inDbProcedure = !inDbProcedure;
- }
-
- void endOfStatement(String line) {
- // end of Db procedure
- sb.append(line);
- statements.add(sb.toString().trim());
- sb = new StringBuilder();
- }
-
- void nextLine(String line) {
-
- if (line.contains("$$")) {
- lineContainsDollars(line);
- return;
- }
-
- if (sb.length() == 0 && (line.isEmpty() || line.startsWith("--"))) {
- // ignore leading empty lines and sql comments
- return;
- }
-
- if (inDbProcedure) {
- sb.append(line).append(" ");
- return;
- }
-
- int semiPos = line.indexOf(';');
- if (semiPos == -1) {
- sb.append(line).append(" ");
-
- } else if (semiPos == line.length() - 1) {
- // semicolon at end of line
- endOfStatement(line);
-
- } else {
- // semicolon in middle of line
- String preSemi = line.substring(0, semiPos);
- endOfStatement(preSemi);
- sb.append(line.substring(semiPos + 1));
- }
- }
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/DdlRunner.java b/src/main/java/com/avaje/ebean/dbmigration/DdlRunner.java
deleted file mode 100644
index 30eb26a0b..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/DdlRunner.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.avaje.ebean.dbmigration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.StringReader;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Runs DDL scripts.
- */
-public class DdlRunner {
-
- protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class);
-
- private DdlParser ddlParser = new DdlParser();
-
- private final String scriptName;
-
- private final boolean expectErrors;
-
- /**
- * Construct with a script name (for logging) and flag indicating if errors are expected.
- */
- public DdlRunner(boolean expectErrors, String scriptName) {
- this.expectErrors = expectErrors;
- this.scriptName = scriptName;
- }
-
- /**
- * Parse the content into sql statements and execute them in a transaction.
- */
- public int runAll(String content, Connection connection) throws SQLException {
-
- List statements = ddlParser.parse(new StringReader(content));
- return runStatements(statements, connection);
- }
-
- /**
- * Execute all the statements in a single transaction.
- */
- private int runStatements(List statements, Connection connection) throws SQLException {
-
- List noDuplicates = new ArrayList();
-
- for (String statement : statements) {
- if (!noDuplicates.contains(statement)) {
- noDuplicates.add(statement);
- }
- }
-
- logger.info("Executing {} - {} statements", scriptName, noDuplicates.size());
-
- for (int i = 0; i < noDuplicates.size(); i++) {
- String xOfy = (i + 1) + " of " + noDuplicates.size();
- runStatement(expectErrors, xOfy, noDuplicates.get(i), connection);
- }
-
- return noDuplicates.size();
- }
-
- /**
- * Execute the statement.
- */
- private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException {
-
- PreparedStatement pstmt = null;
- try {
-
- // trim and remove trailing ; or /
- stmt = stmt.trim();
- if (stmt.endsWith(";")) {
- stmt = stmt.substring(0, stmt.length() - 1);
- } else if (stmt.endsWith("/")) {
- stmt = stmt.substring(0, stmt.length() - 1);
- }
-
- if (logger.isDebugEnabled()) {
- logger.debug("executing " + oneOf + " " + getSummary(stmt));
- }
-
- pstmt = c.prepareStatement(stmt);
- pstmt.execute();
-
- } catch (SQLException e) {
- if (expectErrors) {
- logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
- } else {
- String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
- throw new SQLException(msg, e);
- }
-
- } finally {
- if (pstmt != null) {
- try {
- pstmt.close();
- } catch (SQLException e) {
- logger.error("Error closing pstmt", e);
- }
- }
- }
- }
-
- private String getSummary(String s) {
- if (s.length() > 80) {
- return s.substring(0, 80).trim() + "...";
- }
- return s;
- }
-
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/MigrationRunner.java b/src/main/java/com/avaje/ebean/dbmigration/MigrationRunner.java
deleted file mode 100644
index a4a217dee..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/MigrationRunner.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.avaje.ebean.dbmigration;
-
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.config.DbMigrationConfig;
-import com.avaje.ebean.config.ServerConfig;
-import com.avaje.ebean.dbmigration.runner.LocalMigrationResource;
-import com.avaje.ebean.dbmigration.runner.LocalMigrationResources;
-import com.avaje.ebean.dbmigration.runner.MigrationTable;
-import com.avaje.ebeaninternal.util.JdbcClose;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.sql.DataSource;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.List;
-
-/**
- * Runs the DB migration typically on application start.
- */
-public class MigrationRunner {
-
- public static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.DbMigration");
-
- private final EbeanServer server;
-
- private final ServerConfig config;
-
- private final DbMigrationConfig migrationConfig;
-
- public MigrationRunner(EbeanServer server, DbMigrationConfig migrationConfig) {
- this.server = server;
- this.config = server.getPluginApi().getServerConfig();
- this.migrationConfig = migrationConfig;
- }
-
- /**
- * Run the migrations if there are any that need running.
- */
- public void run() {
-
- LocalMigrationResources resources = new LocalMigrationResources(config, migrationConfig);
- if (!resources.readResources()) {
- logger.debug("no migrations to check");
- return;
- }
-
- String migrationUser = migrationConfig.getDbUser();
- String migrationPwd = migrationConfig.getDbPassword();
- if (migrationUser == null) {
- throw new IllegalStateException("No DB migration user specified (to run the db migration) ?");
- }
-
- DataSource dataSource = server.getPluginApi().getDataSource();
- if (dataSource == null) {
- throw new IllegalStateException("No dataSource when trying to run migration? "
- +"Maybe trying to generate DBMigration when ebean.migration.run=true is set? "
- +"Perhaps need to set ebean.migration.run=false in test-ebean.properties?");
- }
-
- Connection connection;
- try {
- connection = dataSource.getConnection(migrationUser, migrationPwd);
- } catch (SQLException e) {
- throw new IllegalArgumentException("Error trying to connect to database using DB Migration user [" + migrationUser + "]", e);
- }
-
- try {
- logger.debug("using db user [{}] to run migrations ...");
- connection.setAutoCommit(false);
- runMigrations(resources, connection);
-
- connection.commit();
-
- } catch (Exception e) {
- JdbcClose.rollback(connection);
- throw new RuntimeException(e);
-
- } finally {
- JdbcClose.close(connection);
- }
- }
-
- /**
- * Run all the migrations as needed.
- */
- private void runMigrations(LocalMigrationResources resources, Connection connection) throws SQLException, IOException {
-
- MigrationTable table = new MigrationTable(server, migrationConfig, connection);
- table.createIfNeeded();
-
- // get the migrations in version order
- List localVersions = resources.getVersions();
-
- logger.info("local migrations:{} existing migrations:{}", localVersions.size(), table.size());
-
- LocalMigrationResource priorVersion = null;
-
- // run migrations in order
- for (int i = 0; i < localVersions.size(); i++) {
- LocalMigrationResource localVersion = localVersions.get(i);
- if (!table.shouldRun(localVersion, priorVersion)) {
- break;
- }
- priorVersion = localVersion;
- connection.commit();
- }
- }
-
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/Checksum.java b/src/main/java/com/avaje/ebean/dbmigration/runner/Checksum.java
deleted file mode 100644
index 515bf3606..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/Checksum.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.zip.CRC32;
-
-/**
- * Calculates the checksum for the given string content.
- */
-public class Checksum {
-
- /**
- * Returns the checksum of this string.
- */
- public static int calculate(String str) {
-
- final CRC32 crc32 = new CRC32();
-
- BufferedReader bufferedReader = new BufferedReader(new StringReader(str));
- try {
- String line;
- while ((line = bufferedReader.readLine()) != null) {
- crc32.update(line.getBytes("UTF-8"));
- }
- } catch (IOException e) {
- throw new RuntimeException("Failed to calculate checksum", e);
- }
-
- return (int) crc32.getValue();
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResource.java b/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResource.java
deleted file mode 100644
index a2d71aebc..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResource.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import com.avaje.ebean.dbmigration.model.MigrationVersion;
-import org.avaje.classpath.scanner.Resource;
-
-/**
- * A DB migration resource (DDL script with version).
- */
-public class LocalMigrationResource implements Comparable {
-
- /**
- * Code for repeatable migrations.
- */
- private static final String REPEAT_TYPE = "R";
-
- /**
- * Code for version migrations.
- */
- private static final String VERSION_TYPE = "V";
-
- private final MigrationVersion version;
-
- private final String location;
-
- private final Resource resource;
-
- /**
- * Construct with version and resource.
- */
- public LocalMigrationResource(MigrationVersion version, String location, Resource resource) {
- this.version = version;
- this.location = location;
- this.resource = resource;
- }
-
- public String toString() {
- return version.toString();
- }
-
- /**
- * Return true if the underlying version is "repeatable".
- */
- public boolean isRepeatable() {
- return version.isRepeatable();
- }
-
- /**
- * Return the "key" that identifies the migration.
- */
- public String key() {
- if (isRepeatable()) {
- return version.getComment().toLowerCase();
- } else {
- return version.normalised();
- }
- }
-
- /**
- * Return the migration comment.
- */
- public String getComment() {
- String comment = version.getComment();
- return (comment == null || comment.isEmpty()) ? "-" : comment;
- }
-
- /**
- * Default ordering by version.
- */
- @Override
- public int compareTo(LocalMigrationResource o) {
- return version.compareTo(o.version);
- }
-
- /**
- * Return the underlying migration version.
- */
- public MigrationVersion getVersion() {
- return version;
- }
-
- /**
- * Return the resource location.
- */
- public String getLocation() {
- return location;
- }
-
- /**
- * Return the content for the migration apply ddl script.
- */
- public String getContent() {
- return resource.loadAsString("UTF-8");
- }
-
- /**
- * Return the type code ("R" or "V") for this migration.
- */
- public String getType() {
- return isRepeatable() ? REPEAT_TYPE : VERSION_TYPE;
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResources.java b/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResources.java
deleted file mode 100644
index f7c3ee4f1..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/LocalMigrationResources.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import com.avaje.ebean.config.DbMigrationConfig;
-import com.avaje.ebean.config.ServerConfig;
-import com.avaje.ebean.dbmigration.model.MigrationVersion;
-import org.avaje.classpath.scanner.Resource;
-import org.avaje.classpath.scanner.ResourceFilter;
-import org.avaje.classpath.scanner.Scanner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Loads the DB migration resources and sorts them into execution order.
- */
-public class LocalMigrationResources {
-
- private static final Logger logger = LoggerFactory.getLogger(LocalMigrationResources.class);
-
- private final ServerConfig serverConfig;
-
- private final DbMigrationConfig migrationConfig;
-
- private final List versions = new ArrayList();
-
- /**
- * Construct with configuration options.
- */
- public LocalMigrationResources(ServerConfig serverConfig, DbMigrationConfig migrationConfig) {
- this.serverConfig = serverConfig;
- this.migrationConfig = migrationConfig;
- }
-
- /**
- * Read all the migration resources (SQL scripts) returning true if there are versions.
- */
- public boolean readResources() {
-
- String migrationPath = migrationConfig.getMigrationPath();
-
- ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
-
- Scanner scanner = new Scanner(classLoader);
- List resourceList = scanner.scanForResources(migrationPath, new Match(migrationConfig));
-
- logger.debug("resources: {}", resourceList);
-
- for (Resource resource : resourceList) {
- String filename = resource.getFilename();
- if (filename.endsWith(migrationConfig.getApplySuffix())) {
- int pos = filename.lastIndexOf(migrationConfig.getApplySuffix());
- String mainName = filename.substring(0, pos);
-
- MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
- LocalMigrationResource res = new LocalMigrationResource(migrationVersion, resource.getLocation(), resource);
- versions.add(res);
- }
- }
-
- Collections.sort(versions);
- return !versions.isEmpty();
- }
-
- /**
- * Return the list of migration resources in version order.
- */
- public List getVersions() {
- return versions;
- }
-
-
- /**
- * Filter used to find the migration scripts.
- */
- private static class Match implements ResourceFilter {
-
- private final DbMigrationConfig migrationConfig;
-
- Match(DbMigrationConfig migrationConfig) {
- this.migrationConfig = migrationConfig;
- }
-
- @Override
- public boolean isMatch(String name) {
- return name.endsWith(migrationConfig.getApplySuffix());
- }
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationMetaRow.java b/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationMetaRow.java
deleted file mode 100644
index 2da2556b5..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationMetaRow.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import com.avaje.ebean.SqlRow;
-import com.avaje.ebean.SqlUpdate;
-
-import java.sql.Timestamp;
-
-/**
- * Bean holding migration execution details stored in the migration table.
- */
-class MigrationMetaRow {
-
- private int id;
-
- private String type;
-
- private String version;
-
- private String comment;
-
- private int checksum;
-
- private Timestamp runOn;
-
- private String runBy;
-
- private long runTime;
-
- /**
- * Construct for inserting into table.
- */
- MigrationMetaRow(int id, String type, String version, String comment, int checksum, String runBy, Timestamp runOn, long runTime) {
- this.id = id;
- this.type = type;
- this.version = version;
- this.checksum = checksum;
- this.comment = comment;
- this.runBy = runBy;
- this.runOn = runOn;
- this.runTime = runTime;
- }
-
- /**
- * Construct from the SqlRow (read from table).
- */
- MigrationMetaRow(SqlRow row) {
- id = row.getInteger("id");
- type = row.getString("mtype");
- version = row.getString("mversion");
- comment = row.getString("mcomment");
- checksum = row.getInteger("mchecksum");
- runBy = row.getString("run_by");
- runTime = row.getLong("run_time");
- }
-
- public String toString() {
- return "id:" + id + " type:" + type + " runVersion:" + version + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
- }
-
- /**
- * Return the id for this migration.
- */
- int getId() {
- return id;
- }
-
- /**
- * Return the normalised version for this migration.
- */
- String getVersion() {
- return version;
- }
-
- /**
- * Return the checksum for this migration.
- */
- int getChecksum() {
- return checksum;
- }
-
- /**
- * Bind to the insert statement.
- */
- void bindInsert(SqlUpdate insert) {
- insert.setParameter(1, id);
- insert.setParameter(2, type);
- insert.setParameter(3, "SUCCESS");
- insert.setParameter(4, version);
- insert.setParameter(5, comment);
- insert.setParameter(6, checksum);
- insert.setParameter(7, runOn);
- insert.setParameter(8, runBy);
- insert.setParameter(9, runTime);
- }
-
- /**
- * Return the SQL insert given the table migration meta data is stored in.
- */
- static String insertSql(String table) {
- return "insert into " + table
- + " (id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_time)"
- + " values (?,?,?,?,?,?,?,?,?)";
- }
-
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationScriptRunner.java b/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationScriptRunner.java
deleted file mode 100644
index a2e0c6da5..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationScriptRunner.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import com.avaje.ebean.dbmigration.DdlRunner;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-/**
- * Runs the DDL migration scripts.
- */
-public class MigrationScriptRunner {
-
- private final Connection connection;
-
- /**
- * Construct with a given connection.
- */
- public MigrationScriptRunner(Connection connection) {
- this.connection = connection;
- }
-
- /**
- * Execute all the DDL statements in the script.
- */
- int runScript(boolean expectErrors, String content, String scriptName) throws SQLException {
-
- DdlRunner runner = new DdlRunner(expectErrors, scriptName);
- return runner.runAll(content, connection);
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationTable.java b/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationTable.java
deleted file mode 100644
index a1c620507..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/MigrationTable.java
+++ /dev/null
@@ -1,263 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.SqlQuery;
-import com.avaje.ebean.SqlRow;
-import com.avaje.ebean.SqlUpdate;
-import com.avaje.ebean.config.DbMigrationConfig;
-import com.avaje.ebean.config.ServerConfig;
-import com.avaje.ebean.config.dbplatform.DatabasePlatform;
-import com.avaje.ebean.dbmigration.MigrationRunner;
-import com.avaje.ebean.plugin.SpiServer;
-import com.avaje.ebeaninternal.server.transaction.ExternalJdbcTransaction;
-import com.avaje.ebeaninternal.util.IOUtils;
-import org.slf4j.Logger;
-
-import java.io.IOException;
-import java.net.URL;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Manages the migration table.
- */
-public class MigrationTable {
-
- private static final Logger logger = MigrationRunner.logger;
-
- private final Connection connection;
-
- private final EbeanServer server;
-
- private final DatabasePlatform databasePlatform;
-
- private final String catalog;
- private final String schema;
- private final String table;
- private final ServerConfig serverConfig;
- private final String envUserName;
-
- private final Timestamp runOn = new Timestamp(System.currentTimeMillis());
-
- private final ScriptTransform scriptTransform;
-
- private final String insertSql;
-
- private final LinkedHashMap migrations;
-
- private MigrationMetaRow lastMigration;
-
- /**
- * Construct with server, configuration and jdbc connection (DB admin user).
- */
- public MigrationTable(EbeanServer server, DbMigrationConfig migrationConfig, Connection connection) {
- this.connection = connection;
- this.server = server;
- this.migrations = new LinkedHashMap();
-
- SpiServer pluginApi = server.getPluginApi();
- this.serverConfig = pluginApi.getServerConfig();
- this.databasePlatform = pluginApi.getDatabasePlatform();
- this.catalog = null;
- this.schema = null;
- this.table = migrationConfig.getMetaTable();
- this.insertSql = MigrationMetaRow.insertSql(table);
- this.scriptTransform = createScriptTransform(migrationConfig);
- this.envUserName = System.getProperty("user.name");
- }
-
- /**
- * Return the number of migrations in the DB migration table.
- */
- public int size() {
- return migrations.size();
- }
-
- /**
- * Create the ScriptTransform for placeholder key/value replacement.
- */
- private ScriptTransform createScriptTransform(DbMigrationConfig config) {
-
- Map map = PlaceholderBuilder.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
- return new ScriptTransform(map);
- }
-
- /**
- * Create the table is it does not exist.
- */
- public void createIfNeeded() throws SQLException, IOException {
-
- if (!tableExists(connection)) {
- createTable(connection);
- }
-
- ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
- SqlQuery sqlQuery = server.createSqlQuery("select * from " + table + " order by id for update");
- List metaRows = server.findList(sqlQuery, t);
-
- for (SqlRow row : metaRows) {
- MigrationMetaRow metaRow = new MigrationMetaRow(row);
- addMigration(metaRow.getVersion(), metaRow);
- }
- }
-
-
- private void createTable(Connection connection) throws IOException, SQLException {
-
- String script = ScriptTransform.table(table, getCreateTableScript());
-
- MigrationScriptRunner run = new MigrationScriptRunner(connection);
- run.runScript(false, script, "create migration table");
- }
-
- /**
- * Return the create table script.
- */
- private String getCreateTableScript() throws IOException {
- // supply a script to override the default table create script
- String script = readResource("migration-support/create-table.sql");
- if (script == null) {
- // no, just use the default script
- script = readResource("migration-support/default-create-table.sql");
- }
- return script;
- }
-
- private String readResource(String location) throws IOException {
-
- Enumeration resources = serverConfig.getClassLoadConfig().getResources(location);
- if (resources.hasMoreElements()) {
- URL url = resources.nextElement();
- return IOUtils.readUtf8(url.openStream());
- }
- return null;
- }
-
- /**
- * Return true if the table exists.
- */
- private boolean tableExists(Connection connection) throws SQLException {
- boolean exists = databasePlatform.tableExists(connection, catalog, schema, table);
- if (!exists) {
- exists = databasePlatform.tableExists(connection, catalog, schema, table.toUpperCase());
- }
- return exists;
- }
-
- /**
- * Return true if the migration ran successfully and false if the migration failed.
- */
- public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) throws SQLException {
-
- if (priorVersion != null && !localVersion.isRepeatable()) {
- if (!migrationExists(priorVersion)) {
- logger.error("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
- return false;
- }
- }
-
- MigrationMetaRow existing = migrations.get(localVersion.key());
- return runMigration(localVersion, existing);
- }
-
- /**
- * Run the migration script.
- *
- * @param local The local migration resource
- * @param existing The information for this migration existing in the table
- *
- * @return True if the migrations should continue
- */
- private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException {
-
- String script = convertScript(local.getContent());
- int checksum = Checksum.calculate(script);
-
- if (existing != null) {
-
- boolean matchChecksum = (existing.getChecksum() == checksum);
-
- if (!local.isRepeatable()) {
- if (!matchChecksum) {
- logger.error("Checksum mismatch on migration {}", local.getLocation());
- }
- return true;
-
- } else if (matchChecksum) {
- logger.trace("... skip unchanged repeatable migration {}", local.getLocation());
- return true;
- }
- }
-
- runMigration(local, script, checksum);
- return true;
- }
-
- /**
- * Run a migration script as new migration or update on existing repeatable migration.
- */
- private void runMigration(LocalMigrationResource local, String script, int checksum) throws SQLException {
-
- logger.debug("run migration {}", local.getLocation());
-
- long start = System.currentTimeMillis();
- MigrationScriptRunner run = new MigrationScriptRunner(connection);
- run.runScript(false, script, "run migration version: " + local.getVersion());
-
- long exeMillis = System.currentTimeMillis() - start;
- // insert new migration row
- SqlUpdate insert = server.createSqlUpdate(insertSql);
- MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
- metaRow.bindInsert(insert);
- server.execute(insert, new ExternalJdbcTransaction(connection));
-
- addMigration(local.key(), metaRow);
- }
-
- /**
- * Create the MigrationMetaRow for this migration.
- */
- private MigrationMetaRow createMetaRow(LocalMigrationResource migration, int checksum, long exeMillis) {
-
- int nextId = 1;
- if (lastMigration != null) {
- nextId = lastMigration.getId() + 1;
- }
-
- String type = migration.getType();
- String runVersion = migration.key();
- String comment = migration.getComment();
-
- return new MigrationMetaRow(nextId, type, runVersion, comment, checksum, envUserName, runOn, exeMillis);
- }
-
- /**
- * Return true if the migration exists.
- */
- private boolean migrationExists(LocalMigrationResource priorVersion) {
- return migrations.containsKey(priorVersion.key());
- }
-
- /**
- * Apply the placeholder key/value replacement on the script.
- */
- private String convertScript(String script) {
- return scriptTransform.transform(script);
- }
-
- /**
- * Register the successfully executed migration (to allow dependant scripts to run).
- */
- private void addMigration(String key, MigrationMetaRow metaRow) {
- lastMigration = metaRow;
- if (metaRow.getVersion() == null) {
- throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
- }
- migrations.put(key, metaRow);
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilder.java b/src/main/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilder.java
deleted file mode 100644
index eec825394..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Joins placeholder map and comma/equals delimited string.
- */
-class PlaceholderBuilder {
-
- private final Map map = new HashMap();
-
- /**
- * Create with raw comma and equals delimited pairs plus map of key value pairs.
- */
- public static Map build(String commaDelimited, Map placeholders) {
-
- PlaceholderBuilder builder = new PlaceholderBuilder();
- builder.add(commaDelimited);
- builder.add(placeholders);
-
- return builder.map;
- }
-
- private PlaceholderBuilder() {
-
- }
-
- /**
- * Add a comma and equals delimited string to parse for key value pairs.
- */
- public void add(String commaDelimited) {
-
- if (commaDelimited != null) {
- String[] split = commaDelimited.split("[,;]");
- for (String keyValue : split) {
- String[] pair = keyValue.split("=");
- if (pair.length == 2) {
- map.put(pair[0].trim(), pair[1].trim());
- }
- }
- }
- }
-
- /**
- * Add a map of key value placeholder pairs.
- */
- public void add(Map placeholders) {
- if (placeholders != null) {
- map.putAll(placeholders);
- }
- }
-}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/runner/ScriptTransform.java b/src/main/java/com/avaje/ebean/dbmigration/runner/ScriptTransform.java
deleted file mode 100644
index 31a0419fd..000000000
--- a/src/main/java/com/avaje/ebean/dbmigration/runner/ScriptTransform.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Transforms a SQL script given a map of key/value substitutions.
- */
-class ScriptTransform {
-
- /**
- * Transform just ${table} with the table name.
- */
- public static String table(String tableName, String script) {
- return script.replace("${table}", tableName);
- }
-
- private final Map placeholders = new HashMap();
-
- ScriptTransform(Map map) {
- for (Map.Entry entry : map.entrySet()) {
- placeholders.put(wrapKey(entry.getKey()), entry.getValue());
- }
- }
-
- private String wrapKey(String key) {
- return "${"+key+"}";
- }
-
- /**
- * Return true if this contains no placeholders.
- */
- boolean isEmpty() {
- return placeholders.isEmpty();
- }
-
- /**
- * Transform the script replacing placeholders in the form ${key} with value.
- */
- String transform(String source) {
-
- for (Map.Entry entry : placeholders.entrySet()) {
- source = source.replace(entry.getKey(), entry.getValue());
- }
- return source;
- }
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index 602e9b778..3c346ce43 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -14,7 +14,6 @@ import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.DdlGenerator;
-import com.avaje.ebean.dbmigration.MigrationRunner;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
@@ -65,6 +64,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
+import org.avaje.dbmigration.MigrationRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -344,10 +344,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
DbMigrationConfig migrationConfig = serverConfig.getMigrationConfig();
if (migrationConfig != null) {
migrationConfig.generateOnStart(this);
- }
- if (migrationConfig.isRunMigration()) {
- new MigrationRunner(this, migrationConfig).run();
+ if (migrationConfig.isRunMigration()) {
+ // classLoader used to load resources
+ ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
+ MigrationRunner runner = migrationConfig.createRunner(classLoader);
+ runner.run(serverConfig.getDataSource());
+ }
}
}
diff --git a/src/test/java/com/avaje/ebean/dbmigration/DdlParserTest.java b/src/test/java/com/avaje/ebean/dbmigration/DdlParserTest.java
deleted file mode 100644
index e2115d1af..000000000
--- a/src/test/java/com/avaje/ebean/dbmigration/DdlParserTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.avaje.ebean.dbmigration;
-
-import org.junit.Test;
-
-import java.io.StringReader;
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-
-public class DdlParserTest {
-
- DdlParser parser = new DdlParser();
-
- @Test
- public void parse_ignoresEmptyLines() throws Exception {
-
- List stmts = parser.parse(new StringReader("\n\none;\n\ntwo;\n\n"));
-
- assertThat(stmts).hasSize(2);
- assertThat(stmts).contains("one;","two;");
- }
-
- @Test
- public void parse_ignoresComments_whenFirst() throws Exception {
-
- List stmts = parser.parse(new StringReader("-- comment\ntwo;"));
-
- assertThat(stmts).hasSize(1);
- assertThat(stmts).contains("two;");
- }
-
- @Test
- public void parse_ignoresEmptyLines_whenFirst() throws Exception {
-
- List stmts = parser.parse(new StringReader("\n\n-- comment\ntwo;\n\n"));
- assertThat(stmts).hasSize(1);
- assertThat(stmts).contains("two;");
- }
-
- @Test
- public void parse_inlineEmptyLines_replacedWithSpace() throws Exception {
-
- List stmts = parser.parse(new StringReader("\n\n-- comment\none\ntwo;\n\n"));
- assertThat(stmts).hasSize(1);
- assertThat(stmts).contains("one two;");
- }
-
-
- @Test
- public void parse_ignoresComments() throws Exception {
-
- List stmts = parser.parse(new StringReader("one;\n-- comment\ntwo;"));
-
- assertThat(stmts).hasSize(2);
- assertThat(stmts).contains("one;","two;");
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/ebean/dbmigration/MigrationRunnerTest.java b/src/test/java/com/avaje/ebean/dbmigration/MigrationRunnerTest.java
deleted file mode 100644
index 426319e44..000000000
--- a/src/test/java/com/avaje/ebean/dbmigration/MigrationRunnerTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.avaje.ebean.dbmigration;
-
-import com.avaje.ebean.BaseTestCase;
-import com.avaje.ebean.Ebean;
-import com.avaje.ebean.EbeanServer;
-import com.avaje.ebean.config.DbMigrationConfig;
-import org.junit.Test;
-
-
-public class MigrationRunnerTest extends BaseTestCase {
-
-
- @Test
- public void test() {
-
- if (!isH2()) {
- return;
- }
-
- EbeanServer server = Ebean.getDefaultServer();
-
- DbMigrationConfig config = new DbMigrationConfig();
- config.setMigrationPath("test-dbmigration");
- config.setRunMigration(true);
- config.setDbUser("sa");
- config.setDbPassword("");
-
- new MigrationRunner(server, config).run();
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/ebean/dbmigration/runner/ChecksumTest.java b/src/test/java/com/avaje/ebean/dbmigration/runner/ChecksumTest.java
deleted file mode 100644
index b268c4444..000000000
--- a/src/test/java/com/avaje/ebean/dbmigration/runner/ChecksumTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ChecksumTest {
-
- @Test
- public void test_calculate() throws Exception {
-
- int checkFoo = Checksum.calculate("foo");
-
- assertThat(Checksum.calculate("foo")).isEqualTo(checkFoo);
- assertThat(Checksum.calculate("Foo")).isNotEqualTo(checkFoo);
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilderTest.java b/src/test/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilderTest.java
deleted file mode 100644
index 48f49b466..000000000
--- a/src/test/java/com/avaje/ebean/dbmigration/runner/PlaceholderBuilderTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import org.assertj.core.data.MapEntry;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-
-public class PlaceholderBuilderTest {
-
- @Test
- public void empty() throws Exception {
-
- assertThat(PlaceholderBuilder.build(null, null)).isEmpty();
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void comma() throws Exception {
-
- assertThat(PlaceholderBuilder.build("a=1", null))
- .containsExactly(MapEntry.entry("a","1"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void comma_withSpace() throws Exception {
-
- assertThat(PlaceholderBuilder.build(" a=1 ", null))
- .containsExactly(MapEntry.entry("a","1"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void comma_withSpace_withSemi() throws Exception {
-
- assertThat(PlaceholderBuilder.build(" a=1 ; ", null))
- .containsExactly(MapEntry.entry("a","1"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void commaPair() throws Exception {
-
- assertThat(PlaceholderBuilder.build("a=1;b=2", null))
- .containsExactly(MapEntry.entry("a","1"),MapEntry.entry("b","2"));
- }
-
-
- @Test
- @SuppressWarnings("unchecked")
- public void mapPair() throws Exception {
-
- Map in = new HashMap();
- in.put("a", "1");
- in.put("b", "2");
-
- assertThat(PlaceholderBuilder.build(null, in))
- .containsExactly(MapEntry.entry("a","1"),MapEntry.entry("b","2"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void mapPair_override() throws Exception {
-
- Map in = new HashMap();
- in.put("a", "1");
- in.put("b", "2");
-
- assertThat(PlaceholderBuilder.build("a=11;b=12", in))
- .containsExactly(MapEntry.entry("a","1"),MapEntry.entry("b","2"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void mapPair_join() throws Exception {
-
- Map in = new HashMap();
- in.put("c", "3");
-
- assertThat(PlaceholderBuilder.build("a=1;b=2", in))
- .containsExactly(MapEntry.entry("a","1"),MapEntry.entry("b","2"), MapEntry.entry("c","3"));
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/ebean/dbmigration/runner/ScriptTransformTest.java b/src/test/java/com/avaje/ebean/dbmigration/runner/ScriptTransformTest.java
deleted file mode 100644
index 3aeff2413..000000000
--- a/src/test/java/com/avaje/ebean/dbmigration/runner/ScriptTransformTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.avaje.ebean.dbmigration.runner;
-
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.*;
-
-public class ScriptTransformTest {
-
- @Test
- public void transform() throws Exception {
-
- Map map = new HashMap();
- map.put("one", "PLACE1");
- map.put("two", "PLACE2");
-
- ScriptTransform transform = new ScriptTransform(map);
-
- assertEquals(transform.transform("${one}"), "PLACE1");
- assertEquals(transform.transform("${two}"), "PLACE2");
- assertEquals(transform.transform("${one}${two}"), "PLACE1PLACE2");
- assertEquals(transform.transform("${two}${one}"), "PLACE2PLACE1");
- assertEquals(transform.transform("A${one}B${two}C"), "APLACE1BPLACE2C");
- assertEquals(transform.transform(" ${one} ${two} "), " PLACE1 PLACE2 ");
-
- assertEquals(transform.transform("$${one}"), "$PLACE1");
- assertEquals(transform.transform("$${one}}"), "$PLACE1}");
- }
-
-}
\ No newline at end of file
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
index 466673fd5..60e028cb1 100644
--- a/src/test/resources/logback-test.xml
+++ b/src/test/resources/logback-test.xml
@@ -76,15 +76,15 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+