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

au.net.causal.maven.plugins.boxdb.StartMojo Maven / Gradle / Ivy

There is a newer version: 3.3
Show newest version
package au.net.causal.maven.plugins.boxdb;

import au.net.causal.maven.plugins.boxdb.db.BoxContext;
import au.net.causal.maven.plugins.boxdb.db.BoxDatabase;
import au.net.causal.maven.plugins.boxdb.db.BoxDatabaseException;
import au.net.causal.maven.plugins.boxdb.db.DatabaseLog;
import au.net.causal.maven.plugins.boxdb.db.DatabaseStage;
import au.net.causal.maven.plugins.boxdb.db.DatabaseTarget;
import au.net.causal.maven.plugins.boxdb.db.DockerService;
import au.net.causal.maven.plugins.boxdb.db.JdbcConnectionInfo;
import au.net.causal.maven.plugins.boxdb.db.ProjectConfiguration;
import au.net.causal.maven.plugins.boxdb.db.ScriptExecution;
import au.net.causal.maven.plugins.boxdb.db.ScriptSelection;
import io.fabric8.maven.docker.access.DockerAccessException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

/**
 * Starts a database.  When this goal has finished executing, the database will have been started and set up.
 */
@Mojo(name="start", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, requiresProject = false)
public class StartMojo extends AbstractDatabaseMojo
{
    /**
     * SQL files to execute after the database has started up.  These files are executed using
     * database-native tool (e.g. psql on PostgreSQL).
     */
    @Parameter(property = "db.sql")
    private List sqlFiles = new ArrayList<>();

    /**
     * SQL files to execute after the database has started up as the database admin user.  These files are executed using
     * database-native tool (e.g. psql on PostgreSQL) before boxdb's database creation scripts.
     *
     * @since 3.0
     */
    @Parameter(property = "db.setup.sql")
    private List sqlSetupFiles = new ArrayList<>();

    /**
     * If true, don't trust the plain-socket database-is-up waiting mechanism and poll by attempting to make
     * JDBC connections to the database.  This should be turned on if basic socket connectivity cannot be trusted for
     * containers, which may be the case for certain Docker implementations.
     * 

* * As of version 3.0, JDBC waiting is turned on by default. Turning this off may increase performance of polling * and result in faster database start-up times, but may also result in BoxDB not detecting when a database is * up properly. Turn this off at your own risk. */ @Parameter(property = "db.jdbcWaiting", defaultValue = "true") private boolean useJdbcWaiting; /** * Controls whether/how database logs are dumped to Maven output if the database fails to start. * Valid options are OFF, MAIN and ALL. */ @Parameter(property = "db.dumpLogsOnError", defaultValue = "OFF", required = true) private DumpLogsOnErrorOption dumpLogsOnError; private void setupExtraExecutionsForSqlFiles() { if (!sqlFiles.isEmpty()) { List sqlFileNames = sqlFiles.stream().map(File::getAbsolutePath).collect(Collectors.toList()); ScriptExecution execution = new ScriptExecution(); execution.setStage(DatabaseStage.SETUP); execution.setDirectory(project.getBasedir()); execution.setIgnoreMissing(false); execution.setFiltering(true); execution.setSelection(ScriptSelection.ALL); execution.setScripts(sqlFileNames); box.getScriptExecutions().add(execution); } if (!sqlSetupFiles.isEmpty()) { List sqlFileNames = sqlSetupFiles.stream().map(File::getAbsolutePath).collect(Collectors.toList()); ScriptExecution execution = new ScriptExecution(); execution.setStage(DatabaseStage.CREATE); execution.setDirectory(project.getBasedir()); execution.setIgnoreMissing(false); execution.setFiltering(true); execution.setSelection(ScriptSelection.ALL); execution.setScripts(sqlFileNames); box.getScriptExecutions().add(execution); } } private void waitForDatabaseStart(BoxDatabase database, Duration maxWaitTime, Duration pollTime) throws TimeoutException, BoxDatabaseException { long startTime = System.currentTimeMillis(); long maxEndTime = startTime + maxWaitTime.toMillis(); database.waitUntilStarted(maxWaitTime); if (useJdbcWaiting) { boolean connectionSucceeded = false; try { while (!connectionSucceeded && System.currentTimeMillis() < maxEndTime) { try (Connection con = database.createJdbcConnection(DatabaseTarget.ADMIN)) { //If we get a connection that's all we need, don't bother actually trying to use it //Might try this later on, but require JDBC 4 driver which we can't guarantee //con.isValid() getLog().debug("Successfully connected to database: " + con); connectionSucceeded = true; } catch (SQLException e) { if (database.databaseIsReady(e)) { connectionSucceeded = true; getLog().debug("Failed to connect, but database deemed it is ready: " + e, e); } else { //This might happen, ignore and loop again getLog().debug("Loop waiting for database connectivity failed to get connection", e); } } catch (IOException e) { //This might happen, ignore and loop again getLog().debug("Loop waiting for database connectivity failed to get connection", e); } if (!connectionSucceeded) Thread.sleep(pollTime.toMillis()); } if (!connectionSucceeded) throw new TimeoutException("Timeout waiting for successful connection"); } //while loop catch (InterruptedException e) { throw new BoxDatabaseException("Interrupted waiting for database to start up.", e); } } } @Override protected void executeInternal(ExceptionalSupplier dockerService) throws DockerAccessException, MojoExecutionException { setupExtraExecutionsForSqlFiles(); Duration maxWaitTime = Duration.ofSeconds(startupTimeout); Duration pollTime = Duration.ofMillis(pollTimeMillis); try { BoxDatabase boxDatabase = database(dockerService); if (!boxDatabase.exists()) { getLog().info("Creating database '" + boxDatabase.getName() + "'"); try { boxDatabase.createAndStart(); waitForDatabaseStart(boxDatabase, maxWaitTime, pollTime); } catch (BoxDatabaseException | TimeoutException e) { try { dumpLogs(dumpLogsOnError, boxDatabase); } catch (IOException | BoxDatabaseException | RuntimeException ex) { getLog().error("Error dumping database logs on error: " + ex, ex); } throw e; } getLog().info("Database started."); JdbcConnectionInfo jdbcInfo = boxDatabase.jdbcConnectionInfo(DatabaseTarget.USER); getLog().info("JDBC URL: " + jdbcInfo.getUri() + " user=" + jdbcInfo.getUser() + (displayPasswords ? " password=" + jdbcInfo.getPassword() : "")); runScriptExecutions(box.scriptExecutions(DatabaseStage.CREATE), boxDatabase, dockerService); if (box.isInitializeDatabase()) { try { boxDatabase.configureNewDatabase(); } catch (IOException | SQLException e) { throw new MojoExecutionException("Error setting up database '" + boxDatabase.getName() + "': " + e, e); } } runScriptExecutions(box.scriptExecutions(DatabaseStage.SETUP), boxDatabase, dockerService); if (box.getRestorePath() != null) runDatabaseRestore(box.getRestorePath(), boxDatabase); runScriptExecutions(box.scriptExecutions(DatabaseStage.POST_RESTORE), boxDatabase, dockerService); } else if (!boxDatabase.isRunning()) { //Container exists but is not started - no need to run setup scripts here getLog().info("Starting '" + boxDatabase.getName() + "'"); try { boxDatabase.start(); waitForDatabaseStart(boxDatabase, maxWaitTime, pollTime); } catch (BoxDatabaseException | TimeoutException e) { try { dumpLogs(dumpLogsOnError, boxDatabase); } catch (IOException | BoxDatabaseException ex) { getLog().error("Error dumping database logs on error: " + ex, ex); } throw e; } getLog().info("Database started."); } else getLog().info("Database '" + boxDatabase.getName() + "' is already running"); //Put the JDBC info in properties JdbcConnectionInfo userJdbcInfo = boxDatabase.jdbcConnectionInfo(DatabaseTarget.USER); if (userJdbcInfo.getUri() != null) project.getProperties().setProperty("boxdb.jdbc.url", userJdbcInfo.getUri()); if (userJdbcInfo.getUser() != null) project.getProperties().setProperty("boxdb.jdbc.user", userJdbcInfo.getUser()); if (userJdbcInfo.getPassword() != null) project.getProperties().setProperty("boxdb.jdbc.password", userJdbcInfo.getPassword()); JdbcConnectionInfo adminJdbcInfo = boxDatabase.jdbcConnectionInfo(DatabaseTarget.ADMIN); if (adminJdbcInfo.getUri() != null) project.getProperties().setProperty("boxdb.admin.jdbc.url", adminJdbcInfo.getUri()); if (adminJdbcInfo.getUser() != null) project.getProperties().setProperty("boxdb.admin.jdbc.user", adminJdbcInfo.getUser()); if (adminJdbcInfo.getPassword() != null) project.getProperties().setProperty("boxdb.admin.jdbc.password", adminJdbcInfo.getPassword()); } catch (BoxDatabaseException e) { throw new MojoExecutionException(e.getMessage(), e); } catch (TimeoutException e) { throw new MojoExecutionException("Timeout waiting for database to start up", e); } } private void dumpLogs(DumpLogsOnErrorOption dumpLogsOnError, BoxDatabase boxDatabase) throws BoxDatabaseException, IOException { if (dumpLogsOnError != DumpLogsOnErrorOption.OFF) getLog().error("Error occurred starting database. Dumping logs..."); else getLog().error("Error occurred starting database. Use -Ddb.dumpLogsOnError=ALL or -Ddb.dumpLogsOnError=MAIN to dump database logs and get additional failure details."); switch (dumpLogsOnError) { case OFF: break; case MAIN: DatabaseLog mainLog = boxDatabase.bestLogFile(); if (mainLog != null) dumpLogToError(mainLog); break; case ALL: for (DatabaseLog curLog : boxDatabase.logFiles()) { dumpLogToError(curLog); } break; default: throw new Error("Unknown DumpLogsOnErrorOption: " + dumpLogsOnError); } } private void runDatabaseRestore(Path restoreFile, BoxDatabase boxDatabase) throws MojoExecutionException { if (Files.notExists(restoreFile)) throw new MojoExecutionException("Database restore file " + restoreFile + " does not exist."); try { boxDatabase.restore(restoreFile); } catch (IOException | BoxDatabaseException | SQLException e) { throw new MojoExecutionException("Failed to restore database from backup file " + restoreFile.toString() + ": " + e, e); } } private void runScriptExecutions(Iterable scriptExecutions, BoxDatabase boxDatabase, ExceptionalSupplier dockerService) throws MojoExecutionException { ProjectConfiguration projectConfiguration = projectConfiguration(); BoxContext context = boxContext(dockerService); ScriptRunner scriptRunner = context.createScriptRunner(boxDatabase, box, projectConfiguration); try { for (ScriptExecution scriptExecution : scriptExecutions) { try { scriptRunner.execute(scriptExecution, Duration.ofSeconds(scriptTimeout)); } catch (IOException | SQLException | BoxDatabaseException e) { throw new MojoExecutionException("Error running SQL scripts: " + e, e); } } } finally { tempFilesCreated.addAll(scriptRunner.getTempFilesCreated()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy