play.db.ebean.orm.EbeanDynamicEvolutions Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2009-2014 Typesafe Inc.
*/
package play.db.ebean.orm;
import com.typesafe.config.Config;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.config.DatabaseConfig;
import io.ebean.dbmigration.DbMigration;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Environment;
import play.api.db.evolutions.DynamicEvolutions;
import play.db.ebean.orm.evolution.EvolutionMigration;
import play.inject.ApplicationLifecycle;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* A Play module that automatically manages Ebean configuration.
*
* @since 14.11.27
*/
@Singleton
public class EbeanDynamicEvolutions extends DynamicEvolutions {
/**
* The Logger.
*
* @since 22.03.09
*/
private final Logger logger;
/**
* The Ebean config.
*
* @since 14.11.27
*/
private final EbeanConfig ebeanConfig;
/**
* The Configuration.
*
* @since 16.12.16
*/
private final Config configuration;
/**
* The Environment.
*
* @since 14.11.27
*/
private final Environment environment;
/**
* The Servers.
*
* @since 16.02.17
*/
private final Map servers = new HashMap<>();
/**
* Build a default instance.
*
* @param ebeanConfig The current Ebean servers configuration
* @param environment The current Play environment
* @param configuration The current Play configuration
* @param lifecycle The current Play lifecycle instance
* @since 14.11.27
*/
@Inject
public EbeanDynamicEvolutions(final EbeanConfig ebeanConfig, final Environment environment,
final Config configuration, final ApplicationLifecycle lifecycle) {
this.logger = LoggerFactory.getLogger(EbeanDynamicEvolutions.class);
this.ebeanConfig = ebeanConfig;
this.configuration = configuration;
this.environment = environment;
this.start();
lifecycle.addStopHook(() -> {
this.servers.forEach((database, server) ->
server.shutdown(
false,
false
)
);
return CompletableFuture.completedFuture(null);
});
}
/**
* Helper method that generates the required evolution
* to properly run Ebean.
*
* @param server The EbeanServer
* @return The complete migration script generated by Ebean
* @since 14.11.27
*/
public static String generateEvolutionScript(final Database server) {
try {
final SpiEbeanServer spiServer = (SpiEbeanServer) server.pluginApi();
final CurrentModel ddl = new CurrentModel(spiServer);
final String ups = ddl.getCreateDdl();
final String downs = ddl.getDropAllDdl();
if (ups == null || ups.trim().isEmpty()) {
return null;
}
return (
"# --- Created by Ebean DDL\r\n" +
"# To stop Ebean DDL generation, remove this comment and start using Evolutions\r\n" +
"\r\n" +
"# --- !Ups\r\n" +
"\r\n" +
ups +
"\r\n" +
"# --- !Downs\r\n" +
"\r\n" +
downs
);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Initialise the Ebean servers.
*
* @since 14.11.27
*/
public void start() {
this.ebeanConfig
.serverConfigs()
.forEach((key, serverConfig) ->
this.servers.put(key, DatabaseFactory.create(serverConfig))
);
}
/**
* Generate evolutions.
*
* @since 14.11.27
*/
@Override
public void create() {
if (!this.environment.isProd()) {
this.ebeanConfig
.serverConfigs()
.forEach(this::configureServer);
}
}
/**
* Configure server.
*
* @param serverName the server name
* @param databaseConfig the database config
*/
private void configureServer(final String serverName, final DatabaseConfig databaseConfig) {
if (!this.configuration.hasPath("play.evolutions.db." + serverName + ".enabled")
|| this.configuration.getBoolean("play.evolutions.db." + serverName + ".enabled")) {
final String evolutionScript = EbeanDynamicEvolutions.generateEvolutionScript(this.servers.get(serverName));
final File outputDirectory = this.environment.getFile("conf/evolutions/" + serverName);
final File outputFile = new File(outputDirectory, "1.sql"); //this.environment.getFile("conf/evolutions/" + serverName + "/1.sql");
if (this.shouldEvolutionBeenWritten(evolutionScript, outputFile)) {
this.createDirectory(outputDirectory);
this.applyEvolutionMigration(outputDirectory, databaseConfig);
this.writeEvolution(evolutionScript, outputFile);
}
}
}
/**
* Apply evolution migration.
*
* @param outputDirectory the output directory
* @param databaseConfig the database config
*/
private void applyEvolutionMigration(final File outputDirectory, final DatabaseConfig databaseConfig) {
final PlayEbeanExtraConfig config = databaseConfig.getServiceObject(PlayEbeanExtraConfig.class);
if (!config.getEvolutionMigrationConfig().isEnabled()) {
return;
}
final File modelFolder = new File(outputDirectory, "model");
final File initialSQL = new File(outputDirectory, "1.sql");
if (modelFolder.exists()) {
if (!this.deleteDirectory(modelFolder)) {
throw new RuntimeException("Unable to remove " + modelFolder.getPath());
}
}
if (initialSQL.exists()) {
if (!initialSQL.delete()) {
throw new RuntimeException("Unable to remove " + initialSQL.getPath());
}
}
final DbMigration dbMigration = DbMigration.create();
dbMigration.setServerConfig(databaseConfig);
dbMigration.setServer(DB.byName(databaseConfig.getName()));
dbMigration.setVersion("1");
dbMigration.setPathToResources(this.environment.getFile("conf").getPath());
dbMigration.setMigrationPath("evolutions/" + databaseConfig.getName());
dbMigration.setIncludeGeneratedFileComment(true);
dbMigration.setHeader(EvolutionMigration.getHeader());
try {
final String s = dbMigration.generateMigration();
logger.error("DEBUG : {}", s);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Delete directory boolean.
*
* @param directoryToBeDeleted the directory to be deleted
* @return the boolean
*/
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
/**
* Write evolution.
*
* @param evolutionScript the evolution script
* @param outputFile the output file
*/
private void writeEvolution(final String evolutionScript, final File outputFile) {
try {
Files.write(
outputFile.toPath(),
evolutionScript.getBytes(StandardCharsets.UTF_8)
);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
/**
* Create directory.
*
* @param outputDirectory the output directory
*/
private void createDirectory(final File outputDirectory) {
if (!outputDirectory.exists()) {
if (!outputDirectory.mkdirs()) {
throw new RuntimeException("Can't create 'Evolution' directory");
}
}
}
/**
* Should evolution been written boolean.
*
* @param evolutionScript the evolution script
* @param evolutions the evolutions
* @return the boolean
*/
private boolean shouldEvolutionBeenWritten(final String evolutionScript, final File evolutions) {
if (evolutionScript == null) {
return false;
}
try {
String content = "";
if (evolutions.exists()) {
content = new String(
Files.readAllBytes(evolutions.toPath()),
StandardCharsets.UTF_8
);
}
if (content.isEmpty() || content.startsWith("# --- Created by Ebean DDL")) {
return !content.equals(evolutionScript);
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy