All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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