├── .appveyor-install-ssh.bat ├── .appveyor-run-mvn.bat ├── .drone.yml ├── .gitignore ├── .idea ├── encodings.xml ├── runConfigurations │ └── test.xml └── vcs.xml ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── pom.xml └── src ├── main └── java │ ├── de │ └── flapdoodle │ │ └── embed │ │ └── process │ │ └── store │ │ ├── CachedPostgresArtifactStore.java │ │ ├── EmptyFileSet.java │ │ ├── IMutableArtifactStore.java │ │ ├── NonCachedPostgresArtifactStoreBuilder.java │ │ ├── PostgresArtifactStore.java │ │ ├── PostgresArtifactStoreBuilder.java │ │ └── PostgresFilesToExtract.java │ └── ru │ └── yandex │ └── qatools │ └── embed │ └── postgresql │ ├── AbstractPGExecutable.java │ ├── AbstractPGProcess.java │ ├── Command.java │ ├── CreateDbExecutable.java │ ├── CreateDbProcess.java │ ├── CreateuserExecutable.java │ ├── CreateuserProcess.java │ ├── EmbeddedPostgres.java │ ├── InitDbExecutable.java │ ├── InitDbProcess.java │ ├── PackagePaths.java │ ├── PgCtlExecutable.java │ ├── PgCtlProcess.java │ ├── PostgresExecutable.java │ ├── PostgresProcess.java │ ├── PostgresStarter.java │ ├── PsqlExecutable.java │ ├── PsqlProcess.java │ ├── config │ ├── AbstractPostgresConfig.java │ ├── IMutableDownloadConfig.java │ ├── MutableDownloadConfig.java │ ├── PostgresConfig.java │ ├── PostgresDownloadConfigBuilder.java │ ├── PostgresProcessOutputConfig.java │ ├── RuntimeConfigBuilder.java │ └── SupportConfig.java │ ├── distribution │ └── Version.java │ ├── ext │ ├── LogWatchStreamProcessor.java │ └── SubdirTempDir.java │ └── util │ └── SocketUtil.java └── test ├── java └── ru │ └── yandex │ └── qatools │ └── embed │ └── postgresql │ ├── AbstractPsqlTest.java │ ├── EmbeddedPostgresTest.java │ ├── PostgresqlService.java │ ├── TestDownloads.java │ ├── TestMultipleInstance.java │ ├── TestPostgresCachedDirStarter.java │ ├── TestPostgresStarter.java │ ├── TestPostgresStoredData.java │ ├── TestPostgresWithPgCtl.java │ ├── TestPostgresWithinSpringContext.java │ ├── TestPsqlExport.java │ ├── TestPsqlImport.java │ ├── TestPsqlRestore.java │ ├── TestStop.java │ └── config │ └── StorageTest.java └── resources ├── README.md ├── log4j.properties ├── test-spring-context.xml ├── test.backup └── test.binary_dump /.appveyor-install-ssh.bat: -------------------------------------------------------------------------------- 1 | @setlocal EnableDelayedExpansion EnableExtensions 2 | title Installing Openssh. Please wait... 3 | 4 | if not defined OPENSSH_URL set OPENSSH_URL=http://www.mls-software.com/files/setupssh-7.2p2-1-v1.exe 5 | if not defined SSHD_PASSWORD set SSHD_PASSWORD=%1 6 | 7 | net user /add postgres %SSHD_PASSWORD% 8 | net localgroup "Remote Desktop Users" postgres /ADD 9 | 10 | for %%i in (%OPENSSH_URL%) do set OPENSSH_EXE=%%~nxi 11 | set OPENSSH_DIR=%TEMP%\openssh 12 | set OPENSSH_PATH=%OPENSSH_DIR%\%OPENSSH_EXE% 13 | 14 | echo ==^> Creating "%OPENSSH_DIR%" 15 | mkdir "%OPENSSH_DIR%" 16 | pushd "%OPENSSH_DIR%" 17 | 18 | if exist "%SystemRoot%\_download.cmd" ( 19 | call "%SystemRoot%\_download.cmd" "%OPENSSH_URL%" "%OPENSSH_PATH%" 20 | ) else ( 21 | echo ==^> Downloading "%OPENSSH_URL%" to "%OPENSSH_PATH%" 22 | powershell -Command "(New-Object System.Net.WebClient).DownloadFile('%OPENSSH_URL%', '%OPENSSH_PATH%')" Blocking SSH port 22 on the firewall 27 | netsh advfirewall firewall add rule name="SSHD" dir=in action=block program="%ProgramFiles%\OpenSSH\usr\sbin\sshd.exe" enable=yes 28 | netsh advfirewall firewall add rule name="ssh" dir=in action=block protocol=TCP localport=22 29 | 30 | echo ==^> Installing OpenSSH 31 | "%OPENSSH_PATH%" /S /port=22 /privsep=1 /password=%SSHD_PASSWORD% 32 | 33 | :: wait for opensshd service to finish starting 34 | timeout 5 35 | 36 | sc query opensshd | findstr "RUNNING" >nul 37 | if errorlevel 1 goto sshd_not_running 38 | 39 | echo ==^> Stopping the sshd service 40 | sc stop opensshd 41 | 42 | :is_sshd_running 43 | 44 | timeout 1 45 | 46 | sc query opensshd | findstr "STOPPED" >nul 47 | if errorlevel 1 goto is_sshd_running 48 | 49 | :sshd_not_running 50 | ver>nul 51 | 52 | echo ==^> Unblocking SSH port 22 on the firewall 53 | netsh advfirewall firewall delete rule name="SSHD" 54 | netsh advfirewall firewall delete rule name="ssh" 55 | 56 | echo ==^> Setting temp location 57 | rmdir /q /s "%ProgramFiles%\OpenSSH\tmp" 58 | mklink /d "%ProgramFiles%\OpenSSH\tmp" "%SystemRoot%\Temp" 59 | 60 | icacls "%SystemRoot%\Temp" /grant postgres:(OI)(CI)F 61 | icacls "%SystemRoot%\Temp" /grant %USERNAME%:(OI)(CI)F 62 | 63 | echo ==^> Adding missing environment variables to %USERPROFILE%\.ssh\environment 64 | 65 | if not exist "%USERPROFILE%\.ssh" mkdir "%USERPROFILE%\.ssh" 66 | if not exist "C:\Users\postgres\.ssh" mkdir "C:\Users\postgres\.ssh" 67 | 68 | set SSHENV=C:\Users\postgres\.ssh\environment 69 | 70 | type nul >"%SSHENV%" 71 | echo APPDATA=%SystemDrive%\Users\%USERNAME%\AppData\Roaming>>"%SSHENV%" 72 | echo COMMONPROGRAMFILES=%SystemDrive%\Program Files\Common Files>>"%SSHENV%" 73 | echo LOCALAPPDATA=%SystemDrive%\Users\%USERNAME%\AppData\Local>>"%SSHENV%" 74 | echo PROGRAMDATA=%SystemDrive%\ProgramData>>"%SSHENV%" 75 | echo PROGRAMFILES=%SystemDrive%\Program Files>>"%SSHENV%" 76 | echo PSMODULEPATH=%SystemDrive%\Windows\system32\WindowsPowerShell\v1.0\Modules\>>"%SSHENV%" 77 | echo PUBLIC=%SystemDrive%\Users\Public>>"%SSHENV%" 78 | echo SESSIONNAME=Console>>"%SSHENV%" 79 | echo TEMP=%SystemDrive%\Users\%USERNAME%\AppData\Local\Temp>>"%SSHENV%" 80 | echo TMP=%SystemDrive%\Users\%USERNAME%\AppData\Local\Temp>>"%SSHENV%" 81 | echo JAVA_HOME=%ProgramFiles%\Java\jdk1.8.0>>"%SSHENV%" 82 | echo M3_HOME=/cygdrive/c/maven/apache-maven-3.2.5>>"%SSHENV%" 83 | :: echo MAVEN_HOME=/cygdrive/c/maven/apache-maven-3.2.5>>"%SSHENV%" 84 | echo MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g>>"%SSHENV%" 85 | echo JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g>>"%SSHENV%" 86 | :: echo PATH=/cygdrive/c/maven/apache-maven-3.2.5/bin:%PATH%>>"%SSHENV%" 87 | 88 | :: This fix simply masks the issue, we need to fix the underlying cause 89 | :: to override sshd_server: 90 | :: echo USERNAME=%USERNAME%>>"%SSHENV%" 91 | echo USERNAME=postgres>>"%SSHENV%" 92 | 93 | if exist "%SystemDrive%\Program Files (x86)" ( 94 | echo COMMONPROGRAMFILES^(X86^)=%SystemDrive%\Program Files ^(x86^)\Common Files>>"%SSHENV%" 95 | echo COMMONPROGRAMW6432=%SystemDrive%\Program Files\Common Files>>"%SSHENV%" 96 | echo PROGRAMFILES^(X86^)=%SystemDrive%\Program Files ^(x86^)>>"%SSHENV%" 97 | echo PROGRAMW6432=%SystemDrive%\Program Files>>"%SSHENV%" 98 | ) 99 | 100 | echo ==^> Replacing C:\ with /cygdrive/c 101 | powershell -Command "(Get-Content '%SSHENV%') | Foreach-Object { $_ -replace 'C:\\', '/cygdrive/c/' } | Set-Content '%SSHENV%'" 102 | 103 | echo ==^> Here is what's in %SSHENV% file 104 | type %SSHENV% 105 | 106 | echo ==^> Fixing opensshd's configuration to be less strict 107 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\sshd_config') | Foreach-Object { $_ -replace 'StrictModes yes', 'StrictModes no' } | Set-Content '%ProgramFiles%\OpenSSH\etc\sshd_config'" 108 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\sshd_config') | Foreach-Object { $_ -replace '#PubkeyAuthentication yes', 'PubkeyAuthentication yes' } | Set-Content '%ProgramFiles%\OpenSSH\etc\sshd_config'" 109 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\sshd_config') | Foreach-Object { $_ -replace '#PermitUserEnvironment no', 'PermitUserEnvironment yes' } | Set-Content '%ProgramFiles%\OpenSSH\etc\sshd_config'" 110 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\sshd_config') | Foreach-Object { $_ -replace '#UseDNS yes', 'UseDNS no' } | Set-Content '%ProgramFiles%\OpenSSH\etc\sshd_config'" 111 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\sshd_config') | Foreach-Object { $_ -replace 'Banner /etc/banner.txt', '#Banner /etc/banner.txt' } | Set-Content '%ProgramFiles%\OpenSSH\etc\sshd_config'" 112 | 113 | echo ==^> Opening SSH port 22 on the firewall 114 | netsh advfirewall firewall add rule name="SSHD" dir=in action=allow program="%ProgramFiles%\OpenSSH\usr\sbin\sshd.exe" enable=yes 115 | netsh advfirewall firewall add rule name="ssh" dir=in action=allow protocol=TCP localport=22 116 | 117 | echo ==^> Ensuring user postgres can login 118 | icacls "C:\Users\postgres" /grant postgres:(OI)(CI)F 119 | icacls "%ProgramFiles%\OpenSSH\bin" /grant postgres:(OI)RX 120 | icacls "%ProgramFiles%\OpenSSH\usr\sbin" /grant postgres:(OI)RX 121 | 122 | echo ==^> Ensuring user %USERNAME% can login 123 | icacls "C:\Users\%USERNAME%" /grant %USERNAME%:(OI)(CI)F 124 | icacls "%ProgramFiles%\OpenSSH\bin" /grant %USERNAME%:(OI)RX 125 | icacls "%ProgramFiles%\OpenSSH\usr\sbin" /grant %USERNAME%:(OI)RX 126 | 127 | echo ==^> Setting user's home directories to their windows profile directory 128 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\passwd') | Foreach-Object { $_ -replace '/home/(\w+)', '/cygdrive/c/Users/$1' } | Set-Content '%ProgramFiles%\OpenSSH\etc\passwd'" 129 | powershell -Command "(Get-Content '%ProgramFiles%\OpenSSH\etc\passwd') | Foreach-Object { $_ -replace 'U-%USERDOMAIN%\\postgres', 'postgres' } | Set-Content '%ProgramFiles%\OpenSSH\etc\passwd'" 130 | 131 | type "C:\Program Files\OpenSSH\etc\ssh_host_rsa_key.pub" > C:\Users\postgres\.ssh\authorized_keys 132 | 133 | taskkill /IM sshd.exe /F 134 | sc stop opensshd 135 | sc start opensshd 136 | :: This fix simply masks the issue, we need to fix the underlying cause 137 | :: echo ==^> Overriding sshd_server username in environment 138 | :: reg add "HKLM\Software\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "@for %%i in (%%USERPROFILE%%) do @set USERNAME=%%~ni" /f 139 | 140 | :exit0 141 | 142 | ver>nul 143 | 144 | goto :exit 145 | 146 | :exit1 147 | 148 | verify other 2>nul 149 | 150 | :exit -------------------------------------------------------------------------------- /.appveyor-run-mvn.bat: -------------------------------------------------------------------------------- 1 | SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0 2 | SET M3_HOME=C:\maven\apache-maven-3.2.5 3 | SET TEMP=C:\Users\postgres\AppData\Local\Temp 4 | SET TMP=C:\Users\postgres\AppData\Local\Temp 5 | SET M2_HOME= 6 | SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g 7 | SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g 8 | SET PATH=C:\maven\apache-maven-3.2.5\bin;%JAVA_HOME%\bin;C:\Program Files\OpenSSH\bin;%PATH% 9 | call mvn %* 10 | echo %errorlevel% 1> C:\Users\postgres\src\target\exit-code.txt -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | build: 2 | image: teaci/cygwin32 3 | shell: cygwin32 4 | pull: true 5 | commands: 6 | - cmd /c c:/cygwin-installer.exe --site http://mirrors.tea-ci.org/cygwin --local-package-dir Z:/tmp/cygwin -W -P cabextract,unzip -q 7 | - echo '' > run.bat 8 | - wget http://repo.apache.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 9 | - unzip apache-maven-3.3.9-bin.zip 10 | - wget '--header' 'Cookie:oraclelicense=accept-securebackup-cookie' '--no-cookies' '--no-check-certificate' "http://download.oracle.com/otn-pub/java/jdk/8u91-b15/jdk-8u91-windows-i586.exe" 11 | - chmod ogu+rwx jdk-8u91-windows-i586.exe 12 | - cabextract jdk-8u91-windows-i586.exe || echo "ignoring errors" 13 | - mkdir jdk && mv tools.zip jdk && cd jdk && unzip tools.zip && for i in jre/lib/charsets jre/lib/deploy jre/lib/ext/localedata jre/lib/javaws jre/lib/jsse jre/lib/plugin jre/lib/rt lib/tools; do ./bin/unpack200 -r -v -l "" $i.pack $i.jar; done && cd - 14 | - echo set JAVA_HOME=$(cygpath.exe --windows $PWD/jdk) > run.bat 15 | - echo set M2_HOME=$(cygpath.exe --windows $PWD/apache-maven-3.3.9) >> run.bat 16 | - echo 'set Path=%JAVA_HOME%\bin;%M2_HOME%\bin;%Path%' >> run.bat 17 | - echo '%JAVA_HOME%\bin\java -cp %M2_HOME%\boot\plexus-classworlds-2.5.2.jar -Dclassworlds.conf=%M2_HOME%/bin/m2.conf -Dmaven.home=%M2_HOME% -Dmaven.multiModuleProjectDirectory=. org.codehaus.plexus.classworlds.launcher.Launcher clean install' >> run.bat 18 | - cmd /c run.bat 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | temp/ 3 | velocity.log 4 | *~ 5 | .classpath 6 | .project 7 | .settings 8 | .externalToolBuilders 9 | 10 | .idea/* 11 | !.idea/vcs.xml 12 | !.idea/codeStyleSettings.xml 13 | !.idea/runConfigurations/ 14 | !.idea/encodings.xml 15 | !.idea/inspectionProfiles/ 16 | *.iml 17 | *.ipr 18 | *.iws -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/runConfigurations/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: java 4 | 5 | cache: 6 | directories: 7 | - $HOME/.m2 8 | - $HOME/.embedpostgresql 9 | 10 | before_install: 11 | - java -version 12 | 13 | matrix: 14 | include: 15 | - os: linux 16 | jdk: oraclejdk8 17 | - os: osx 18 | osx_image: xcode8 19 | # - dist: trusty 20 | # sudo: required 21 | # group: edge 22 | # jdk: oraclejdk9 23 | # addons: 24 | # apt: 25 | # packages: 26 | # - oracle-java9-installer 27 | # script: 28 | # - jdk_switcher use oraclejdk9 29 | # - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 YANDEX 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Embedded PostgreSQL Server 2 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/ru.yandex.qatools.embed/postgresql-embedded/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/ru.yandex.qatools.embed/postgresql-embedded) 3 | [![Build status](https://travis-ci.org/yandex-qatools/postgresql-embedded.svg?branch=master)](https://travis-ci.org/yandex-qatools/postgresql-embedded/) 4 | [![Windows build status](https://ci.appveyor.com/api/projects/status/00ov87k6fe2euwvo?svg=true)](https://ci.appveyor.com/project/smecsia/postgresql-embedded) 5 | 6 | Embedded PostgreSQL server provides a platform neutral way for running postgres binaries in unittests. 7 | This library is based on [Flapdoodle OSS's embed process](https://github.com/flapdoodle-oss/de.flapdoodle.embed.process). 8 | 9 | ## Note: this project is not being actively maintained anymore 10 | Sorry for any inconvinience, but this project needs active maintainers. If anyone is interested in becoming the maintainer - please let me ([@smecsia](https://github.com/smecsia)) know. 11 | 12 | ## Officially recommended alternative 13 | Please be adviced that the main maintainer of this project has successfuly migrated to the use of [Test Containers project](https://www.testcontainers.org/modules/databases/postgres/). This is the best possible alternative nowadays. 14 | 15 | ## Motivation 16 | 17 | * It's much easier than installing specific version manually 18 | * You can choose the version right from the code 19 | * You can start your development environment with the PostgreSQL embedded with the single command 20 | 21 | ### Maven 22 | 23 | Add the following dependency to your pom.xml: 24 | ```xml 25 | 26 | ru.yandex.qatools.embed 27 | postgresql-embedded 28 |     2.10 29 | 30 | ``` 31 | ### Gradle 32 | 33 | Add a line to build.gradle: 34 | ```groovy 35 | compile 'ru.yandex.qatools.embed:postgresql-embedded:2.10' 36 | ``` 37 | 38 | ## Howto 39 | 40 | Here is the example of how to launch and use the embedded PostgreSQL instance 41 | ```java 42 | // starting Postgres 43 | final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6); 44 | // predefined data directory 45 | // final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6, "/path/to/predefined/data/directory"); 46 | final String url = postgres.start("localhost", 5432, "dbName", "userName", "password"); 47 | 48 | // connecting to a running Postgres and feeding up the database 49 | final Connection conn = DriverManager.getConnection(url); 50 | conn.createStatement().execute("CREATE TABLE films (code char(5));"); 51 | conn.createStatement().execute("INSERT INTO films VALUES ('movie');"); 52 | 53 | // ... or you can execute SQL files... 54 | //postgres.getProcess().importFromFile(new File("someFile.sql")) 55 | // ... or even SQL files with PSQL variables in them... 56 | //postgres.getProcess().importFromFileWithArgs(new File("someFile.sql"), "-v", "tblName=someTable") 57 | // ... or even restore database from dump file 58 | //postgres.getProcess().restoreFromFile(new File("src/test/resources/test.binary_dump")) 59 | 60 | // performing some assertions 61 | final Statement statement = conn.createStatement(); 62 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 63 | assertThat(statement.getResultSet().next(), is(true)); 64 | assertThat(statement.getResultSet().getString("code"), is("movie")); 65 | 66 | // close db connection 67 | conn.close(); 68 | // stop Postgres 69 | postgres.stop(); 70 | ``` 71 | 72 | Note that EmbeddedPostgres implements [java.lang.AutoCloseable](https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html), 73 | which means that you can use it with a [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) 74 | statement (in Java >= 7) to have it automatically stopped. 75 | 76 | ### How to avoid archive extraction on every run 77 | 78 | You can specify the cached artifact store to avoid archives downloading and extraction (in case if a directory remains on every run). 79 | ```java 80 | final EmbeddedPostgres postgres = new EmbeddedPostgres(); 81 | postgres.start(cachedRuntimeConfig("/path/to/my/extracted/postgres")); 82 | ``` 83 | 84 | ### How to configure logging 85 | 86 | Just configure your own `slf4j` appenders. Here is the example of typical `src/test/resources/log4j.properties` file: 87 | 88 | ```java 89 | # suppress inspection "UnusedProperty" for whole file 90 | log4j.rootLogger=DEBUG, stdout 91 | 92 | # reduce logging for postgresql-embedded 93 | log4j.logger.ru.yandex.qatools.embed=INFO 94 | log4j.logger.de.flapdoodle.embed=INFO 95 | 96 | # Direct log messages to stdout 97 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 98 | log4j.appender.stdout.Target=System.out 99 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 100 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 101 | log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer 102 | ``` 103 | 104 | ### How to use your custom version of PostgreSQL 105 | 106 | Pass the required `IVersion` interface implementation as a first argument of the `EmbeddedPostgres` object: 107 | 108 | ```java 109 | final EmbeddedPostgres postgres = new EmbeddedPostgres(() -> (IS_OS_WINDOWS) ? "9.6.2-2" : "9.6.2-1"); 110 | ``` 111 | 112 | ### Known issues 113 | * A lot of issues have been reported for this library under Windows. Please try to use the suggested way of start up and use 114 | the cached artifact storage (to avoid extraction of the archive as extraction is extremely slow under Windows): 115 | ```java 116 | postgres.start(cachedRuntimeConfig("C:\\Users\\vasya\\pgembedded-installation")); 117 | ``` 118 | 119 | * PostgreSQL server is known to not start under the privileged user (which means you cannot start it under root/Administrator of your system): 120 | 121 | > `initdb must be run as the user that will own the server process, because the server needs to have access to the files and directories that initdb creates. Since the server cannot be run as root, you must not run initdb as root either. (It will in fact refuse to do so.)` 122 | ([link](http://www.postgresql.org/docs/9.5/static/app-initdb.html)). 123 | 124 | However some users have launched it successfully on Windows under Administrator, so you can try anyway. 125 | 126 | ### Supported Versions 127 | 128 | * 11.2: on Mac OS X and Windows 64 bit 129 | * 10.7, 9.6.12, 9.5.16: on Linux, Windows, Mac OS X 130 | * any custom version 131 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | os: Windows Server 2012 3 | install: 4 | - ps: | 5 | Add-Type -AssemblyName System.IO.Compression.FileSystem 6 | if (!(Test-Path -Path "C:\maven" )) { 7 | (new-object System.Net.WebClient).DownloadFile( 8 | 'http://www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip', 9 | 'C:\maven-bin.zip' 10 | ) 11 | [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven") 12 | } 13 | - cmd: SET PROJ=C:\Users\postgres 14 | - cmd: SET SRC=%PROJ%\src 15 | - cmd: mkdir %SRC% 16 | - cmd: xcopy /s %CD% %SRC% 17 | - cmd: icacls %PROJ% /t /grant Everyone:(OI)(CI)F 18 | - cmd: icacls %SRC% /t /grant Everyone:(OI)(CI)F 19 | - cmd: cd %SRC% 20 | - cmd: SET PASSWORD=zklDDH243765s 21 | - cmd: SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0 22 | - cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5 23 | - cmd: icacls C:\maven /t /grant Everyone:(OI)(CI)F 24 | - cmd: SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g 25 | - cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g 26 | - cmd: .\.appveyor-install-ssh.bat %PASSWORD% 27 | - cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;%JAVA_HOME%\bin;C:\Program Files\OpenSSH\bin;%PATH% 28 | - cmd: SET RSAKEY=C:\Program Files\OpenSSH\etc\ssh_host_rsa_key 29 | build_script: 30 | - ssh.exe -i "%RSAKEY%" -oStrictHostKeyChecking=no postgres@localhost "cd ~/src && cmd /c .appveyor-run-mvn.bat clean install -B -DskipTests -Djna.nosys=true" 31 | - echo errorlevel=%ERRORLEVEL% 32 | - set /p EXITCODE= 2 | 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 9 8 | 9 | 10 | 4.0.0 11 | ru.yandex.qatools.embed 12 | postgresql-embedded 13 | 2.11-SNAPSHOT 14 | Embedded PostgreSQL (based on Flapdoodle-OSS embedded services) 15 | 16 | An embedded PostgreSQL... 17 | 18 | 19 | Yandex 20 | http://company.yandex.com 21 | 22 | 23 | 24 | The Apache Software License, Version 2.0 25 | http://www.apache.org/licenses/LICENSE-2.0.txt 26 | repo 27 | 28 | 29 | 30 | GitHub Issues 31 | https://github.com/yandex-qatools/postgresql-embedded/issues 32 | 33 | 34 | 35 | scm:git:git@github.com:yandex-qatools/postgresql-embedded.git 36 | scm:git:git@github.com:yandex-qatools/postgresql-embedded.git 37 | git@github.com:yandex-qatools/postgresql-embedded.git 38 | HEAD 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-compiler-plugin 47 | 2.5.1 48 | 49 | ${jdk} 50 | ${jdk} 51 | UTF-8 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-resources-plugin 58 | 2.5 59 | 60 | UTF-8 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-release-plugin 66 | 2.5 67 | 68 | deploy -DskipTests=true 69 | 70 | 71 | 72 | org.apache.maven.scm 73 | maven-scm-api 74 | 1.9.1 75 | 76 | 77 | org.apache.maven.scm 78 | maven-scm-provider-gitexe 79 | 1.9.1 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-source-plugin 86 | 2.1.2 87 | 88 | 89 | attach-sources 90 | verify 91 | 92 | jar 93 | 94 | 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-javadoc-plugin 100 | 2.10.3 101 | 102 | -Xdoclint:none 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | release-sign-artifacts 111 | 112 | 113 | performRelease 114 | true 115 | 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-gpg-plugin 122 | 1.1 123 | 124 | 125 | sign-artifacts 126 | verify 127 | 128 | sign 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | de.flapdoodle.embed 141 | de.flapdoodle.embed.process 142 | 2.0.5 143 | 144 | 145 | commons-io 146 | commons-io 147 | 2.4 148 | 149 | 150 | 151 | org.postgresql 152 | postgresql 153 | 9.3-1100-jdbc41 154 | test 155 | 156 | 157 | 158 | 159 | org.springframework 160 | spring-beans 161 | ${spring.version} 162 | test 163 | 164 | 165 | org.springframework 166 | spring-core 167 | ${spring.version} 168 | test 169 | 170 | 171 | org.springframework 172 | spring-context 173 | ${spring.version} 174 | test 175 | 176 | 177 | org.springframework 178 | spring-test 179 | ${spring.version} 180 | test 181 | 182 | 183 | junit 184 | junit 185 | ${junit.version} 186 | test 187 | 188 | 189 | org.mockito 190 | mockito-core 191 | ${mockito.version} 192 | test 193 | 194 | 195 | org.hamcrest 196 | hamcrest-all 197 | ${hamcrest.version} 198 | test 199 | 200 | 201 | org.kubek2k 202 | springockito 203 | 1.0.9 204 | test 205 | 206 | 207 | org.slf4j 208 | slf4j-log4j12 209 | ${deps.slf4j} 210 | test 211 | 212 | 213 | org.slf4j 214 | jcl-over-slf4j 215 | ${deps.slf4j} 216 | test 217 | 218 | 219 | org.slf4j 220 | jul-to-slf4j 221 | ${deps.slf4j} 222 | test 223 | 224 | 225 | log4j 226 | log4j 227 | 1.2.17 228 | test 229 | 230 | 231 | 232 | 233 | 1.10.19 234 | 4.12 235 | 1.3 236 | 4.3.2.RELEASE 237 | 1.7.12 238 | 8 239 | 240 | 241 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/CachedPostgresArtifactStore.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileSet; 4 | import de.flapdoodle.embed.process.config.store.FileType; 5 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 6 | import de.flapdoodle.embed.process.distribution.Distribution; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | import de.flapdoodle.embed.process.extract.ITempNaming; 9 | import de.flapdoodle.embed.process.extract.ImmutableExtractedFileSet.Builder; 10 | import de.flapdoodle.embed.process.io.directories.IDirectory; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.nio.file.Path; 17 | 18 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 19 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 20 | import static de.flapdoodle.embed.process.extract.ImmutableExtractedFileSet.builder; 21 | import static java.nio.file.Files.exists; 22 | import static java.nio.file.Paths.get; 23 | import static org.apache.commons.io.FileUtils.iterateFiles; 24 | import static org.apache.commons.io.filefilter.TrueFileFilter.TRUE; 25 | 26 | public class CachedPostgresArtifactStore extends PostgresArtifactStore { 27 | private static final Logger LOGGER = LoggerFactory.getLogger(CachedPostgresArtifactStore.class); 28 | private IDownloadConfig downloadConfig; 29 | private IDirectory eDir; 30 | 31 | public CachedPostgresArtifactStore(IDownloadConfig downloadConfig, IDirectory eDir, 32 | ITempNaming executableNaming, IDownloader downloader) { 33 | super(downloadConfig, eDir, executableNaming, downloader); 34 | this.downloadConfig = downloadConfig; 35 | this.eDir = eDir; 36 | } 37 | 38 | @Override 39 | public void removeFileSet(Distribution distribution, IExtractedFileSet all) { 40 | // do nothing 41 | } 42 | 43 | @Override 44 | public IExtractedFileSet extractFileSet(Distribution distribution) throws IOException { 45 | try { 46 | final File dir = this.eDir.asFile(); 47 | final FileSet filesSet = downloadConfig.getPackageResolver().getFileSet(distribution); 48 | final Path path = get(dir.getPath(), 49 | "pgsql" + "-" + distribution.getVersion().asInDownloadPath(), "pgsql"); 50 | if (exists(path)) { 51 | final Builder extracted = builder(dir).baseDirIsGenerated(false); 52 | iterateFiles(path.toFile(), TRUE, TRUE).forEachRemaining(file -> { 53 | FileType type = Library; 54 | if (filesSet.entries().stream() 55 | .anyMatch(entry -> entry.matchingPattern().matcher(file.getPath()).matches())) { 56 | type = Executable; 57 | } 58 | extracted.file(type, file); 59 | }); 60 | return extracted.build(); 61 | } else { 62 | return super.extractFileSet(distribution); 63 | } 64 | } catch (Exception e) { 65 | LOGGER.error("Failed to extract file set", e); 66 | return new EmptyFileSet(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/EmptyFileSet.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileType; 4 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 5 | 6 | import java.io.File; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * @author Ilya Sadykov 12 | */ 13 | public class EmptyFileSet implements IExtractedFileSet { 14 | @Override 15 | public File executable() { 16 | return null; 17 | } 18 | 19 | @Override 20 | public List files(FileType type) { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public File baseDir() { 26 | return null; 27 | } 28 | 29 | @Override 30 | public boolean baseDirIsGenerated() { 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/IMutableArtifactStore.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 4 | 5 | public interface IMutableArtifactStore extends IArtifactStore { 6 | void setDownloadConfig(IDownloadConfig downloadConfig); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/NonCachedPostgresArtifactStoreBuilder.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | public class NonCachedPostgresArtifactStoreBuilder extends PostgresArtifactStoreBuilder { 4 | 5 | @Override 6 | public IArtifactStore build() { 7 | return new PostgresArtifactStore(get(DOWNLOAD_CONFIG), get(TEMP_DIR_FACTORY), get(EXECUTABLE_NAMING), get(DOWNLOADER)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/PostgresArtifactStore.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileSet; 4 | import de.flapdoodle.embed.process.config.store.FileType; 5 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 6 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 7 | import de.flapdoodle.embed.process.distribution.ArchiveType; 8 | import de.flapdoodle.embed.process.distribution.Distribution; 9 | import de.flapdoodle.embed.process.extract.Extractors; 10 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 11 | import de.flapdoodle.embed.process.extract.IExtractor; 12 | import de.flapdoodle.embed.process.extract.ITempNaming; 13 | import de.flapdoodle.embed.process.io.directories.IDirectory; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.util.EnumSet; 20 | 21 | import static org.apache.commons.io.FileUtils.deleteQuietly; 22 | 23 | /** 24 | * @author Ilya Sadykov 25 | * Hacky ArtifactStore. Just to override the default FilesToExtract with PostgresFilesToExtract 26 | */ 27 | public class PostgresArtifactStore implements IMutableArtifactStore { 28 | private static final Logger LOGGER = LoggerFactory.getLogger(PostgresArtifactStore.class); 29 | private IDownloadConfig downloadConfig; 30 | private IDirectory tempDirFactory; 31 | private ITempNaming executableNaming; 32 | private IDownloader downloader; 33 | 34 | PostgresArtifactStore(IDownloadConfig downloadConfig, IDirectory tempDirFactory, ITempNaming executableNaming, IDownloader downloader) { 35 | this.downloadConfig = downloadConfig; 36 | this.tempDirFactory = tempDirFactory; 37 | this.executableNaming = executableNaming; 38 | this.downloader = downloader; 39 | } 40 | 41 | public IDirectory getTempDir() { 42 | return tempDirFactory; 43 | } 44 | 45 | @Override 46 | public void removeFileSet(Distribution distribution, IExtractedFileSet all) { 47 | for (FileType type : EnumSet.complementOf(EnumSet.of(FileType.Executable))) { 48 | for (File file : all.files(type)) { 49 | if (file.exists() && !deleteQuietly(file)) 50 | LOGGER.trace("Could not delete {} NOW: {}", type, file); 51 | } 52 | } 53 | File exe = all.executable(); 54 | if (exe.exists() && !deleteQuietly(exe)) { 55 | LOGGER.trace("Could not delete executable NOW: {}", exe); 56 | } 57 | 58 | if (all.baseDirIsGenerated() && !deleteQuietly(all.baseDir())) { 59 | LOGGER.trace("Could not delete generatedBaseDir: {}", all.baseDir()); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean checkDistribution(Distribution distribution) throws IOException { 65 | if (!LocalArtifactStore.checkArtifact(downloadConfig, distribution)) { 66 | return LocalArtifactStore.store(downloadConfig, distribution, downloader.download(downloadConfig, distribution)); 67 | } 68 | return true; 69 | } 70 | 71 | public IDownloadConfig getDownloadConfig() { 72 | return downloadConfig; 73 | } 74 | 75 | @Override 76 | public void setDownloadConfig(IDownloadConfig downloadConfig) { 77 | this.downloadConfig = downloadConfig; 78 | } 79 | 80 | @Override 81 | public IExtractedFileSet extractFileSet(Distribution distribution) throws IOException { 82 | IPackageResolver packageResolver = downloadConfig.getPackageResolver(); 83 | File artifact = getArtifact(downloadConfig, distribution); 84 | final ArchiveType archiveType = packageResolver.getArchiveType(distribution); 85 | IExtractor extractor = Extractors.getExtractor(archiveType); 86 | try { 87 | final FileSet fileSet = packageResolver.getFileSet(distribution); 88 | return extractor.extract(downloadConfig, artifact, 89 | new PostgresFilesToExtract(tempDirFactory, executableNaming, fileSet, distribution)); 90 | } catch (Exception e) { 91 | LOGGER.error("Failed to extract file set:", e); 92 | return new EmptyFileSet(); 93 | } 94 | } 95 | 96 | private File getArtifact(IDownloadConfig runtime, Distribution distribution) { 97 | File dir = createOrGetBaseDir(runtime); 98 | File artifactFile = new File(dir, runtime.getPackageResolver().getPath(distribution)); 99 | if ((artifactFile.exists()) && (artifactFile.isFile())) 100 | return artifactFile; 101 | return null; 102 | } 103 | 104 | private File createOrGetBaseDir(IDownloadConfig runtime) { 105 | File dir = runtime.getArtifactStorePath().asFile(); 106 | createOrCheckDir(dir); 107 | return dir; 108 | } 109 | 110 | private void createOrCheckDir(File dir) { 111 | if (!dir.exists() && !dir.mkdirs()) { 112 | throw new IllegalArgumentException("Could NOT create Directory " + dir); 113 | } 114 | if (!dir.isDirectory()) 115 | throw new IllegalArgumentException("" + dir + " is not a Directory"); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/PostgresArtifactStoreBuilder.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.extract.UUIDTempNaming; 4 | import ru.yandex.qatools.embed.postgresql.Command; 5 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 6 | import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; 7 | 8 | public class PostgresArtifactStoreBuilder extends 9 | de.flapdoodle.embed.process.store.ArtifactStoreBuilder { 10 | 11 | public PostgresArtifactStoreBuilder defaults(Command command) { 12 | tempDir().setDefault(new SubdirTempDir()); 13 | executableNaming().setDefault(new UUIDTempNaming()); 14 | download().setDefault(new PostgresDownloadConfigBuilder().defaultsForCommand(command).build()); 15 | downloader().setDefault(new Downloader()); 16 | return this; 17 | } 18 | 19 | @Override 20 | public IArtifactStore build() { 21 | return new CachedPostgresArtifactStore(get(DOWNLOAD_CONFIG), get(TEMP_DIR_FACTORY), get(EXECUTABLE_NAMING), get(DOWNLOADER)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/de/flapdoodle/embed/process/store/PostgresFilesToExtract.java: -------------------------------------------------------------------------------- 1 | package de.flapdoodle.embed.process.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileSet; 4 | import de.flapdoodle.embed.process.config.store.FileType; 5 | import de.flapdoodle.embed.process.distribution.Distribution; 6 | import de.flapdoodle.embed.process.extract.*; 7 | import de.flapdoodle.embed.process.io.directories.IDirectory; 8 | import de.flapdoodle.embed.process.io.file.Files; 9 | import org.apache.commons.compress.archivers.ArchiveEntry; 10 | import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.lang.reflect.Field; 18 | import java.nio.file.Path; 19 | import java.nio.file.Paths; 20 | 21 | 22 | /** 23 | * @author Ilya Sadykov 24 | * Hacky strategy of extraction. Allows to extract the full postgres binaries. 25 | */ 26 | public class PostgresFilesToExtract extends FilesToExtract { 27 | private static final Logger LOGGER = LoggerFactory.getLogger(PostgresFilesToExtract.class); 28 | 29 | private static final String SKIP_PATTERN = "pgsql/(doc|include|symbols|pgAdmin[^/]*)/.+"; 30 | private static final String EXECUTABLE_PATTERN = "pgsql/bin/.+"; 31 | 32 | private final FileSet fileSet; 33 | private final String extractBasePath; 34 | 35 | public PostgresFilesToExtract(IDirectory dirFactory, ITempNaming executableNaming, FileSet fileSet, Distribution distribution) { 36 | super(dirFactory, executableNaming, fileSet); 37 | this.fileSet = fileSet; 38 | 39 | if (dirFactory.asFile() != null) { 40 | final File file = new File(dirFactory.asFile(), "pgsql-" + distribution.getVersion().asInDownloadPath()); 41 | if (!file.exists()) { 42 | //noinspection ResultOfMethodCallIgnored 43 | file.mkdir(); 44 | } 45 | this.extractBasePath = file.getPath(); 46 | } else { 47 | this.extractBasePath = null; 48 | } 49 | } 50 | 51 | /** 52 | * This is actually the very dirty hack method to adopt the Flapdoodle's API to the compatible way to extract and run 53 | * TODO: hacky method. Should be considered for complete rewriting //NOSONAR 54 | */ 55 | @Override 56 | public IExtractionMatch find(final IArchiveEntry entry) {//NOSONAR 57 | if (this.extractBasePath == null) { 58 | return null; 59 | } 60 | if (entry.getName().matches(SKIP_PATTERN)) { 61 | return null; 62 | } 63 | 64 | final Path path = Paths.get(this.extractBasePath, entry.getName()); 65 | return new IExtractionMatch() { //NOSONAR 66 | @Override 67 | public File write(InputStream source, long size) throws IOException { //NOSONAR 68 | boolean isSymLink = false; 69 | String linkName = ""; 70 | if (entry instanceof CommonsArchiveEntryAdapter) { 71 | try { 72 | // hack to allow symlinks extraction (ONLY tar archives are supported!) 73 | Field archiveEntryField = CommonsArchiveEntryAdapter.class.getDeclaredField("_entry"); 74 | archiveEntryField.setAccessible(true); 75 | ArchiveEntry archiveEntry = (ArchiveEntry) archiveEntryField.get(entry); 76 | if (archiveEntry instanceof TarArchiveEntry 77 | && (isSymLink = ((TarArchiveEntry) archiveEntry).isSymbolicLink())) { //NOSONAR 78 | linkName = ((TarArchiveEntry) archiveEntry).getLinkName(); 79 | } 80 | archiveEntry.getSize(); 81 | } catch (NoSuchFieldException | IllegalAccessException e) { 82 | throw new RuntimeException("Check the version of de.flapdoodle.embed.process API. " + //NOSONAR 83 | "Has it changed?", e); 84 | } 85 | } 86 | // I got some problems with concurrency. Not sure this is required. 87 | synchronized (PostgresFilesToExtract.class) { 88 | final File outputFile = path.toFile(); 89 | if (entry.isDirectory()) { 90 | if (!outputFile.exists()) { 91 | Files.createDir(outputFile); 92 | } 93 | } else { 94 | if (!outputFile.exists()) { // prevent double extraction (for other binaries) 95 | if (isSymLink) { 96 | try { // NOSONAR 97 | final Path target = path.getParent().resolve(Paths.get(linkName)); 98 | java.nio.file.Files.createSymbolicLink(outputFile.toPath(), target); 99 | } catch (Exception e) { 100 | LOGGER.trace("Failed to extract symlink", e); 101 | } 102 | } else { 103 | Files.write(source, outputFile); 104 | } 105 | } 106 | // hack to mark binaries as executable 107 | if (entry.getName().matches(EXECUTABLE_PATTERN)) { 108 | outputFile.setExecutable(true); 109 | } 110 | } 111 | return outputFile; 112 | } 113 | } 114 | 115 | @Override 116 | public FileType type() { 117 | // does this archive entry match to any of the provided fileset entries? 118 | for (FileSet.Entry matchingEntry : fileSet.entries()) { 119 | if (matchingEntry.matchingPattern().matcher(path.toString()).matches()) { 120 | return matchingEntry.type(); 121 | } 122 | } 123 | // Otherwise - it's just an library file 124 | return FileType.Library; 125 | } 126 | }; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/AbstractPGExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import de.flapdoodle.embed.process.runtime.Executable; 7 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 8 | 9 | public abstract class AbstractPGExecutable 10 | extends Executable { 11 | 12 | public AbstractPGExecutable(Distribution distribution, C config, IRuntimeConfig runtimeConfig, IExtractedFileSet executable) { 13 | super(distribution, config, runtimeConfig, executable); 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/AbstractPGProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.runtime.AbstractProcess; 6 | import de.flapdoodle.embed.process.runtime.Executable; 7 | import de.flapdoodle.embed.process.runtime.IStopable; 8 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 9 | 10 | import java.io.IOException; 11 | 12 | import static java.io.File.*; 13 | import static org.apache.commons.lang3.SystemUtils.JAVA_HOME; 14 | 15 | public abstract class AbstractPGProcess, P extends IStopable> 16 | extends AbstractProcess { 17 | 18 | public AbstractPGProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 19 | super(distribution, config, runtimeConfig, executable); 20 | } 21 | 22 | @Override 23 | protected void onBeforeProcessStart(ProcessBuilder processBuilder, PostgresConfig config, IRuntimeConfig runtimeConfig) { 24 | if (config.credentials() != null) { 25 | processBuilder.environment().put("PGUSER", config.credentials().username()); 26 | processBuilder.environment().put("PGPASSWORD", config.credentials().password()); 27 | } 28 | processBuilder.environment().put("PATH", 29 | processBuilder.environment().get("PATH") + pathSeparatorChar 30 | + JAVA_HOME + separator + "bin" 31 | + pathSeparatorChar 32 | + JAVA_HOME + separator + "jre" + separator + "bin" 33 | ); 34 | } 35 | 36 | @Override 37 | protected void stopInternal() { 38 | 39 | } 40 | 41 | @Override 42 | protected void cleanupInternal() { 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/Command.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 4 | 5 | public enum Command { 6 | Postgres("postgres", PostgresExecutable.class), 7 | InitDb("initdb", InitDbExecutable.class), 8 | CreateDb("createdb", CreateDbExecutable.class), 9 | PgCtl("pg_ctl", PgCtlExecutable.class), 10 | Psql("psql", PsqlExecutable.class), 11 | PgDump("pg_dump", PsqlExecutable.class), 12 | PgRestore("pg_restore", PsqlExecutable.class), 13 | Createuser("createuser", PsqlExecutable.class), 14 | ; 15 | 16 | private final String commandName; 17 | private final Class> executableClass; 18 | 19 | Command(String commandName, 20 | Class> 21 | executableClass) { 22 | this.commandName = commandName; 23 | this.executableClass = executableClass; 24 | } 25 | 26 | public , P extends AbstractPGProcess> Class executableClass() { 27 | return (Class) this.executableClass; 28 | } 29 | 30 | public String commandName() { 31 | return this.commandName; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/CreateDbExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * createdb executor 12 | * (helper to initialize the DB) 13 | */ 14 | class CreateDbExecutable extends AbstractPGExecutable { 15 | 16 | public CreateDbExecutable(Distribution distribution, 17 | PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet redisdExecutable) { 18 | super(distribution, config, runtimeConfig, redisdExecutable); 19 | } 20 | 21 | @Override 22 | protected CreateDbProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 23 | throws IOException { 24 | return new CreateDbProcess<>(distribution, config, runtime, this); 25 | } 26 | 27 | @Override 28 | public synchronized void stop() { 29 | // We don't want to cleanup after this particular single invocation 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/CreateDbProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * createdb process 15 | * (helper to initialize the DB) 16 | */ 17 | class CreateDbProcess extends AbstractPGProcess { 18 | 19 | public CreateDbProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 20 | super(distribution, config, runtimeConfig, executable); 21 | } 22 | 23 | @Override 24 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 25 | throws IOException { 26 | List ret = new ArrayList<>(); 27 | ret.add(exe.executable().getAbsolutePath()); 28 | ret.addAll(Arrays.asList( 29 | "-h", config.net().host(), 30 | "-p", String.valueOf(config.net().port()) 31 | )); 32 | ret.add(config.storage().dbName()); 33 | 34 | return ret; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/CreateuserExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * psql executor 12 | * (helper to initialize the DB) 13 | */ 14 | public class CreateuserExecutable extends AbstractPGExecutable { 15 | 16 | public CreateuserExecutable(Distribution distribution, 17 | PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet redisdExecutable) { 18 | super(distribution, config, runtimeConfig, redisdExecutable); 19 | } 20 | 21 | @Override 22 | protected CreateuserProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 23 | throws IOException { 24 | return new CreateuserProcess<>(distribution, config, runtime, this); 25 | } 26 | 27 | @Override 28 | public synchronized void stop() { 29 | // We don't want to cleanup after this particular single invocation 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/CreateuserProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static java.util.Arrays.asList; 13 | 14 | /** 15 | * createuser process 16 | * (helper to initialize the DB) 17 | */ 18 | public class CreateuserProcess extends AbstractPGProcess { 19 | 20 | public CreateuserProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 21 | super(distribution, config, runtimeConfig, executable); 22 | } 23 | 24 | @Override 25 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 26 | throws IOException { 27 | List ret = new ArrayList<>(); 28 | ret.add(exe.executable().getAbsolutePath()); 29 | ret.addAll(asList( 30 | "-h", config.net().host(), 31 | "-p", String.valueOf(config.net().port()) 32 | )); 33 | ret.addAll(config.args()); 34 | return ret; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/EmbeddedPostgres.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.IVersion; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | import de.flapdoodle.embed.process.io.directories.FixedPath; 7 | import de.flapdoodle.embed.process.runtime.ICommandLinePostProcessor; 8 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 9 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 10 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 11 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 12 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 13 | 14 | import java.io.IOException; 15 | import java.nio.file.Path; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Optional; 19 | 20 | import static java.lang.String.format; 21 | import static java.util.Arrays.asList; 22 | import static java.util.Optional.ofNullable; 23 | import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION; 24 | import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort; 25 | 26 | /** 27 | * Helper class simplifying the start up configuration for embedded postgres 28 | */ 29 | public class EmbeddedPostgres implements AutoCloseable { 30 | public static final String DEFAULT_USER = "postgres";//NOSONAR 31 | public static final String DEFAULT_PASSWORD = "postgres";//NOSONAR 32 | public static final String DEFAULT_DB_NAME = "postgres";//NOSONAR 33 | public static final String DEFAULT_HOST = "localhost"; 34 | private static final List DEFAULT_ADD_PARAMS = asList( 35 | "-E", "SQL_ASCII", 36 | "--locale=C", 37 | "--lc-collate=C", 38 | "--lc-ctype=C"); 39 | private static final List DEFAULT_POSTGRES_PARAMS = asList(); 40 | private final String dataDir; 41 | private final IVersion version; 42 | private PostgresProcess process; 43 | private PostgresConfig config; 44 | 45 | public EmbeddedPostgres() { 46 | this(PRODUCTION); 47 | } 48 | 49 | public EmbeddedPostgres(IVersion version) { 50 | this(version, null); 51 | } 52 | 53 | public EmbeddedPostgres(String dataDir){ 54 | this(PRODUCTION, dataDir); 55 | } 56 | 57 | public EmbeddedPostgres(IVersion version, String dataDir){ 58 | this.version = version; 59 | this.dataDir = dataDir; 60 | } 61 | 62 | /** 63 | * Initializes the default runtime configuration using the temporary directory. 64 | * 65 | * @return runtime configuration required for postgres to start. 66 | */ 67 | public static IRuntimeConfig defaultRuntimeConfig() { 68 | return new RuntimeConfigBuilder() 69 | .defaults(Command.Postgres) 70 | .artifactStore(new PostgresArtifactStoreBuilder() 71 | .defaults(Command.Postgres) 72 | .download(new PostgresDownloadConfigBuilder() 73 | .defaultsForCommand(Command.Postgres) 74 | .build())) 75 | .commandLinePostProcessor(privilegedWindowsRunasPostprocessor()) 76 | .build(); 77 | } 78 | 79 | private static ICommandLinePostProcessor privilegedWindowsRunasPostprocessor() { 80 | if (Platform.detect().equals(Platform.Windows)) { 81 | try { 82 | // Based on https://stackoverflow.com/a/11995662 83 | final int adminCommandResult = Runtime.getRuntime().exec("net session").waitFor(); 84 | if (adminCommandResult == 0) { 85 | return runWithoutPrivileges(); 86 | } 87 | } catch (Exception e) { 88 | // Log maybe? 89 | } 90 | } 91 | return doNothing(); 92 | } 93 | 94 | private static ICommandLinePostProcessor runWithoutPrivileges() { 95 | return (distribution, args) -> { 96 | if (args.size() > 0 && args.get(0).endsWith("postgres.exe")) { 97 | return Arrays.asList("runas", "/trustlevel:0x20000", String.format("\"%s\"", String.join(" ", args))); 98 | } 99 | return args; 100 | }; 101 | } 102 | 103 | private static ICommandLinePostProcessor doNothing() { 104 | return (distribution, args) -> args; 105 | } 106 | 107 | /** 108 | * Initializes runtime configuration for cached directory. 109 | * If a provided directory is empty, postgres will be extracted into it. 110 | * 111 | * @param cachedPath path where postgres is supposed to be extracted 112 | * @return runtime configuration required for postgres to start 113 | */ 114 | public static IRuntimeConfig cachedRuntimeConfig(Path cachedPath) { 115 | final Command cmd = Command.Postgres; 116 | final FixedPath cachedDir = new FixedPath(cachedPath.toString()); 117 | return new RuntimeConfigBuilder() 118 | .defaults(cmd) 119 | .artifactStore(new PostgresArtifactStoreBuilder() 120 | .defaults(cmd) 121 | .tempDir(cachedDir) 122 | .download(new PostgresDownloadConfigBuilder() 123 | .defaultsForCommand(cmd) 124 | .packageResolver(new PackagePaths(cmd, cachedDir)) 125 | .build())) 126 | .commandLinePostProcessor(privilegedWindowsRunasPostprocessor()) 127 | .build(); 128 | } 129 | 130 | public String start() throws IOException { 131 | return start(DEFAULT_HOST, findFreePort(), DEFAULT_DB_NAME); 132 | } 133 | 134 | public String start(String host, int port, String dbName) throws IOException { 135 | return start(host, port, dbName, DEFAULT_USER, DEFAULT_PASSWORD, DEFAULT_ADD_PARAMS); 136 | } 137 | 138 | public String start(String host, int port, String dbName, String user, String password) throws IOException { 139 | return start(defaultRuntimeConfig(), host, port, dbName, user, password, DEFAULT_ADD_PARAMS); 140 | } 141 | 142 | public String start(String host, int port, String dbName, String user, String password, List additionalParams) throws IOException { 143 | return start(defaultRuntimeConfig(), host, port, dbName, user, password, additionalParams); 144 | } 145 | 146 | public String start(IRuntimeConfig runtimeConfig) throws IOException { 147 | return start(runtimeConfig, DEFAULT_HOST, findFreePort(), DEFAULT_DB_NAME, DEFAULT_USER, DEFAULT_PASSWORD, DEFAULT_ADD_PARAMS); 148 | } 149 | 150 | /** 151 | * Starts up the embedded postgres 152 | * 153 | * @param runtimeConfig required runtime configuration 154 | * @param host host to bind to 155 | * @param port port to bind to 156 | * @param dbName name of the database to initialize 157 | * @param user username to connect 158 | * @param password password for the provided username 159 | * @param additionalParams additional database init params (if required) 160 | * @return connection url for the initialized postgres instance 161 | * @throws IOException if an I/O error occurs during the process startup 162 | */ 163 | public String start(IRuntimeConfig runtimeConfig, String host, int port, String dbName, String user, String password, 164 | List additionalParams) throws IOException { 165 | return start(runtimeConfig, host, port, dbName, user, password, additionalParams, DEFAULT_POSTGRES_PARAMS); 166 | } 167 | 168 | /** 169 | * Starts up the embedded postgres 170 | * 171 | * @param runtimeConfig required runtime configuration 172 | * @param host host to bind to 173 | * @param port port to bind to 174 | * @param dbName name of the database to initialize 175 | * @param user username to connect 176 | * @param password password for the provided username 177 | * @param additionalInitDbParams additional database init params (if required) 178 | * @param additionalPostgresParams additional postgresql params (if required) 179 | * @return connection url for the initialized postgres instance 180 | * @throws IOException if an I/O error occurs during the process startup 181 | */ 182 | public String start(IRuntimeConfig runtimeConfig, String host, int port, String dbName, String user, String password, 183 | List additionalInitDbParams, List additionalPostgresParams) throws IOException { 184 | final PostgresStarter runtime = PostgresStarter.getInstance(runtimeConfig); 185 | config = new PostgresConfig(version, 186 | new AbstractPostgresConfig.Net(host, port), 187 | new AbstractPostgresConfig.Storage(dbName, dataDir), 188 | new AbstractPostgresConfig.Timeout(), 189 | new AbstractPostgresConfig.Credentials(user, password) 190 | ); 191 | config.getAdditionalInitDbParams().addAll(additionalInitDbParams); 192 | config.getAdditionalPostgresParams().addAll(additionalPostgresParams); 193 | PostgresExecutable exec = runtime.prepare(config); 194 | this.process = exec.start(); 195 | return formatConnUrl(config); 196 | } 197 | 198 | /** 199 | * Returns the configuration of started process 200 | * 201 | * @return empty if process has not been started yet 202 | */ 203 | public Optional getConfig() { 204 | return ofNullable(config); 205 | } 206 | 207 | 208 | /** 209 | * Returns the process if started 210 | * 211 | * @return empty if process has not been started yet 212 | */ 213 | public Optional getProcess() { 214 | return ofNullable(process); 215 | } 216 | 217 | /** 218 | * Returns the connection url for the running postgres instance 219 | * 220 | * @return empty if process has not been started yet 221 | */ 222 | public Optional getConnectionUrl() { 223 | return getConfig().map(this::formatConnUrl); 224 | } 225 | 226 | private String formatConnUrl(PostgresConfig config) { 227 | return format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s",//NOSONAR 228 | config.net().host(), 229 | config.net().port(), 230 | config.storage().dbName(), 231 | config.credentials().username(), 232 | config.credentials().password() 233 | ); 234 | } 235 | 236 | public void stop() { 237 | getProcess().orElseThrow(() -> new IllegalStateException("Cannot stop not started instance!")).stop(); 238 | } 239 | 240 | @Override 241 | public void close() { 242 | this.stop(); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/InitDbExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * initdb executor 12 | * (helper to initialize the DB) 13 | */ 14 | class InitDbExecutable extends AbstractPGExecutable { 15 | 16 | public InitDbExecutable(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) { 17 | super(distribution, config, runtimeConfig, exe); 18 | } 19 | 20 | @Override 21 | protected InitDbProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 22 | throws IOException { 23 | return new InitDbProcess<>(distribution, config, runtime, this); 24 | } 25 | 26 | 27 | @Override 28 | public synchronized void stop() { 29 | // We don't want to cleanup after this particular single invocation 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/InitDbProcess.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 3 | * Michael Mosmann 4 | * Martin Jöhren 5 | *

6 | * with contributions from 7 | * konstantin-ba@github, Archimedes Trajano (trajano@github), Christian Bayer (chrbayer84@googlemail.com) 8 | *

9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | *

13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | *

15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | package ru.yandex.qatools.embed.postgresql; 22 | 23 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 24 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 25 | import de.flapdoodle.embed.process.distribution.Distribution; 26 | import de.flapdoodle.embed.process.distribution.Platform; 27 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 28 | import de.flapdoodle.embed.process.io.Processors; 29 | import de.flapdoodle.embed.process.io.StreamToLineProcessor; 30 | import de.flapdoodle.embed.process.io.file.Files; 31 | import de.flapdoodle.embed.process.runtime.ProcessControl; 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 35 | import ru.yandex.qatools.embed.postgresql.ext.LogWatchStreamProcessor; 36 | import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; 37 | 38 | import java.io.File; 39 | import java.io.IOException; 40 | import java.util.ArrayList; 41 | import java.util.List; 42 | 43 | import static de.flapdoodle.embed.process.io.file.Files.createTempFile; 44 | import static java.util.Arrays.asList; 45 | import static java.util.Collections.singleton; 46 | import static java.util.UUID.randomUUID; 47 | 48 | /** 49 | * initdb process 50 | * (helper to initialize the DB) 51 | */ 52 | class InitDbProcess extends AbstractPGProcess { 53 | private static final Logger LOGGER = LoggerFactory.getLogger(InitDbProcess.class); 54 | 55 | public InitDbProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 56 | super(distribution, config, runtimeConfig, executable); 57 | } 58 | 59 | @Override 60 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 61 | throws IOException { 62 | List ret = new ArrayList<>(); 63 | ret.add(exe.executable().getAbsolutePath()); 64 | if (getConfig().credentials() != null) { 65 | final File pwFile = createTempFile(SubdirTempDir.defaultInstance(), "pwfile" + randomUUID()); 66 | pwFile.deleteOnExit(); 67 | Files.write(getConfig().credentials().password(), pwFile); 68 | ret.addAll(asList( 69 | "-A", "password", 70 | "-U", getConfig().credentials().username(), 71 | "--pwfile=" + pwFile.getAbsolutePath() 72 | )); 73 | } 74 | if (distribution.getPlatform() == Platform.Windows) { 75 | ret.addAll(config.getAdditionalInitDbParams()); 76 | } 77 | ret.add(config.storage().dbDir().getAbsolutePath()); 78 | if (distribution.getPlatform() != Platform.Windows) { 79 | ret.addAll(config.getAdditionalInitDbParams()); 80 | } 81 | return ret; 82 | } 83 | 84 | @Override 85 | protected void onAfterProcessStart(ProcessControl process, IRuntimeConfig runtimeConfig) throws IOException { 86 | final ProcessOutput outputConfig = runtimeConfig.getProcessOutput(); 87 | final LogWatchStreamProcessor logWatch = new LogWatchStreamProcessor( 88 | "performing post-bootstrap initialization", 89 | singleton("[initdb error]"), StreamToLineProcessor.wrap(outputConfig.getOutput())); 90 | Processors.connect(process.getReader(), logWatch); 91 | Processors.connect(process.getError(), StreamToLineProcessor.wrap(outputConfig.getError())); 92 | logWatch.waitForResult(getConfig().timeout().startupTimeout()); 93 | } 94 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PackagePaths.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileSet; 4 | import de.flapdoodle.embed.process.config.store.FileType; 5 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 6 | import de.flapdoodle.embed.process.distribution.ArchiveType; 7 | import de.flapdoodle.embed.process.distribution.Distribution; 8 | import de.flapdoodle.embed.process.distribution.IVersion; 9 | import de.flapdoodle.embed.process.io.directories.IDirectory; 10 | 11 | /** 12 | * Paths builder 13 | */ 14 | public class PackagePaths implements IPackageResolver { 15 | 16 | private final Command command; 17 | private final IDirectory tempDir; 18 | 19 | public PackagePaths(Command command, IDirectory tempDir) { 20 | this.command = command; 21 | this.tempDir = tempDir; 22 | } 23 | 24 | protected static String getVersionPart(IVersion version) { 25 | return version.asInDownloadPath(); 26 | } 27 | 28 | public IDirectory getTempDir() { 29 | return tempDir; 30 | } 31 | 32 | @Override 33 | public FileSet getFileSet(Distribution distribution) { 34 | String cmdPattern; 35 | switch (distribution.getPlatform()) { 36 | case Linux: 37 | case OS_X: 38 | cmdPattern = command.commandName(); 39 | break; 40 | case Windows: 41 | cmdPattern = command.commandName() + ".exe"; 42 | break; 43 | default: 44 | throw new IllegalArgumentException("Unknown Platform " 45 | + distribution.getPlatform()); 46 | } 47 | try { 48 | return FileSet.builder() 49 | .addEntry(FileType.Executable, tempDir.asFile().getPath(), 50 | "^.*pgsql\\\\bin\\\\" + cmdPattern + "$") 51 | .addEntry(FileType.Executable, tempDir.asFile().getPath(), 52 | "^.*pgsql/bin/" + cmdPattern + "$") 53 | .build(); 54 | } catch (Exception e) { 55 | throw new RuntimeException(e); 56 | } 57 | } 58 | 59 | @Override 60 | public ArchiveType getArchiveType(Distribution distribution) { 61 | ArchiveType archiveType; 62 | switch (distribution.getPlatform()) { 63 | case Linux: 64 | archiveType = ArchiveType.TGZ; 65 | break; 66 | case OS_X: 67 | case Windows: 68 | archiveType = ArchiveType.ZIP; 69 | break; 70 | default: 71 | throw new IllegalArgumentException("Unknown Platform " 72 | + distribution.getPlatform()); 73 | } 74 | return archiveType; 75 | } 76 | 77 | @Override 78 | public String getPath(Distribution distribution) { 79 | String downloadVersion = getVersionPart(distribution.getVersion()); 80 | 81 | ArchiveType archiveType = getArchiveType(distribution); 82 | String sarchiveType; 83 | switch (archiveType) { 84 | case TGZ: 85 | sarchiveType = "tar.gz"; 86 | break; 87 | case ZIP: 88 | sarchiveType = "zip"; 89 | break; 90 | default: 91 | throw new IllegalArgumentException("Unknown ArchiveType " 92 | + archiveType); 93 | } 94 | 95 | String splatform; 96 | switch (distribution.getPlatform()) { 97 | case Linux: 98 | splatform = "linux"; 99 | break; 100 | case Windows: 101 | splatform = "windows"; 102 | break; 103 | case OS_X: 104 | splatform = "osx"; 105 | break; 106 | default: 107 | throw new IllegalArgumentException("Unknown Platform " 108 | + distribution.getPlatform()); 109 | } 110 | 111 | String bitsize = ""; 112 | switch (distribution.getBitsize()) { 113 | case B32: 114 | switch (distribution.getPlatform()) { 115 | case Windows: 116 | case Linux: 117 | case OS_X: 118 | break; 119 | default: 120 | throw new IllegalArgumentException( 121 | "32 bit supported only on Windows, MacOS, Linux, platform is " 122 | + distribution.getPlatform()); 123 | } 124 | break; 125 | case B64: 126 | switch (distribution.getPlatform()) { 127 | case Linux: 128 | bitsize = "-x64"; 129 | break; 130 | case Windows: 131 | bitsize = "-x64"; 132 | // win x64 has different download paths 133 | // See https://github.com/yandex-qatools/postgresql-embedded/issues/109 134 | switch (downloadVersion) { 135 | case "10.1-1": 136 | downloadVersion = "10.1-2"; 137 | break; 138 | case "9.6.6-1": 139 | downloadVersion = "9.6.6-2"; 140 | break; 141 | } 142 | break; 143 | case OS_X: 144 | break; 145 | default: 146 | throw new IllegalArgumentException( 147 | "64 bit supported only on Linux and Windows, platform is " 148 | + distribution.getPlatform()); 149 | } 150 | break; 151 | default: 152 | throw new IllegalArgumentException("Unknown BitSize " + distribution.getBitsize()); 153 | } 154 | 155 | return "postgresql-" + downloadVersion + "-" + splatform + bitsize + "-binaries" + "." + sarchiveType; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PgCtlExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * pg_ctl executor 12 | * (helper to initialize the DB) 13 | */ 14 | class PgCtlExecutable extends AbstractPGExecutable { 15 | 16 | public PgCtlExecutable(Distribution distribution, 17 | PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) { 18 | super(distribution, config, runtimeConfig, exe); 19 | } 20 | 21 | @Override 22 | protected PgCtlProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 23 | throws IOException { 24 | return new PgCtlProcess<>(distribution, config, runtime, this); 25 | } 26 | 27 | @Override 28 | public synchronized void stop() { 29 | // We don't want to cleanup after this particular single invocation 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PgCtlProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static java.util.Arrays.asList; 13 | 14 | /** 15 | * pg_ctl process 16 | * (helper to initialize the DB) 17 | */ 18 | class PgCtlProcess extends AbstractPGProcess { 19 | 20 | public PgCtlProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 21 | super(distribution, config, runtimeConfig, executable); 22 | } 23 | 24 | @Override 25 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 26 | throws IOException { 27 | List ret = new ArrayList<>(); 28 | ret.addAll(asList(exe.executable().getAbsolutePath())); 29 | ret.addAll(asList( 30 | "-o", 31 | String.format("-p %s -h %s", config.net().port(), config.net().host()), 32 | "-D", config.storage().dbDir().getAbsolutePath(), 33 | "-w" 34 | )); 35 | if (config.args().isEmpty()) { 36 | ret.add("start"); 37 | } else { 38 | ret.addAll( 39 | config.args() 40 | ); 41 | } 42 | return ret; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * postgres executable 12 | */ 13 | public class PostgresExecutable extends AbstractPGExecutable { 14 | final IRuntimeConfig runtimeConfig; 15 | 16 | public PostgresExecutable(Distribution distribution, 17 | PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) { 18 | super(distribution, config, runtimeConfig, exe); 19 | this.runtimeConfig = runtimeConfig; 20 | } 21 | 22 | @Override 23 | protected PostgresProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 24 | throws IOException { 25 | return new PostgresProcess(distribution, config, runtime, this); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PostgresProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 5 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 6 | import de.flapdoodle.embed.process.distribution.Distribution; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | import de.flapdoodle.embed.process.io.Slf4jLevel; 9 | import de.flapdoodle.embed.process.io.Slf4jStreamProcessor; 10 | import de.flapdoodle.embed.process.io.directories.IDirectory; 11 | import de.flapdoodle.embed.process.io.progress.Slf4jProgressListener; 12 | import de.flapdoodle.embed.process.runtime.Executable; 13 | import de.flapdoodle.embed.process.runtime.ProcessControl; 14 | import de.flapdoodle.embed.process.store.IArtifactStore; 15 | import de.flapdoodle.embed.process.store.IMutableArtifactStore; 16 | import de.flapdoodle.embed.process.store.PostgresArtifactStore; 17 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 18 | import org.apache.commons.lang3.ArrayUtils; 19 | import org.slf4j.Logger; 20 | import ru.yandex.qatools.embed.postgresql.config.IMutableDownloadConfig; 21 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 22 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 23 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 24 | import ru.yandex.qatools.embed.postgresql.ext.LogWatchStreamProcessor; 25 | import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.nio.file.Path; 30 | import java.nio.file.Paths; 31 | import java.util.ArrayList; 32 | import java.util.HashSet; 33 | import java.util.List; 34 | import java.util.Set; 35 | 36 | import static de.flapdoodle.embed.process.io.file.Files.forceDelete; 37 | import static java.lang.System.currentTimeMillis; 38 | import static java.lang.Thread.sleep; 39 | import static java.util.Arrays.asList; 40 | import static java.util.Collections.emptySet; 41 | import static java.util.Collections.singleton; 42 | import static java.util.Collections.singletonList; 43 | import static org.apache.commons.io.FileUtils.readLines; 44 | import static org.apache.commons.lang3.StringUtils.isEmpty; 45 | import static org.slf4j.LoggerFactory.getLogger; 46 | import static ru.yandex.qatools.embed.postgresql.Command.CreateDb; 47 | import static ru.yandex.qatools.embed.postgresql.Command.InitDb; 48 | import static ru.yandex.qatools.embed.postgresql.Command.PgDump; 49 | import static ru.yandex.qatools.embed.postgresql.Command.PgRestore; 50 | import static ru.yandex.qatools.embed.postgresql.Command.Psql; 51 | import static ru.yandex.qatools.embed.postgresql.PostgresStarter.getCommand; 52 | import static ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig.Storage; 53 | 54 | /** 55 | * postgres process 56 | */ 57 | public class PostgresProcess extends AbstractPGProcess { 58 | private static final int MAX_CREATEDB_TRIALS = 3; 59 | private static final int DEFAULT_CMD_TIMEOUT = 2000; 60 | private static Logger LOGGER = getLogger(PostgresProcess.class); 61 | private final IRuntimeConfig runtimeConfig; 62 | 63 | private volatile boolean processReady = false; 64 | private volatile boolean stopped = false; 65 | 66 | public PostgresProcess(Distribution distribution, PostgresConfig config, 67 | IRuntimeConfig runtimeConfig, PostgresExecutable executable) throws IOException { 68 | super(distribution, config, runtimeConfig, executable); 69 | this.runtimeConfig = runtimeConfig; 70 | } 71 | 72 | private static String runCmd(PostgresConfig config, IRuntimeConfig parentRuntimeCfg, Command cmd, String successOutput, 73 | Set failOutput, String... args) { 74 | return runCmd(false, config, parentRuntimeCfg, cmd, successOutput, failOutput, args); 75 | } 76 | 77 | private static String runCmd(boolean silent, 78 | PostgresConfig config, IRuntimeConfig parentRuntimeCfg, Command cmd, String successOutput, 79 | Set failOutput, String... args) { 80 | try { 81 | final LogWatchStreamProcessor logWatch = new LogWatchStreamProcessor(successOutput, 82 | failOutput, new Slf4jStreamProcessor(LOGGER, Slf4jLevel.TRACE)); 83 | 84 | IArtifactStore artifactStore = parentRuntimeCfg.getArtifactStore(); 85 | IDownloadConfig downloadCfg = ((PostgresArtifactStore) artifactStore).getDownloadConfig(); 86 | 87 | if (downloadCfg instanceof IMutableDownloadConfig) { 88 | IDirectory tempDir = SubdirTempDir.defaultInstance(); 89 | if (downloadCfg.getPackageResolver() instanceof PackagePaths) { 90 | tempDir = ((PackagePaths) downloadCfg.getPackageResolver()).getTempDir(); 91 | } 92 | ((IMutableDownloadConfig) downloadCfg).setPackageResolver(new PackagePaths(cmd, tempDir)); 93 | } else { 94 | LOGGER.warn("Could not use the configured download configuration for '" + cmd.commandName() + 95 | "', falling back to default!"); 96 | downloadCfg = new PostgresDownloadConfigBuilder().defaultsForCommand(cmd) 97 | .progressListener(new Slf4jProgressListener(LOGGER)).build(); 98 | } 99 | if (artifactStore instanceof IMutableArtifactStore) { 100 | ((IMutableArtifactStore) artifactStore).setDownloadConfig(downloadCfg); 101 | } else { 102 | LOGGER.warn("Could not use the configured artifact store for '" + cmd.commandName() + 103 | "', falling back to default!"); 104 | artifactStore = new PostgresArtifactStoreBuilder().defaults(cmd).download(downloadCfg).build(); 105 | } 106 | 107 | final IRuntimeConfig runtimeCfg = new RuntimeConfigBuilder().defaults(cmd) 108 | .daemonProcess(false) 109 | .processOutput(new ProcessOutput(logWatch, logWatch, logWatch)) 110 | .artifactStore(artifactStore) 111 | .commandLinePostProcessor(parentRuntimeCfg.getCommandLinePostProcessor()).build(); 112 | 113 | final PostgresConfig postgresConfig = new PostgresConfig(config).withArgs(args); 114 | if (Command.InitDb == cmd) { 115 | postgresConfig.withAdditionalInitDbParams(config.getAdditionalInitDbParams()); 116 | } 117 | final Executable exec = getCommand(cmd, runtimeCfg) 118 | .prepare(postgresConfig); 119 | AbstractPGProcess proc = exec.start(); 120 | logWatch.waitForResult(DEFAULT_CMD_TIMEOUT); 121 | if (!logWatch.isInitWithSuccess() && !silent) { 122 | LOGGER.warn("Possibly failed to run {}:\n{}", cmd.commandName(), logWatch.getOutput()); 123 | } 124 | proc.waitFor(); 125 | return logWatch.getOutput(); 126 | } catch (IOException | InterruptedException e) { 127 | if (!silent) { 128 | LOGGER.warn("Failed to run command {}", cmd.commandName(), e); 129 | } 130 | } 131 | return null; 132 | } 133 | 134 | private static boolean shutdownPostgres(PostgresConfig config, IRuntimeConfig runtimeConfig) { 135 | try { 136 | return isEmpty(runCmd(true, config, runtimeConfig, Command.PgCtl, "server stopped", emptySet(), "stop")); 137 | } catch (Exception e) { 138 | LOGGER.trace("Failed to stop postgres by pg_ctl!", e); 139 | } 140 | return false; 141 | } 142 | 143 | @Override 144 | protected synchronized void stopInternal() { 145 | if (!stopped && isProcessRunning()) { 146 | stopped = true; 147 | LOGGER.info("trying to stop postgresql"); 148 | if (!sendStopToPostgresqlInstance() && !sendTermToProcess() && waitUntilProcessHasStopped(2000)) { 149 | LOGGER.warn("could not stop postgresql with pg_ctl/SIGTERM, trying to kill it..."); 150 | if (!sendKillToProcess() && !tryKillToProcess() && waitUntilProcessHasStopped(3000)) { 151 | LOGGER.warn("could not kill postgresql within 4s!"); 152 | } 153 | } 154 | } 155 | if (waitUntilProcessHasStopped(5000)) { 156 | LOGGER.error("Postgres has not been stopped within 10s! Something's wrong!"); 157 | } 158 | deleteTempFiles(); 159 | } 160 | 161 | private boolean waitUntilProcessHasStopped(int timeoutMillis) { 162 | long started = currentTimeMillis(); 163 | while (currentTimeMillis() - started < timeoutMillis && isProcessRunning()) { 164 | try { 165 | sleep(50); 166 | } catch (InterruptedException e) { 167 | LOGGER.warn("Failed to wait with timeout until the process has been killed", e); 168 | } 169 | } 170 | return isProcessRunning(); 171 | } 172 | 173 | protected final boolean sendStopToPostgresqlInstance() { 174 | final boolean result = shutdownPostgres(getConfig(), runtimeConfig); 175 | if (runtimeConfig.getArtifactStore() instanceof PostgresArtifactStore) { 176 | final IDirectory tempDir = ((PostgresArtifactStore) runtimeConfig.getArtifactStore()).getTempDir(); 177 | if (tempDir != null && tempDir.asFile() != null && tempDir.isGenerated()) { 178 | LOGGER.info("Cleaning up after the embedded process (removing {})...", tempDir.asFile().getAbsolutePath()); 179 | forceDelete(tempDir.asFile()); 180 | } 181 | } 182 | return result; 183 | } 184 | 185 | @Override 186 | protected void onBeforeProcess(IRuntimeConfig runtimeConfig) 187 | throws IOException { 188 | super.onBeforeProcess(runtimeConfig); 189 | PostgresConfig config = getConfig(); 190 | 191 | final File dbDir = config.storage().dbDir(); 192 | if (dbDir.exists() && dbDir.listFiles() != null && dbDir.listFiles().length > 0) { 193 | return; 194 | } 195 | 196 | runCmd(config, runtimeConfig, InitDb, "Success", singleton("[initdb error]")); 197 | } 198 | 199 | @Override 200 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 201 | throws IOException { 202 | List ret = new ArrayList<>(); 203 | switch (config.supportConfig().getName()) { 204 | case "postgres": //NOSONAR 205 | ret.addAll(asList(exe.executable().getAbsolutePath(), 206 | "-p", String.valueOf(config.net().port()), 207 | "-h", config.net().host(), 208 | "-D", config.storage().dbDir().getAbsolutePath() 209 | )); 210 | ret.addAll(config.getAdditionalPostgresParams()); 211 | break; 212 | case "pg_ctl": //NOSONAR 213 | ret.addAll(asList(exe.executable().getAbsolutePath(), 214 | "-o", 215 | String.format("-p %s -h %s", config.net().port(), config.net().host()), 216 | "-D", config.storage().dbDir().getAbsolutePath(), 217 | "-w", 218 | "start" 219 | )); 220 | break; 221 | default: 222 | throw new RuntimeException("Failed to launch Postgres: Unknown command " + 223 | config.supportConfig().getName() + "!"); 224 | } 225 | return ret; 226 | } 227 | 228 | protected void deleteTempFiles() { 229 | final Storage storage = getConfig().storage(); 230 | if (storage.dbDir() == null) { 231 | return; 232 | } 233 | if (!storage.isTmpDir()) { 234 | return; 235 | } 236 | 237 | if (!forceDelete(storage.dbDir())) { 238 | LOGGER.warn("Could not delete temp db dir: {}", storage.dbDir()); 239 | } 240 | } 241 | 242 | @Override 243 | protected final void onAfterProcessStart(ProcessControl process, 244 | IRuntimeConfig runtimeConfig) throws IOException { 245 | final Storage storage = getConfig().storage(); 246 | final Path pidFilePath = Paths.get(storage.dbDir().getAbsolutePath(), "postmaster.pid"); 247 | final File pidFile = new File(pidFilePath.toAbsolutePath().toString()); 248 | int timeout = TIMEOUT; 249 | while (!pidFile.exists() && ((timeout = timeout - 100) > 0)) { 250 | try { 251 | sleep(100); 252 | } catch (InterruptedException ie) { /* safe to ignore */ } 253 | } 254 | int pid = -1; 255 | try { 256 | pid = Integer.valueOf(readLines(pidFilePath.toFile()).get(0)); 257 | } catch (Exception e) { 258 | LOGGER.error("Failed to read PID file ({})", e.getMessage(), e); 259 | } 260 | if (pid != -1) { 261 | setProcessId(pid); 262 | } else { 263 | // fallback, try to read pid file. will throw IOException if that fails 264 | setProcessId(getPidFromFile(pidFile())); 265 | } 266 | 267 | int trial = 0; 268 | do { 269 | String output = runCmd(getConfig(), 270 | runtimeConfig, 271 | CreateDb, 272 | "", 273 | new HashSet<>(singleton("database creation failed")), 274 | storage.dbName()); 275 | try { 276 | if (isEmpty(output) || !output.contains("could not connect to database")) { 277 | this.processReady = true; 278 | break; 279 | } 280 | LOGGER.warn("Could not create database first time ({} of {} trials)", trial, MAX_CREATEDB_TRIALS); 281 | sleep(100); 282 | } catch (InterruptedException ie) { /* safe to ignore */ } 283 | } while (trial++ < MAX_CREATEDB_TRIALS); 284 | } 285 | 286 | /** 287 | * Import into database from file 288 | * 289 | * @param file The file to import into database 290 | */ 291 | public void importFromFile(File file) { 292 | importFromFileWithArgs(file); 293 | } 294 | 295 | /** 296 | * Import into database from file with additional args 297 | * 298 | * @param file 299 | * @param cliArgs additional arguments for psql (be sure to separate args from their values) 300 | */ 301 | public void importFromFileWithArgs(File file, String... cliArgs) { 302 | if (file.exists()) { 303 | String[] args = { 304 | "-U", getConfig().credentials().username(), 305 | "-d", getConfig().storage().dbName(), 306 | "-h", getConfig().net().host(), 307 | "-p", String.valueOf(getConfig().net().port()), 308 | "-f", file.getAbsolutePath()}; 309 | if (cliArgs != null && cliArgs.length != 0) { 310 | args = ArrayUtils.addAll(args, cliArgs); 311 | } 312 | runCmd(getConfig(), runtimeConfig, Psql, "", new HashSet<>(singletonList("import into " + getConfig().storage().dbName() + " failed")), args); 313 | } 314 | } 315 | 316 | /** 317 | * Import into database from file with additional args 318 | * 319 | * @param file 320 | * @param cliArgs additional arguments for psql (be sure to separate args from their values) 321 | */ 322 | public void restoreFromFile(File file, String... cliArgs) { 323 | if (file.exists()) { 324 | String[] args = { 325 | "-U", getConfig().credentials().username(), 326 | "-d", getConfig().storage().dbName(), 327 | "-h", getConfig().net().host(), 328 | "-p", String.valueOf(getConfig().net().port()), 329 | file.getAbsolutePath()}; 330 | if (cliArgs != null && cliArgs.length != 0) { 331 | args = ArrayUtils.addAll(args, cliArgs); 332 | } 333 | runCmd(getConfig(), runtimeConfig, PgRestore, "", new HashSet<>(singletonList("restore into " + getConfig().storage().dbName() + " failed")), args); 334 | } 335 | } 336 | 337 | public void exportToFile(File file) { 338 | runCmd(getConfig(), runtimeConfig, PgDump, "", new HashSet<>(singletonList("export from " + getConfig().storage().dbName() + " failed")), 339 | "-U", getConfig().credentials().username(), 340 | "-d", getConfig().storage().dbName(), 341 | "-h", getConfig().net().host(), 342 | "-p", String.valueOf(getConfig().net().port()), 343 | "-f", file.getAbsolutePath() 344 | ); 345 | } 346 | 347 | public void exportSchemeToFile(File file) { 348 | runCmd(getConfig(), runtimeConfig, PgDump, "", new HashSet<>(singletonList("export from " + getConfig().storage().dbName() + " failed")), 349 | "-U", getConfig().credentials().username(), 350 | "-d", getConfig().storage().dbName(), 351 | "-h", getConfig().net().host(), 352 | "-p", String.valueOf(getConfig().net().port()), 353 | "-f", file.getAbsolutePath(), 354 | "-s" 355 | ); 356 | } 357 | 358 | public void exportDataToFile(File file) { 359 | runCmd(getConfig(), runtimeConfig, PgDump, "", new HashSet<>(singletonList("export from " + getConfig().storage().dbName() + " failed")), 360 | "-U", getConfig().credentials().username(), 361 | "-d", getConfig().storage().dbName(), 362 | "-h", getConfig().net().host(), 363 | "-p", String.valueOf(getConfig().net().port()), 364 | "-f", file.getAbsolutePath(), 365 | "-a" 366 | ); 367 | } 368 | 369 | public boolean isProcessReady() { 370 | return processReady; 371 | } 372 | 373 | @Override 374 | protected void cleanupInternal() { 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PostgresStarter.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 5 | import de.flapdoodle.embed.process.distribution.Distribution; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | import de.flapdoodle.embed.process.io.Slf4jLevel; 8 | import de.flapdoodle.embed.process.io.Slf4jStreamProcessor; 9 | import de.flapdoodle.embed.process.runtime.Starter; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 13 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 14 | import ru.yandex.qatools.embed.postgresql.ext.LogWatchStreamProcessor; 15 | 16 | import java.lang.reflect.Constructor; 17 | import java.util.HashSet; 18 | 19 | import static java.util.Collections.singletonList; 20 | import static org.slf4j.LoggerFactory.getLogger; 21 | 22 | 23 | /** 24 | * Starter for every pg process 25 | */ 26 | public class PostgresStarter, P extends AbstractPGProcess> 27 | extends Starter { 28 | private static final Logger LOGGER = LoggerFactory.getLogger(PostgresStarter.class); 29 | final Class execClass; 30 | 31 | public PostgresStarter(final Class execClass, final IRuntimeConfig runtimeConfig) { 32 | super(runtimeConfig); 33 | this.execClass = execClass; 34 | } 35 | 36 | public static PostgresStarter getInstance(IRuntimeConfig config) { 37 | return new PostgresStarter<>(PostgresExecutable.class, config); 38 | } 39 | 40 | public static PostgresStarter getDefaultInstance() { 41 | return getInstance(runtimeConfig(Command.Postgres)); 42 | } 43 | 44 | public static IRuntimeConfig runtimeConfig(Command cmd) { 45 | LogWatchStreamProcessor logWatch = new LogWatchStreamProcessor( 46 | "started", new HashSet<>(singletonList("failed")), 47 | new Slf4jStreamProcessor(getLogger("postgres"), Slf4jLevel.TRACE)); 48 | return new RuntimeConfigBuilder() 49 | .defaults(cmd) 50 | .processOutput(new ProcessOutput(logWatch, logWatch, logWatch)).build(); 51 | } 52 | 53 | public static , P extends AbstractPGProcess> 54 | PostgresStarter getCommand(Command command, IRuntimeConfig config) { 55 | return new PostgresStarter<>(command.executableClass(), config); 56 | } 57 | 58 | public static , P extends AbstractPGProcess> 59 | PostgresStarter getCommand(Command command) { 60 | return getCommand(command, runtimeConfig(command)); 61 | } 62 | 63 | @Override 64 | protected E newExecutable(PostgresConfig config, Distribution distribution, 65 | IRuntimeConfig runtime, IExtractedFileSet exe) { 66 | try { 67 | Constructor c = execClass.getConstructor( 68 | Distribution.class, PostgresConfig.class, 69 | IRuntimeConfig.class, IExtractedFileSet.class 70 | ); 71 | return c.newInstance(distribution, config, runtime, exe); 72 | } catch (Exception e) { 73 | LOGGER.warn("Exception while trying to create executable", e); 74 | throw new RuntimeException("Failed to initialize the executable (" + e.getMessage() + ")", e); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PsqlExecutable.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * psql executor 12 | * (helper to initialize the DB) 13 | */ 14 | public class PsqlExecutable extends AbstractPGExecutable { 15 | 16 | public PsqlExecutable(Distribution distribution, 17 | PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet redisdExecutable) { 18 | super(distribution, config, runtimeConfig, redisdExecutable); 19 | } 20 | 21 | @Override 22 | protected PsqlProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) 23 | throws IOException { 24 | return new PsqlProcess<>(distribution, config, runtime, this); 25 | } 26 | 27 | @Override 28 | public synchronized void stop() { 29 | // We don't want to cleanup after this particular single invocation 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/PsqlProcess.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * psql process 15 | * (helper to initialize the DB) 16 | */ 17 | public class PsqlProcess extends AbstractPGProcess { 18 | 19 | public PsqlProcess(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, E executable) throws IOException { 20 | super(distribution, config, runtimeConfig, executable); 21 | } 22 | 23 | @Override 24 | protected List getCommandLine(Distribution distribution, PostgresConfig config, IExtractedFileSet exe) 25 | throws IOException { 26 | List ret = new ArrayList<>(); 27 | ret.add(exe.executable().getAbsolutePath()); 28 | ret.addAll(Arrays.asList( 29 | "-h", config.net().host(), 30 | "-p", String.valueOf(config.net().port()) 31 | )); 32 | ret.addAll(config.args()); 33 | 34 | return ret; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/AbstractPostgresConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.ExecutableProcessConfig; 4 | import de.flapdoodle.embed.process.distribution.IVersion; 5 | import de.flapdoodle.embed.process.io.file.Files; 6 | import ru.yandex.qatools.embed.postgresql.Command; 7 | import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import static de.flapdoodle.embed.process.runtime.Network.getFreeServerPort; 15 | import static de.flapdoodle.embed.process.runtime.Network.getLocalHost; 16 | import static java.util.Arrays.asList; 17 | import static org.apache.commons.lang3.StringUtils.isEmpty; 18 | 19 | /** 20 | * Common postgres config 21 | */ 22 | public abstract class AbstractPostgresConfig extends ExecutableProcessConfig { 23 | 24 | private final Storage storage; 25 | protected final Net network; 26 | protected final Timeout timeout; 27 | protected final Credentials credentials; 28 | protected List args = new ArrayList<>(); 29 | protected List additionalInitDbParams = new ArrayList<>(); 30 | protected List additionalPostgresParams = new ArrayList<>(); 31 | 32 | protected AbstractPostgresConfig(AbstractPostgresConfig config, Command postgres) { 33 | this(config.version, config.net(), config.storage, config.timeout(), config.credentials, new SupportConfig(postgres)); 34 | } 35 | 36 | protected AbstractPostgresConfig(AbstractPostgresConfig config) { 37 | this(config, Command.Postgres); 38 | } 39 | 40 | public AbstractPostgresConfig(IVersion version, Net network, Storage storage, Timeout timeout, Credentials cred, SupportConfig supportConfig) { 41 | super(version, supportConfig); 42 | this.network = network; 43 | this.timeout = timeout; 44 | this.storage = storage; 45 | this.credentials = cred; 46 | } 47 | 48 | public AbstractPostgresConfig(IVersion version, Net network, Storage storage, Timeout timeout) { 49 | this(version, network, storage, timeout, null, new SupportConfig(Command.Postgres)); 50 | } 51 | 52 | public Net net() { 53 | return network; 54 | } 55 | 56 | public Timeout timeout() { 57 | return timeout; 58 | } 59 | 60 | public Storage storage() { 61 | return storage; 62 | } 63 | 64 | public Credentials credentials() { 65 | return credentials; 66 | } 67 | 68 | public List args() { 69 | return args; 70 | } 71 | 72 | public C withArgs(String... args) { 73 | args().addAll(asList(args)); 74 | return (C) this; 75 | } 76 | 77 | public C withAdditionalInitDbParams(List additionalInitDbParams) { 78 | this.additionalInitDbParams.addAll(additionalInitDbParams); 79 | return (C) this; 80 | } 81 | 82 | /** 83 | * You may add here additional arguments for the {@code initdb} executable.
84 | *

85 | * Example.
86 | * to support german umlauts you would add here this additional arguments.
87 | *

 88 |      * getAdditionalInitDbParams().addAll(
 89 |      *      java.util.Arrays.asList(
 90 |      *          "-E", "'UTF-8'",
 91 |      *          "--lc-collate='de_DE.UTF-8'",
 92 |      *          "--lc-ctype=locale='de_DE.UTF-8'")
 93 |      * )
 94 |      * 
95 | * 96 | * @return The list of additional parameters for the {@code initdb} executable.
97 | * Not {@code null}.
98 | */ 99 | public List getAdditionalInitDbParams() { 100 | return additionalInitDbParams; 101 | } 102 | 103 | /** 104 | * You may add here additional arguments for the {@code postgres} executable.
105 | *

106 | * Example.
107 | * to use custom number of maximum connection.
108 | *

109 |      * getAdditionalPostgresParams().addAll(
110 |      *      java.util.Arrays.asList(
111 |      *          "-c", "max_connections=11"
112 |      * )
113 |      * 
114 | * @return The list of additional parameters for the {@code postgres} executable.
115 | * Not {@code null}.
116 | * @see Parameter Interaction via the Shell 117 | */ 118 | public List getAdditionalPostgresParams() { 119 | return additionalPostgresParams; 120 | } 121 | 122 | public static class Storage { 123 | private final File dbDir; 124 | private final String dbName; 125 | private final boolean isTmpDir; 126 | 127 | public Storage(String dbName) throws IOException { 128 | this(dbName, null); 129 | } 130 | 131 | public Storage(String dbName, String databaseDir) throws IOException { 132 | this.dbName = dbName; 133 | if (isEmpty(databaseDir)) { 134 | isTmpDir = true; 135 | dbDir = Files.createTempDir(SubdirTempDir.defaultInstance(), "db-content"); 136 | } else { 137 | dbDir = Files.createOrCheckDir(databaseDir); 138 | isTmpDir = false; 139 | } 140 | } 141 | 142 | public File dbDir() { 143 | return dbDir; 144 | } 145 | 146 | public boolean isTmpDir() { 147 | return isTmpDir; 148 | } 149 | 150 | public String dbName() { 151 | return dbName; 152 | } 153 | 154 | @Override 155 | public String toString() { 156 | return "Storage{" + 157 | "dbDir=" + dbDir + 158 | ", dbName='" + dbName + '\'' + 159 | ", isTmpDir=" + isTmpDir + 160 | '}'; 161 | } 162 | } 163 | 164 | public static class Credentials { 165 | 166 | private final String username; 167 | private final String password; 168 | 169 | 170 | public Credentials(String username, String password) { 171 | this.username = username; 172 | this.password = password; 173 | } 174 | 175 | public String username() { 176 | return username; 177 | } 178 | 179 | public String password() { 180 | return password; 181 | } 182 | 183 | @Override 184 | public String toString() { 185 | return "Credentials{" + 186 | "username='" + username + '\'' + 187 | ", password='" + password + '\'' + //NOSONAR 188 | '}'; 189 | } 190 | } 191 | 192 | public static class Net { 193 | 194 | private final String host; 195 | private final int port; 196 | 197 | public Net() throws IOException { 198 | this(getLocalHost().getHostAddress(), getFreeServerPort()); 199 | } 200 | 201 | public Net(String host, int port) { 202 | this.host = host; 203 | this.port = port; 204 | } 205 | 206 | public int port() { 207 | return port; 208 | } 209 | 210 | public String host() { 211 | return host; 212 | } 213 | 214 | @Override 215 | public String toString() { 216 | return "Net{" + 217 | "host='" + host + '\'' + 218 | ", port=" + port + 219 | '}'; 220 | } 221 | } 222 | 223 | public static class Timeout { 224 | 225 | private final long startupTimeout; 226 | 227 | public Timeout() { 228 | this(15000); 229 | } 230 | 231 | public Timeout(long startupTimeout) { 232 | this.startupTimeout = startupTimeout; 233 | } 234 | 235 | public long startupTimeout() { 236 | return startupTimeout; 237 | } 238 | 239 | @Override 240 | public String toString() { 241 | return "Timeout{" + 242 | "startupTimeout=" + startupTimeout + 243 | '}'; 244 | } 245 | } 246 | 247 | @Override 248 | public String toString() { 249 | return "AbstractPostgresConfig{" + 250 | "storage=" + storage + 251 | ", network=" + network + 252 | ", timeout=" + timeout + 253 | ", credentials=" + credentials + 254 | ", args=" + args + 255 | ", additionalInitDbParams=" + additionalInitDbParams + 256 | '}'; 257 | } 258 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/IMutableDownloadConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 4 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 5 | 6 | public interface IMutableDownloadConfig extends IDownloadConfig { 7 | void setPackageResolver(IPackageResolver packageResolver); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/MutableDownloadConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.store.IDownloadPath; 4 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 5 | import de.flapdoodle.embed.process.config.store.IProxyFactory; 6 | import de.flapdoodle.embed.process.config.store.ITimeoutConfig; 7 | import de.flapdoodle.embed.process.extract.ITempNaming; 8 | import de.flapdoodle.embed.process.io.directories.IDirectory; 9 | import de.flapdoodle.embed.process.io.progress.IProgressListener; 10 | 11 | public class MutableDownloadConfig implements IMutableDownloadConfig { 12 | 13 | private final IDownloadPath downloadPath; 14 | private final IProgressListener progressListener; 15 | private final IDirectory artifactStorePath; 16 | private final ITempNaming fileNaming; 17 | private final String downloadPrefix; 18 | private final String userAgent; 19 | private final ITimeoutConfig timeoutConfig; 20 | private final IProxyFactory proxyFactory; 21 | private IPackageResolver packageResolver; 22 | 23 | public MutableDownloadConfig(IDownloadPath downloadPath, String downloadPrefix, IPackageResolver packageResolver,//NOSONAR 24 | IDirectory artifactStorePath, ITempNaming fileNaming, IProgressListener progressListener, String userAgent,//NOSONAR 25 | ITimeoutConfig timeoutConfig, IProxyFactory proxyFactory) { //NOSONAR 26 | super(); 27 | this.downloadPath = downloadPath; 28 | this.downloadPrefix = downloadPrefix; 29 | this.packageResolver = packageResolver; 30 | this.artifactStorePath = artifactStorePath; 31 | this.fileNaming = fileNaming; 32 | this.progressListener = progressListener; 33 | this.userAgent = userAgent; 34 | this.timeoutConfig = timeoutConfig; 35 | this.proxyFactory = proxyFactory; 36 | } 37 | 38 | @Override 39 | public IDownloadPath getDownloadPath() { 40 | return downloadPath; 41 | } 42 | 43 | @Override 44 | public IProgressListener getProgressListener() { 45 | return progressListener; 46 | } 47 | 48 | @Override 49 | public IDirectory getArtifactStorePath() { 50 | return artifactStorePath; 51 | } 52 | 53 | @Override 54 | public ITempNaming getFileNaming() { 55 | return fileNaming; 56 | } 57 | 58 | @Override 59 | public String getDownloadPrefix() { 60 | return downloadPrefix; 61 | } 62 | 63 | @Override 64 | public String getUserAgent() { 65 | return userAgent; 66 | } 67 | 68 | @Override 69 | public IPackageResolver getPackageResolver() { 70 | return packageResolver; 71 | } 72 | 73 | @Override 74 | public void setPackageResolver(IPackageResolver packageResolver) { 75 | this.packageResolver = packageResolver; 76 | } 77 | 78 | @Override 79 | public ITimeoutConfig getTimeoutConfig() { 80 | return timeoutConfig; 81 | } 82 | 83 | @Override 84 | public IProxyFactory proxyFactory() { 85 | return proxyFactory; 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/PostgresConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.distribution.IVersion; 4 | import ru.yandex.qatools.embed.postgresql.Command; 5 | 6 | import java.io.IOException; 7 | 8 | import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION; 9 | 10 | /** 11 | * Configuration for postgres 12 | */ 13 | public class PostgresConfig extends AbstractPostgresConfig { 14 | 15 | public PostgresConfig(AbstractPostgresConfig config, Command command) { 16 | super(config, command); 17 | } 18 | 19 | public PostgresConfig(AbstractPostgresConfig config) { 20 | super(config); 21 | } 22 | 23 | public PostgresConfig(IVersion version, String dbName) throws IOException { 24 | this(version, new Net(), new Storage(dbName), new Timeout()); 25 | } 26 | 27 | public PostgresConfig(IVersion version, String host, int port, String dbName) throws IOException { 28 | this(version, new Net(host, port), new Storage(dbName), new Timeout()); 29 | } 30 | 31 | public PostgresConfig(IVersion version, Net network, Storage storage, Timeout timeout, Credentials cred, Command command) { 32 | super(version, network, storage, timeout, cred, new SupportConfig(command)); 33 | } 34 | 35 | public PostgresConfig(IVersion version, Net network, Storage storage, Timeout timeout, Credentials cred) { 36 | this(version, network, storage, timeout, cred, Command.Postgres); 37 | } 38 | 39 | public PostgresConfig(IVersion version, Net network, Storage storage, Timeout timeout) { 40 | super(version, network, storage, timeout); 41 | } 42 | 43 | public static PostgresConfig defaultWithDbName(String dbName, String user, String password) throws IOException { 44 | return new PostgresConfig(PRODUCTION, new Net(), new Storage(dbName), new Timeout(), 45 | new Credentials(user, password)); 46 | } 47 | 48 | public static PostgresConfig defaultWithDbName(String dbName) throws IOException { 49 | return new PostgresConfig(PRODUCTION, dbName); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/PostgresDownloadConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.builder.IProperty; 4 | import de.flapdoodle.embed.process.builder.TypedProperty; 5 | import de.flapdoodle.embed.process.config.store.DownloadConfigBuilder; 6 | import de.flapdoodle.embed.process.config.store.DownloadPath; 7 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 8 | import de.flapdoodle.embed.process.config.store.IDownloadPath; 9 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 10 | import de.flapdoodle.embed.process.config.store.IProxyFactory; 11 | import de.flapdoodle.embed.process.config.store.ITimeoutConfig; 12 | import de.flapdoodle.embed.process.extract.ITempNaming; 13 | import de.flapdoodle.embed.process.extract.UUIDTempNaming; 14 | import de.flapdoodle.embed.process.io.directories.IDirectory; 15 | import de.flapdoodle.embed.process.io.directories.UserHome; 16 | import de.flapdoodle.embed.process.io.progress.IProgressListener; 17 | import de.flapdoodle.embed.process.io.progress.StandardConsoleProgressListener; 18 | import ru.yandex.qatools.embed.postgresql.Command; 19 | import ru.yandex.qatools.embed.postgresql.PackagePaths; 20 | import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; 21 | 22 | 23 | /** 24 | * Download config builder for postgres 25 | */ 26 | public class PostgresDownloadConfigBuilder extends DownloadConfigBuilder { 27 | private static final TypedProperty USER_AGENT = TypedProperty.with("UserAgent", UserAgent.class); 28 | private static final TypedProperty PROGRESS_LISTENER = TypedProperty.with("ProgressListener", IProgressListener.class); 29 | private static final TypedProperty FILE_NAMING = TypedProperty.with("FileNaming", ITempNaming.class); 30 | private static final TypedProperty ARTIFACT_STORE_PATH = TypedProperty.with("ArtifactStorePath", IDirectory.class); 31 | private static final TypedProperty PACKAGE_RESOLVER = TypedProperty.with("PackageResolver", IPackageResolver.class); 32 | private static final TypedProperty DOWNLOAD_PREFIX = TypedProperty.with("DownloadPrefix", DownloadPrefix.class); 33 | private static final TypedProperty DOWNLOAD_PATH = TypedProperty.with("DownloadPath", IDownloadPath.class); 34 | 35 | private static final TypedProperty TIMEOUT_CONFIG = TypedProperty.with("TimeoutConfig", ITimeoutConfig.class); 36 | private static final TypedProperty PROXY_FACTORY = TypedProperty.with("ProxyFactory", IProxyFactory.class); 37 | 38 | public PostgresDownloadConfigBuilder defaultsForCommand(Command command) { 39 | fileNaming().setDefault(new UUIDTempNaming()); 40 | // I've found the only open and easy to use cross platform binaries 41 | downloadPath().setDefault(new DownloadPath("http://get.enterprisedb.com/postgresql/")); 42 | packageResolver().setDefault(new PackagePaths(command, SubdirTempDir.defaultInstance())); 43 | artifactStorePath().setDefault(new UserHome(".embedpostgresql")); 44 | downloadPrefix().setDefault(new DownloadPrefix("postgresql-download")); 45 | userAgent().setDefault(new UserAgent("Mozilla/5.0 (compatible; Embedded postgres; +https://github.com/yandex-qatools)")); 46 | progressListener().setDefault(new StandardConsoleProgressListener() { 47 | @Override 48 | public void info(String label, String message) { 49 | if (label.startsWith("Extract")) { 50 | System.out.print(".");//NOSONAR 51 | } else { 52 | super.info(label, message);//NOSONAR 53 | } 54 | } 55 | }); 56 | return this; 57 | } 58 | 59 | @Override 60 | public IDownloadConfig build() { 61 | final IDownloadPath downloadPath = get(DOWNLOAD_PATH); 62 | final String downloadPrefix = get(DOWNLOAD_PREFIX).value(); 63 | final IPackageResolver packageResolver = get(PACKAGE_RESOLVER); 64 | final IDirectory artifactStorePath = get(ARTIFACT_STORE_PATH); 65 | final ITempNaming fileNaming = get(FILE_NAMING); 66 | final IProgressListener progressListener = get(PROGRESS_LISTENER); 67 | final String userAgent = get(USER_AGENT).value(); 68 | final ITimeoutConfig timeoutConfig = get(TIMEOUT_CONFIG); 69 | final IProxyFactory proxyFactory = get(PROXY_FACTORY); 70 | 71 | return new MutableDownloadConfig(downloadPath, downloadPrefix, packageResolver, artifactStorePath, fileNaming, 72 | progressListener, userAgent, timeoutConfig, proxyFactory); 73 | } 74 | 75 | @Override 76 | public DownloadConfigBuilder downloadPath(String path) { 77 | set(DOWNLOAD_PATH, new DownloadPath(path)); 78 | return this; 79 | } 80 | 81 | @Override 82 | protected IProperty downloadPath() { 83 | return property(DOWNLOAD_PATH); 84 | } 85 | 86 | @Override 87 | public DownloadConfigBuilder downloadPrefix(String prefix) { 88 | set(DOWNLOAD_PREFIX, new DownloadPrefix(prefix)); 89 | return this; 90 | } 91 | 92 | @Override 93 | protected IProperty downloadPrefix() { 94 | return property(DOWNLOAD_PREFIX); 95 | } 96 | 97 | @Override 98 | public DownloadConfigBuilder packageResolver(IPackageResolver packageResolver) { 99 | set(PACKAGE_RESOLVER, packageResolver); 100 | return this; 101 | } 102 | 103 | @Override 104 | protected IProperty packageResolver() { 105 | return property(PACKAGE_RESOLVER); 106 | } 107 | 108 | @Override 109 | public DownloadConfigBuilder artifactStorePath(IDirectory artifactStorePath) { 110 | set(ARTIFACT_STORE_PATH, artifactStorePath); 111 | return this; 112 | } 113 | 114 | @Override 115 | protected IProperty artifactStorePath() { 116 | return property(ARTIFACT_STORE_PATH); 117 | } 118 | 119 | @Override 120 | public DownloadConfigBuilder fileNaming(ITempNaming fileNaming) { 121 | set(FILE_NAMING, fileNaming); 122 | return this; 123 | } 124 | 125 | @Override 126 | protected IProperty fileNaming() { 127 | return property(FILE_NAMING); 128 | } 129 | 130 | @Override 131 | public DownloadConfigBuilder progressListener(IProgressListener progressListener) { 132 | set(PROGRESS_LISTENER, progressListener); 133 | return this; 134 | } 135 | 136 | @Override 137 | protected IProperty progressListener() { 138 | return property(PROGRESS_LISTENER); 139 | } 140 | 141 | @Override 142 | public DownloadConfigBuilder userAgent(String userAgent) { 143 | set(USER_AGENT, new UserAgent(userAgent)); 144 | return this; 145 | } 146 | 147 | @Override 148 | protected IProperty userAgent() { 149 | return property(USER_AGENT); 150 | } 151 | 152 | @Override 153 | public DownloadConfigBuilder timeoutConfig(ITimeoutConfig timeoutConfig) { 154 | set(TIMEOUT_CONFIG, timeoutConfig); 155 | return this; 156 | } 157 | 158 | @Override 159 | protected IProperty timeoutConfig() { 160 | return property(TIMEOUT_CONFIG); 161 | } 162 | 163 | @Override 164 | public DownloadConfigBuilder proxyFactory(IProxyFactory proxyFactory) { 165 | set(PROXY_FACTORY, proxyFactory); 166 | return this; 167 | } 168 | 169 | @Override 170 | protected IProperty proxyFactory() { 171 | return property(PROXY_FACTORY); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/PostgresProcessOutputConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 4 | import ru.yandex.qatools.embed.postgresql.Command; 5 | 6 | /** 7 | * @author Alexey Zhokhov 8 | */ 9 | public class PostgresProcessOutputConfig { 10 | 11 | private PostgresProcessOutputConfig() {} 12 | 13 | public static ProcessOutput getDefaultInstance(Command command) { 14 | return ProcessOutput.getDefaultInstance(command.commandName()); 15 | } 16 | 17 | public static ProcessOutput getInstance(Command command, org.slf4j.Logger logger) { 18 | return ProcessOutput.getInstance(command.commandName(), logger); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/RuntimeConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 4 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 5 | import de.flapdoodle.embed.process.io.progress.Slf4jProgressListener; 6 | import de.flapdoodle.embed.process.runtime.ICommandLinePostProcessor; 7 | import ru.yandex.qatools.embed.postgresql.Command; 8 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 9 | 10 | /** 11 | * Configuration builder 12 | */ 13 | public class RuntimeConfigBuilder extends de.flapdoodle.embed.process.config.RuntimeConfigBuilder { 14 | 15 | public RuntimeConfigBuilder defaults(Command command) { 16 | daemonProcess().setDefault(false); 17 | processOutput().setDefault(ProcessOutput.getDefaultInstance(command.commandName())); 18 | commandLinePostProcessor().setDefault(new ICommandLinePostProcessor.Noop()); 19 | artifactStore().setDefault(storeBuilder().defaults(command).build()); 20 | return this; 21 | } 22 | 23 | public RuntimeConfigBuilder defaultsWithLogger(Command command, org.slf4j.Logger logger) { 24 | defaults(command); 25 | processOutput().overwriteDefault(PostgresProcessOutputConfig.getInstance(command, logger)); 26 | 27 | IDownloadConfig downloadConfig = new PostgresDownloadConfigBuilder() 28 | .defaultsForCommand(command) 29 | .progressListener(new Slf4jProgressListener(logger)) 30 | .build(); 31 | 32 | artifactStore().overwriteDefault(storeBuilder().defaults(command).download(downloadConfig).build()); 33 | return this; 34 | } 35 | 36 | private PostgresArtifactStoreBuilder storeBuilder() { 37 | return new PostgresArtifactStoreBuilder(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/config/SupportConfig.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import de.flapdoodle.embed.process.config.ISupportConfig; 4 | import ru.yandex.qatools.embed.postgresql.Command; 5 | 6 | public class SupportConfig implements ISupportConfig { 7 | private final Command command; 8 | 9 | public SupportConfig(Command command) { 10 | this.command = command; 11 | } 12 | 13 | @Override 14 | public String getName() { 15 | return command.commandName(); 16 | } 17 | 18 | @Override 19 | public String getSupportUrl() { 20 | return "https://github.com/yandex-qatools/postgresql-embedded/issues\n"; 21 | } 22 | 23 | @Override 24 | public String messageOnException(Class context, Exception exception) { 25 | return null; 26 | } 27 | 28 | @Override 29 | public long maxStopTimeoutMillis() { 30 | return 100; 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/distribution/Version.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.distribution; 2 | 3 | import de.flapdoodle.embed.process.distribution.IVersion; 4 | 5 | /** 6 | * PostgreSQL Version enum 7 | */ 8 | public enum Version implements IVersion { 9 | /** 10 | * 11 for Mac OS X and Windows x86-64 only because EnterpriseDB reduced the 11 | * supported platforms 12 | * on their 13 | * binary download site. 14 | */ 15 | V11_2("11.2-1"), 16 | V10_7("10.7-1"), 17 | V9_6_12("9.6.12-1"), 18 | @Deprecated V9_5_16("9.5.16-1"),; 19 | 20 | private final String specificVersion; 21 | 22 | Version(String vName) { 23 | this.specificVersion = vName; 24 | } 25 | 26 | @Override 27 | public String asInDownloadPath() { 28 | return specificVersion; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "Version{" + specificVersion + '}'; 34 | } 35 | 36 | public enum Main implements IVersion { 37 | @Deprecated V9_5(V9_5_16), 38 | V9_6(V9_6_12), 39 | V10(V10_7), 40 | PRODUCTION(V10_7), 41 | /** 42 | * 11 for Mac OS X and Windows x86-64 only because EnterpriseDB reduced the 43 | * supported platforms 44 | * on their 45 | * binary download site. 46 | */ 47 | V11(V11_2); 48 | 49 | private final IVersion _latest; 50 | 51 | Main(IVersion latest) { 52 | _latest = latest; 53 | } 54 | 55 | @Override 56 | public String asInDownloadPath() { 57 | return _latest.asInDownloadPath(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/ext/LogWatchStreamProcessor.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.ext; 2 | 3 | import de.flapdoodle.embed.process.io.IStreamProcessor; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.Set; 8 | 9 | import static java.util.Optional.ofNullable; 10 | 11 | /** 12 | * @author Ilya Sadykov 13 | */ 14 | public class LogWatchStreamProcessor extends de.flapdoodle.embed.process.io.LogWatchStreamProcessor { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(LogWatchStreamProcessor.class); 16 | private final StringBuilder output = new StringBuilder(); 17 | private final Object mutex = new Object(); 18 | private final String success; 19 | private final Set failures; 20 | private volatile boolean found = false; 21 | private volatile boolean initWithSuccess = false; 22 | 23 | public LogWatchStreamProcessor(String success, Set failures, IStreamProcessor destination) { 24 | super(success, failures, destination); 25 | this.success = ofNullable(success).orElse(""); 26 | this.failures = failures; 27 | } 28 | 29 | @Override 30 | public void process(String block) { 31 | LOGGER.debug(block); 32 | output.append(block).append("\n"); 33 | initWithSuccess = containsSuccess(block); 34 | if (initWithSuccess || containsFailure(block)) { 35 | synchronized (mutex) { 36 | found = true; 37 | mutex.notifyAll(); 38 | } 39 | } else { 40 | super.process(block); 41 | } 42 | } 43 | 44 | private boolean containsSuccess(String block) { 45 | return block.contains(success); 46 | } 47 | 48 | private boolean containsFailure(String block) { 49 | for (String failure : failures) { 50 | if (block.contains(failure)) { 51 | return true; 52 | } 53 | } 54 | return false; 55 | } 56 | 57 | @Override 58 | public void waitForResult(long timeout) { 59 | synchronized (mutex) { 60 | try { 61 | if (!found) { 62 | mutex.wait(timeout); 63 | } 64 | } catch (InterruptedException e) { 65 | LOGGER.error("Failed to wait for the result: '{}' not found in: \n{}", success, output); 66 | e.printStackTrace(); 67 | } 68 | } 69 | } 70 | 71 | @Override 72 | public boolean isInitWithSuccess() { 73 | return initWithSuccess || getOutput().contains(success); 74 | } 75 | 76 | @Override 77 | public String getOutput() { 78 | return output.toString(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/ext/SubdirTempDir.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.ext; 2 | 3 | import de.flapdoodle.embed.process.io.directories.IDirectory; 4 | import de.flapdoodle.embed.process.io.directories.PropertyOrPlatformTempDir; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | import static de.flapdoodle.embed.process.io.file.Files.createTempDir; 10 | 11 | /** 12 | * @author Ilya Sadykov 13 | * Temporary dir creating the temp dir inside of the system temp dir. 14 | */ 15 | public class SubdirTempDir extends PropertyOrPlatformTempDir { 16 | private static final File tempDir; 17 | private static SubdirTempDir _instance = new SubdirTempDir(); 18 | 19 | static { 20 | try { 21 | String customTempDir = System.getProperty("de.flapdoodle.embed.io.tmpdir"); 22 | if (customTempDir != null) { 23 | tempDir = new File(customTempDir); 24 | } else { 25 | tempDir = createTempDir(new File(System.getProperty("java.io.tmpdir")), "postgresql-embed"); 26 | } 27 | tempDir.deleteOnExit(); 28 | } catch (IOException e) { 29 | throw new RuntimeException("Failed to create temp dir", e); 30 | } 31 | } 32 | 33 | public static IDirectory defaultInstance() { 34 | return _instance; 35 | } 36 | 37 | @Override 38 | public File asFile() { 39 | return tempDir; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/ru/yandex/qatools/embed/postgresql/util/SocketUtil.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.IOException; 7 | import java.net.ServerSocket; 8 | 9 | public final class SocketUtil { 10 | private static final Logger LOGGER = LoggerFactory.getLogger(SocketUtil.class); 11 | 12 | private SocketUtil() { 13 | } 14 | 15 | 16 | /** 17 | * Returns a free port number on localhost. 18 | *

19 | * Heavily inspired from org.eclipse.jdt.launching.SocketUtil (to avoid a dependency to JDT just because of this). 20 | * Slightly improved with close() missing in JDT. And throws exception instead of returning -1. 21 | * 22 | * @return a free port number on localhost 23 | * @throws IllegalStateException if unable to find a free port 24 | */ 25 | public static int findFreePort() { 26 | ServerSocket socket = null; 27 | try { 28 | socket = new ServerSocket(0); 29 | socket.setReuseAddress(true); 30 | int port = socket.getLocalPort(); 31 | closeQuietly(socket); 32 | return port; 33 | } catch (IOException e) { 34 | LOGGER.trace("Failed to open socket", e); 35 | } finally { 36 | if (socket != null) { 37 | closeQuietly(socket); 38 | } 39 | } 40 | throw new IllegalStateException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on"); 41 | } 42 | 43 | private static void closeQuietly(ServerSocket socket) { 44 | try { 45 | socket.close(); 46 | } catch (IOException e) { 47 | LOGGER.trace("Failed to close socket", e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/AbstractPsqlTest.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | 8 | import java.sql.Connection; 9 | import java.sql.DriverManager; 10 | 11 | import static java.lang.String.format; 12 | import static java.util.Arrays.asList; 13 | import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION; 14 | import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort; 15 | 16 | /** 17 | * @author Ilya Sadykov 18 | */ 19 | public abstract class AbstractPsqlTest { 20 | protected PostgresProcess process; 21 | protected Connection conn; 22 | 23 | @Before 24 | public void setUp() throws Exception { 25 | PostgresStarter runtime = PostgresStarter.getDefaultInstance(); 26 | final PostgresConfig config = new PostgresConfig(PRODUCTION, new AbstractPostgresConfig.Net( 27 | "localhost", findFreePort() 28 | ), new AbstractPostgresConfig.Storage("test"), new AbstractPostgresConfig.Timeout(), 29 | new AbstractPostgresConfig.Credentials("user", "password")); 30 | config.getAdditionalInitDbParams().addAll(asList( 31 | "-E", "SQL_ASCII", 32 | "--locale=C", 33 | "--lc-collate=C", 34 | "--lc-ctype=C" 35 | )); 36 | PostgresExecutable exec = runtime.prepare(config); 37 | process = exec.start(); 38 | String url = format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s", 39 | config.net().host(), 40 | config.net().port(), 41 | config.storage().dbName(), 42 | config.credentials().username(), 43 | config.credentials().password() 44 | ); 45 | conn = DriverManager.getConnection(url); 46 | } 47 | 48 | @After 49 | public void tearDown() throws Exception { 50 | conn.close(); 51 | process.stop(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/EmbeddedPostgresTest.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.nio.file.Paths; 8 | import java.sql.Connection; 9 | import java.sql.DriverManager; 10 | import java.sql.SQLException; 11 | import java.sql.Statement; 12 | 13 | import static java.lang.String.format; 14 | import static java.util.Collections.emptyList; 15 | import static org.hamcrest.CoreMatchers.endsWith; 16 | import static org.hamcrest.CoreMatchers.equalTo; 17 | import static org.hamcrest.CoreMatchers.startsWith; 18 | import static org.hamcrest.core.Is.is; 19 | import static org.junit.Assert.assertThat; 20 | import static ru.yandex.qatools.embed.postgresql.EmbeddedPostgres.DEFAULT_DB_NAME; 21 | import static ru.yandex.qatools.embed.postgresql.EmbeddedPostgres.DEFAULT_PASSWORD; 22 | import static ru.yandex.qatools.embed.postgresql.EmbeddedPostgres.DEFAULT_USER; 23 | import static ru.yandex.qatools.embed.postgresql.EmbeddedPostgres.cachedRuntimeConfig; 24 | 25 | public class EmbeddedPostgresTest { 26 | 27 | private EmbeddedPostgres postgres; 28 | 29 | @Before 30 | public void setUp() throws Exception { 31 | postgres = new EmbeddedPostgres(); 32 | } 33 | 34 | @After 35 | public void tearDown() throws Exception { 36 | postgres.getProcess().ifPresent(PostgresProcess::stop); 37 | } 38 | 39 | @Test 40 | public void itShouldStartWithDefaults() throws Exception { 41 | final String url = postgres.start(); 42 | assertThat(url, startsWith("jdbc:postgresql://localhost:")); 43 | assertThat(url, endsWith(format("/%s?user=%s&password=%s", DEFAULT_DB_NAME, DEFAULT_USER, DEFAULT_PASSWORD))); 44 | ensurePostgresIsWorking(url); 45 | } 46 | 47 | @Test 48 | public void itShouldBeEmptyForNonStartedInstance() throws Exception { 49 | assertThat(postgres.getConnectionUrl().isPresent(), equalTo(false)); 50 | assertThat(postgres.getConfig().isPresent(), equalTo(false)); 51 | assertThat(postgres.getProcess().isPresent(), equalTo(false)); 52 | } 53 | 54 | @Test(expected = IllegalStateException.class) 55 | public void itShouldThrowExceptionForNonStartedInstance() throws Exception { 56 | postgres.stop(); 57 | } 58 | 59 | @Test 60 | public void itShouldWorkForNonDefaultConfig() throws Exception { 61 | final String url = postgres.start("localhost", 15433, "pgDataBase", "pgUser", "pgPassword", emptyList()); 62 | assertThat(url, equalTo("jdbc:postgresql://localhost:15433/pgDataBase?user=pgUser&password=pgPassword")); 63 | ensurePostgresIsWorking(url); 64 | } 65 | 66 | @Test 67 | public void itShouldWorkWithCachedRuntimeConfig() throws Exception { 68 | final String url = postgres.start(cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed"))); 69 | ensurePostgresIsWorking(url); 70 | } 71 | 72 | private void ensurePostgresIsWorking(String url) { 73 | try { 74 | final Connection conn = DriverManager.getConnection(url); 75 | assertThat(conn.createStatement().execute("CREATE TABLE films (code char(5));"), is(false)); 76 | assertThat(conn.createStatement().execute("INSERT INTO films VALUES ('movie');"), is(false)); 77 | final Statement statement = conn.createStatement(); 78 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 79 | assertThat(statement.getResultSet().next(), is(true)); 80 | assertThat(statement.getResultSet().getString("code"), is("movie")); 81 | } catch (SQLException e) { 82 | throw new RuntimeException(e); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/PostgresqlService.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 5 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 7 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 8 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 9 | 10 | import java.sql.Connection; 11 | import java.sql.DriverManager; 12 | 13 | import static java.lang.String.format; 14 | import static java.util.Arrays.asList; 15 | import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION; 16 | import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort; 17 | 18 | /** 19 | * @author Ilya Sadykov 20 | */ 21 | public class PostgresqlService { 22 | 23 | private PostgresProcess process; 24 | private Connection conn; 25 | 26 | public void start() throws Exception { 27 | IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder() 28 | .defaults(Command.Postgres) 29 | .artifactStore(new PostgresArtifactStoreBuilder() 30 | .defaults(Command.Postgres) 31 | .download(new PostgresDownloadConfigBuilder() 32 | .defaultsForCommand(Command.Postgres).build() 33 | ) 34 | ).build(); 35 | PostgresStarter runtime = PostgresStarter.getInstance(runtimeConfig); 36 | final PostgresConfig config = new PostgresConfig(PRODUCTION, new AbstractPostgresConfig.Net("localhost", findFreePort()), 37 | new AbstractPostgresConfig.Storage("test"), new AbstractPostgresConfig.Timeout(), 38 | new AbstractPostgresConfig.Credentials("user", "password")); 39 | config.getAdditionalInitDbParams().addAll(asList( 40 | "-E", "SQL_ASCII", 41 | "--locale=C", 42 | "--lc-collate=C", 43 | "--lc-ctype=C" 44 | )); 45 | PostgresExecutable exec = runtime.prepare(config); 46 | process = exec.start(); 47 | String url = format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s", 48 | config.net().host(), 49 | config.net().port(), 50 | config.storage().dbName(), 51 | config.credentials().username(), 52 | config.credentials().password() 53 | ); 54 | conn = DriverManager.getConnection(url); 55 | } 56 | 57 | Connection getConn() { 58 | return conn; 59 | } 60 | 61 | public void stop() throws Exception { 62 | conn.close(); 63 | process.stop(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestDownloads.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.distribution.BitSize; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.distribution.IVersion; 6 | import de.flapdoodle.embed.process.distribution.Platform; 7 | import de.flapdoodle.embed.process.store.IArtifactStore; 8 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 9 | import org.junit.Test; 10 | import ru.yandex.qatools.embed.postgresql.distribution.Version; 11 | 12 | import java.io.IOException; 13 | 14 | import static java.util.Arrays.asList; 15 | import static org.hamcrest.CoreMatchers.is; 16 | import static org.junit.Assert.assertThat; 17 | 18 | public class TestDownloads { 19 | 20 | /** Version 11 binary downloads are available for OS X and Windows 64 bit only */ 21 | private boolean supported(Distribution distribution) { 22 | if (! distribution.getVersion().asInDownloadPath().startsWith("11.")) { 23 | return true; 24 | } 25 | switch (distribution.getPlatform()) { 26 | case OS_X: 27 | return true; 28 | case Windows: 29 | return distribution.getBitsize() == BitSize.B64; 30 | default: 31 | return false; 32 | } 33 | } 34 | 35 | @Test 36 | public void testDownloads() throws IOException { 37 | IArtifactStore artifactStore = new PostgresArtifactStoreBuilder().defaults(Command.Postgres).build(); 38 | 39 | for (Platform p : asList(Platform.OS_X, Platform.Linux, Platform.Windows)) { 40 | for (BitSize b : BitSize.values()) { 41 | for (IVersion version : Version.Main.values()) { 42 | Distribution distribution = new Distribution(version, p, b); 43 | if (! supported(distribution)) { 44 | continue; 45 | } 46 | assertThat("Distribution: " + distribution + " should be accessible", artifactStore.checkDistribution(distribution), is(true)); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestMultipleInstance.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.Test; 4 | import ru.yandex.qatools.embed.postgresql.distribution.Version; 5 | 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.Statement; 9 | 10 | import static org.hamcrest.core.Is.is; 11 | import static org.hamcrest.core.StringContains.containsString; 12 | import static org.junit.Assert.assertThat; 13 | 14 | public class TestMultipleInstance { 15 | @Test 16 | public void itShouldAllowToRunTwoInstancesWithDifferentVersions() throws Exception { 17 | final EmbeddedPostgres postgres0 = new EmbeddedPostgres(); 18 | start(postgres0); 19 | checkVersion(postgres0, "PostgreSQL 10."); 20 | postgres0.stop(); 21 | 22 | final EmbeddedPostgres postgres1 = new EmbeddedPostgres(Version.Main.V9_6); 23 | start(postgres1); 24 | checkVersion(postgres1, "PostgreSQL 9.6"); 25 | postgres1.stop(); 26 | } 27 | 28 | @Test 29 | public void itShouldAllowToRunTwoInstancesAtSameTime() throws Exception { 30 | final EmbeddedPostgres postgres0 = new EmbeddedPostgres(); 31 | start(postgres0); 32 | 33 | final EmbeddedPostgres postgres1 = new EmbeddedPostgres(); 34 | start(postgres1); 35 | 36 | checkVersion(postgres0, "PostgreSQL 10."); 37 | checkVersion(postgres1, "PostgreSQL 10."); 38 | 39 | postgres0.stop(); 40 | postgres1.stop(); 41 | } 42 | 43 | @Test 44 | public void itShouldAllowToRunTwoInstancesAtSameTimeAndWithDifferentVersions() throws Exception { 45 | final EmbeddedPostgres postgres0 = new EmbeddedPostgres(Version.Main.V9_6); 46 | start(postgres0); 47 | 48 | final EmbeddedPostgres postgres1 = new EmbeddedPostgres(Version.Main.V10); 49 | start(postgres1); 50 | 51 | checkVersion(postgres0, "PostgreSQL 9.6"); 52 | checkVersion(postgres1, "PostgreSQL 10."); 53 | 54 | postgres0.stop(); 55 | postgres1.stop(); 56 | } 57 | 58 | private void start(EmbeddedPostgres postgres) throws Exception { 59 | postgres.start(); 60 | assertThat(postgres.getConnectionUrl().isPresent(), is(true)); 61 | } 62 | 63 | private void checkVersion(EmbeddedPostgres postgres, String expectedVersion) throws Exception { 64 | String jdbcUrl = postgres.getConnectionUrl().get(); 65 | try (final Connection conn = DriverManager.getConnection(jdbcUrl); 66 | final Statement statement = conn.createStatement()) { 67 | assertThat(statement.execute("SELECT version();"), is(true)); 68 | assertThat(statement.getResultSet().next(), is(true)); 69 | assertThat(statement.getResultSet().getString("version"), containsString(expectedVersion)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPostgresCachedDirStarter.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.io.directories.FixedPath; 5 | import de.flapdoodle.embed.process.store.PostgresArtifactStoreBuilder; 6 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 7 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 8 | 9 | import java.io.File; 10 | 11 | import static org.apache.commons.io.FileUtils.getTempDirectory; 12 | 13 | public class TestPostgresCachedDirStarter extends TestPostgresStarter { 14 | 15 | @Override 16 | protected IRuntimeConfig buildRuntimeConfig() { 17 | // turns off the default functionality of unzipping on every run. 18 | final String tmpDir = new File(getTempDirectory(), "pgembed").getPath(); 19 | final Command cmd = Command.Postgres; 20 | final FixedPath cachedDir = new FixedPath(tmpDir); 21 | return new RuntimeConfigBuilder() 22 | .defaults(cmd) 23 | .artifactStore(new PostgresArtifactStoreBuilder() 24 | .defaults(cmd) 25 | .tempDir(cachedDir) 26 | .download(new PostgresDownloadConfigBuilder() 27 | .defaultsForCommand(cmd) 28 | .packageResolver(new PackagePaths(cmd, cachedDir)) 29 | .build())) 30 | .build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPostgresStarter.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 4 | import de.flapdoodle.embed.process.io.progress.LoggingProgressListener; 5 | import de.flapdoodle.embed.process.store.NonCachedPostgresArtifactStoreBuilder; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 10 | import ru.yandex.qatools.embed.postgresql.config.PostgresDownloadConfigBuilder; 11 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 12 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 13 | 14 | import java.sql.Connection; 15 | import java.sql.DriverManager; 16 | import java.sql.Statement; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.logging.Handler; 20 | import java.util.logging.Level; 21 | import java.util.logging.LogRecord; 22 | import java.util.logging.Logger; 23 | 24 | import static java.lang.String.format; 25 | import static java.util.Arrays.asList; 26 | import static org.hamcrest.CoreMatchers.not; 27 | import static org.hamcrest.CoreMatchers.nullValue; 28 | import static org.hamcrest.core.Is.is; 29 | import static org.junit.Assert.assertThat; 30 | import static ru.yandex.qatools.embed.postgresql.distribution.Version.Main.PRODUCTION; 31 | import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort; 32 | 33 | public class TestPostgresStarter { 34 | 35 | private static final Logger logger = Logger.getLogger(TestPostgresStarter.class.getName()); 36 | private final TestHandler testHandler = new TestHandler(); 37 | protected PostgresProcess process; 38 | private Connection conn; 39 | 40 | @Before 41 | public void setUp() throws Exception { 42 | logger.setLevel(Level.INFO); 43 | logger.addHandler(testHandler); 44 | // turns off the default functionality of unzipping on every run. 45 | IRuntimeConfig runtimeConfig = buildRuntimeConfig(); 46 | 47 | PostgresStarter runtime = PostgresStarter.getInstance(runtimeConfig); 48 | final PostgresConfig config = new PostgresConfig(PRODUCTION, new AbstractPostgresConfig.Net( 49 | "localhost", findFreePort() 50 | ), new AbstractPostgresConfig.Storage("test"), new AbstractPostgresConfig.Timeout(), 51 | new AbstractPostgresConfig.Credentials("user", "password")); 52 | config.getAdditionalInitDbParams().addAll(asList( 53 | "-E", "SQL_ASCII", 54 | "--locale=C", 55 | "--lc-collate=C", 56 | "--lc-ctype=C" 57 | )); 58 | 59 | PostgresExecutable exec = runtime.prepare(config); 60 | process = exec.start(); 61 | String url = format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s", 62 | config.net().host(), 63 | config.net().port(), 64 | config.storage().dbName(), 65 | config.credentials().username(), 66 | config.credentials().password() 67 | ); 68 | conn = DriverManager.getConnection(url); 69 | } 70 | 71 | protected IRuntimeConfig buildRuntimeConfig() { 72 | return new RuntimeConfigBuilder() 73 | .defaults(Command.Postgres) 74 | .artifactStore(new NonCachedPostgresArtifactStoreBuilder() 75 | .defaults(Command.Postgres) 76 | .download(new PostgresDownloadConfigBuilder() 77 | .defaultsForCommand(Command.Postgres) 78 | .progressListener(new LoggingProgressListener(logger, Level.ALL)) 79 | .build())) 80 | 81 | .build(); 82 | } 83 | 84 | @After 85 | public void tearDown() throws Exception { 86 | conn.close(); 87 | process.stop(); 88 | } 89 | 90 | @Test 91 | public void testPostgres() throws Exception { 92 | assertThat(conn, not(nullValue())); 93 | assertThat(conn.createStatement().execute("CREATE TABLE films (code char(5));"), is(false)); 94 | assertThat(conn.createStatement().execute("INSERT INTO films VALUES ('movie');"), is(false)); 95 | final Statement statement = conn.createStatement(); 96 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 97 | assertThat(statement.getResultSet().next(), is(true)); 98 | assertThat(statement.getResultSet().getString("code"), is("movie")); 99 | 100 | // verify no logs 101 | assertThat(testHandler.RECORDS.size(), is(0)); 102 | } 103 | 104 | private static class TestHandler extends Handler { 105 | 106 | public final List RECORDS = new ArrayList<>(); 107 | 108 | @Override 109 | public void publish(LogRecord record) { 110 | RECORDS.add(record); 111 | } 112 | 113 | @Override 114 | public void flush() { 115 | 116 | } 117 | 118 | @Override 119 | public void close() throws SecurityException { 120 | 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPostgresStoredData.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.BeforeClass; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Test; 6 | import org.junit.runners.MethodSorters; 7 | import ru.yandex.qatools.embed.postgresql.distribution.Version; 8 | 9 | import java.io.File; 10 | import java.nio.file.Files; 11 | import java.sql.Connection; 12 | import java.sql.DriverManager; 13 | import java.sql.SQLException; 14 | import java.sql.Statement; 15 | 16 | import static org.hamcrest.CoreMatchers.not; 17 | import static org.hamcrest.CoreMatchers.nullValue; 18 | import static org.hamcrest.core.Is.is; 19 | import static org.junit.Assert.assertThat; 20 | 21 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 22 | public class TestPostgresStoredData { 23 | static File baseDir = null; 24 | 25 | @BeforeClass 26 | public static void setUpClass() throws Exception { 27 | baseDir = Files.createTempDirectory("data").toFile(); 28 | baseDir.deleteOnExit(); 29 | } 30 | 31 | @Test 32 | public void testIndependent() throws Exception { 33 | final String dataDir = Files.createTempDirectory("data").toFile().getAbsolutePath(); 34 | 35 | { 36 | final EmbeddedPostgres embeddedPostgres = new EmbeddedPostgres(dataDir); 37 | final String jdbcUrl = embeddedPostgres.start(); 38 | try (Connection connection = DriverManager.getConnection(jdbcUrl)) { 39 | initSchema(connection); 40 | checkRecords(connection); 41 | } finally { 42 | embeddedPostgres.stop(); 43 | } 44 | } 45 | { 46 | final EmbeddedPostgres embeddedPostgres = new EmbeddedPostgres(dataDir); 47 | final String jdbcUrl = embeddedPostgres.start(); 48 | try (Connection connection = DriverManager.getConnection(jdbcUrl)) { 49 | checkRecords(connection); 50 | } finally { 51 | embeddedPostgres.stop(); 52 | } 53 | } 54 | } 55 | 56 | @Test 57 | public void testStep0() throws Exception { 58 | final EmbeddedPostgres embeddedPostgres = new EmbeddedPostgres(baseDir.getAbsolutePath()); 59 | final String jdbcUrl = embeddedPostgres.start(); 60 | try (Connection connection = DriverManager.getConnection(jdbcUrl)) { 61 | initSchema(connection); 62 | checkRecords(connection); 63 | } finally { 64 | embeddedPostgres.stop(); 65 | } 66 | } 67 | 68 | @Test 69 | public void testStep1() throws Exception { 70 | final EmbeddedPostgres embeddedPostgres = new EmbeddedPostgres(Version.Main.PRODUCTION, baseDir.getAbsolutePath()); 71 | final String jdbcUrl = embeddedPostgres.start(); 72 | try (Connection connection = DriverManager.getConnection(jdbcUrl)) { 73 | checkRecords(connection); 74 | } finally { 75 | embeddedPostgres.stop(); 76 | } 77 | } 78 | 79 | private void initSchema(Connection conn) throws SQLException { 80 | assertThat(conn, not(nullValue())); 81 | assertThat(conn.createStatement().execute("CREATE TABLE films (code CHAR(5));"), is(false)); 82 | assertThat(conn.createStatement().execute("INSERT INTO films VALUES ('movie');"), is(false)); 83 | } 84 | 85 | private void checkRecords(Connection conn) throws SQLException { 86 | final Statement statement = conn.createStatement(); 87 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 88 | assertThat(statement.getResultSet().next(), is(true)); 89 | assertThat(statement.getResultSet().getString("code"), is("movie")); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPostgresWithPgCtl.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | import ru.yandex.qatools.embed.postgresql.config.AbstractPostgresConfig; 8 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 9 | import ru.yandex.qatools.embed.postgresql.config.RuntimeConfigBuilder; 10 | import ru.yandex.qatools.embed.postgresql.distribution.Version; 11 | 12 | import java.sql.Connection; 13 | import java.sql.DriverManager; 14 | import java.sql.Statement; 15 | 16 | import static java.lang.String.format; 17 | import static java.lang.Thread.sleep; 18 | import static java.util.Arrays.asList; 19 | import static org.hamcrest.CoreMatchers.not; 20 | import static org.hamcrest.CoreMatchers.nullValue; 21 | import static org.hamcrest.core.Is.is; 22 | import static org.junit.Assert.assertThat; 23 | import static ru.yandex.qatools.embed.postgresql.util.SocketUtil.findFreePort; 24 | 25 | public class TestPostgresWithPgCtl { 26 | 27 | private PostgresProcess process; 28 | private Connection conn; 29 | 30 | @Before 31 | public void setUp() throws Exception { 32 | PostgresStarter runtime = PostgresStarter.getInstance( 33 | new RuntimeConfigBuilder().defaults(Command.PgCtl).build()); 34 | final PostgresConfig config = new PostgresConfig(Version.Main.PRODUCTION, new AbstractPostgresConfig.Net( 35 | "localhost", findFreePort() 36 | ), new AbstractPostgresConfig.Storage("test"), new AbstractPostgresConfig.Timeout(), 37 | new AbstractPostgresConfig.Credentials("user", "password"), Command.PgCtl); 38 | config.getAdditionalInitDbParams().addAll(asList( 39 | "-E", "SQL_ASCII", 40 | "--locale=C", 41 | "--lc-collate=C", 42 | "--lc-ctype=C" 43 | )); 44 | PostgresExecutable exec = runtime.prepare(config); 45 | process = exec.start(); 46 | sleep(2000); 47 | String url = format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s", 48 | config.net().host(), 49 | config.net().port(), 50 | config.storage().dbName(), 51 | config.credentials().username(), 52 | config.credentials().password() 53 | ); 54 | conn = DriverManager.getConnection(url); 55 | } 56 | 57 | @After 58 | public void tearDown() throws Exception { 59 | conn.close(); 60 | process.stop(); 61 | } 62 | 63 | @Test 64 | public void testPostgres() throws Exception { 65 | assertThat(conn, not(nullValue())); 66 | assertThat(conn.createStatement().execute("CREATE TABLE films (code char(5));"), is(false)); 67 | assertThat(conn.createStatement().execute("INSERT INTO films VALUES ('movie');"), is(false)); 68 | final Statement statement = conn.createStatement(); 69 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 70 | assertThat(statement.getResultSet().next(), is(true)); 71 | assertThat(statement.getResultSet().getString("code"), is("movie")); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPostgresWithinSpringContext.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | import java.sql.Statement; 10 | 11 | import static org.hamcrest.CoreMatchers.not; 12 | import static org.hamcrest.CoreMatchers.nullValue; 13 | import static org.hamcrest.core.Is.is; 14 | import static org.junit.Assert.assertThat; 15 | 16 | /** 17 | * @author Ilya Sadykov 18 | */ 19 | @RunWith(SpringJUnit4ClassRunner.class) 20 | @ContextConfiguration(locations = {"classpath*:test-spring-context.xml"}) 21 | public class TestPostgresWithinSpringContext { 22 | 23 | @Autowired 24 | PostgresqlService service; 25 | 26 | @Test 27 | public void testPostgresWithinSpring() throws Exception { 28 | assertThat(service.getConn(), not(nullValue())); 29 | assertThat(service.getConn().createStatement().execute("CREATE TABLE films (code char(5));"), is(false)); 30 | assertThat(service.getConn().createStatement().execute("INSERT INTO films VALUES ('movie');"), is(false)); 31 | final Statement statement = service.getConn().createStatement(); 32 | assertThat(statement.execute("SELECT * FROM films;"), is(true)); 33 | assertThat(statement.getResultSet().next(), is(true)); 34 | assertThat(statement.getResultSet().getString("code"), is("movie")); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPsqlExport.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | 7 | import static org.hamcrest.CoreMatchers.not; 8 | import static org.hamcrest.CoreMatchers.nullValue; 9 | import static org.junit.Assert.assertThat; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | public class TestPsqlExport extends AbstractPsqlTest { 13 | 14 | @Test 15 | public void testPsqlExport() throws Exception { 16 | process.importFromFile(new File("src/test/resources/test.backup")); 17 | assertThat(conn, not(nullValue())); 18 | 19 | File fullExportDump = File.createTempFile("full_", ".dmp"); 20 | try { 21 | process.exportToFile(fullExportDump); 22 | assertTrue(fullExportDump.length() > 0); 23 | } finally { 24 | assertTrue(fullExportDump.delete()); 25 | } 26 | 27 | File schemeDump = File.createTempFile("scheme_", ".dmp"); 28 | try { 29 | process.exportSchemeToFile(schemeDump); 30 | assertTrue(schemeDump.length() > 0); 31 | } finally { 32 | assertTrue(schemeDump.delete()); 33 | } 34 | 35 | File dataExportDump = File.createTempFile("data_", ".dmp"); 36 | try { 37 | process.exportToFile(dataExportDump); 38 | assertTrue(dataExportDump.length() > 0); 39 | } finally { 40 | assertTrue(dataExportDump.delete()); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPsqlImport.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | import static org.hamcrest.CoreMatchers.not; 11 | import static org.hamcrest.CoreMatchers.nullValue; 12 | import static org.hamcrest.core.Is.is; 13 | import static org.junit.Assert.assertEquals; 14 | import static org.junit.Assert.assertThat; 15 | 16 | public class TestPsqlImport extends AbstractPsqlTest { 17 | 18 | @Test 19 | public void testPsqlImport() throws Exception { 20 | process.importFromFile(new File("src/test/resources/test.backup")); 21 | assertThat(conn, not(nullValue())); 22 | 23 | Statement statement = conn.createStatement(); 24 | 25 | String expected; 26 | try (ResultSet res = statement.executeQuery("SELECT * FROM table1;")) { 27 | assertThat(res, not(nullValue())); 28 | String tableString = readTable(res); 29 | 30 | assertThat("Missing content in relation 'table1' in dump file!", tableString, not(nullValue())); 31 | 32 | expected = "test\t1\ta\n" + 33 | "test\t2\tb\n" + 34 | "test\t3\tc\n" + 35 | "test\t4\td\n"; 36 | assertEquals(expected, tableString); 37 | } 38 | 39 | assertThat(conn.createStatement().execute("INSERT INTO table1 VALUES ('test',5,'e');"), is(false)); 40 | assertThat(conn.createStatement().execute("INSERT INTO table1 VALUES ('test',6,'f');"), is(false)); 41 | 42 | try (ResultSet res = statement.executeQuery("SELECT * FROM table1;")) { 43 | assertThat(res, not(nullValue())); 44 | String tableString = readTable(res); 45 | 46 | assertThat("Missing content in relation 'table1' in dump file!", tableString, not(nullValue())); 47 | 48 | expected += "test\t5\te\n" + "test\t6\tf\n"; 49 | assertEquals(expected, tableString); 50 | } 51 | 52 | } 53 | 54 | private String readTable(ResultSet res) throws SQLException { 55 | StringBuilder sb = null; 56 | while (res.next()) { 57 | if (null == sb) 58 | sb = new StringBuilder(); 59 | sb.append(res.getString("col1")); 60 | sb.append("\t"); 61 | sb.append(res.getInt("col2")); 62 | sb.append("\t"); 63 | sb.append(res.getString("col3")); 64 | sb.append("\n"); 65 | } 66 | return null != sb ? sb.toString() : null; 67 | } 68 | } -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestPsqlRestore.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | import static org.hamcrest.CoreMatchers.not; 11 | import static org.hamcrest.CoreMatchers.nullValue; 12 | import static org.hamcrest.core.Is.is; 13 | import static org.junit.Assert.assertEquals; 14 | import static org.junit.Assert.assertThat; 15 | 16 | /** 17 | * This test does the same than {@link TestPsqlImport} but the initial dump is restored using pg_restore. The dump file 18 | * is a binary dump {@link /test.binary_dump} built as a copy of text dump {@link /test.backup}. 19 | * 20 | * Except initial "import" which is replaced by "restore", the test is identical 21 | * 22 | * @author Arnaud Thimel (Code Lutin) 23 | */ 24 | public class TestPsqlRestore extends AbstractPsqlTest{ 25 | 26 | @Test 27 | public void testPsqlRestore() throws Exception { 28 | process.restoreFromFile(new File("src/test/resources/test.binary_dump")); 29 | assertThat(conn, not(nullValue())); 30 | 31 | Statement statement = conn.createStatement(); 32 | 33 | String expected; 34 | try (ResultSet res = statement.executeQuery("SELECT * FROM table1;")) { 35 | assertThat(res, not(nullValue())); 36 | String tableString = readTable(res); 37 | 38 | assertThat("Missing content in relation 'table1' in dump file!", tableString, not(nullValue())); 39 | 40 | expected = "test\t1\ta\n" + 41 | "test\t2\tb\n" + 42 | "test\t3\tc\n" + 43 | "test\t4\td\n"; 44 | assertEquals(expected, tableString); 45 | } 46 | 47 | assertThat(conn.createStatement().execute("INSERT INTO table1 VALUES ('test',5,'e');"), is(false)); 48 | assertThat(conn.createStatement().execute("INSERT INTO table1 VALUES ('test',6,'f');"), is(false)); 49 | 50 | try (ResultSet res = statement.executeQuery("SELECT * FROM table1;")) { 51 | assertThat(res, not(nullValue())); 52 | String tableString = readTable(res); 53 | 54 | assertThat("Missing content in relation 'table1' in dump file!", tableString, not(nullValue())); 55 | 56 | expected += "test\t5\te\n" + "test\t6\tf\n"; 57 | assertEquals(expected, tableString); 58 | } 59 | 60 | } 61 | 62 | private String readTable(ResultSet res) throws SQLException { 63 | StringBuilder sb = null; 64 | while (res.next()) { 65 | if (null == sb) 66 | sb = new StringBuilder(); 67 | sb.append(res.getString("col1")); 68 | sb.append("\t"); 69 | sb.append(res.getInt("col2")); 70 | sb.append("\t"); 71 | sb.append(res.getString("col3")); 72 | sb.append("\n"); 73 | } 74 | return null != sb ? sb.toString() : null; 75 | } 76 | } -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/TestStop.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; 6 | import ru.yandex.qatools.embed.postgresql.distribution.Version; 7 | 8 | /** 9 | * @author Ilya Sadykov 10 | */ 11 | public class TestStop { 12 | private static final Logger LOG = LoggerFactory.getLogger(TestStop.class); 13 | 14 | public static void main(String[] args) throws Exception { 15 | LOG.info("Starting postgres"); 16 | PostgresProcess process = PostgresStarter.getDefaultInstance().prepare(new PostgresConfig(Version.Main.PRODUCTION, 17 | "test-db")).start(); 18 | 19 | LOG.info("Stopping postgres"); 20 | process.stop(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/ru/yandex/qatools/embed/postgresql/config/StorageTest.java: -------------------------------------------------------------------------------- 1 | package ru.yandex.qatools.embed.postgresql.config; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import ru.yandex.qatools.embed.postgresql.EmbeddedPostgres; 6 | 7 | import static org.hamcrest.core.IsNot.not; 8 | import static org.junit.Assert.assertThat; 9 | 10 | public class StorageTest { 11 | 12 | @Test 13 | public void itShouldAllowToMakeTwoStorageWithOneDatabaseName() throws Exception { 14 | final AbstractPostgresConfig.Storage storage0 = new AbstractPostgresConfig.Storage(EmbeddedPostgres.DEFAULT_DB_NAME); 15 | Assert.assertTrue(storage0.dbDir().exists()); 16 | 17 | final AbstractPostgresConfig.Storage storage1 = new AbstractPostgresConfig.Storage(EmbeddedPostgres.DEFAULT_DB_NAME); 18 | Assert.assertTrue(storage1.dbDir().exists()); 19 | 20 | assertThat(storage0.dbDir().getPath(), not(storage1.dbDir().getPath())); 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/resources/README.md: -------------------------------------------------------------------------------- 1 | ### How to build test.binary_dump ? 2 | 3 | It has been build as a copy of test.backup using the following commands on a standalone PostgreSQL : 4 | 5 | createdb -E UTF-8 -O user postgresql-embedded 6 | psql postgresql-embedded < test.backup 7 | pg_dump -Fc postgresql-embedded > test.binary_dump 8 | 9 | It may be completely built by the postgresql-embedded tests (using pg_dump). 10 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | log4j.rootLogger=DEBUG, stdout 3 | 4 | # reduce logging for postgresql-embedded 5 | log4j.logger.ru.yandex.qatools.embed=TRACE 6 | log4j.logger.de.flapdoodle.embed=TRACE 7 | 8 | # Direct log messages to stdout 9 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 10 | log4j.appender.stdout.Target=System.out 11 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 12 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 13 | log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer 14 | -------------------------------------------------------------------------------- /src/test/resources/test-spring-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/test/resources/test.backup: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 9.1.4 6 | -- Dumped by pg_dump version 9.2.0 7 | -- Started on 2015-11-13 15:59:02 CET 8 | 9 | SET statement_timeout = 0; 10 | SET client_encoding = 'UTF8'; 11 | SET standard_conforming_strings = on; 12 | SET check_function_bodies = false; 13 | SET client_min_messages = warning; 14 | 15 | -- 16 | -- TOC entry 162 (class 3079 OID 11907) 17 | -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 18 | -- 19 | 20 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 21 | 22 | 23 | -- 24 | -- TOC entry 2120 (class 0 OID 0) 25 | -- Dependencies: 162 26 | -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 27 | -- 28 | 29 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 30 | 31 | 32 | SET search_path = public, pg_catalog; 33 | 34 | SET default_tablespace = ''; 35 | 36 | SET default_with_oids = false; 37 | 38 | -- 39 | -- TOC entry 161 (class 1259 OID 1302590) 40 | -- Name: table1; Type: TABLE; Schema: public; Owner: user; Tablespace: 41 | -- 42 | 43 | CREATE TABLE table1 ( 44 | col1 character varying(255), 45 | col2 integer, 46 | col3 character(1) 47 | ); 48 | 49 | 50 | ALTER TABLE public.table1 OWNER TO user; 51 | 52 | -- 53 | -- TOC entry 2112 (class 0 OID 1302590) 54 | -- Dependencies: 161 55 | -- Data for Name: table1; Type: TABLE DATA; Schema: public; Owner: user 56 | -- 57 | 58 | COPY table1 (col1, col2, col3) FROM stdin; 59 | test 1 a 60 | test 2 b 61 | test 3 c 62 | test 4 d 63 | \. 64 | 65 | 66 | -- 67 | -- TOC entry 2119 (class 0 OID 0) 68 | -- Dependencies: 5 69 | -- Name: public; Type: ACL; Schema: -; Owner: user 70 | -- 71 | 72 | REVOKE ALL ON SCHEMA public FROM PUBLIC; 73 | REVOKE ALL ON SCHEMA public FROM user; 74 | GRANT ALL ON SCHEMA public TO user; 75 | GRANT ALL ON SCHEMA public TO PUBLIC; 76 | 77 | 78 | -- Completed on 2015-11-13 15:59:02 CET 79 | 80 | -- 81 | -- PostgreSQL database dump complete 82 | -- 83 | 84 | -------------------------------------------------------------------------------- /src/test/resources/test.binary_dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex-qatools/postgresql-embedded/7ed616e251dda4ca1f6b45217a9005f5128d7fd1/src/test/resources/test.binary_dump --------------------------------------------------------------------------------