Last active
January 7, 2022 11:04
-
-
Save JanCizmar/fa419d8fdd79f95e34e85fb58404360d to your computer and use it in GitHub Desktop.
Running PostgresSQL server from SpringBoot App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.tolgee.configuration | |
import io.tolgee.configuration.tolgee.PostgresAutostartProperties | |
import io.tolgee.postgresStarters.PostgresRunner | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty | |
import org.springframework.boot.jdbc.DataSourceBuilder | |
import org.springframework.context.annotation.Bean | |
import org.springframework.context.annotation.Configuration | |
import javax.sql.DataSource | |
@Configuration | |
@ConditionalOnProperty(name = ["tolgee.postgres-autostart.enabled"], havingValue = "true") | |
class PostgresAutoStartConfiguration( | |
val postgresAutostartProperties: PostgresAutostartProperties, | |
val postgresRunner: PostgresRunner | |
) { | |
private var _dataSource: DataSource? = null | |
@Bean | |
fun getDataSource(): DataSource { | |
_dataSource?.let { return it } | |
postgresRunner.run() | |
_dataSource = buildDataSource() | |
return _dataSource!! | |
} | |
private fun buildDataSource(): DataSource { | |
val dataSourceBuilder = DataSourceBuilder.create() | |
dataSourceBuilder.url(postgresRunner.datasourceUrl) | |
dataSourceBuilder.username(postgresAutostartProperties.user) | |
dataSourceBuilder.password(postgresAutostartProperties.password) | |
return dataSourceBuilder.build() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.tolgee.configuration.tolgee | |
import org.springframework.boot.context.properties.ConfigurationProperties | |
@ConfigurationProperties(prefix = "tolgee.postgres-autostart") | |
class PostgresAutostartProperties { | |
var enabled: Boolean = true | |
var user: String = "postgres" | |
var password: String = "postgres" | |
var databaseName: String = "postgres" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.tolgee.postgresStarters | |
import io.tolgee.configuration.tolgee.FileStorageProperties | |
import io.tolgee.configuration.tolgee.PostgresAutostartProperties | |
import io.tolgee.fixtures.waitFor | |
import org.slf4j.LoggerFactory | |
import org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_SINGLETON | |
import org.springframework.context.annotation.Scope | |
import org.springframework.stereotype.Component | |
import java.io.IOException | |
import java.io.InputStream | |
import java.net.Socket | |
import java.util.concurrent.atomic.AtomicBoolean | |
import javax.annotation.PreDestroy | |
import kotlin.concurrent.thread | |
@Component | |
@Scope(SCOPE_SINGLETON) | |
class PostgresRunner( | |
private val postgresAutostartProperties: PostgresAutostartProperties, | |
private val storageProperties: FileStorageProperties | |
) : PostgresRunner { | |
private val logger = LoggerFactory.getLogger(javaClass) | |
private lateinit var proc: Process | |
private var running: AtomicBoolean = AtomicBoolean(false) | |
override fun run() { | |
if (!::proc.isInitialized) { | |
startPostgresProcess() | |
startWatchingThread() | |
waitForPostgres() | |
} | |
} | |
private fun startPostgresProcess() { | |
val processBuilder = buildProcess() | |
logger.info("Starting embedded Postgres DB...") | |
proc = processBuilder.start() | |
running.set(true) | |
} | |
private fun buildProcess(): ProcessBuilder { | |
val processBuilder = ProcessBuilder() | |
.command("bash", "-c", "postgres-entrypoint.sh postgres") | |
initProcessEnv(processBuilder) | |
return processBuilder | |
} | |
private fun initProcessEnv(processBuilder: ProcessBuilder) { | |
val env = processBuilder.environment() | |
env.putAll( | |
mapOf( | |
"POSTGRES_PASSWORD" to postgresAutostartProperties.password, | |
"POSTGRES_USER" to postgresAutostartProperties.user, | |
"POSTGRES_DB" to postgresAutostartProperties.databaseName, | |
"PGDATA" to storageProperties.fsDataPath + "/postgres" | |
) | |
) | |
} | |
private fun startWatchingThread() { | |
thread(start = true) { | |
logOutput( | |
mapOf( | |
proc.inputStream to logger::info, | |
proc.errorStream to logger::error | |
) | |
) | |
if (proc.exitValue() != 0) { | |
throw Exception("Postgres failed to start...") | |
} | |
} | |
} | |
private fun waitForPostgres() { | |
waitFor(20000) { | |
isPostgresUp() | |
} | |
} | |
override val datasourceUrl by lazy { | |
// It's not that easy to change port in embedded version, since there is no env prop for that | |
"jdbc:postgresql://localhost:$POSTGRES_PORT/${postgresAutostartProperties.databaseName}" | |
} | |
private fun isPostgresUp(): Boolean { | |
var s: Socket? = null | |
return try { | |
s = Socket("localhost", POSTGRES_PORT) | |
true | |
} catch (e: java.lang.Exception) { | |
false | |
} finally { | |
if (s != null) try { | |
s.close() | |
} catch (e: java.lang.Exception) { | |
e.printStackTrace() | |
} | |
} | |
} | |
@PreDestroy | |
fun preDestroy() { | |
proc.destroy() | |
proc.waitFor() | |
running.set(false) | |
} | |
private fun logOutput(loggerMap: Map<InputStream, (message: String) -> Unit>) { | |
while (running.get()) { | |
try { | |
val allNull = loggerMap.entries.map { (inputStream, logger) -> | |
val line = inputStream.bufferedReader().readLine() | |
logLine(line, logger) | |
line == null | |
}.all { it } | |
if (allNull) { | |
break | |
} | |
} catch (e: IOException) { | |
logger.debug("Resolved IOException ${e.message}") | |
} | |
} | |
} | |
private fun logLine(line: String?, logger: (message: String) -> Unit) { | |
if (line != null) { | |
logger("Postgres: $line") | |
} | |
} | |
companion object { | |
private const val POSTGRES_PORT = 5432 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment