Skip to content

Instantly share code, notes, and snippets.

@JanCizmar
Last active January 7, 2022 11:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JanCizmar/fa419d8fdd79f95e34e85fb58404360d to your computer and use it in GitHub Desktop.
Save JanCizmar/fa419d8fdd79f95e34e85fb58404360d to your computer and use it in GitHub Desktop.
Running PostgresSQL server from SpringBoot App
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()
}
}
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"
}
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