Skip to content

Commit

Permalink
PR ebean-orm#3152 - NEW: DefaultDbMigration is configurable from prop…
Browse files Browse the repository at this point in the history
…erties
  • Loading branch information
rPraml committed Aug 10, 2023
1 parent e7f157e commit e666277
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.ebeaninternal.dbmigration;

import java.io.IOException;

import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;

/**
* Plugin to generate db-migration scripts automatically.
* @author Roland Praml, FOCONIS AG
*/
public class DbMigrationPlugin implements Plugin {

private DefaultDbMigration dbMigration;

private static String lastMigration;
private static String lastInit;

@Override
public void configure(SpiServer server) {
dbMigration = new DefaultDbMigration();
dbMigration.setServer(server);
}

@Override
public void online(boolean online) {
try {
lastInit = null;
lastMigration = null;
if (dbMigration.generate) {
String tmp = lastMigration = dbMigration.generateMigration();
if (tmp == null) {
return;
}
}
if (dbMigration.generateInit) {
lastInit = dbMigration.generateInitMigration();
}
} catch (IOException e) {
throw new RuntimeException("Error while generating migration", e);
}
}

@Override
public void shutdown() {
dbMigration = null;
}

public static String getLastInit() {
return lastInit;
}

public static String getLastMigration() {
return lastMigration;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package io.ebeaninternal.dbmigration;

import io.avaje.applog.AppLog;
import io.avaje.classpath.scanner.core.Location;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.annotation.Platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.PlatformConfig;
import io.ebean.config.PropertiesWrapper;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DatabasePlatformProvider;
import io.ebean.dbmigration.DbMigration;
import io.ebean.util.IOUtils;
import io.ebean.util.StringHelper;
import io.ebeaninternal.api.DbOffline;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
Expand All @@ -26,10 +25,7 @@
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.*;

import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
import static java.lang.System.Logger.Level.*;
Expand Down Expand Up @@ -61,8 +57,8 @@ public class DefaultDbMigration implements DbMigration {
private static final String initialVersion = "1.0";
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";

private final List<DatabasePlatformProvider> platformProviders = new ArrayList<>();
protected final boolean online;
private List<DatabasePlatformProvider> platformProviders = new ArrayList<>();
protected boolean online;
private boolean logToSystemOut = true;
protected SpiEbeanServer server;
protected String pathToResources = "src/main/resources";
Expand All @@ -77,8 +73,10 @@ public class DefaultDbMigration implements DbMigration {
protected List<Pair> platforms = new ArrayList<>();
protected DatabaseConfig databaseConfig;
protected DbConstraintNaming constraintNaming;
@Deprecated
protected Boolean strictMode;
protected Boolean includeGeneratedFileComment;
protected boolean includeGeneratedFileComment;
@Deprecated
protected String header;
protected String applyPrefix = "";
protected String version;
Expand All @@ -88,6 +86,9 @@ public class DefaultDbMigration implements DbMigration {
private int lockTimeoutSeconds;
protected boolean includeBuiltInPartitioning = true;
protected boolean includeIndex;
protected boolean generate = false;
protected boolean generateInit = false;
private boolean keepLastInit = true;

/**
* Create for offline migration generation.
Expand Down Expand Up @@ -123,12 +124,66 @@ public void setServerConfig(DatabaseConfig config) {
if (constraintNaming == null) {
this.constraintNaming = databaseConfig.getConstraintNaming();
}
if (databasePlatform == null) {
this.databasePlatform = databaseConfig.getDatabasePlatform();
}
Properties properties = config.getProperties();
if (properties != null) {
PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, null);
PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, config.getClassLoadConfig());
migrationPath = props.get("migration.migrationPath", migrationPath);
migrationInitPath = props.get("migration.migrationInitPath", migrationInitPath);
pathToResources = props.get("migration.pathToResources", pathToResources);
addForeignKeySkipCheck = props.getBoolean("migration.addForeignKeySkipCheck", addForeignKeySkipCheck);
applyPrefix = props.get("migration.applyPrefix", applyPrefix);
databasePlatform = props.createInstance(DatabasePlatform.class, "migration.databasePlatform", databasePlatform);
generatePendingDrop = props.get("migration.generatePendingDrop", generatePendingDrop);
includeBuiltInPartitioning = props.getBoolean("migration.includeBuiltInPartitioning", includeBuiltInPartitioning);
includeGeneratedFileComment = props.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
includeIndex = props.getBoolean("migration.includeIndex", includeIndex);
lockTimeoutSeconds = props.getInt("migration.lockTimeoutSeconds", lockTimeoutSeconds);
logToSystemOut = props.getBoolean("migration.logToSystemOut", logToSystemOut);
modelPath = props.get("migration.modelPath", modelPath);
modelSuffix = props.get("migration.modelSuffix", modelSuffix);
name = props.get("migration.name", name);
online = props.getBoolean("migration.online", online);
vanillaPlatform = props.getBoolean("migration.vanillaPlatform", vanillaPlatform);
version = props.get("migration.version", version);
generate = props.getBoolean("migration.generate", generate);
generateInit = props.getBoolean("migration.generateInit", generateInit);
// header & strictMode must be configured at DatabaseConfig level
parsePlatforms(props, config);
}
}

protected void parsePlatforms(PropertiesWrapper props, DatabaseConfig config) {
String platforms = props.get("migration.platforms");
if (platforms == null || platforms.isEmpty()) {
return;
}
String[] tmp = StringHelper.splitNames(platforms);
for (String plat : tmp) {
DatabasePlatform dbPlatform;
String platformName = plat;
String platformPrefix = null;
int pos = plat.indexOf('=');
if (pos != -1) {
platformName = plat.substring(0, pos);
platformPrefix = plat.substring(pos + 1);
}

if (platformName.indexOf('.') == -1) {
// parse platform as enum value
Platform platform = Enum.valueOf(Platform.class, platformName.toUpperCase());
dbPlatform = platform(platform);
} else {
// parse platform as class
dbPlatform = (DatabasePlatform) config.getClassLoadConfig().newInstance(platformName);
}
if (platformPrefix == null) {
platformPrefix = dbPlatform.platform().name().toLowerCase();
}

addDatabasePlatform(dbPlatform, platformPrefix);
}
}

Expand Down Expand Up @@ -319,7 +374,18 @@ private String generateMigrationFor(boolean initMigration) throws IOException {
}

String pendingVersion = generatePendingDrop();
if (pendingVersion != null) {
if ("auto".equals(pendingVersion)) {
StringJoiner sj = new StringJoiner(",");
String diff = generateDiff(request);
if (diff != null) {
sj.add(diff);
request = createRequest(initMigration);
}
for (String pendingDrop : request.getPendingDrops()) {
sj.add(generatePendingDrop(request, pendingDrop));
}
return sj.length() == 0 ? null : sj.toString();
} else if (pendingVersion != null) {
return generatePendingDrop(request, pendingVersion);
} else {
return generateDiff(request);
Expand Down Expand Up @@ -554,7 +620,7 @@ private String generateMigration(Request request, Migration dbMigration, String
return null;
} else {
if (!platforms.isEmpty()) {
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir, request.initMigration && keepLastInit);

} else if (databasePlatform != null) {
// writer needs the current model to provide table/column details for
Expand Down Expand Up @@ -634,12 +700,17 @@ private String toUnderScore(String name) {
/**
* Write any extra platform ddl.
*/
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath, boolean clear) throws IOException {
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
for (Pair pair : platforms) {
DdlWrite writer = new DdlWrite(new MConfiguration(), currentModel.read(), options);
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
if (clear) {
for (File existing : subPath.listFiles()) {
existing.delete();
}
}
platformWriter.processMigration(dbMigration, writer, subPath, fullVersion);
}
}
Expand All @@ -657,7 +728,7 @@ private boolean writeMigrationXml(Migration dbMigration, File resourcePath, Stri
if (file.exists()) {
return false;
}
String comment = Boolean.TRUE.equals(includeGeneratedFileComment) ? GENERATED_COMMENT : null;
String comment = includeGeneratedFileComment ? GENERATED_COMMENT : null;
MigrationXmlWriter xmlWriter = new MigrationXmlWriter(comment);
xmlWriter.write(dbMigration, file);
return true;
Expand All @@ -675,6 +746,9 @@ private void setDefaults() {
databasePlatform = server.databasePlatform();
}
if (databaseConfig != null) {
// FIXME: StrictMode and header may be defined HERE and in DatabaseConfig.
// We shoild change either DefaultDbMigration or databaseConfig, so that it is only
// defined on one place
if (strictMode != null) {
databaseConfig.setDdlStrictMode(strictMode);
}
Expand Down Expand Up @@ -749,15 +823,20 @@ public File migrationDirectory() {
* Return the file path to write the xml and sql to.
*/
File migrationDirectory(boolean initMigration) {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
if (!resourceRootDir.exists()) {
String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
throw new UnknownResourcePathException(msg);
}
String resourcePath = migrationPath(initMigration);
Location resourcePath = migrationPath(initMigration);
// expect to be a path to something like - src/main/resources/dbmigration
File path = new File(resourceRootDir, resourcePath);
File path;
if (resourcePath.isClassPath()) {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
if (!resourceRootDir.exists()) {
String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
throw new UnknownResourcePathException(msg);
}
path = new File(resourceRootDir, resourcePath.path());
} else {
path = new File(resourcePath.path());
}
if (!path.exists()) {
if (!path.mkdirs()) {
logInfo("Warning - Unable to ensure migration directory exists at %s", path.getAbsolutePath());
Expand All @@ -766,8 +845,9 @@ File migrationDirectory(boolean initMigration) {
return path;
}

private String migrationPath(boolean initMigration) {
return initMigration ? migrationInitPath : migrationPath;
private Location migrationPath(boolean initMigration) {
// remove classpath: or filesystem: prefix
return new Location(initMigration ? migrationInitPath : migrationPath);
}

/**
Expand Down
1 change: 1 addition & 0 deletions ebean-ddl-generator/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module io.ebean.ddl.generator {

uses io.ebean.plugin.Plugin;
exports io.ebean.dbmigration;

provides io.ebean.dbmigration.DbMigration with io.ebeaninternal.dbmigration.DefaultDbMigration;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.ebeaninternal.dbmigration.DbMigrationPlugin
24 changes: 0 additions & 24 deletions ebean-ddl-generator/src/test/resources/application-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,3 @@ datasource.h2.url=jdbc:h2:mem:h2AutoTune
datasource.pg.username=sa
datasource.pg.password=
datasource.pg.url=jdbc:h2:mem:h2AutoTune

# parameters for migration test
datasource.migrationtest.username=SA
datasource.migrationtest.password=SA
datasource.migrationtest.url=jdbc:h2:mem:migration
ebean.migrationtest.applyPrefix=V
ebean.migrationtest.ddl.generate=false
ebean.migrationtest.ddl.run=false
ebean.migrationtest.ddl.header=-- Migrationscripts for ebean unittest
ebean.migrationtest.migration.appName=migrationtest
ebean.migrationtest.migration.migrationPath=dbmigration/migrationtest
ebean.migrationtest.migration.strict=true

# parameters for migration test
datasource.migrationtest-history.username=SA
datasource.migrationtest-history.password=SA
datasource.migrationtest-history.url=jdbc:h2:mem:migration
ebean.migrationtest-history.applyPrefix=V
ebean.migrationtest-history.ddl.generate=false
ebean.migrationtest-history.ddl.run=false
ebean.migrationtest-history.ddl.header=-- Migrationscripts for ebean unittest DbMigrationDropHistoryTest
ebean.migrationtest-history.migration.appName=migrationtest-history
ebean.migrationtest-history.migration.migrationPath=dbmigration/migrationtest-history
ebean.migrationtest-history.migration.strict=true
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,11 @@ public static void main(String[] args) throws IOException {
List<String> pendingDrops = migration.getPendingDrops();
assertThat(pendingDrops).contains("1.1");

//System.setProperty("ddl.migration.pendingDropsFor", "1.1");
migration.setGeneratePendingDrop("1.1");
assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1");
assertThatThrownBy(()->migration.generateMigration())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("No 'pendingDrops'"); // subsequent call
System.clearProperty("ddl.migration.pendingDropsFor");

server.shutdown();
logger.info("end");
Expand Down
Loading

0 comments on commit e666277

Please sign in to comment.