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.io.directories.FixedPath;
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.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 {
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<String> DEFAULT_ADD_PARAMS = asList(
"-E", "SQL_ASCII",
"--locale=C",
"--lc-collate=C",
"--lc-ctype=C");
private final IVersion version;
private PostgresProcess process;
private PostgresConfig config;
public EmbeddedPostgres() {
this(PRODUCTION);
}
public EmbeddedPostgres(IVersion version) {
this.version = version;
}
/**
* 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()))
.build();
}
/**
* 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()))
.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<String> 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<String> additionalParams) throws IOException {
final PostgresStarter<PostgresExecutable, PostgresProcess> runtime = PostgresStarter.getInstance(runtimeConfig);
config = new PostgresConfig(version,
new AbstractPostgresConfig.Net(host, port),
new AbstractPostgresConfig.Storage(dbName),
new AbstractPostgresConfig.Timeout(),
new AbstractPostgresConfig.Credentials(user, password)
);
config.getAdditionalInitDbParams().addAll(additionalParams);
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<PostgresConfig> getConfig() {
return ofNullable(config);
}
/**
* Returns the process if started
*
* @return empty if process has not been started yet
*/
public Optional<PostgresProcess> getProcess() {
return ofNullable(process);
}
/**
* Returns the connection url for the running postgres instance
*
* @return empty if process has not been started yet
*/
public Optional<String> 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();
}
}