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

ru.yandex.qatools.embed.postgresql.EmbeddedPostgres Maven / Gradle / Ivy

The newest version!
package ru.yandex.qatools.embed.postgresql;

import de.flapdoodle.embed.process.config.IRuntimeConfig;
import de.flapdoodle.embed.process.distribution.IVersion;
import de.flapdoodle.embed.process.distribution.Platform;
import de.flapdoodle.embed.process.io.directories.FixedPath;
import de.flapdoodle.embed.process.runtime.ICommandLinePostProcessor;
import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder;
import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig;
import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder;
import ru.yandex.qatools.embed.postgresql.config.PostgresConfig;
import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION;
import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort;

/**
 * Helper class simplifying the start up configuration for embedded postgres
 */
public class EmbeddedPostgres implements AutoCloseable {
    public static final String DEFAULT_USER = "postgres";//NOSONAR
    public static final String DEFAULT_PASSWORD = "postgres";//NOSONAR
    public static final String DEFAULT_DB_NAME = "postgres";//NOSONAR
    public static final String DEFAULT_HOST = "localhost";
    private static final List DEFAULT_ADD_PARAMS = asList(
            "-E", "SQL_ASCII",
            "--locale=C",
            "--lc-collate=C",
            "--lc-ctype=C");
    private static final List DEFAULT_POSTGRES_PARAMS = asList();
    private final String dataDir;
    private final IVersion version;
    private PostgresProcess process;
    private PostgresConfig config;

    public EmbeddedPostgres() {
        this(PRODUCTION);
    }

    public EmbeddedPostgres(IVersion version) {
        this(version, null);
    }

    public EmbeddedPostgres(String dataDir){
        this(PRODUCTION, dataDir);
    }

    public EmbeddedPostgres(IVersion version, String dataDir){
        this.version = version;
        this.dataDir = dataDir;
    }

    /**
     * Initializes the default runtime configuration using the temporary directory.
     *
     * @return runtime configuration required for postgres to start.
     */
    public static IRuntimeConfig defaultRuntimeConfig() {
        return new RuntimeConfigBuilder()
                .defaults(Command.Postgres)
                .artifactStore(new PostgresArtifactStoreBuilder()
                        .defaults(Command.Postgres)
                        .download(new PostgresDownloadConfigBuilder()
                                .defaultsForCommand(Command.Postgres)
                                .build()))
                .commandLinePostProcessor(privilegedWindowsRunasPostprocessor())
                .build();
    }

    private static ICommandLinePostProcessor privilegedWindowsRunasPostprocessor() {
        if (Platform.detect().equals(Platform.Windows)) {
            try {
                // Based on https://stackoverflow.com/a/11995662
                final int adminCommandResult = Runtime.getRuntime().exec("net session").waitFor();
                if (adminCommandResult == 0) {
                    return runWithoutPrivileges();
                }
            } catch (Exception e) {
                // Log maybe?
            }
        }
        return doNothing();
    }

    private static ICommandLinePostProcessor runWithoutPrivileges() {
        return (distribution, args) -> {
            if (args.size() > 0 && args.get(0).endsWith("postgres.exe")) {
                return Arrays.asList("runas", "/trustlevel:0x20000", String.format("\"%s\"", String.join(" ", args)));
            }
            return args;
        };
    }

    private static ICommandLinePostProcessor doNothing() {
        return (distribution, args) -> args;
    }

    /**
     * Initializes runtime configuration for cached directory.
     * If a provided directory is empty, postgres will be extracted into it.
     *
     * @param cachedPath path where postgres is supposed to be extracted
     * @return runtime configuration required for postgres to start
     */
    public static IRuntimeConfig cachedRuntimeConfig(Path cachedPath) {
        final Command cmd = Command.Postgres;
        final FixedPath cachedDir = new FixedPath(cachedPath.toString());
        return new RuntimeConfigBuilder()
                .defaults(cmd)
                .artifactStore(new PostgresArtifactStoreBuilder()
                        .defaults(cmd)
                        .tempDir(cachedDir)
                        .download(new PostgresDownloadConfigBuilder()
                                .defaultsForCommand(cmd)
                                .packageResolver(new PackagePaths(cmd, cachedDir))
                                .build()))
                .commandLinePostProcessor(privilegedWindowsRunasPostprocessor())
                .build();
    }

    public String start() throws IOException {
        return start(DEFAULT_HOST, findFreePort(), DEFAULT_DB_NAME);
    }

    public String start(String host, int port, String dbName) throws IOException {
        return start(host, port, dbName, DEFAULT_USER, DEFAULT_PASSWORD, DEFAULT_ADD_PARAMS);
    }

    public String start(String host, int port, String dbName, String user, String password) throws IOException {
        return start(defaultRuntimeConfig(), host, port, dbName, user, password, DEFAULT_ADD_PARAMS);
    }

    public String start(String host, int port, String dbName, String user, String password, List additionalParams) throws IOException {
        return start(defaultRuntimeConfig(), host, port, dbName, user, password, additionalParams);
    }

    public String start(IRuntimeConfig runtimeConfig) throws IOException {
        return start(runtimeConfig, DEFAULT_HOST, findFreePort(), DEFAULT_DB_NAME, DEFAULT_USER, DEFAULT_PASSWORD, DEFAULT_ADD_PARAMS);
    }

    /**
     * Starts up the embedded postgres
     *
     * @param runtimeConfig    required runtime configuration
     * @param host             host to bind to
     * @param port             port to bind to
     * @param dbName           name of the database to initialize
     * @param user             username to connect
     * @param password         password for the provided username
     * @param additionalParams additional database init params (if required)
     * @return connection url for the initialized postgres instance
     * @throws IOException if an I/O error occurs during the process startup
     */
    public String start(IRuntimeConfig runtimeConfig, String host, int port, String dbName, String user, String password,
                        List additionalParams) throws IOException {
        return start(runtimeConfig, host, port, dbName, user, password, additionalParams, DEFAULT_POSTGRES_PARAMS);
    }
    
    /**
     * Starts up the embedded postgres
     *
     * @param runtimeConfig    required runtime configuration
     * @param host             host to bind to
     * @param port             port to bind to
     * @param dbName           name of the database to initialize
     * @param user             username to connect
     * @param password         password for the provided username
     * @param additionalInitDbParams additional database init params (if required)
     * @param additionalPostgresParams additional postgresql params (if required)
     * @return connection url for the initialized postgres instance
     * @throws IOException if an I/O error occurs during the process startup
     */
    public String start(IRuntimeConfig runtimeConfig, String host, int port, String dbName, String user, String password,
                        List additionalInitDbParams, List additionalPostgresParams) throws IOException {
        final PostgresStarter runtime = PostgresStarter.getInstance(runtimeConfig);
        config = new PostgresConfig(version,
                new AbstractPostgresConfig.Net(host, port),
                new AbstractPostgresConfig.Storage(dbName, dataDir),
                new AbstractPostgresConfig.Timeout(),
                new AbstractPostgresConfig.Credentials(user, password)
        );
        config.getAdditionalInitDbParams().addAll(additionalInitDbParams);
        config.getAdditionalPostgresParams().addAll(additionalPostgresParams);
        PostgresExecutable exec = runtime.prepare(config);
        this.process = exec.start();
        return formatConnUrl(config);
    }

    /**
     * Returns the configuration of started process
     *
     * @return empty if process has not been started yet
     */
    public Optional getConfig() {
        return ofNullable(config);
    }


    /**
     * Returns the process if started
     *
     * @return empty if process has not been started yet
     */
    public Optional getProcess() {
        return ofNullable(process);
    }

    /**
     * Returns the connection url for the running postgres instance
     *
     * @return empty if process has not been started yet
     */
    public Optional getConnectionUrl() {
        return getConfig().map(this::formatConnUrl);
    }

    private String formatConnUrl(PostgresConfig config) {
        return format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s",//NOSONAR
                config.net().host(),
                config.net().port(),
                config.storage().dbName(),
                config.credentials().username(),
                config.credentials().password()
        );
    }

    public void stop() {
        getProcess().orElseThrow(() -> new IllegalStateException("Cannot stop not started instance!")).stop();
    }

    @Override
    public void close() {
        this.stop();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy