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

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

package au.net.causal.maven.plugins.boxdb;

import au.net.causal.maven.plugins.boxdb.db.BoxConfiguration;
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.BoxDatabaseFactory;
import au.net.causal.maven.plugins.boxdb.db.BoxLookup;
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.DockerService;
import au.net.causal.maven.plugins.boxdb.db.ExecutionMode;
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.config.ImagePullPolicy;
import io.fabric8.maven.docker.service.ImagePullManager;
import io.fabric8.maven.docker.service.ImagePullManager.CacheStore;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.shared.filtering.MavenReaderFilter;
import org.apache.maven.shared.filtering.MavenResourcesFiltering;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.DependencyResolutionException;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public abstract class AbstractDatabaseMojo extends AbstractDockerDbMojo
{
    /**
     * Chooses the database type to use.  For example: postgres.  Use the versions goal
     * to list all available database types and versions.
     */
    @Parameter(property = "db.type")
    private String databaseType;

    /**
     * Chooses the version fo the database to use.  If not specified, the default version for the given database
     * type will be used.  Use the versions goal to list all available database types and versions.
     */
    @Parameter(property = "db.version")
    private String databaseVersion;

    /**
     * The name of the database to create.  Some database implementations support hosting multiple databases in the
     * one container.  This controls the name of this database that will be generated.  Might not be used on all
     * database implementations.
     */
    @Parameter(property = "db.name")
    private String databaseName;

    /**
     * The name to give the database container.  For Docker-based databases, this controls the Docker container name.
     * For Vagrant-based databases, this controls the Vagrant box name.  Not used for non-container-based
     * databases.  Defaults to a name derived from the database name.
     */
    @Parameter(property = "db.containerName")
    private String containerName;

    /**
     * For file-based databases, controls the name of the database file or directory.
     */
    @Parameter(property = "db.file") 
    private File databaseFile;

    /**
     * If specified, this backup file will be used to restore data from the database.  The backup file can be generated
     * using the backup goal, or using database-specific tools.
     */
    @Parameter(property = "db.restoreFile")
    private File databaseRestoreFile;

    /**
     * The name of the user that will own the target database.  This user will typically only
     * have access to the target database.  This user is created when setting up the database.
     * Defaults to a database-specific username.
     */
    @Parameter(property = "db.user")
    private String databaseUser;

    /**
     * The password to use for the user that will own the target database.  This user will typically only
     * have access to the target database.
     */
    @Parameter(property = "db.password")
    private String databasePassword;

    /**
     * The collation to use for creating the database.  Default is dependent on database type.
     * 
     * @since 2.0
     */
    @Parameter(property = "db.collation")
    private String databaseCollation;

    /**
     * The encoding to use for creating the database.  Default is dependent on database type.
     * 
     * @since 2.0
     */
    @Parameter(property = "db.encoding")
    private String databaseEncoding;

    /**
     * Controls whether the database image and binaries are checked for updates in remote repositories.
     * Some database images, such as Docker images, may be updated remotely.  When this option is turned on, the plugin
     * will check for updates and download them if available.  When turned off, images are only downloaded when it does
     * not exist locally.
     * 

* * This global setting overrides any configured setting in each database configuration. * * @since 3.0 */ //Note: db.updateImage is referred to in prepareMojo() strings @Parameter(property = "db.updateImage") private Boolean updateImage; /** * The port to run the database on. Default is dependent on database type, and might not be configurable for all * database types. * * @since 3.0 */ @Parameter(property = "db.port") private Integer databasePort; /** * Shortcut for setting additional box database configuration options globally. Available options are dependent on database type. */ @Parameter private Map databaseConfiguration = new LinkedHashMap<>(); /** * Only used to allow databaseConfiguration parameter to be set from the command line. In plugin * configurations, only databaseConfiguration should be used. Each entry is in the form key=value. */ @Parameter(property = "db.configuration") private List databaseConfigurationList = new ArrayList<>(); @Parameter(property = "db.initialize") private Boolean databaseInitialize; /** * Maximum time to wait in seconds for database to start up. */ @Parameter(property = "boxdb.startupTimeout", defaultValue = "1800", required = true) protected long startupTimeout; /** * Maximum time in seconds a SQL script may run before it is terminated. Timeouts may not have effect on all database types. */ @Parameter(property = "boxdb.scriptTimeout", defaultValue = "3600", required = true) protected long scriptTimeout; /** * Maximum time in seconds a database backup or restore may run before it is terminated. Timeouts may not have effect * on all database types. */ @Parameter(property = "boxdb.backupTimeout", defaultValue = "3600", required = true) protected long backupTimeout; /** * Time to wait in milliseconds during graceful database stop before doing a forced kill. Timeouts may not have effect on all database types. * Timeout of zero means wait indefinitely. */ @Parameter(property = "boxdb.killTimeout", defaultValue = "0", required = true) protected long killTimeout; /** * Amount of time in milliseconds to wait between polling when checking database operations, scripts, etc. * When polling is used depends on database type. */ @Parameter(property = "boxdb.pollTimeMillis", defaultValue = "300", required = true) protected long pollTimeMillis; /** * If true, passwords for connections are displayed in console output. If false (the default), they are not. */ @Parameter(property = "boxdb.displayPasswords", defaultValue = "false") protected boolean displayPasswords; @Parameter(defaultValue = "${project.build.directory}/boxdb", required = true) protected File workDirectory; @Parameter(defaultValue = "${user.home}/.boxdb", required = true) protected File globalConfigDirectory; @Parameter protected BoxConfiguration box = new BoxConfiguration(); @Parameter(defaultValue = ScriptExecution.DEFAULT_BASE_DIRECTORY) private File defaultScriptDirectory; @Parameter(property = "db.deleteOnStop") protected boolean deleteOnStop; /** * Additional Maven artifacts to readTags for BoxDatabase implementations. * Each element in the list is a string in the form groupId:artifactId:version. *

* * This can be a handy alternative to specifying additional dependency elements when configuring the * plugin, especially when running this Maven plugin without a Maven project. */ @Parameter(property = "boxdb.artifacts") private List additionalBoxArtifacts = new ArrayList<>(); /** * If true, completely skip execution of BoxDB. Useful for skipping execution from command line. */ @Parameter(property = "boxdb.skip", defaultValue = "false") private boolean skipExecution; @Component protected MavenResourcesFiltering resourcesFiltering; @Component protected MavenReaderFilter readerFilter; @Component protected RepositorySystem repositorySystem; @Component protected ArchiverManager archiverManager; @Parameter(defaultValue = "${repositorySystemSession}", readonly = true) protected RepositorySystemSession repoSession; @Parameter(defaultValue = "${project.remotePluginRepositories}", readonly = true) protected List remoteRepos; protected final List tempFilesCreated = new ArrayList<>(); /** * Cache to create classloaders at static level to prevent as much as possible to create too many classloaders. */ protected static final ClassLoaderCache classLoaderCache = new ClassLoaderCache(); @Override public void execute() throws MojoExecutionException, MojoFailureException { if (skipExecution) { getLog().info("Skipping execution"); return; } //If we don't have a real project then use tempdir as work directory boolean usingTempDirectory = false; if (project == null || project.getFile() == null) { //Can't do this - to map scripts to docker machine they need to be inside user dir on Mac //workDirectory = new File(System.getProperty("java.io.tmpdir")); workDirectory = new File(".").getAbsoluteFile(); usingTempDirectory = true; } if (databaseType != null) box.setDatabaseType(databaseType); if (databaseVersion != null) box.setDatabaseVersion(databaseVersion); if (containerName != null) box.setContainerName(containerName); if (databaseName != null) box.setDatabaseName(databaseName); if (databaseFile != null) box.setDatabaseFile(databaseFile.toPath()); if (databaseRestoreFile != null) box.setRestoreFile(databaseRestoreFile); if (databaseUser != null) box.setDatabaseUser(databaseUser); if (databasePassword != null) box.setDatabasePassword(databasePassword); if (databaseCollation != null) box.setDatabaseCollation(databaseCollation); if (databaseEncoding != null) box.setDatabaseEncoding(databaseEncoding); if (databasePort != null) box.setDatabasePort(databasePort); if (!databaseConfigurationList.isEmpty()) fillInMapFromListParameter(databaseConfiguration, databaseConfigurationList); if (!databaseConfiguration.isEmpty()) box.getConfiguration().putAll(databaseConfiguration); if (databaseInitialize != null) box.setInitializeDatabase(databaseInitialize); if (updateImage != null) box.setUpdateImage(updateImage); //Fill in box default scripts if needed if (box.getScriptExecutions().isEmpty()) box.setScriptExecutions(defaultScriptExecutions()); try { super.execute(); } finally { //If we don't have a project we have used the user's temp directory //so try and clean this up after execution if (usingTempDirectory) { //Delete in reverse order, so we get directories last Collections.reverse(tempFilesCreated); for (Path tempFile : tempFilesCreated) { try { getLog().info("Delete temp file " + tempFile); Files.delete(tempFile); } catch (IOException e) { getLog().warn("Error deleting temp file " + tempFile); } } } } } private void fillInMapFromListParameter(Map map, List list) { for (String item : list) { String[] kv = item.split(Pattern.quote("="), 2); if (kv.length == 2) map.put(kv[0], kv[1]); } } private List defaultScriptExecutions() { ScriptExecution createExecution = new ScriptExecution(); createExecution.setDirectory(defaultScriptDirectory); createExecution.setIgnoreMissing(true); createExecution.setSelection(ScriptSelection.FIRST); createExecution.setStage(DatabaseStage.CREATE); createExecution.setMode(ExecutionMode.NATIVE); createExecution.getScripts().add("create-${box.databaseType}-${box.databaseVersion}.sql"); createExecution.getScripts().add("create-${box.databaseType}-${box.databaseVersion.major}.sql"); createExecution.getScripts().add("create-${box.databaseType}.sql"); createExecution.getScripts().add("create.sql"); ScriptExecution createJdbcExecution = new ScriptExecution(); createJdbcExecution.setDirectory(defaultScriptDirectory); createJdbcExecution.setIgnoreMissing(true); createJdbcExecution.setSelection(ScriptSelection.FIRST); createJdbcExecution.setStage(DatabaseStage.CREATE); createJdbcExecution.setMode(ExecutionMode.JDBC); createJdbcExecution.getScripts().add("create-jdbc-${box.databaseType}-${box.databaseVersion}.sql"); createJdbcExecution.getScripts().add("create-jdbc-${box.databaseType}-${box.databaseVersion.major}.sql"); createJdbcExecution.getScripts().add("create-jdbc-${box.databaseType}.sql"); createJdbcExecution.getScripts().add("create-jdbc.sql"); ScriptExecution setupExecution = new ScriptExecution(); setupExecution.setDirectory(defaultScriptDirectory); setupExecution.setIgnoreMissing(true); setupExecution.setSelection(ScriptSelection.FIRST); setupExecution.setStage(DatabaseStage.SETUP); setupExecution.setMode(ExecutionMode.NATIVE); setupExecution.getScripts().add("setup-${box.databaseType}-${box.databaseVersion}.sql"); setupExecution.getScripts().add("setup-${box.databaseType}-${box.databaseVersion.major}.sql"); setupExecution.getScripts().add("setup-${box.databaseType}.sql"); setupExecution.getScripts().add("setup.sql"); ScriptExecution setupJdbcExecution = new ScriptExecution(); setupJdbcExecution.setDirectory(defaultScriptDirectory); setupJdbcExecution.setIgnoreMissing(true); setupJdbcExecution.setSelection(ScriptSelection.FIRST); setupJdbcExecution.setStage(DatabaseStage.SETUP); setupJdbcExecution.setMode(ExecutionMode.JDBC); setupJdbcExecution.getScripts().add("setup-jdbc-${box.databaseType}-${box.databaseVersion}.sql"); setupJdbcExecution.getScripts().add("setup-jdbc-${box.databaseType}-${box.databaseVersion.major}.sql"); setupJdbcExecution.getScripts().add("setup-jdbc-${box.databaseType}.sql"); setupJdbcExecution.getScripts().add("setup-jdbc.sql"); ScriptExecution postRestoreExecution = new ScriptExecution(); postRestoreExecution.setDirectory(defaultScriptDirectory); postRestoreExecution.setIgnoreMissing(true); postRestoreExecution.setSelection(ScriptSelection.FIRST); postRestoreExecution.setStage(DatabaseStage.POST_RESTORE); postRestoreExecution.setMode(ExecutionMode.NATIVE); postRestoreExecution.getScripts().add("postrestore-${box.databaseType}-${box.databaseVersion}.sql"); postRestoreExecution.getScripts().add("postrestore-${box.databaseType}-${box.databaseVersion.major}.sql"); postRestoreExecution.getScripts().add("postrestore-${box.databaseType}.sql"); postRestoreExecution.getScripts().add("postrestore.sql"); ScriptExecution postRestoreJdbcExecution = new ScriptExecution(); postRestoreJdbcExecution.setDirectory(defaultScriptDirectory); postRestoreJdbcExecution.setIgnoreMissing(true); postRestoreJdbcExecution.setSelection(ScriptSelection.FIRST); postRestoreJdbcExecution.setStage(DatabaseStage.POST_RESTORE); postRestoreJdbcExecution.setMode(ExecutionMode.JDBC); postRestoreJdbcExecution.getScripts().add("postrestore-jdbc-${box.databaseType}-${box.databaseVersion}.sql"); postRestoreJdbcExecution.getScripts().add("postrestore-jdbc-${box.databaseType}-${box.databaseVersion.major}.sql"); postRestoreJdbcExecution.getScripts().add("postrestore-jdbc-${box.databaseType}.sql"); postRestoreJdbcExecution.getScripts().add("postrestore-jdbc.sql"); return Arrays.asList(createExecution, createJdbcExecution, setupExecution, setupJdbcExecution, postRestoreExecution, postRestoreJdbcExecution); } protected ClassLoader createBoxClassLoader() throws MojoExecutionException { getLog().debug("Additional artifacts: " + additionalBoxArtifacts); if (additionalBoxArtifacts == null || additionalBoxArtifacts.isEmpty()) return AbstractDatabaseMojo.class.getClassLoader(); try { List jarFiles = DependencyUtils.resolveDependenciesFromStrings(additionalBoxArtifacts, repositorySystem, repoSession, remoteRepos); List jarUrls = new ArrayList<>(jarFiles.size()); for (Path jarFile : jarFiles) { jarUrls.add(jarFile.toUri().toURL()); } URL[] jarUrlArray = jarUrls.stream().toArray(URL[]::new); return URLClassLoader.newInstance(jarUrlArray, AbstractDatabaseMojo.class.getClassLoader()); } catch (DependencyResolutionException e) { throw new MojoExecutionException("Failed to resolve additional box artifacts: " + e.getMessage(), e); } catch (MalformedURLException e) { throw new MojoExecutionException("Failed to generate URLs from dependency JARs: " + e, e); } } protected BoxDatabaseFactory databaseFactory(ExceptionalSupplier serviceHub) throws MojoExecutionException, BoxDatabaseException { ClassLoader boxClassLoader = createBoxClassLoader(); String dbType = box.getDatabaseType(); BoxLookup lookup = new BoxLookup(getLog(), boxClassLoader); BoxDatabaseFactory factory = lookup.findFactory(dbType); if (factory == null) { throw new MojoExecutionException("Database type '" + dbType + "' not supported. Available database types: " + lookup.getAvailableBoxFactoryNames()); } return factory; } protected ProjectConfiguration projectConfiguration() { return new ProjectConfiguration(project, settings, Duration.ofSeconds(scriptTimeout), Duration.ofSeconds(backupTimeout), Duration.ofMillis(pollTimeMillis), killTimeout == 0 ? null : Duration.ofMillis(killTimeout)); } protected BoxContext boxContext(ExceptionalSupplier serviceHub) { CacheStore sessionCacheStore = getSessionCacheStore(); ImagePullManager updatingImagePullManager = new ImagePullManager(sessionCacheStore, ImagePullPolicy.Always.name(), null); ImagePullManager nonUpdatingImagePullManager = new ImagePullManager(sessionCacheStore, ImagePullPolicy.IfNotPresent.name(), null); return new BoxContext(serviceHub, authConfigFactory, workDirectory.toPath(), globalConfigDirectory.toPath(), getLog(), logSpecFactory, session, resourcesFiltering, readerFilter, repositorySystem, repoSession, remoteRepos, archiverManager, classLoaderCache, updatingImagePullManager, nonUpdatingImagePullManager, getAuthConfig(), isSkipExtendedAuth()); } protected BoxDatabase database(ExceptionalSupplier serviceHub) throws MojoExecutionException, BoxDatabaseException { BoxDatabaseFactory factory = databaseFactory(serviceHub); return factory.create(box, projectConfiguration(), boxContext(serviceHub)); } protected void stopDatabaseContainer(BoxDatabase boxDatabase) throws MojoExecutionException, BoxDatabaseException { boolean exists = true; if (!boxDatabase.exists()) { getLog().warn("Database '" + boxDatabase.getName() + "' does not exist"); exists = false; } else if (!boxDatabase.isRunning()) getLog().info("Database '" + boxDatabase.getName() + "' is not running"); else { getLog().info("Stopping database '" + boxDatabase.getName() + "'"); boxDatabase.stop(); getLog().info("Database stopped."); } if (exists && deleteOnStop) { getLog().info("Deleting database '" + boxDatabase.getName() + "'"); boxDatabase.delete(); } } protected void dumpLog(DatabaseLog dbLog) throws BoxDatabaseException, IOException { getLog().info(dbLog.getName() + ":"); try (StringWriter w = new StringWriter()) { dbLog.save(w); getLog().info(w.getBuffer()); } } protected void dumpLogToError(DatabaseLog dbLog) throws BoxDatabaseException, IOException { getLog().error(dbLog.getName() + ":"); try (StringWriter w = new StringWriter()) { dbLog.save(w); getLog().error(w.getBuffer()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy