├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── appveyor.yml ├── pom.xml ├── release.sh ├── wix-embedded-mysql-download-and-extract ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── wix │ │ └── mysql │ │ └── MysqlDownloadAndExtract.java │ └── test │ └── java │ └── com │ └── wix │ └── mysql │ └── MysqlDownloadAndExtractIT.scala └── wix-embedded-mysql ├── pom.xml └── src ├── main └── java │ └── com │ └── wix │ └── mysql │ ├── EmbeddedMysql.java │ ├── MysqlClient.java │ ├── MysqldExecutable.java │ ├── MysqldProcess.java │ ├── MysqldStarter.java │ ├── PackagePaths.java │ ├── ScriptResolver.java │ ├── Sources.java │ ├── SqlScriptSource.java │ ├── config │ ├── AdditionalConfig.java │ ├── Charset.java │ ├── DownloadConfig.java │ ├── DownloadConfigBuilder.java │ ├── MysqldConfig.java │ ├── ProxyFactory.java │ ├── RuntimeConfigBuilder.java │ ├── SchemaConfig.java │ ├── directories │ │ └── TargetGeneratedFixedPath.java │ └── extract │ │ ├── NoopNaming.java │ │ └── PathPrefixingNaming.java │ ├── distribution │ ├── FileSet.java │ ├── Service.java │ ├── Setup.java │ ├── Version.java │ ├── fileset │ │ ├── FileSetEmitter.java │ │ ├── Nix.java │ │ ├── Nix55FileSetEmitter.java │ │ ├── Nix56FileSetEmitter.java │ │ ├── Nix57FileSetEmitter.java │ │ ├── Nix57_18_AndUpFileSetEmitter.java │ │ ├── Nix8FileSetEmitter.java │ │ ├── OSX8FileSetEmitter.java │ │ ├── Win56FileSetEmitter.java │ │ ├── Win57FileSetEmitter.java │ │ ├── Win57_18_UpFileSetEmitter.java │ │ └── Win8FileSetEmitter.java │ ├── service │ │ ├── BaseCommandEmitter.java │ │ ├── CommandEmitter.java │ │ ├── Mysql57CommandEmitter.java │ │ ├── Mysql8CommandEmitter.java │ │ ├── Pre57CommandEmitter.java │ │ ├── ServiceCommandBuilder.java │ │ └── UserProvidedArgumentsEmitter.java │ └── setup │ │ ├── FilePermissionsInitializer.java │ │ ├── Initializer.java │ │ ├── Mysql57Initializer.java │ │ ├── Mysql8Initializer.java │ │ ├── NixBefore57Initializer.java │ │ └── ProcessRunner.java │ ├── exceptions │ ├── CommandFailedException.java │ ├── MissingDependencyException.java │ └── UnsupportedPlatformException.java │ ├── io │ ├── ConsoleProgressListener.java │ ├── NotifyingStreamProcessor.java │ ├── ProgressStepper.java │ └── TimingOutProcessExecutor.java │ ├── store │ ├── SafeExtractedArtifactStore.java │ └── SafeExtractedArtifactStoreBuilder.java │ └── utils │ └── Utils.java └── test ├── lib └── jar │ └── external-jar-with-schemas │ ├── 1.0.1 │ ├── _maven.repositories │ ├── _remote.repositories │ ├── external-jar-with-schemas-1.0.1.jar │ └── external-jar-with-schemas-1.0.1.pom │ └── maven-metadata-local.xml ├── resources ├── data │ ├── arbitrary_script.sql │ └── arbitrary_script2.sql ├── db │ ├── 001_init.sql │ ├── 002_update1.sql │ ├── 003_update2.sql │ └── 004_update3.sql └── logback.xml └── scala └── com └── wix └── mysql ├── EmbeddedMysqlTest.scala ├── ExtendedCharsetTest.scala ├── JavaUsageExamplesTest.java ├── MysqlTest.scala ├── ParallelEmbeddedMysqlTest.scala ├── SchemaConfigTest.scala ├── ScriptResolverTest.scala ├── SupportedVersionsTest.scala ├── UtilsTest.scala ├── config ├── DownloadConfigTest.scala └── MysqldConfigTest.scala ├── distribution ├── MacOsSierraTest.scala ├── VersionTest.scala └── service │ └── ServiceCommandBuilderTest.scala ├── io ├── ProgressStepperTest.scala ├── ResultMatchingListenerTest.scala └── TimingOutProcessExecutorTest.scala └── support ├── HttpProxyServerSupport.scala ├── InstanceMatchers.scala ├── IntegrationTest.scala ├── MysqlCacheServingHttpServer.scala └── TestResourceSupport.scala /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | target/ 5 | *.iml 6 | .idea 7 | dependency-reduced-pom.xml 8 | 9 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: $CMD 3 | branches: 4 | only: 5 | - master 6 | 7 | env: 8 | global: 9 | - CMD='mvn -q -B verify -Dsurefire.rerunFailingTestsCount=2' 10 | 11 | before_install: 12 | # os x - workaround for JAVA_HOME not set and mvn missing 13 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then export JAVA_HOME=$(/usr/libexec/java_home); fi 14 | # linux - installing libaio1 15 | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update -qq && sudo apt-get install -y libaio1; fi 16 | # to work-around jvm buffer overflow 17 | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo hostname "$(hostname | cut -c1-63)"; fi 18 | ## Get latest install-jdk.sh script 19 | # - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh 20 | matrix: 21 | fast_finish: true 22 | include: 23 | - os: linux 24 | dist: trusty 25 | sudo: required 26 | jdk: oraclejdk8 27 | - os: linux 28 | dist: trusty 29 | sudo: required 30 | jdk: oraclejdk9 31 | - os: linux 32 | dist: trusty 33 | sudo: required 34 | jdk: oraclejdk11 35 | - os: osx 36 | language: java 37 | 38 | notifications: 39 | email: 40 | - noamal@gmail.com 41 | - vilius@wix.com -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Wix.com Ltd. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Wix.com Ltd. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wix Embedded MySql [![Build Status (Travis: Linux/OSX)](https://img.shields.io/travis/wix/wix-embedded-mysql/master.svg?label=linux%2FOSX%20build)](https://travis-ci.org/wix/wix-embedded-mysql) [![Build Status (AppVeyor: Windows)](https://img.shields.io/appveyor/ci/viliusl/wix-embedded-mysql/master.svg?label=windows%20build)](https://ci.appveyor.com/project/viliusl/wix-embedded-mysql) [![Maven Central](https://img.shields.io/maven-central/v/com.wix/wix-embedded-mysql.svg)](http://mvnrepository.com/artifact/com.wix/wix-embedded-mysql) 2 | 3 | **[DEPRECATED]** - mostly due to substantial changes in packaging between version changes and emergence of new and better ways to run embedded mysql. Please check-out [Testcontainers](https://www.testcontainers.org/) as a better alternative. 4 | 5 | ## Why? 6 | - Your tests can run on production-like environment: match version, encoding, timezone, database/schema/user settings; 7 | - Its easy, much easier than installing right version by hand; 8 | - You can use different versions/configuration per project without any local set-up; 9 | - Supports multiple platforms: Windows, Linux and OSX; 10 | - Provides constantly updated multiple versions of MySql - 5.5, 5.6, 5.7, 8.0; 11 | - Testing matrix for all supported OSes (x86/x64) and versions (5.5, 5.6, 5.7, 8.0). 12 | 13 | ## Maven 14 | Add dependency to your pom.xml: 15 | 16 | ```xml 17 | 18 | com.wix 19 | wix-embedded-mysql 20 | x.y.z 21 | test 22 | 23 | ``` 24 | 25 | ## Usage 26 | - [Basic usage example](#basic-usage-example) 27 | - [Customizing mysqld settings](#customizing-mysqld-settings) 28 | - [Customizing download settings](#customizing-download-settings) 29 | - [Multiple schemas/databases](multiple-schemasdatabases) 30 | - [Resetting schemas between tests](#resetting-schemas-between-tests) 31 | - [Using in a hermetic environment](#using-in-a-hermetic-environment) 32 | 33 | ### Basic usage example 34 | 35 | You can start an embedded mysql with defaults and a single schema: 36 | 37 | ```java 38 | import com.wix.mysql.EmbeddedMysql; 39 | 40 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 41 | import static com.wix.mysql.ScriptResolver.classPathScript; 42 | import static com.wix.mysql.distribution.Version.v5_7_latest; 43 | 44 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_7_latest) 45 | .addSchema("aschema", classPathScript("db/001_init.sql")) 46 | .start(); 47 | 48 | //do work 49 | 50 | mysqld.stop(); //optional, as there is a shutdown hook 51 | ``` 52 | 53 | ### Customizing mysqld settings 54 | 55 | If you need more control in configuring embeded mysql instance, you can use [MysqldConfig](wix-embedded-mysql/src/main/java/com/wix/mysql/config/MysqldConfig.java) builder: 56 | 57 | ```java 58 | import com.wix.mysql.config.MysqldConfig; 59 | import com.wix.mysql.EmbeddedMysql; 60 | import static com.wix.mysql.ScriptResolver; 61 | 62 | import java.util.concurrent.TimeUnit; 63 | 64 | import static com.wix.mysql.config.MysqldConfig.aMysqldConfig; 65 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 66 | import static com.wix.mysql.distribution.Version.v5_6_23; 67 | import static com.wix.mysql.config.Charset.UTF8; 68 | 69 | MysqldConfig config = aMysqldConfig(v5_6_23) 70 | .withCharset(UTF8) 71 | .withPort(2215) 72 | .withUser("differentUser", "anotherPassword") 73 | .withTimeZone("Europe/Vilnius") 74 | .withTimeout(2, TimeUnit.MINUTES) 75 | .withServerVariable("max_connect_errors", 666) 76 | .build(); 77 | 78 | EmbeddedMysql mysqld = anEmbeddedMysql(config) 79 | .addSchema("aschema", ScriptResolver.classPathScript("db/001_init.sql")) 80 | .addSchema("aschema2", ScriptResolver.classPathScripts("db/*.sql")) 81 | .start(); 82 | 83 | //do work 84 | 85 | mysqld.stop(); //optional, as there is a shutdown hook 86 | ``` 87 | 88 | ### Customizing download settings 89 | 90 | In addition you can control additional settings of embedded mysql by providing configs that have base type of [AdditionalConfig](wix-embedded-mysql/src/main/java/com/wix/mysql/config/AdditionalConfig.java) by providing those to `anEmbeddedMysql` builder: 91 | 92 | ```java 93 | import com.wix.mysql.EmbeddedMysql; 94 | 95 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 96 | import static com.wix.mysql.ScriptResolver.classPathScript; 97 | import static com.wix.mysql.distribution.Version.v5_7_latest; 98 | import static com.wix.mysql.config.DownloadConfig.aDownloadConfig; 99 | import static com.wix.mysql.config.ProxyFactory.aHttpProxy; 100 | 101 | DownloadConfig downloadConfig = aDownloadConfig() 102 | .withProxy(aHttpProxy("remote.host", 8080)) 103 | .withCacheDir(System.getProperty("java.io.tmpdir")) 104 | .build(); 105 | 106 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_7_latest, downloadConfig) 107 | .addSchema("aschema", classPathScript("db/001_init.sql")) 108 | .start(); 109 | 110 | //do work 111 | 112 | mysqld.stop(); //optional, as there is a shutdown hook 113 | ``` 114 | 115 | ### Multiple schemas/databases 116 | 117 | EmbeddedMysql supports multiple schemas and additional configuration options provided via [SchemaConfig](wix-embedded-mysql/src/main/java/com/wix/mysql/config/SchemaConfig.java) builder: 118 | 119 | ```java 120 | import com.wix.mysql.EmbeddedMysql; 121 | import com.wix.mysql.config.SchemaConfig; 122 | 123 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 124 | import static com.wix.mysql.ScriptResolver.classPathScript; 125 | import static com.wix.mysql.ScriptResolver.classPathScripts; 126 | import static com.wix.mysql.config.SchemaConfig.aSchemaConfig; 127 | import static com.wix.mysql.config.Charset.LATIN1; 128 | import static com.wix.mysql.distribution.Version.v5_6_latest; 129 | 130 | SchemaConfig schema = aSchemaConfig("aSchema") 131 | .withScripts(classPathScript("db/001_init.sql")) 132 | .withCharset(LATIN1) 133 | .build(); 134 | 135 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 136 | .addSchema(schema) 137 | .addSchema("aschema2", classPathScripts("db/*.sql")) 138 | .start(); 139 | 140 | //do work 141 | 142 | mysqld.stop(); //optional, as there is a shutdown hook 143 | ``` 144 | 145 | ### Resetting schemas between tests 146 | 147 | It is intended to be started once per test-suite, but you can reset schema in between tests which recreates database and applies provided migrations: 148 | 149 | ```java 150 | import com.wix.mysql.EmbeddedMysql; 151 | 152 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 153 | import static com.wix.mysql.ScriptResolver.classPathScript; 154 | import static com.wix.mysql.distribution.Version.v5_6_latest; 155 | 156 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 157 | .addSchema("aschema", classPathScript("db/001_init.sql")) 158 | .start(); 159 | 160 | //do work 161 | 162 | mysqld.reloadSchema("aschema", classPathScript("db/001_init.sql")); 163 | 164 | //continue on doing work 165 | 166 | mysqld.stop(); //optional, as there is a shutdown hook 167 | ``` 168 | 169 | Source for examples can be found [here](https://github.com/wix/wix-embedded-mysql/blob/master/wix-embedded-mysql/src/test/scala/com/wix/mysql/JavaUsageExamplesTest.java) 170 | 171 | ### Using in a hermetic environment 172 | 173 | Some build tools strongly encourages you to have tests which are isolated from the internet. 174 | To support such a use-case you can use the `wix-embedded-mysql-download-and-extract` utility. 175 | It produces a runnable jar (`wix-embedded-mysql-download-and-extract-[[VERSION]]-jar-with-dependencies.jar`) which you can call with `java -jar wix-embedded-mysql-download-and-extract-[[VERSION]]-jar-with-dependencies.jar $downloadDir $majorVersion $minorVersion` and it will download and extract the needed installer for you. 176 | Additionally you should pass the download directory to your test so that it can configure your `DownloadConfig#withCacheDir` to use that directory instead of downloading it from the internet. 177 | 178 | # Dependencies 179 | Build on top of [de.flapdoodle.embed.process](https://github.com/flapdoodle-oss/de.flapdoodle.embed.process) 180 | 181 | # How it works 182 | - After detecting current platform and requested version, Wix Embedded MySql will download the correct version from [dev.mysql.com](https://dev.mysql.com/downloads/) and extract needed files to local folder. Note that this is a **one-time** action, where subsequent invocations use pre-downloaded/pre-extracted cached package. 183 | - Upon execution needed files are being copied into **target** folder, database created, service started and post-configuration (user, schema, etc.) performed. 184 | - On jvm shutdown mysqld process is stopped and temporary files cleaned-up. 185 | 186 | # Tested on 187 | - latest OSX; 188 | - ubuntu precise 32/64; 189 | - windows 2012; 190 | 191 | # Known issues 192 | - starting version 5.7.18, Microsoft Visual C++ 2013 Redistributable Package needs to be pre-installed on windows. 193 | - starting version 5.5.10 `libaio1.so` needs to be pre-installed on linux, but that is not the case for some linux distributions. Proper error is emitted if it's missing and you have to install it manually (ex. ubuntu): 194 | 195 | ```bash 196 | sudo apt-get install libaio1 197 | ``` 198 | 199 | - if tests are failing with 'Broken pipe' or 'Stream closed' exceptions on linux, you might be missing `libncurses5`: 200 | 201 | ```bash 202 | sudo apt-get install libncurses5 203 | ``` 204 | 205 | - starting version 8.x.x more libraries from underlying os are required. Known list: 206 | ``` 207 | libnuma1 libssl1.0 libcrypto++6 208 | ``` 209 | 210 | # License 211 | Use of this source code is governed by a [BSD License](LICENSE.md), which basically means you can use and modify it freely. 212 | 213 | # Similar project 214 | [MariaDB4j](https://github.com/vorburger/MariaDB4j) is an unrelated similar project. 215 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | skip_tags: true 3 | skip_branch_with_pr: true 4 | clone_depth: 5 5 | build: off 6 | 7 | platform: 8 | - x86 9 | - x64 10 | 11 | environment: 12 | matrix: 13 | - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 14 | 15 | install: 16 | - ps: | 17 | Add-Type -AssemblyName System.IO.Compression.FileSystem 18 | if (!(Test-Path -Path "C:\maven\apache-maven-3.3.9" )) { 19 | (new-object System.Net.WebClient).DownloadFile( 20 | 'http://www.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip', 21 | 'C:\maven-bin.zip' 22 | ) 23 | [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven") 24 | } 25 | - cmd: SET PATH=C:\maven\apache-maven-3.3.9\bin;%JAVA_HOME%\bin;%PATH% 26 | 27 | test_script: 28 | - mvn -q -B verify -Dsurefire.rerunFailingTestsCount=2 29 | 30 | matrix: 31 | fast_finish: true 32 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.wix 5 | wix-embedded-mysql-modules 6 | 4.6.3-SNAPSHOT 7 | Wix Embedded MySql Modules aggregator 8 | Wix Embedded MySql Modules aggregator 9 | https://github.com/wix/wix-embedded-mysql 10 | pom 11 | 12 | 13 | wix-embedded-mysql 14 | wix-embedded-mysql-download-and-extract 15 | 16 | 17 | 18 | 19 | Noam Almog 20 | noama@wix.com 21 | 22 | 23 | Vilius Lukosius 24 | vilius@wix.com 25 | 26 | 27 | 28 | 29 | scm:git:git@github.com:wix/wix-embedded-mysql.git 30 | scm:git:git@github.com:wix/wix-embedded-mysql.git 31 | git@github.com:wix/wix-embedded-mysql.git 32 | HEAD 33 | 34 | 35 | 36 | 37 | BSD License 38 | http://www.opensource.org/licenses/bsd-license.php 39 | repo 40 | 41 | 42 | 43 | 44 | 1.8 45 | 1.8 46 | UTF-8 47 | 48 | 2.0.3 49 | 2.12 50 | 2.12.6 51 | 4.3.4 52 | 53 | 54 | 55 | 56 | 57 | net.alchim31.maven 58 | scala-maven-plugin 59 | 3.2.2 60 | 61 | 62 | 63 | testCompile 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-surefire-plugin 71 | 2.19.1 72 | 73 | 1 74 | 75 | 76 | true 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-failsafe-plugin 83 | 2.19.1 84 | 85 | 1 86 | 87 | 88 | true 89 | 90 | 91 | 92 | 93 | 94 | integration-test 95 | verify 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-release-plugin 104 | 105 | 106 | org.apache.maven.scm 107 | maven-scm-provider-gitexe 108 | 1.9.4 109 | 110 | 111 | 2.5.3 112 | 113 | true 114 | false 115 | release 116 | deploy 117 | 118 | 119 | 120 | org.sonatype.plugins 121 | nexus-staging-maven-plugin 122 | 1.6.6 123 | true 124 | 125 | ossrh 126 | https://oss.sonatype.org/ 127 | true 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | release 136 | 137 | 138 | 139 | maven-source-plugin 140 | 141 | 142 | package-sources 143 | 144 | jar 145 | 146 | 147 | 148 | 149 | 150 | maven-javadoc-plugin 151 | 152 | 153 | package-javadoc 154 | package 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | maven-gpg-plugin 163 | 1.5 164 | 165 | 166 | sign-artifacts 167 | verify 168 | 169 | sign 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | ossrh 182 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 183 | 184 | 185 | ossrh 186 | https://oss.sonatype.org/content/repositories/snapshots 187 | 188 | 189 | 190 | 191 | 192 | repo 193 | 194 | true 195 | ignore 196 | 197 | 198 | false 199 | 200 | file://${basedir}/src/test/lib/ 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mvn -Darguments="-DskipTests" release:clean release:prepare release:perform --settings ~/.m2/settings-deploy.xml 4 | -------------------------------------------------------------------------------- /wix-embedded-mysql-download-and-extract/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.wix 6 | wix-embedded-mysql-download-and-extract 7 | 4.6.3-SNAPSHOT 8 | Wix Embedded Mysql Download and Extract 9 | Cli tool to predownload/precache mysql artifacts for wix-embedded-mysql 10 | https://github.com/wix/wix-embedded-mysql/tree/master/wix-embedded-mysql-download-and-extract 11 | 12 | 13 | com.wix 14 | wix-embedded-mysql-modules 15 | 4.6.3-SNAPSHOT 16 | 17 | 18 | 19 | 20 | 21 | com.wix 22 | wix-embedded-mysql 23 | 4.6.3-SNAPSHOT 24 | 25 | 26 | 27 | de.flapdoodle.embed 28 | de.flapdoodle.embed.process 29 | ${flapdoodle.process.version} 30 | 31 | 32 | 33 | org.scala-lang 34 | scala-library 35 | ${scala.version} 36 | test 37 | 38 | 39 | 40 | org.specs2 41 | specs2-core_${scala.library.version} 42 | ${specs2.version} 43 | test 44 | 45 | 46 | 47 | org.specs2 48 | specs2-junit_${scala.library.version} 49 | ${specs2.version} 50 | test 51 | 52 | 53 | 54 | org.specs2 55 | specs2-matcher-extra_${scala.library.version} 56 | ${specs2.version} 57 | test 58 | 59 | 60 | 61 | commons-io 62 | commons-io 63 | 2.4 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-assembly-plugin 74 | 75 | 76 | package 77 | 78 | single 79 | 80 | 81 | 82 | 83 | 84 | com.wix.mysql.MysqlDownloadAndExtract 85 | 86 | 87 | 88 | 89 | jar-with-dependencies 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /wix-embedded-mysql-download-and-extract/src/main/java/com/wix/mysql/MysqlDownloadAndExtract.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.DownloadConfig; 4 | import com.wix.mysql.config.MysqldConfig; 5 | import com.wix.mysql.config.RuntimeConfigBuilder; 6 | import com.wix.mysql.distribution.Version; 7 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 8 | 9 | import static com.wix.mysql.config.DownloadConfig.aDownloadConfig; 10 | 11 | public class MysqlDownloadAndExtract { 12 | 13 | public static void main(String[] args) { 14 | DownloadConfig downloadConfig = aDownloadConfig().withCacheDir(args[0]).build(); 15 | MysqldConfig mysqldConfig = MysqldConfig.aMysqldConfig(Version.valueOf(version(args))).build(); 16 | IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder().defaults(mysqldConfig, downloadConfig).build(); 17 | MysqldStarter mysqldStarter = new MysqldStarter(runtimeConfig); 18 | mysqldStarter.prepare(mysqldConfig); 19 | } 20 | 21 | private static String version(final String[] args) { 22 | final String majorVersion = args[1]; 23 | final String minorVersion = args.length > 2 ? args[2] : "latest"; 24 | return "v" + majorVersion.replace('.', '_') + "_" + minorVersion; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wix-embedded-mysql-download-and-extract/src/test/java/com/wix/mysql/MysqlDownloadAndExtractIT.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.io.File 4 | import java.nio.file.{Files, Path, Paths} 5 | 6 | import org.apache.commons.io.FileUtils.deleteDirectory 7 | import org.specs2.matcher.{FileMatchers, Matchers} 8 | import org.specs2.mutable.SpecWithJUnit 9 | 10 | import scala.collection.JavaConverters.iterableAsScalaIterableConverter 11 | 12 | class MysqlDownloadAndExtractIT extends SpecWithJUnit with Matchers with FileMatchers { 13 | 14 | "MysqlDownloadAndExtract" should { 15 | 16 | "store download-and-extract cache in custom location" in { 17 | val someVersion = "5.6" 18 | withTempDir { tempDir => 19 | 20 | run(mysqlDownloadAndExtractDeployable, tempDir, someVersion) == Success 21 | 22 | extractedInstallersFolder(tempDir) must beADirectory and exist 23 | } 24 | } 25 | 26 | "download-and-extract artifact according to input version" >> { 27 | 28 | "uses the major version" in { 29 | val majorVersion = "5.7" 30 | withTempDir { tempDir => 31 | 32 | run(mysqlDownloadAndExtractDeployable, tempDir, s"$majorVersion") == Success 33 | 34 | findMajorVersionDir(tempDir, majorVersion) must beRight[File] 35 | } 36 | } 37 | 38 | "uses the minor version" in { 39 | val someMajorVersion = "5.6" 40 | val minorVersion = "35" 41 | withTempDir { tempDir => 42 | 43 | run(mysqlDownloadAndExtractDeployable, tempDir, someMajorVersion, minorVersion) == Success 44 | 45 | findMinorVersionDir(tempDir, someMajorVersion, minorVersion) must beRight[File] 46 | } 47 | } 48 | } 49 | 50 | } 51 | private val Success = 0 52 | private def findMajorVersionDir(basedir: String, majorVersion: String): Either[String, File] = 53 | installersContainerFolder(basedir).right.map(findMajorVersionDir(_, majorVersion)).joinRight 54 | 55 | private def findMajorVersionDir(installersContainerFolder: File, majorVersion: String) = { 56 | val installersFolders = installersContainerFolder.listFiles() 57 | val maybeMajorVersionFolder = installersFolders.find(_.getName.contains(majorVersion)) 58 | maybeMajorVersionFolder.toRight(s"Download folder (${installersContainerFolder.getAbsolutePath}) " + 59 | s"did not contain a folder for *major* version ($majorVersion), children = ${installersFolders.toList}") 60 | } 61 | 62 | private def installersContainerFolder(basedir: String) = 63 | osSpecificChildFolder(extractedInstallersFolder(basedir)) 64 | 65 | private def osSpecificChildFolder(downloadFolder: File) = 66 | downloadFolder.listFiles().toList.headOption.toRight(s"expected a file under ${downloadFolder.getAbsolutePath} but got none") 67 | 68 | private def extractedInstallersFolder(basedir: String): File = new File(basedir, "extracted") 69 | 70 | private def findMinorVersionDir(basedir: String, majorVersion: String, minorVersion: String): Either[String, File] = { 71 | findMajorVersionDir(basedir, majorVersion).right.map { majorVersionFolder => 72 | val minorVersionFolders = majorVersionFolder.listFiles() 73 | val maybeMinorVersionFolder = minorVersionFolders.find(_.getName.contains(s"$majorVersion.$minorVersion")) 74 | maybeMinorVersionFolder.toRight(s"Major version folder (${majorVersionFolder.getAbsolutePath}) exists " + 75 | s"but did not contain a folder for *minor* version ($minorVersion), children = ${minorVersionFolders.toList}") 76 | }.joinRight 77 | } 78 | 79 | def withTempDir[T](f: String => T): T = { 80 | val tempDir = Files.createTempDirectory("embed-mysql-test").toFile 81 | 82 | try { 83 | f(tempDir.getAbsolutePath) 84 | } finally { 85 | deleteDirectory(tempDir) 86 | } 87 | } 88 | 89 | private def run(runnable: Path, args: String*): Int = { 90 | import sys.process._ 91 | ("java" +: "-jar" +: runnable.toAbsolutePath.toString +: args).mkString(" ").! 92 | } 93 | 94 | private def mysqlDownloadAndExtractDeployable: Path = { 95 | val targetDir = Paths.get("./target") 96 | val maybePath = if (Files.exists(targetDir) && Files.isDirectory(targetDir)) { 97 | Files.newDirectoryStream(targetDir).asScala.filter(Files.isRegularFile(_)) 98 | .find(_.toString.endsWith("jar-with-dependencies.jar")) 99 | } else { 100 | None 101 | } 102 | maybePath.getOrElse(throw new RuntimeException( 103 | """ 104 | |!!! ERROR !!! jar-ball artifact with name wix-embedded-mysql-download-and-extract-some-version-jar-with-dependencies.jar cannot be found. Please verify the following: 105 | |1. The project is built and packaged using Maven: '$$ mvn package' should do the trick 106 | |2. Your working directory is set to the module directory ($$MODULE_DIR$$) 107 | """.stripMargin)) 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.wix 5 | wix-embedded-mysql 6 | 4.6.3-SNAPSHOT 7 | Wix Embedded MySql 8 | Embedded MySql for E2E/IT tests 9 | https://github.com/wix/wix-embedded-mysql/tree/master/wix-embedded-mysql 10 | 11 | 12 | com.wix 13 | wix-embedded-mysql-modules 14 | 4.6.3-SNAPSHOT 15 | 16 | 17 | 18 | 4.2.5.RELEASE 19 | 8.0.11 20 | 1.7.10 21 | 1.1.6 22 | 1.19 23 | 24 | 25 | 26 | 27 | 28 | de.flapdoodle.embed 29 | de.flapdoodle.embed.process 30 | ${flapdoodle.process.version} 31 | 32 | 33 | 34 | commons-io 35 | commons-io 36 | 2.6 37 | 38 | 39 | 40 | org.apache.commons 41 | commons-compress 42 | ${commons-compress.version} 43 | 44 | 45 | 46 | org.tukaani 47 | xz 48 | 1.8 49 | 50 | 51 | 52 | ch.qos.logback 53 | logback-classic 54 | ${logback.version} 55 | test 56 | 57 | 58 | 59 | org.springframework 60 | spring-jdbc 61 | ${spring.jdbc.version} 62 | test 63 | 64 | 65 | 66 | mysql 67 | mysql-connector-java 68 | ${mysql.connector.version} 69 | test 70 | 71 | 72 | 73 | org.apache.commons 74 | commons-dbcp2 75 | 2.1.1 76 | test 77 | 78 | 79 | 80 | org.scala-lang 81 | scala-library 82 | ${scala.version} 83 | test 84 | 85 | 86 | 87 | org.specs2 88 | specs2-core_${scala.library.version} 89 | ${specs2.version} 90 | test 91 | 92 | 93 | 94 | org.specs2 95 | specs2-junit_${scala.library.version} 96 | ${specs2.version} 97 | test 98 | 99 | 100 | 101 | org.specs2 102 | specs2-matcher-extra_${scala.library.version} 103 | ${specs2.version} 104 | test 105 | 106 | 107 | 108 | org.nanohttpd 109 | nanohttpd-nanolets 110 | 2.3.1 111 | test 112 | 113 | 114 | 115 | jar 116 | external-jar-with-schemas 117 | 1.0.1 118 | test 119 | 120 | 121 | 122 | org.littleshoot 123 | littleproxy 124 | 1.1.2 125 | test 126 | 127 | 128 | 129 | org.nanohttpd 130 | nanohttpd 131 | 2.3.1 132 | test 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/EmbeddedMysql.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.*; 4 | import com.wix.mysql.config.MysqldConfig.SystemDefaults; 5 | import com.wix.mysql.distribution.Version; 6 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.concurrent.atomic.AtomicBoolean; 15 | import java.util.concurrent.locks.ReentrantLock; 16 | 17 | import static com.wix.mysql.config.DownloadConfig.aDownloadConfig; 18 | import static com.wix.mysql.config.MysqldConfig.SystemDefaults.SCHEMA; 19 | import static com.wix.mysql.config.SchemaConfig.aSchemaConfig; 20 | import static com.wix.mysql.utils.Utils.or; 21 | import static java.lang.String.format; 22 | 23 | public class EmbeddedMysql { 24 | private final static Logger logger = LoggerFactory.getLogger(EmbeddedMysql.class); 25 | private static final ReentrantLock localRepository = new ReentrantLock(); 26 | 27 | protected final MysqldConfig config; 28 | protected final MysqldExecutable executable; 29 | private AtomicBoolean isRunning = new AtomicBoolean(true); 30 | 31 | protected EmbeddedMysql( 32 | final MysqldConfig mysqldConfig, 33 | final DownloadConfig downloadConfig) { 34 | logger.info("Preparing EmbeddedMysql version '{}'...", mysqldConfig.getVersion()); 35 | this.config = mysqldConfig; 36 | IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder().defaults(mysqldConfig, downloadConfig).build(); 37 | MysqldStarter mysqldStarter = new MysqldStarter(runtimeConfig); 38 | 39 | localRepository.lock(); 40 | try { 41 | this.executable = mysqldStarter.prepare(mysqldConfig); 42 | } finally { 43 | localRepository.unlock(); 44 | } 45 | 46 | try { 47 | executable.start(); 48 | getClient(SCHEMA, mysqldConfig.getCharset()).executeCommands( 49 | format("CREATE USER '%s'@'%%' IDENTIFIED BY '%s';", mysqldConfig.getUsername(), mysqldConfig.getPassword())); 50 | } catch (IOException e) { 51 | throw new RuntimeException(e); 52 | } 53 | } 54 | 55 | public MysqldConfig getConfig() { 56 | return this.config; 57 | } 58 | 59 | public void reloadSchema(final String schemaName, final SqlScriptSource... scripts) { 60 | reloadSchema(aSchemaConfig(schemaName).withScripts(scripts).build()); 61 | } 62 | 63 | public void reloadSchema(final String schemaName, final List scripts) { 64 | reloadSchema(aSchemaConfig(schemaName).withScripts(scripts).build()); 65 | } 66 | 67 | public void reloadSchema(final SchemaConfig config) { 68 | dropSchema(config); 69 | addSchema(config); 70 | } 71 | 72 | public void dropSchema(final SchemaConfig schema) { 73 | Charset effectiveCharset = or(schema.getCharset(), config.getCharset()); 74 | getClient(SystemDefaults.SCHEMA, effectiveCharset).executeCommands(format("DROP DATABASE %s", schema.getName())); 75 | } 76 | 77 | public List executeScripts(final String schemaName, final SqlScriptSource... scripts) { 78 | return getClient(schemaName, config.getCharset()).executeScripts(Arrays.asList(scripts)); 79 | } 80 | 81 | public List executeScripts(final String schemaName, final List scripts) { 82 | return getClient(schemaName, config.getCharset()).executeScripts(scripts); 83 | } 84 | 85 | public EmbeddedMysql addSchema(final SchemaConfig schema) { 86 | Charset effectiveCharset = or(schema.getCharset(), config.getCharset()); 87 | 88 | getClient(SystemDefaults.SCHEMA, effectiveCharset).executeCommands( 89 | format("CREATE DATABASE `%s` CHARACTER SET = %s COLLATE = %s;", 90 | schema.getName(), effectiveCharset.getCharset(), effectiveCharset.getCollate()), 91 | format("GRANT ALL ON `%s`.* TO '%s'@'%%';", schema.getName(), config.getUsername())); 92 | 93 | MysqlClient client = getClient(schema.getName(), effectiveCharset); 94 | client.executeScripts(schema.getScripts()); 95 | 96 | return this; 97 | } 98 | 99 | public synchronized void stop() { 100 | if (isRunning.getAndSet(false)) { 101 | executable.stop(); 102 | } 103 | } 104 | 105 | private MysqlClient getClient(final String schemaName, final Charset charset) { 106 | return new MysqlClient(config, executable, schemaName, charset); 107 | } 108 | 109 | public static Builder anEmbeddedMysql( 110 | final Version version, 111 | final AdditionalConfig... additionalConfigs) { 112 | 113 | MysqldConfig mysqldConfig = MysqldConfig.aMysqldConfig(version).build(); 114 | DownloadConfig downloadConfig = resolveDownloadConfig(additionalConfigs); 115 | return new Builder(mysqldConfig, downloadConfig); 116 | } 117 | 118 | public static Builder anEmbeddedMysql( 119 | final MysqldConfig mysqldConfig, 120 | final AdditionalConfig... additionalConfigs) { 121 | 122 | DownloadConfig downloadConfig = resolveDownloadConfig(additionalConfigs); 123 | return new Builder(mysqldConfig, downloadConfig); 124 | } 125 | 126 | private static DownloadConfig resolveDownloadConfig(AdditionalConfig[] additionalConfig) { 127 | AdditionalConfig first = additionalConfig.length > 0 ? additionalConfig[0] : null; 128 | 129 | if (first != null && first instanceof DownloadConfig) { 130 | return (DownloadConfig) first; 131 | } else { 132 | return aDownloadConfig().build(); 133 | } 134 | } 135 | 136 | public static class Builder { 137 | private final MysqldConfig mysqldConfig; 138 | private DownloadConfig downloadConfig; 139 | private List schemas = new ArrayList<>(); 140 | 141 | public Builder( 142 | final MysqldConfig mysqldConfig, 143 | final DownloadConfig downloadConfig) { 144 | this.mysqldConfig = mysqldConfig; 145 | this.downloadConfig = downloadConfig; 146 | } 147 | 148 | public Builder withDownloadConfig(final DownloadConfig downloadConfig) { 149 | this.downloadConfig = downloadConfig; 150 | return this; 151 | } 152 | 153 | public Builder addSchema(final String name, final SqlScriptSource... scripts) { 154 | this.schemas.add(aSchemaConfig(name).withScripts(scripts).build()); 155 | return this; 156 | } 157 | 158 | public Builder addSchema(final String name, final List scripts) { 159 | this.schemas.add(aSchemaConfig(name).withScripts(scripts).build()); 160 | return this; 161 | } 162 | 163 | public Builder addSchema(final SchemaConfig config) { 164 | this.schemas.add(config); 165 | return this; 166 | } 167 | 168 | public EmbeddedMysql start() { 169 | EmbeddedMysql instance = new EmbeddedMysql(mysqldConfig, downloadConfig); 170 | 171 | for (SchemaConfig schema : schemas) { 172 | instance.addSchema(schema); 173 | } 174 | 175 | return instance; 176 | } 177 | } 178 | } 179 | 180 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/MysqlClient.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.Charset; 4 | import com.wix.mysql.config.MysqldConfig; 5 | import com.wix.mysql.config.MysqldConfig.SystemDefaults; 6 | import com.wix.mysql.exceptions.CommandFailedException; 7 | import de.flapdoodle.embed.process.distribution.Platform; 8 | import org.apache.commons.io.IOUtils; 9 | 10 | import java.io.IOException; 11 | import java.io.StringReader; 12 | import java.nio.file.Paths; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static com.wix.mysql.utils.Utils.isNullOrEmpty; 17 | import static de.flapdoodle.embed.process.distribution.Platform.Windows; 18 | import static java.lang.String.format; 19 | 20 | class MysqlClient { 21 | 22 | private final MysqldConfig config; 23 | private final MysqldExecutable executable; 24 | private final String schemaName; 25 | private final Charset effectiveCharset; 26 | 27 | public MysqlClient( 28 | final MysqldConfig config, 29 | final MysqldExecutable executable, 30 | final String schemaName, 31 | final Charset effectiveCharset) { 32 | this.config = config; 33 | this.executable = executable; 34 | this.schemaName = schemaName; 35 | this.effectiveCharset = effectiveCharset; 36 | } 37 | 38 | List executeScripts(final List sqls) { 39 | List res = new ArrayList<>(sqls.size()); 40 | try { 41 | for (SqlScriptSource sql : sqls) { 42 | res.add(execute(sql.read())); 43 | } 44 | } catch (IOException e) { 45 | throw new RuntimeException(e); 46 | } 47 | return res; 48 | } 49 | 50 | public List executeCommands(final String... sqls) { 51 | List res = new ArrayList<>(sqls.length); 52 | for (String sql : sqls) { 53 | res.add(execute(sql)); 54 | } 55 | return res; 56 | } 57 | 58 | private String execute(final String sql) { 59 | String command = (Platform.detect() == Windows) ? format("\"%s\"", sql) : sql; 60 | String out = ""; 61 | try { 62 | Process p = new ProcessBuilder(new String[]{ 63 | Paths.get(executable.getBaseDir().getAbsolutePath(), "bin", "mysql").toString(), 64 | "--protocol=tcp", 65 | "--host=localhost", 66 | "--password=", 67 | format("--default-character-set=%s", effectiveCharset.getCharset()), 68 | format("--user=%s", SystemDefaults.USERNAME), 69 | format("--port=%s", config.getPort()), 70 | schemaName}).start(); 71 | 72 | // Process can fail due to missing shared library, thus attempt to input cmd will hide initial problem 73 | if (!p.isAlive()) { 74 | String err = IOUtils.toString(p.getErrorStream()); 75 | throw new CommandFailedException(command, schemaName, p.exitValue(), err); 76 | } 77 | 78 | IOUtils.copy(new StringReader(sql), p.getOutputStream(), java.nio.charset.Charset.defaultCharset()); 79 | p.getOutputStream().close(); 80 | 81 | out = IOUtils.toString(p.getInputStream()); 82 | 83 | if (p.waitFor() != 0) { 84 | 85 | String err = IOUtils.toString(p.getErrorStream()); 86 | 87 | if (isNullOrEmpty(out)) 88 | throw new CommandFailedException(command, schemaName, p.waitFor(), err); 89 | else 90 | throw new CommandFailedException(command, schemaName, p.waitFor(), out); 91 | } 92 | 93 | } catch (IOException | InterruptedException e) { 94 | throw new CommandFailedException(command, schemaName, e.getMessage(), e); 95 | } 96 | return out; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/MysqldExecutable.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Setup; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.distribution.Distribution; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | import de.flapdoodle.embed.process.runtime.Executable; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | class MysqldExecutable extends Executable { 16 | 17 | private final static Logger logger = LoggerFactory.getLogger(MysqldExecutable.class); 18 | 19 | private final IExtractedFileSet executable; 20 | 21 | MysqldExecutable( 22 | final Distribution distribution, 23 | final MysqldConfig config, 24 | final IRuntimeConfig runtimeConfig, 25 | final IExtractedFileSet executable) { 26 | super(distribution, config, runtimeConfig, executable); 27 | this.executable = executable; 28 | } 29 | 30 | @Override 31 | protected MysqldProcess start( 32 | final Distribution distribution, 33 | final MysqldConfig config, 34 | final IRuntimeConfig runtime) throws IOException { 35 | logger.info("Preparing mysqld for startup"); 36 | Setup.apply(config, executable, runtime); 37 | logger.info("Starting MysqldProcess"); 38 | return new MysqldProcess(distribution, config, runtime, this); 39 | } 40 | 41 | File getBaseDir() { 42 | return executable.baseDir(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/MysqldProcess.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Service; 5 | import com.wix.mysql.io.NotifyingStreamProcessor; 6 | import com.wix.mysql.io.NotifyingStreamProcessor.ResultMatchingListener; 7 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 8 | import de.flapdoodle.embed.process.distribution.Distribution; 9 | import de.flapdoodle.embed.process.distribution.Platform; 10 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 11 | import de.flapdoodle.embed.process.io.Processors; 12 | import de.flapdoodle.embed.process.io.StreamToLineProcessor; 13 | import de.flapdoodle.embed.process.runtime.AbstractProcess; 14 | import de.flapdoodle.embed.process.runtime.ProcessControl; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStreamReader; 20 | import java.io.Reader; 21 | import java.lang.reflect.Field; 22 | import java.nio.file.Paths; 23 | import java.util.List; 24 | 25 | import static com.wix.mysql.utils.Utils.closeCloseables; 26 | import static com.wix.mysql.utils.Utils.readToString; 27 | import static de.flapdoodle.embed.process.distribution.Platform.Windows; 28 | import static java.lang.String.format; 29 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 30 | 31 | public class MysqldProcess extends AbstractProcess { 32 | 33 | private final static Logger logger = LoggerFactory.getLogger(MysqldProcess.class); 34 | 35 | private NotifyingStreamProcessor outputWatch; 36 | 37 | public MysqldProcess( 38 | final Distribution distribution, 39 | final MysqldConfig config, 40 | final IRuntimeConfig runtimeConfig, 41 | final MysqldExecutable executable) throws IOException { 42 | super(distribution, config, runtimeConfig, executable); 43 | } 44 | 45 | @Override 46 | public void onAfterProcessStart(final ProcessControl process, final IRuntimeConfig runtimeConfig) throws IOException { 47 | outputWatch = new NotifyingStreamProcessor(StreamToLineProcessor.wrap(runtimeConfig.getProcessOutput().getOutput())); 48 | Processors.connect(process.getReader(), outputWatch); 49 | Processors.connect(process.getError(), outputWatch); 50 | ResultMatchingListener startupListener = outputWatch.addListener(new ResultMatchingListener("ready for connections")); 51 | 52 | try { 53 | startupListener.waitForResult(getConfig().getTimeout(MILLISECONDS)); 54 | 55 | if (!startupListener.isInitWithSuccess()) { 56 | throw new RuntimeException("mysql start failed with error: " + startupListener.getFailureFound()); 57 | } 58 | } catch (Exception e) { 59 | // emit IO exception for {@link AbstractProcess} would try to stop running process gracefully 60 | throw new IOException(e); 61 | } 62 | } 63 | 64 | @Override 65 | protected List getCommandLine(Distribution distribution, MysqldConfig config, IExtractedFileSet exe) throws IOException { 66 | return Service.commandLine(config, exe); 67 | } 68 | 69 | @Override 70 | protected synchronized void stopInternal() { 71 | logger.info("try to stop mysqld"); 72 | if (!stopUsingMysqldadmin()) { 73 | logger.warn("could not stop mysqld via mysqladmin, try next"); 74 | if (!sendKillToProcess()) { 75 | logger.warn("could not stop mysqld, try next"); 76 | if (!sendTermToProcess()) { 77 | logger.warn("could not stop mysqld, try next"); 78 | if (!tryKillToProcess()) { 79 | logger.warn("could not stop mysqld the second time, try one last thing"); 80 | try { 81 | stopProcess(); 82 | } catch (IllegalStateException e) { 83 | logger.error("error while trying to stop mysql process", e); 84 | } 85 | } 86 | } 87 | } 88 | 89 | } 90 | } 91 | 92 | @Override 93 | protected void cleanupInternal() { 94 | } 95 | 96 | private boolean stopUsingMysqldadmin() { 97 | ResultMatchingListener shutdownListener = outputWatch.addListener(new ResultMatchingListener(": Shutdown complete")); 98 | boolean retValue = false; 99 | Reader stdErr = null; 100 | 101 | try { 102 | String cmd = Paths.get(getExecutable().getFile().baseDir().getAbsolutePath(), "bin", "mysqladmin").toString(); 103 | 104 | Process p = Runtime.getRuntime().exec(new String[]{ 105 | cmd, "--no-defaults", "--protocol=tcp", 106 | format("-u%s", MysqldConfig.SystemDefaults.USERNAME), 107 | format("--port=%s", getConfig().getPort()), 108 | "shutdown"}); 109 | 110 | //TODO: make wait with timeout 111 | retValue = p.waitFor() == 0; 112 | 113 | stdErr = new InputStreamReader(p.getErrorStream()); 114 | 115 | if (retValue) { 116 | shutdownListener.waitForResult(getConfig().getTimeout(MILLISECONDS)); 117 | 118 | //TODO: figure out a better strategy for this. It seems windows does not actually shuts down process after it says it does. 119 | if (Platform.detect() == Windows) { 120 | Thread.sleep(2000); 121 | } 122 | 123 | if (!shutdownListener.isInitWithSuccess()) { 124 | logger.error("mysql shutdown failed. Expected to find in output: 'Shutdown complete', got: " + shutdownListener.getFailureFound()); 125 | retValue = false; 126 | } else { 127 | logger.debug("mysql shutdown succeeded."); 128 | retValue = true; 129 | } 130 | 131 | } else { 132 | String errOutput = readToString(stdErr); 133 | 134 | if (errOutput.contains("Can't connect to MySQL server on")) { 135 | logger.warn("mysql was already shutdown - no need to add extra shutdown hook - process does it out of the box."); 136 | retValue = true; 137 | } else { 138 | logger.error("mysql shutdown failed with error code: " + p.waitFor() + " and message: " + errOutput); 139 | } 140 | } 141 | 142 | } catch (InterruptedException | IOException e) { 143 | logger.warn("Encountered error why shutting down process.", e); 144 | } finally { 145 | closeCloseables(stdErr); 146 | } 147 | 148 | return retValue; 149 | } 150 | 151 | /** 152 | * Work-around to get Executable in hooks where it's not provided and as 153 | * all init is done in base class constructor, local vars are still not 154 | * initialized:/ 155 | */ 156 | private MysqldExecutable getExecutable() { 157 | try { 158 | Field f = AbstractProcess.class.getDeclaredField("executable"); 159 | f.setAccessible(true); 160 | return (MysqldExecutable) f.get(this); 161 | } catch (Exception e) { 162 | throw new RuntimeException(e); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/MysqldStarter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 5 | import de.flapdoodle.embed.process.distribution.Distribution; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | import de.flapdoodle.embed.process.runtime.Starter; 8 | 9 | class MysqldStarter extends Starter { 10 | 11 | public MysqldStarter(final IRuntimeConfig config) { 12 | super(config); 13 | } 14 | 15 | @Override 16 | protected MysqldExecutable newExecutable( 17 | final MysqldConfig config, 18 | final Distribution distribution, 19 | final IRuntimeConfig runtime, 20 | final IExtractedFileSet exe) { 21 | return new MysqldExecutable(distribution, config, runtime, exe); 22 | } 23 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/PackagePaths.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.config.store.IPackageResolver; 6 | import de.flapdoodle.embed.process.distribution.ArchiveType; 7 | import de.flapdoodle.embed.process.distribution.BitSize; 8 | import de.flapdoodle.embed.process.distribution.Distribution; 9 | 10 | import static de.flapdoodle.embed.process.distribution.ArchiveType.*; 11 | import static de.flapdoodle.embed.process.distribution.BitSize.B32; 12 | import static de.flapdoodle.embed.process.distribution.BitSize.B64; 13 | import static java.lang.String.format; 14 | 15 | public class PackagePaths implements IPackageResolver { 16 | @Override 17 | public FileSet getFileSet(Distribution distribution) { 18 | return com.wix.mysql.distribution.FileSet.emit( 19 | distribution.getPlatform(), 20 | (Version) distribution.getVersion()); 21 | } 22 | 23 | @Override 24 | public ArchiveType getArchiveType(Distribution distribution) { 25 | Version version = (Version)distribution.getVersion(); 26 | return version.archiveType(); 27 | } 28 | 29 | @Override 30 | public String getPath(Distribution distribution) { 31 | String downloadPath = distribution.getVersion().asInDownloadPath(); 32 | Version version = (Version)distribution.getVersion(); 33 | 34 | BitSize bs = distribution.getBitsize(); 35 | switch (distribution.getPlatform()) { 36 | case OS_X: 37 | String arch = (String)System.getProperties().get("os.arch"); 38 | if (arch.equals("aarch64")) { 39 | bs = B64; 40 | } 41 | return format("%s-x86%s.tar.gz", downloadPath, bs == B32 ? "" : "_64"); 42 | case Linux: 43 | String gzOrXz = version.archiveType() == TXZ ? "xz" : "gz"; 44 | return format("%s-%s.tar.%s", downloadPath, bs == B32 ? "i686" : "x86_64", gzOrXz); 45 | case Windows: 46 | return format("%s-win%s.zip", downloadPath, bs == B32 ? "32" : "x64"); 47 | default: 48 | throw new RuntimeException("Not implemented for: " + distribution.getPlatform()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/ScriptResolver.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import org.apache.commons.io.filefilter.WildcardFileFilter; 4 | 5 | import java.io.File; 6 | import java.io.FileFilter; 7 | import java.io.IOException; 8 | import java.net.URISyntaxException; 9 | import java.net.URL; 10 | import java.net.URLDecoder; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.Enumeration; 14 | import java.util.List; 15 | import java.util.jar.JarEntry; 16 | import java.util.jar.JarFile; 17 | 18 | import static com.wix.mysql.utils.Utils.join; 19 | import static java.lang.String.format; 20 | import static java.util.Arrays.asList; 21 | import static java.util.Arrays.copyOfRange; 22 | 23 | /** 24 | * Helper for locating schema init scripts in a classpath. 25 | */ 26 | public class ScriptResolver { 27 | 28 | /** 29 | * Locates classPathFiles matching pattern, ordered using natural alphanumeric order 30 | * Note: Supports only wildcards ('*') and only in file names for matching 31 | * 32 | * @param pattern ex. 'db/*.sql' 33 | * @return list of resolved SqlScriptSource objects 34 | */ 35 | public static List classPathScripts(final String pattern) { 36 | List results = new ArrayList<>(); 37 | 38 | String[] parts = pattern.split("/"); 39 | String path = join(asList(copyOfRange(parts, 0, parts.length - 1)), "/"); 40 | String normalizedPath = path.startsWith("/") ? path : format("/%s", path); 41 | 42 | URL baseFolder = ScriptResolver.class.getResource(normalizedPath); 43 | 44 | if (baseFolder == null) 45 | throw new ScriptResolutionException(normalizedPath); 46 | 47 | if (baseFolder.getProtocol().equals("jar")) { 48 | String normalizedPattern = pattern.startsWith("/") ? pattern : format("/%s", pattern); 49 | String regexPattern = normalizedPattern.replace("*", ".*"); 50 | String jarPath = baseFolder.getPath().substring(5, baseFolder.getPath().indexOf("!")); 51 | JarFile jar; 52 | 53 | try { 54 | jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8")); 55 | } catch (IOException e) { 56 | throw new RuntimeException(e); 57 | } 58 | 59 | Enumeration entries = jar.entries(); 60 | List names = new ArrayList<>(); 61 | while (entries.hasMoreElements()) { 62 | String name = "/" + entries.nextElement().getName(); 63 | if (name.matches(regexPattern)) { 64 | names.add(name); 65 | } 66 | } 67 | Collections.sort(names); 68 | for (String found : names) { 69 | results.add(classPathScript(found)); 70 | } 71 | } else { 72 | FileFilter filter = new WildcardFileFilter(parts[parts.length - 1]); 73 | List filesInPath = asList(asFile(baseFolder).listFiles(filter)); 74 | Collections.sort(filesInPath); 75 | for (File f : filesInPath) { 76 | results.add(Sources.fromFile(f)); 77 | } 78 | } 79 | 80 | return results; 81 | } 82 | 83 | /** 84 | * Locates a single classPathFile in a classpath, ex. 'db/init_schema.sql' 85 | * 86 | * @param path path to file 87 | * @return resolved SqlScriptSource 88 | */ 89 | public static SqlScriptSource classPathScript(final String path) { 90 | String normalizedPath = path.startsWith("/") ? path : format("/%s", path); 91 | URL resource = ScriptResolver.class.getResource(normalizedPath); 92 | 93 | if (resource == null) 94 | throw new ScriptResolutionException(normalizedPath); 95 | 96 | return Sources.fromURL(resource); 97 | } 98 | 99 | /** 100 | * Locates a single classPathFile in a classpath, ex. 'db/init_schema.sql' 101 | * 102 | * @param path path to file 103 | * @return resolved SqlScriptSource 104 | * @deprecated in favor of {@link #classPathScript(String)} 105 | */ 106 | @Deprecated 107 | public static SqlScriptSource classPathFile(final String path) { 108 | return classPathScript(path); 109 | } 110 | 111 | /** 112 | * Locates classPathFiles matching pattern, ordered using natural alphanumeric order 113 | * Note: Supports only wildcards ('*') and only in file names for matching 114 | * 115 | * @param pattern ex. 'db/*.sql' 116 | * @return list of resolved SqlScriptSource objects 117 | * @deprecated in favor of {@link #classPathScripts(String)} 118 | */ 119 | @Deprecated 120 | public static List classPathFiles(final String pattern) { 121 | return classPathScripts(pattern); 122 | } 123 | 124 | private static File asFile(final URL resource) { 125 | try { 126 | return new File(resource.toURI()); 127 | } catch (URISyntaxException e) { 128 | throw new RuntimeException(e); 129 | } 130 | } 131 | 132 | public static class ScriptResolutionException extends RuntimeException { 133 | ScriptResolutionException(final String path) { 134 | super(format("No script(s) found for path '%s'", path)); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/Sources.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | import org.apache.commons.io.IOUtils; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.net.URL; 10 | 11 | public class Sources { 12 | 13 | public static SqlScriptSource fromString(final String str) { 14 | return new StringSource(str); 15 | } 16 | 17 | public static SqlScriptSource fromFile(final File str) { 18 | return new FileSource(str); 19 | } 20 | 21 | public static SqlScriptSource fromURL(final URL str) { 22 | return new URLSource(str); 23 | } 24 | 25 | private static class StringSource implements SqlScriptSource { 26 | final String str; 27 | 28 | StringSource(final String str) { 29 | this.str = str; 30 | } 31 | 32 | @Override 33 | public String read() { 34 | return str; 35 | } 36 | } 37 | 38 | private static class FileSource implements SqlScriptSource { 39 | final File str; 40 | 41 | FileSource(final File str) { 42 | this.str = str; 43 | } 44 | 45 | @Override 46 | public String read() throws IOException { 47 | return FileUtils.readFileToString(str); 48 | } 49 | } 50 | 51 | private static class URLSource implements SqlScriptSource { 52 | final URL url; 53 | 54 | URLSource(final URL str) { 55 | this.url = str; 56 | } 57 | 58 | @Override 59 | public String read() throws IOException { 60 | InputStream in = url.openStream(); 61 | 62 | try { 63 | return IOUtils.toString(in); 64 | } finally { 65 | IOUtils.closeQuietly(in); 66 | } 67 | } 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/SqlScriptSource.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import java.io.IOException; 4 | 5 | public interface SqlScriptSource { 6 | String read() throws IOException; 7 | } 8 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/AdditionalConfig.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | public interface AdditionalConfig { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/Charset.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Charset { 6 | public static final Charset UTF8MB4 = new Charset("utf8mb4", "utf8mb4_unicode_ci"); 7 | public static final Charset UTF8 = new Charset("utf8", "utf8_general_ci"); 8 | public static final Charset LATIN1 = new Charset("latin1", "latin1_swedish_ci"); 9 | 10 | private final String charset; 11 | private final String collate; 12 | 13 | private Charset(final String charset, final String collate) { 14 | this.charset = charset; 15 | this.collate = collate; 16 | } 17 | 18 | public static Charset aCharset(final String charset, final String collate) { 19 | return new Charset(charset, collate); 20 | } 21 | 22 | public static Charset defaults() { 23 | return UTF8MB4; 24 | } 25 | 26 | public String getCharset() { 27 | return charset; 28 | } 29 | 30 | public String getCollate() { 31 | return collate; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Charset{" + 37 | "charset='" + charset + '\'' + 38 | ", collate='" + collate + '\'' + 39 | '}'; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) return true; 45 | if (o == null || getClass() != o.getClass()) return false; 46 | 47 | Charset that = (Charset) o; 48 | 49 | return areObjectsEqual(this.charset, that.charset) && 50 | areObjectsEqual(this.collate, that.collate); 51 | } 52 | 53 | private boolean areObjectsEqual(T o1, T o2) { 54 | return o1 == o2 || (o1 != null && o1.equals(o2)); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Arrays.hashCode(new Object[]{charset, collate}); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/DownloadConfig.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import de.flapdoodle.embed.process.config.store.IProxyFactory; 4 | import de.flapdoodle.embed.process.config.store.NoProxyFactory; 5 | 6 | import java.io.File; 7 | 8 | public class DownloadConfig implements AdditionalConfig { 9 | private final String cacheDir; 10 | private final String baseUrl; 11 | private final IProxyFactory proxyFactory; 12 | 13 | private DownloadConfig( 14 | final String cacheDir, 15 | final String baseUrl, 16 | final IProxyFactory proxy) { 17 | this.cacheDir = cacheDir; 18 | this.baseUrl = baseUrl; 19 | this.proxyFactory = proxy; 20 | } 21 | 22 | public IProxyFactory getProxyFactory() { 23 | return proxyFactory; 24 | } 25 | 26 | /** 27 | * @deprecated in favour of getCacheDir 28 | */ 29 | @Deprecated 30 | public String getDownloadCacheDir() { 31 | return cacheDir; 32 | } 33 | 34 | public String getCacheDir() { 35 | return cacheDir; 36 | } 37 | 38 | public String getBaseUrl() { 39 | return baseUrl; 40 | } 41 | 42 | public static Builder aDownloadConfig() { 43 | return new Builder(); 44 | } 45 | 46 | public static class Builder { 47 | private IProxyFactory proxyFactory = new NoProxyFactory(); 48 | private String cacheDir = new File(System.getProperty("user.home"), ".embedmysql").getPath(); 49 | private String baseUrl = "https://dev.mysql.com/get/Downloads/"; 50 | 51 | /** 52 | * Download cache location override that by default is set to '~/.embedmysql'. 53 | * 54 | * @deprecated in favor of withCacheDir 55 | * 56 | * @param downloadCacheDir custom path 57 | * @return Builder 58 | */ 59 | @Deprecated 60 | public Builder withDownloadCacheDir(String downloadCacheDir) { 61 | this.cacheDir = downloadCacheDir; 62 | return this; 63 | } 64 | 65 | /** 66 | * Download cache location override that by default is set to '~/.embedmysql'. 67 | * 68 | * @param cacheDir custom path 69 | * @return Builder 70 | */ 71 | public Builder withCacheDir(String cacheDir) { 72 | this.cacheDir = cacheDir; 73 | return this; 74 | } 75 | 76 | 77 | /** 78 | * base url override that defaults to "https://dev.mysql.com/get/Downloads" where actual mysql binary path must conform to 79 | * what mysql provides (or otherwise is stored in ~/.embedmysql) - ex. https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.18-macos10.12-x86_64.dmg 80 | * 81 | * @param baseUrl custom download url 82 | * @return Builder 83 | */ 84 | public Builder withBaseUrl(String baseUrl) { 85 | this.baseUrl = baseUrl; 86 | return this; 87 | } 88 | 89 | public Builder withProxy(final IProxyFactory proxy) { 90 | this.proxyFactory = proxy; 91 | return this; 92 | } 93 | 94 | public DownloadConfig build() { 95 | return new DownloadConfig(cacheDir, baseUrl, proxyFactory); 96 | 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/DownloadConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import com.wix.mysql.PackagePaths; 4 | import com.wix.mysql.io.ConsoleProgressListener; 5 | import de.flapdoodle.embed.process.config.store.DownloadPath; 6 | import de.flapdoodle.embed.process.config.store.TimeoutConfigBuilder; 7 | import de.flapdoodle.embed.process.extract.UUIDTempNaming; 8 | import de.flapdoodle.embed.process.io.directories.FixedPath; 9 | 10 | public class DownloadConfigBuilder extends de.flapdoodle.embed.process.config.store.DownloadConfigBuilder { 11 | 12 | public DownloadConfigBuilder defaults( 13 | final DownloadConfig downloadConfig) { 14 | fileNaming().setDefault(new UUIDTempNaming()); 15 | downloadPath().setDefault(new DownloadPath(downloadConfig.getBaseUrl())); 16 | progressListener().setDefault(new ConsoleProgressListener()); 17 | artifactStorePath().setDefault(new FixedPath(downloadConfig.getCacheDir())); 18 | downloadPrefix().setDefault(new DownloadPrefix("embedmysql-download")); 19 | userAgent().setDefault(new UserAgent("Mozilla/5.0 (compatible; Embedded MySql; +https://github.com/wix/wix-embedded-mysql)")); 20 | packageResolver().setDefault(new PackagePaths()); 21 | timeoutConfig().setDefault(new TimeoutConfigBuilder().connectionTimeout(10000).readTimeout(60000).build()); 22 | proxyFactory().setDefault(downloadConfig.getProxyFactory()); 23 | return this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/MysqldConfig.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.ExecutableProcessConfig; 5 | import de.flapdoodle.embed.process.config.ISupportConfig; 6 | import de.flapdoodle.embed.process.distribution.IVersion; 7 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 8 | import org.apache.commons.lang3.builder.ToStringBuilder; 9 | 10 | import java.io.IOException; 11 | import java.net.ServerSocket; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.TimeZone; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static java.util.concurrent.TimeUnit.SECONDS; 18 | 19 | public class MysqldConfig extends ExecutableProcessConfig { 20 | 21 | private final int port; 22 | private final Charset charset; 23 | private final User user; 24 | private final TimeZone timeZone; 25 | private final Timeout timeout; 26 | private final List serverVariables; 27 | private final String tempDir; 28 | 29 | protected MysqldConfig( 30 | final IVersion version, 31 | final int port, 32 | final Charset charset, 33 | final User user, 34 | final TimeZone timeZone, 35 | final Timeout timeout, 36 | final List serverVariables, 37 | final String tempDir) { 38 | super(version, new ISupportConfig() { 39 | public String getName() { 40 | return "mysqld"; 41 | } 42 | 43 | public String getSupportUrl() { 44 | return "https://github.com/wix/wix-embedded-mysql/issues"; 45 | } 46 | 47 | public String messageOnException(Class context, Exception exception) { 48 | return "no message"; 49 | } 50 | }); 51 | 52 | if (user.name.equals("root")) { 53 | throw new IllegalArgumentException("Usage of username 'root' is forbidden as it's reserved for system use"); 54 | } 55 | 56 | this.port = port; 57 | this.charset = charset; 58 | this.user = user; 59 | this.timeZone = timeZone; 60 | this.timeout = timeout; 61 | this.serverVariables = serverVariables; 62 | this.tempDir = tempDir; 63 | } 64 | 65 | public Version getVersion() { 66 | return (Version) version; 67 | } 68 | 69 | public Charset getCharset() { 70 | return charset; 71 | } 72 | 73 | public int getPort() { 74 | return port; 75 | } 76 | 77 | public long getTimeout(TimeUnit target) { 78 | return this.timeout.to(target); 79 | } 80 | 81 | public String getUsername() { 82 | return user.name; 83 | } 84 | 85 | public String getPassword() { 86 | return user.password; 87 | } 88 | 89 | public TimeZone getTimeZone() { 90 | return timeZone; 91 | } 92 | 93 | public List getServerVariables() { 94 | return serverVariables; 95 | } 96 | 97 | public String getTempDir() { 98 | return tempDir; 99 | } 100 | 101 | public static Builder aMysqldConfig(final Version version) { 102 | return new Builder(version); 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return ToStringBuilder.reflectionToString(this); 108 | } 109 | 110 | public static class Builder { 111 | private IVersion version; 112 | private int port = 3310; 113 | private Charset charset = Charset.defaults(); 114 | private User user = new User("auser", "sa"); 115 | private TimeZone timeZone = TimeZone.getTimeZone("UTC"); 116 | private Timeout timeout = new Timeout(30, SECONDS); 117 | private final List serverVariables = new ArrayList<>(); 118 | private String tempDir = "target/"; 119 | 120 | public Builder(IVersion version) { 121 | this.version = version; 122 | } 123 | 124 | public Builder withPort(int port) { 125 | this.port = port; 126 | return this; 127 | } 128 | 129 | public Builder withFreePort() throws IOException { 130 | try (ServerSocket socket = new ServerSocket(0)) { 131 | socket.setReuseAddress(true); 132 | return withPort(socket.getLocalPort()); 133 | } 134 | } 135 | 136 | public Builder withTimeout(long length, TimeUnit unit) { 137 | this.timeout = new Timeout(length, unit); 138 | return this; 139 | } 140 | 141 | public Builder withCharset(Charset charset) { 142 | this.charset = charset; 143 | return this; 144 | } 145 | 146 | public Builder withUser(String username, String password) { 147 | this.user = new User(username, password); 148 | return this; 149 | } 150 | 151 | public Builder withTimeZone(TimeZone timeZone) { 152 | this.timeZone = timeZone; 153 | return this; 154 | } 155 | 156 | public Builder withTimeZone(String timeZoneId) { 157 | return withTimeZone(TimeZone.getTimeZone(timeZoneId)); 158 | } 159 | 160 | /** 161 | * Provide mysql server option 162 | * 163 | * See http://dev.mysql.com/doc/refman/5.7/en/mysqld-option-tables.html 164 | */ 165 | public Builder withServerVariable(String name, boolean value) { 166 | serverVariables.add(new ServerVariable<>(name, value)); 167 | return this; 168 | } 169 | 170 | /** 171 | * Provide mysql server int variable 172 | * 173 | * See http://dev.mysql.com/doc/refman/5.7/en/mysqld-option-tables.html 174 | */ 175 | public Builder withServerVariable(String name, int value) { 176 | serverVariables.add(new ServerVariable<>(name, value)); 177 | return this; 178 | } 179 | 180 | /** 181 | * Provide mysql server string or enum variable 182 | * 183 | * See http://dev.mysql.com/doc/refman/5.7/en/mysqld-option-tables.html 184 | */ 185 | public Builder withServerVariable(String name, String value) { 186 | serverVariables.add(new ServerVariable<>(name, value)); 187 | return this; 188 | } 189 | 190 | public Builder withTempDir(String tempDir) { 191 | this.tempDir = tempDir; 192 | return this; 193 | } 194 | 195 | 196 | public MysqldConfig build() { 197 | return new MysqldConfig(version, port, charset, user, timeZone, timeout, serverVariables, tempDir); 198 | } 199 | } 200 | 201 | private static class User { 202 | private final String name; 203 | private final String password; 204 | 205 | User(String name, String password) { 206 | this.name = name; 207 | this.password = password; 208 | } 209 | 210 | @Override 211 | public String toString() { 212 | return ReflectionToStringBuilder.toStringExclude(this, "password"); 213 | } 214 | } 215 | 216 | private static class Timeout { 217 | private final long length; 218 | private final TimeUnit unit; 219 | 220 | Timeout(long length, TimeUnit unit) { 221 | this.length = length; 222 | this.unit = unit; 223 | } 224 | 225 | long to(TimeUnit target) { 226 | return target.convert(length, unit); 227 | } 228 | 229 | @Override 230 | public String toString() { 231 | return ToStringBuilder.reflectionToString(this); 232 | } 233 | } 234 | 235 | public static class ServerVariable { 236 | private final String name; 237 | private final T value; 238 | 239 | ServerVariable(final String name, final T value) { 240 | this.name = name; 241 | this.value = value; 242 | } 243 | 244 | public String toCommandLineArgument() { 245 | return String.format("--%s=%s", name, value); 246 | } 247 | 248 | @Override 249 | public String toString() { 250 | return ToStringBuilder.reflectionToString(this); 251 | } 252 | } 253 | 254 | public static class SystemDefaults { 255 | public final static String USERNAME = "root"; 256 | public final static String SCHEMA = "information_schema"; 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/ProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import de.flapdoodle.embed.process.config.store.HttpProxyFactory; 4 | import de.flapdoodle.embed.process.config.store.IProxyFactory; 5 | 6 | public class ProxyFactory { 7 | 8 | public static IProxyFactory aHttpProxy(String hostName, int port) { 9 | return new HttpProxyFactory(hostName, port); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/RuntimeConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import com.wix.mysql.MysqldProcess; 4 | import com.wix.mysql.store.SafeExtractedArtifactStoreBuilder; 5 | import de.flapdoodle.embed.process.config.io.ProcessOutput; 6 | import de.flapdoodle.embed.process.io.IStreamProcessor; 7 | import de.flapdoodle.embed.process.runtime.ICommandLinePostProcessor; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import static de.flapdoodle.embed.process.io.Processors.logTo; 12 | import static de.flapdoodle.embed.process.io.Slf4jLevel.DEBUG; 13 | 14 | public class RuntimeConfigBuilder extends de.flapdoodle.embed.process.config.RuntimeConfigBuilder { 15 | 16 | private Logger logger = LoggerFactory.getLogger(MysqldProcess.class); 17 | private IStreamProcessor log = logTo(logger, DEBUG); 18 | 19 | public RuntimeConfigBuilder defaults( 20 | final MysqldConfig mysqldConfig, 21 | final DownloadConfig downloadConfig) { 22 | 23 | processOutput().setDefault(new ProcessOutput(log, log, log)); 24 | commandLinePostProcessor().setDefault(new ICommandLinePostProcessor.Noop()); 25 | artifactStore().setDefault(new SafeExtractedArtifactStoreBuilder().defaults(mysqldConfig, downloadConfig).build()); 26 | 27 | return this; 28 | } 29 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/SchemaConfig.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config; 2 | 3 | import com.wix.mysql.Sources; 4 | import com.wix.mysql.SqlScriptSource; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public class SchemaConfig { 11 | 12 | private final String name; 13 | private final Charset charset; 14 | private final List scripts; 15 | 16 | private SchemaConfig(String name, Charset charset, List scripts) { 17 | this.name = name; 18 | this.charset = charset; 19 | this.scripts = scripts; 20 | } 21 | 22 | public static Builder aSchemaConfig(final String name) { 23 | return new Builder(name); 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public Charset getCharset() { 31 | return charset; 32 | } 33 | 34 | public List getScripts() { 35 | return scripts; 36 | } 37 | 38 | public static class Builder { 39 | 40 | private final String name; 41 | private Charset charset; 42 | private List scripts = new ArrayList<>(); 43 | 44 | public Builder(final String name) { 45 | this.name = name; 46 | } 47 | 48 | public Builder withCharset(final Charset charset) { 49 | this.charset = charset; 50 | return this; 51 | } 52 | 53 | public Builder withScripts(final SqlScriptSource... scripts) { 54 | return withScripts(Arrays.asList(scripts)); 55 | } 56 | 57 | public Builder withScripts(final List scripts) { 58 | this.scripts.addAll(scripts); 59 | return this; 60 | } 61 | 62 | public Builder withCommands(final String... commands) { 63 | return withCommands(Arrays.asList(commands)); 64 | } 65 | 66 | public Builder withCommands(final List commands) { 67 | for (String cmd: commands) { 68 | this.scripts.add(Sources.fromString(cmd)); 69 | } 70 | return this; 71 | } 72 | 73 | public SchemaConfig build() { 74 | return new SchemaConfig(name, charset, scripts); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/directories/TargetGeneratedFixedPath.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config.directories; 2 | 3 | import de.flapdoodle.embed.process.distribution.Platform; 4 | import de.flapdoodle.embed.process.io.directories.IDirectory; 5 | 6 | import java.io.File; 7 | 8 | public class TargetGeneratedFixedPath implements IDirectory { 9 | 10 | private final String baseDir; 11 | 12 | public TargetGeneratedFixedPath(String baseDir) { 13 | this.baseDir = baseDir; 14 | } 15 | 16 | @Override 17 | public File asFile() { 18 | generateNeededDirs(); 19 | return new File(baseDir).getAbsoluteFile(); 20 | } 21 | 22 | private void generateNeededDirs() { 23 | String[] paths; 24 | 25 | if (Platform.detect() == Platform.Windows ) { 26 | paths = new String[]{"bin", "share/english", "data/test", "data/mysql", "data/performance_schema"}; 27 | } else { 28 | paths = new String[]{"bin", "scripts", "lib/plugin", "share/english", "share", "support-files"}; 29 | } 30 | 31 | for (String dir : paths) { 32 | new File(baseDir + "/" + dir).mkdirs(); 33 | } 34 | } 35 | 36 | @Override 37 | public boolean isGenerated() { 38 | return true; 39 | } 40 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/extract/NoopNaming.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config.extract; 2 | 3 | import de.flapdoodle.embed.process.extract.ITempNaming; 4 | 5 | public class NoopNaming implements ITempNaming { 6 | 7 | @Override 8 | public String nameFor(String prefix, String postfix) { 9 | return postfix; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/config/extract/PathPrefixingNaming.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config.extract; 2 | 3 | import de.flapdoodle.embed.process.extract.ITempNaming; 4 | 5 | public class PathPrefixingNaming implements ITempNaming { 6 | 7 | private final String basePath; 8 | 9 | public PathPrefixingNaming(String basePath) { 10 | this.basePath = basePath; 11 | } 12 | 13 | @Override 14 | public String nameFor(String prefix, String postfix) { 15 | return basePath + postfix; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/FileSet.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution; 2 | 3 | import com.wix.mysql.distribution.fileset.*; 4 | import de.flapdoodle.embed.process.collections.Collections; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.List; 8 | 9 | public class FileSet { 10 | private static List emitters = Collections.newArrayList( 11 | new Win56FileSetEmitter(), 12 | new Win57FileSetEmitter(), 13 | new Win57_18_UpFileSetEmitter(), 14 | new Win8FileSetEmitter(), 15 | new Nix55FileSetEmitter(), 16 | new Nix56FileSetEmitter(), 17 | new Nix57FileSetEmitter(), 18 | new Nix57_18_AndUpFileSetEmitter(), 19 | new Nix8FileSetEmitter(), 20 | new OSX8FileSetEmitter()); 21 | 22 | public static de.flapdoodle.embed.process.config.store.FileSet emit( 23 | final Platform platform, 24 | final Version version) { 25 | 26 | for (FileSetEmitter emitter : emitters) { 27 | if (emitter.matches(platform, version)) { 28 | return emitter.emit(); 29 | } 30 | } 31 | throw new RuntimeException(String.format("FileSetEmitter not found for platform: %s version: %s", platform, version.toString())); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/Service.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.service.*; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class Service { 12 | 13 | private static List emitters = Collections.newArrayList( 14 | new BaseCommandEmitter(), 15 | new Pre57CommandEmitter(), 16 | new Mysql57CommandEmitter(), 17 | new Mysql8CommandEmitter(), 18 | new UserProvidedArgumentsEmitter()); 19 | 20 | public static List commandLine(final MysqldConfig config, final IExtractedFileSet exe) throws IOException { 21 | Version version = config.getVersion(); 22 | ServiceCommandBuilder commandBuilder = new ServiceCommandBuilder(version.toString()); 23 | for (CommandEmitter emitter : emitters) { 24 | if (emitter.matches(version)) { 25 | commandBuilder.addAll(emitter.emit(config, exe)); 26 | } 27 | } 28 | 29 | return commandBuilder.emit(); 30 | } 31 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/Setup.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.setup.*; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | public class Setup { 13 | 14 | private static List initializers = Collections.newArrayList( 15 | new FilePermissionsInitializer(), 16 | new Mysql57Initializer(), 17 | new NixBefore57Initializer(), 18 | new Mysql8Initializer()); 19 | 20 | 21 | public static void apply(MysqldConfig config, IExtractedFileSet files, IRuntimeConfig runtimeConfig) throws IOException { 22 | for (Initializer initializer : initializers) { 23 | if (initializer.matches(config.getVersion())) { 24 | initializer.apply(files, runtimeConfig, config); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/Version.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution; 2 | 3 | import com.wix.mysql.exceptions.UnsupportedPlatformException; 4 | import com.wix.mysql.utils.Utils; 5 | import de.flapdoodle.embed.process.distribution.ArchiveType; 6 | import de.flapdoodle.embed.process.distribution.IVersion; 7 | import de.flapdoodle.embed.process.distribution.Platform; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import static de.flapdoodle.embed.process.distribution.ArchiveType.*; 13 | import static java.lang.String.format; 14 | 15 | public enum Version implements IVersion { 16 | 17 | v5_5_40("5.5", 40, MacOsVersion.v10_6, TGZ, Platform.Linux, Platform.OS_X), 18 | v5_5_50("5.5", 50, MacOsVersion.v10_9, TGZ, Platform.Linux, Platform.OS_X), 19 | v5_5_51("5.5", 51, MacOsVersion.v10_9, TGZ, Platform.Linux, Platform.OS_X), 20 | v5_5_52("5.5", 52, MacOsVersion.v10_9, TGZ, Platform.Linux, Platform.OS_X), 21 | v5_5_latest(v5_5_52), 22 | v5_6_21("5.6", 21, MacOsVersion.v10_9), 23 | v5_6_22("5.6", 22, MacOsVersion.v10_9), 24 | v5_6_23("5.6", 23, MacOsVersion.v10_9), 25 | v5_6_24("5.6", 24, MacOsVersion.v10_9), 26 | v5_6_31("5.6", 31, MacOsVersion.v10_11), 27 | v5_6_32("5.6", 32, MacOsVersion.v10_11), 28 | v5_6_33("5.6", 33, MacOsVersion.v10_11), 29 | v5_6_34("5.6", 34, MacOsVersion.v10_11), 30 | v5_6_35("5.6", 35, MacOsVersion.v10_12), 31 | v5_6_36("5.6", 36, MacOsVersion.v10_12), 32 | v5_6_latest(v5_6_36), 33 | v5_7_10("5.7", 10, MacOsVersion.v10_10), 34 | v5_7_13("5.7", 13, MacOsVersion.v10_11), 35 | v5_7_14("5.7", 14, MacOsVersion.v10_11), 36 | v5_7_15("5.7", 15, MacOsVersion.v10_11), 37 | v5_7_16("5.7", 16, MacOsVersion.v10_11), 38 | v5_7_17("5.7", 17, MacOsVersion.v10_12), 39 | v5_7_18("5.7", 18, MacOsVersion.v10_12), 40 | v5_7_19("5.7", 19, MacOsVersion.v10_12), 41 | v5_7_27("5.7", 27, MacOsVersion.v10_14), 42 | v5_7_latest(v5_7_27), 43 | v8_0_11("8.0", 11, MacOsVersion.v10_13), 44 | v8_0_17("8.0", 17, MacOsVersion.v10_14, TXZ, Platform.Linux, Platform.Windows, Platform.OS_X), 45 | v8_latest(v8_0_17); 46 | 47 | private enum MacOsVersion { 48 | v10_6("osx"), 49 | v10_9("osx"), 50 | v10_10("osx"), 51 | v10_11("osx"), 52 | v10_12("macos"), 53 | v10_13("macos"), 54 | v10_14("macos"); 55 | 56 | private final String osName; 57 | 58 | MacOsVersion(String osName) { 59 | this.osName = osName; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return format("%s%s", osName, name().substring(1).replace('_', '.')); 65 | } 66 | } 67 | 68 | private final String majorVersion; 69 | private final int minorVersion; 70 | private final MacOsVersion macOsVersion; 71 | private final List supportedPlatforms; 72 | private final ArchiveType linuxArchive; 73 | 74 | Version(String majorVersion, int minorVersion, MacOsVersion macOsVersion, ArchiveType linuxArchiveType, Platform... supportedPlatforms) { 75 | this.majorVersion = majorVersion; 76 | this.minorVersion = minorVersion; 77 | this.macOsVersion = macOsVersion; 78 | this.supportedPlatforms = Arrays.asList(supportedPlatforms); 79 | this.linuxArchive = linuxArchiveType; 80 | } 81 | 82 | Version(String majorVersion, int minorVersion, MacOsVersion macOsVersion) { 83 | this(majorVersion, minorVersion, macOsVersion, TGZ, Platform.Linux, Platform.Windows, Platform.OS_X); 84 | } 85 | 86 | Version(Version other) { 87 | this.majorVersion = other.majorVersion; 88 | this.minorVersion = other.minorVersion; 89 | this.macOsVersion = other.macOsVersion; 90 | this.supportedPlatforms = other.supportedPlatforms; 91 | this.linuxArchive = other.linuxArchive; 92 | } 93 | 94 | public boolean supportsCurrentPlatform() { 95 | return supportedPlatforms.contains(currentPlatform()) && (!isMacOsSierra() || worksOnMacOsSierra()); 96 | } 97 | 98 | private boolean isMacOsSierra() { 99 | return currentPlatform() == Platform.OS_X && System.getProperty("os.version").startsWith("10.12"); 100 | } 101 | 102 | private boolean worksOnMacOsSierra() { 103 | return currentPlatform() == Platform.OS_X && !majorVersion.equals("5.7") || minorVersion >= 15; 104 | } 105 | 106 | public ArchiveType archiveType() { 107 | switch (currentPlatform()) { 108 | case Windows: 109 | return ZIP; 110 | case Linux: 111 | return this.linuxArchive; 112 | case OS_X: 113 | default: 114 | return TGZ; 115 | } 116 | } 117 | 118 | @Override 119 | public String asInDownloadPath() { 120 | assertPlatformIsSupported(); 121 | 122 | switch (currentPlatform()) { 123 | case Windows: 124 | return format("/%s/mysql-%s.%s", path(), majorVersion, minorVersion); 125 | case OS_X: 126 | return format("/%s/mysql-%s.%s-%s", path(), majorVersion, minorVersion, macOsVersion); 127 | case Linux: 128 | return format("/%s/mysql-%s.%s-%s", path(), majorVersion, minorVersion, gcLibVersion()); 129 | default: 130 | throw new UnsupportedPlatformException("Unrecognized platform, currently not supported"); 131 | } 132 | } 133 | 134 | @Override 135 | public String toString() { 136 | return String.format("Version %s.%s", majorVersion, minorVersion); 137 | } 138 | 139 | private String gcLibVersion() { 140 | if (majorVersion.equals("8.0")) 141 | return "linux-glibc2.12"; 142 | if (majorVersion.equals("5.7") && minorVersion > 18) 143 | return "linux-glibc2.12"; 144 | if (majorVersion.equals("5.6") || (majorVersion.equals("5.7") && minorVersion <= 18)) 145 | return "linux-glibc2.5"; 146 | if (majorVersion.equals("5.5")) 147 | return "linux2.6"; 148 | throw new UnsupportedOperationException(); 149 | } 150 | 151 | private Platform currentPlatform() { 152 | return Platform.detect(); 153 | } 154 | 155 | private String path() { 156 | return format("MySQL-%s", majorVersion); 157 | } 158 | 159 | private String toVersionString() { 160 | return majorVersion + "." + minorVersion; 161 | } 162 | 163 | private void assertPlatformIsSupported() { 164 | if (isMacOsSierra() && !worksOnMacOsSierra()) { 165 | throw new UnsupportedPlatformException(String.format("%s is not supported on Mac OS Sierra. Minimum supported version is %s", 166 | toString(), 167 | v5_7_15.toVersionString())); 168 | } 169 | 170 | if (!supportsCurrentPlatform()) { 171 | throw new UnsupportedPlatformException(String.format("Platform %s is not in a supported platform list: %s", 172 | currentPlatform().name(), 173 | Utils.join(supportedPlatforms, ","))); 174 | } 175 | } 176 | 177 | public String getMajorVersion() { 178 | return majorVersion; 179 | } 180 | 181 | public int getMinorVersion() { 182 | return minorVersion; 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | public interface FileSetEmitter { 8 | boolean matches(final Platform platform, final Version version); 9 | FileSet emit(); 10 | } 11 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import de.flapdoodle.embed.process.config.store.FileSet; 4 | 5 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 6 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 7 | 8 | class Nix { 9 | FileSet.Builder common() { 10 | return FileSet.builder() 11 | .addEntry(Executable, "bin/mysqld") 12 | .addEntry(Library, "bin/mysql") 13 | .addEntry(Library, "bin/resolveip") 14 | .addEntry(Library, "bin/mysqladmin") 15 | .addEntry(Library, "bin/my_print_defaults") 16 | .addEntry(Library, "share/english/errmsg.sys") 17 | .addEntry(Library, "share/fill_help_tables.sql") 18 | .addEntry(Library, "share/mysql_system_tables.sql") 19 | .addEntry(Library, "share/mysql_system_tables_data.sql"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix55FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 10 | 11 | public class Nix55FileSetEmitter extends Nix implements FileSetEmitter { 12 | @Override 13 | public boolean matches(Platform platform, Version version) { 14 | return platform.isUnixLike() && Objects.equals(version.getMajorVersion(), "5.5"); 15 | } 16 | 17 | @Override 18 | public FileSet emit() { 19 | return common() 20 | .addEntry(Library, "scripts/mysql_install_db") 21 | .build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix56FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 10 | 11 | public class Nix56FileSetEmitter extends Nix implements FileSetEmitter { 12 | @Override 13 | public boolean matches(Platform platform, Version version) { 14 | return platform.isUnixLike() && Objects.equals(version.getMajorVersion(), "5.6"); 15 | } 16 | 17 | @Override 18 | public FileSet emit() { 19 | return common() 20 | .addEntry(Library, "share/mysql_security_commands.sql") 21 | .addEntry(Library, "support-files/my-default.cnf") 22 | .addEntry(Library, "scripts/mysql_install_db") 23 | .build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix57FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 10 | 11 | public class Nix57FileSetEmitter extends Nix implements FileSetEmitter { 12 | @Override 13 | public boolean matches(Platform platform, Version version) { 14 | return platform.isUnixLike() 15 | && Objects.equals(version.getMajorVersion(), "5.7") 16 | && version.getMinorVersion() <= 17; 17 | } 18 | 19 | @Override 20 | public FileSet emit() { 21 | return common() 22 | .addEntry(Library, "share/mysql_security_commands.sql") 23 | .addEntry(Library, "support-files/my-default.cnf") 24 | .build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix57_18_AndUpFileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 10 | 11 | public class Nix57_18_AndUpFileSetEmitter extends Nix implements FileSetEmitter { 12 | @Override 13 | public boolean matches(Platform platform, Version version) { 14 | return platform.isUnixLike() 15 | && Objects.equals(version.getMajorVersion(), "5.7") 16 | && version.getMinorVersion() > 17; 17 | } 18 | 19 | @Override 20 | public FileSet emit() { 21 | return common() 22 | .addEntry(Library, "share/mysql_security_commands.sql") 23 | .build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Nix8FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.config.store.FileType; 6 | import de.flapdoodle.embed.process.distribution.Platform; 7 | 8 | import java.util.Objects; 9 | 10 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 11 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 12 | import static de.flapdoodle.embed.process.distribution.Platform.OS_X; 13 | 14 | public class Nix8FileSetEmitter extends Nix implements FileSetEmitter { 15 | @Override 16 | public boolean matches(Platform platform, Version version) { 17 | return platform.isUnixLike() && (Platform.detect() != OS_X) 18 | && Objects.equals(version.getMajorVersion(), "8.0"); 19 | } 20 | 21 | @Override 22 | public FileSet emit() { 23 | return FileSet.builder() 24 | .addEntry(Executable, "bin/mysqld") 25 | .addEntry(Library, "bin/mysql") 26 | .addEntry(Library, "bin/mysqladmin") 27 | .addEntry(Library, "bin/my_print_defaults") 28 | .addEntry(Library, "share/english/errmsg.sys") 29 | .addEntry(FileType.Library, "lib/libssl.so.1.0.0") 30 | .addEntry(FileType.Library, "lib/libcrypto.so.1.0.0") 31 | .build(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/OSX8FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.config.store.FileType; 6 | import de.flapdoodle.embed.process.distribution.Platform; 7 | 8 | import java.util.Objects; 9 | 10 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 11 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 12 | import static de.flapdoodle.embed.process.distribution.Platform.OS_X; 13 | 14 | public class OSX8FileSetEmitter extends Nix implements FileSetEmitter { 15 | @Override 16 | public boolean matches(Platform platform, Version version) { 17 | return (Platform.detect() == OS_X) && Objects.equals(version.getMajorVersion(), "8.0"); 18 | } 19 | 20 | @Override 21 | public FileSet emit() { 22 | return FileSet.builder() 23 | .addEntry(Executable, "bin/mysqld") 24 | .addEntry(Library, "bin/mysql") 25 | .addEntry(Library, "bin/mysqladmin") 26 | .addEntry(Library, "bin/my_print_defaults") 27 | .addEntry(Library, "share/english/errmsg.sys") 28 | .addEntry(Library, "lib/libssl.1.0.0.dylib") 29 | .addEntry(Library, "lib/libcrypto.1.0.0.dylib") 30 | .build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Win56FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 10 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 11 | 12 | public class Win56FileSetEmitter implements FileSetEmitter { 13 | @Override 14 | public boolean matches(Platform platform, Version version) { 15 | return !platform.isUnixLike() && Objects.equals(version.getMajorVersion(), "5.6"); 16 | } 17 | 18 | @Override 19 | public FileSet emit() { 20 | return FileSet.builder() 21 | .addEntry(Executable, "bin/mysqld.exe") 22 | .addEntry(Library, "bin/mysql.exe") 23 | .addEntry(Library, "bin/resolveip.exe") 24 | .addEntry(Library, "bin/mysqladmin.exe") 25 | .addEntry(Library, "share/english/errmsg.sys") 26 | .addEntry(Library, "data/test/db.opt") 27 | 28 | .addEntry(Library, "data/ib_logfile0") 29 | .addEntry(Library, "data/ib_logfile1") 30 | .addEntry(Library, "data/ibdata1") 31 | 32 | .addEntry(Library, "data/mysql/columns_priv.MYD") 33 | .addEntry(Library, "data/mysql/columns_priv.MYI") 34 | .addEntry(Library, "data/mysql/columns_priv.frm") 35 | .addEntry(Library, "data/mysql/db.MYD") 36 | .addEntry(Library, "data/mysql/db.MYI") 37 | .addEntry(Library, "data/mysql/db.frm") 38 | .addEntry(Library, "data/mysql/event.MYD") 39 | .addEntry(Library, "data/mysql/event.MYI") 40 | .addEntry(Library, "data/mysql/event.frm") 41 | .addEntry(Library, "data/mysql/func.MYD") 42 | .addEntry(Library, "data/mysql/func.MYI") 43 | .addEntry(Library, "data/mysql/func.frm") 44 | .addEntry(Library, "data/mysql/general_log.CSM") 45 | .addEntry(Library, "data/mysql/general_log.CSV") 46 | .addEntry(Library, "data/mysql/general_log.frm") 47 | .addEntry(Library, "data/mysql/help_category.MYD") 48 | .addEntry(Library, "data/mysql/help_category.MYI") 49 | .addEntry(Library, "data/mysql/help_category.frm") 50 | .addEntry(Library, "data/mysql/help_keyword.MYD") 51 | .addEntry(Library, "data/mysql/help_keyword.MYI") 52 | .addEntry(Library, "data/mysql/help_keyword.frm") 53 | .addEntry(Library, "data/mysql/help_relation.MYD") 54 | .addEntry(Library, "data/mysql/help_relation.MYI") 55 | .addEntry(Library, "data/mysql/help_relation.frm") 56 | .addEntry(Library, "data/mysql/help_topic.MYD") 57 | .addEntry(Library, "data/mysql/help_topic.MYI") 58 | .addEntry(Library, "data/mysql/help_topic.frm") 59 | .addEntry(Library, "data/mysql/innodb_index_stats.frm") 60 | .addEntry(Library, "data/mysql/innodb_index_stats.ibd") 61 | .addEntry(Library, "data/mysql/innodb_table_stats.frm") 62 | .addEntry(Library, "data/mysql/innodb_table_stats.ibd") 63 | .addEntry(Library, "data/mysql/ndb_binlog_index.MYD") 64 | .addEntry(Library, "data/mysql/ndb_binlog_index.MYI") 65 | .addEntry(Library, "data/mysql/ndb_binlog_index.frm") 66 | .addEntry(Library, "data/mysql/plugin.MYD") 67 | .addEntry(Library, "data/mysql/plugin.MYI") 68 | .addEntry(Library, "data/mysql/plugin.frm") 69 | .addEntry(Library, "data/mysql/proc.MYD") 70 | .addEntry(Library, "data/mysql/proc.MYI") 71 | .addEntry(Library, "data/mysql/proc.frm") 72 | .addEntry(Library, "data/mysql/procs_priv.MYD") 73 | .addEntry(Library, "data/mysql/procs_priv.MYI") 74 | .addEntry(Library, "data/mysql/procs_priv.frm") 75 | .addEntry(Library, "data/mysql/proxies_priv.MYD") 76 | .addEntry(Library, "data/mysql/proxies_priv.MYI") 77 | .addEntry(Library, "data/mysql/proxies_priv.frm") 78 | .addEntry(Library, "data/mysql/servers.MYD") 79 | .addEntry(Library, "data/mysql/servers.MYI") 80 | .addEntry(Library, "data/mysql/servers.frm") 81 | .addEntry(Library, "data/mysql/slave_master_info.frm") 82 | .addEntry(Library, "data/mysql/slave_master_info.ibd") 83 | .addEntry(Library, "data/mysql/slave_relay_log_info.frm") 84 | .addEntry(Library, "data/mysql/slave_relay_log_info.ibd") 85 | .addEntry(Library, "data/mysql/slave_worker_info.frm") 86 | .addEntry(Library, "data/mysql/slave_worker_info.ibd") 87 | .addEntry(Library, "data/mysql/slow_log.CSM") 88 | .addEntry(Library, "data/mysql/slow_log.CSV") 89 | .addEntry(Library, "data/mysql/slow_log.frm") 90 | .addEntry(Library, "data/mysql/tables_priv.MYD") 91 | .addEntry(Library, "data/mysql/tables_priv.MYI") 92 | .addEntry(Library, "data/mysql/tables_priv.frm") 93 | .addEntry(Library, "data/mysql/time_zone.MYD") 94 | .addEntry(Library, "data/mysql/time_zone.MYI") 95 | .addEntry(Library, "data/mysql/time_zone.frm") 96 | .addEntry(Library, "data/mysql/time_zone_leap_second.MYD") 97 | .addEntry(Library, "data/mysql/time_zone_leap_second.MYI") 98 | .addEntry(Library, "data/mysql/time_zone_leap_second.frm") 99 | .addEntry(Library, "data/mysql/time_zone_name.MYD") 100 | .addEntry(Library, "data/mysql/time_zone_name.MYI") 101 | .addEntry(Library, "data/mysql/time_zone_name.frm") 102 | .addEntry(Library, "data/mysql/time_zone_transition.MYD") 103 | .addEntry(Library, "data/mysql/time_zone_transition.MYI") 104 | .addEntry(Library, "data/mysql/time_zone_transition.frm") 105 | .addEntry(Library, "data/mysql/time_zone_transition_type.MYD") 106 | .addEntry(Library, "data/mysql/time_zone_transition_type.MYI") 107 | .addEntry(Library, "data/mysql/time_zone_transition_type.frm") 108 | .addEntry(Library, "data/mysql/user.MYD") 109 | .addEntry(Library, "data/mysql/user.MYI") 110 | .addEntry(Library, "data/mysql/user.frm") 111 | 112 | .addEntry(Library, "data/performance_schema/accounts.frm") 113 | .addEntry(Library, "data/performance_schema/cond_instances.frm") 114 | .addEntry(Library, "data/performance_schema/db.opt") 115 | .addEntry(Library, "data/performance_schema/events_stages_current.frm") 116 | .addEntry(Library, "data/performance_schema/events_stages_history.frm") 117 | .addEntry(Library, "data/performance_schema/events_stages_history_long.frm") 118 | .addEntry(Library, "data/performance_schema/events_stages_summary_by_account_by_event_name.frm") 119 | .addEntry(Library, "data/performance_schema/events_stages_summary_by_host_by_event_name.frm") 120 | .addEntry(Library, "data/performance_schema/events_stages_summary_by_thread_by_event_name.frm") 121 | .addEntry(Library, "data/performance_schema/events_stages_summary_by_user_by_event_name.frm") 122 | .addEntry(Library, "data/performance_schema/events_stages_summary_global_by_event_name.frm") 123 | .addEntry(Library, "data/performance_schema/events_statements_current.frm") 124 | .addEntry(Library, "data/performance_schema/events_statements_history.frm") 125 | .addEntry(Library, "data/performance_schema/events_statements_history_long.frm") 126 | .addEntry(Library, "data/performance_schema/events_statements_summary_by_account_by_event_name.frm") 127 | .addEntry(Library, "data/performance_schema/events_statements_summary_by_digest.frm") 128 | .addEntry(Library, "data/performance_schema/events_statements_summary_by_host_by_event_name.frm") 129 | .addEntry(Library, "data/performance_schema/events_statements_summary_by_thread_by_event_name.frm") 130 | .addEntry(Library, "data/performance_schema/events_statements_summary_by_user_by_event_name.frm") 131 | .addEntry(Library, "data/performance_schema/events_statements_summary_global_by_event_name.frm") 132 | .addEntry(Library, "data/performance_schema/events_waits_current.frm") 133 | .addEntry(Library, "data/performance_schema/events_waits_history.frm") 134 | .addEntry(Library, "data/performance_schema/events_waits_history_long.frm") 135 | .addEntry(Library, "data/performance_schema/events_waits_summary_by_account_by_event_name.frm") 136 | .addEntry(Library, "data/performance_schema/events_waits_summary_by_host_by_event_name.frm") 137 | .addEntry(Library, "data/performance_schema/events_waits_summary_by_instance.frm") 138 | .addEntry(Library, "data/performance_schema/events_waits_summary_by_thread_by_event_name.frm") 139 | .addEntry(Library, "data/performance_schema/events_waits_summary_by_user_by_event_name.frm") 140 | .addEntry(Library, "data/performance_schema/events_waits_summary_global_by_event_name.frm") 141 | .addEntry(Library, "data/performance_schema/file_instances.frm") 142 | .addEntry(Library, "data/performance_schema/file_summary_by_event_name.frm") 143 | .addEntry(Library, "data/performance_schema/file_summary_by_instance.frm") 144 | .addEntry(Library, "data/performance_schema/host_cache.frm") 145 | .addEntry(Library, "data/performance_schema/hosts.frm") 146 | .addEntry(Library, "data/performance_schema/mutex_instances.frm") 147 | .addEntry(Library, "data/performance_schema/objects_summary_global_by_type.frm") 148 | .addEntry(Library, "data/performance_schema/performance_timers.frm") 149 | .addEntry(Library, "data/performance_schema/rwlock_instances.frm") 150 | .addEntry(Library, "data/performance_schema/session_account_connect_attrs.frm") 151 | .addEntry(Library, "data/performance_schema/session_connect_attrs.frm") 152 | .addEntry(Library, "data/performance_schema/setup_actors.frm") 153 | .addEntry(Library, "data/performance_schema/setup_consumers.frm") 154 | .addEntry(Library, "data/performance_schema/setup_instruments.frm") 155 | .addEntry(Library, "data/performance_schema/setup_objects.frm") 156 | .addEntry(Library, "data/performance_schema/setup_timers.frm") 157 | .addEntry(Library, "data/performance_schema/socket_instances.frm") 158 | .addEntry(Library, "data/performance_schema/socket_summary_by_event_name.frm") 159 | .addEntry(Library, "data/performance_schema/socket_summary_by_instance.frm") 160 | .addEntry(Library, "data/performance_schema/table_io_waits_summary_by_index_usage.frm") 161 | .addEntry(Library, "data/performance_schema/table_io_waits_summary_by_table.frm") 162 | .addEntry(Library, "data/performance_schema/table_lock_waits_summary_by_table.frm") 163 | .addEntry(Library, "data/performance_schema/threads.frm") 164 | .addEntry(Library, "data/performance_schema/users.frm") 165 | .build(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Win57FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 10 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 11 | 12 | public class Win57FileSetEmitter implements FileSetEmitter { 13 | @Override 14 | public boolean matches(Platform platform, Version version) { 15 | return !platform.isUnixLike() && 16 | Objects.equals(version.getMajorVersion(), "5.7") && 17 | version.getMinorVersion() <= 17; 18 | } 19 | 20 | @Override 21 | public FileSet emit() { 22 | return FileSet.builder() 23 | .addEntry(Executable, "bin/mysqld.exe") 24 | .addEntry(Library, "bin/msvcp120.dll") 25 | .addEntry(Library, "bin/msvcr120.dll") 26 | .addEntry(Library, "bin/mysql.exe") 27 | .addEntry(Library, "bin/mysqladmin.exe") 28 | .addEntry(Library, "share/english/errmsg.sys") 29 | .addEntry(Library, "share/fill_help_tables.sql") 30 | .addEntry(Library, "share/mysql_security_commands.sql") 31 | .addEntry(Library, "share/mysql_sys_schema.sql") 32 | .addEntry(Library, "share/mysql_system_tables.sql") 33 | .addEntry(Library, "share/mysql_system_tables_data.sql") 34 | .build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Win57_18_UpFileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 10 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 11 | 12 | public class Win57_18_UpFileSetEmitter implements FileSetEmitter { 13 | @Override 14 | public boolean matches(Platform platform, Version version) { 15 | return !platform.isUnixLike() 16 | && Objects.equals(version.getMajorVersion(), "5.7") 17 | && version.getMinorVersion() > 17; 18 | } 19 | 20 | @Override 21 | public FileSet emit() { 22 | return FileSet.builder() 23 | .addEntry(Executable, "bin/mysqld.exe") 24 | .addEntry(Library, "bin/mysql.exe") 25 | .addEntry(Library, "bin/mysqladmin.exe") 26 | .addEntry(Library, "share/english/errmsg.sys") 27 | .addEntry(Library, "share/fill_help_tables.sql") 28 | .addEntry(Library, "share/mysql_security_commands.sql") 29 | .addEntry(Library, "share/mysql_sys_schema.sql") 30 | .addEntry(Library, "share/mysql_system_tables.sql") 31 | .addEntry(Library, "share/mysql_system_tables_data.sql") 32 | .build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/fileset/Win8FileSetEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.fileset; 2 | 3 | import com.wix.mysql.distribution.Version; 4 | import de.flapdoodle.embed.process.config.store.FileSet; 5 | import de.flapdoodle.embed.process.distribution.Platform; 6 | 7 | import java.util.Objects; 8 | 9 | import static de.flapdoodle.embed.process.config.store.FileType.Executable; 10 | import static de.flapdoodle.embed.process.config.store.FileType.Library; 11 | 12 | public class Win8FileSetEmitter implements FileSetEmitter { 13 | @Override 14 | public boolean matches(Platform platform, Version version) { 15 | return !platform.isUnixLike() && Objects.equals(version.getMajorVersion(), "8.0"); 16 | } 17 | 18 | @Override 19 | public FileSet emit() { 20 | return FileSet.builder() 21 | .addEntry(Executable, "bin/mysqld.exe") 22 | .addEntry(Library, "bin/mysql.exe") 23 | .addEntry(Library, "bin/mysqladmin.exe") 24 | .addEntry(Library, "share/english/errmsg.sys") 25 | // .addEntry(Library, "share/fill_help_tables.sql") 26 | // .addEntry(Library, "share/mysql_sys_schema.sql") 27 | // .addEntry(Library, "share/mysql_system_tables.sql") 28 | // .addEntry(Library, "share/mysql_system_tables_data.sql") 29 | .build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/BaseCommandEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import com.wix.mysql.utils.Utils; 6 | import de.flapdoodle.embed.process.collections.Collections; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.util.List; 13 | 14 | import static java.lang.String.format; 15 | 16 | public class BaseCommandEmitter implements CommandEmitter { 17 | @Override 18 | public boolean matches(Version version) { 19 | return true; 20 | } 21 | 22 | @Override 23 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException { 24 | File baseDir = exe.baseDir(); 25 | return Collections.newArrayList( 26 | exe.executable().getAbsolutePath(), 27 | "--no-defaults", 28 | "--log-output=NONE", 29 | format("--basedir=%s", baseDir), 30 | format("--datadir=%s/data", baseDir), 31 | format("--plugin-dir=%s/lib/plugin", baseDir), 32 | format("--lc-messages-dir=%s/share", baseDir), 33 | format("--port=%s", config.getPort()), 34 | format("--socket=%s", sockFile()), 35 | format("--user=%s", System.getProperty("user.name")), 36 | "--console", 37 | format("--character-set-server=%s", config.getCharset().getCharset()), 38 | format("--collation-server=%s", config.getCharset().getCollate()), 39 | format("--default-time-zone=%s", Utils.asHHmmOffset(config.getTimeZone()))); 40 | } 41 | 42 | /** 43 | * Helper for getting stable sock classPathFile. Saving to local instance variable on service start does not work due 44 | * to the way flapdoodle process library works - it does all init in {@link de.flapdoodle.embed.process.runtime.AbstractProcess} and instance of 45 | * {@link com.wix.mysql.MysqldProcess} is not yet present, so vars are not initialized. 46 | * This algo gives stable sock classPathFile based on single executeCommands profile, but can leave trash sock classPathFiles in tmp dir. 47 | *

48 | * Notes: 49 | * .sock classPathFile needs to be in system temp dir and not in ex. target/... 50 | * This is due to possible problems with existing mysql installation and apparmor profiles 51 | * in linuxes. 52 | */ 53 | private String sockFile() throws IOException { 54 | return Files.createTempFile(null, ".sock").toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/CommandEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | public interface CommandEmitter { 11 | boolean matches(final Version version); 12 | 13 | List emit(final MysqldConfig config, final IExtractedFileSet exe) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Mysql57CommandEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class Mysql57CommandEmitter implements CommandEmitter { 12 | @Override 13 | public boolean matches(Version version) { 14 | return version.getMajorVersion().equals("5.7"); 15 | } 16 | 17 | @Override 18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException { 19 | return Collections.newArrayList("--show_compatibility_56=ON", "--log_syslog=0"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Mysql8CommandEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class Mysql8CommandEmitter implements CommandEmitter { 12 | @Override 13 | public boolean matches(Version version) { 14 | return version.getMajorVersion().equals("8.0"); 15 | } 16 | 17 | @Override 18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException { 19 | return Collections 20 | .newArrayList("--default_authentication_plugin=mysql_native_password"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Pre57CommandEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class Pre57CommandEmitter implements CommandEmitter { 12 | @Override 13 | public boolean matches(Version version) { 14 | return version.getMajorVersion().equals("5.5") || version.getMajorVersion().equals("5.6"); 15 | } 16 | 17 | @Override 18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException { 19 | return Collections.newArrayList("--skip-name-resolve"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/ServiceCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import de.flapdoodle.embed.process.collections.Collections; 4 | 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | import static java.lang.String.format; 11 | 12 | public class ServiceCommandBuilder { 13 | private final String version; 14 | private final List args = Collections.newArrayList(); 15 | private final Set keys = new HashSet<>(); 16 | 17 | public ServiceCommandBuilder(final String version) { 18 | this.version = version; 19 | } 20 | 21 | public ServiceCommandBuilder addAll(Collection args) { 22 | for (String arg : args) { 23 | String argName = argName(arg); 24 | if (!keys.add(argName)) { 25 | throw new RuntimeException(format("argument with name '%s' is already present in argument list.", argName)); 26 | } 27 | } 28 | this.args.addAll(args); 29 | return this; 30 | } 31 | 32 | public List emit() { 33 | if (args.isEmpty()) { 34 | throw new RuntimeException("mysqld startup command was not populated for version: " + version); 35 | } 36 | 37 | return args; 38 | } 39 | 40 | private String argName(String arg) { 41 | return arg.split("=")[0]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/UserProvidedArgumentsEmitter.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.collections.Collections; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class UserProvidedArgumentsEmitter implements CommandEmitter { 12 | @Override 13 | public boolean matches(Version version) { 14 | return true; 15 | } 16 | 17 | @Override 18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException { 19 | List result = Collections.newArrayList(); 20 | for (MysqldConfig.ServerVariable var: config.getServerVariables()) { 21 | result.add(var.toCommandLineArgument()); 22 | } 23 | return result; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/FilePermissionsInitializer.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.config.store.FileType; 7 | import de.flapdoodle.embed.process.distribution.Platform; 8 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | public class FilePermissionsInitializer implements Initializer { 14 | @Override 15 | public boolean matches(Version version) { 16 | return Platform.detect().isUnixLike(); 17 | } 18 | 19 | @Override 20 | public void apply(IExtractedFileSet fileSet, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException { 21 | for (File f : fileSet.files(FileType.Library)) { 22 | f.setExecutable(true); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Initializer.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | 4 | import com.wix.mysql.config.MysqldConfig; 5 | import com.wix.mysql.distribution.Version; 6 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | 9 | import java.io.IOException; 10 | 11 | public interface Initializer { 12 | boolean matches(Version version); 13 | 14 | void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException; 15 | } 16 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Mysql57Initializer.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | import org.apache.commons.io.FileUtils; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import static java.lang.String.format; 13 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 14 | 15 | public class Mysql57Initializer implements Initializer { 16 | @Override 17 | public boolean matches(Version version) { 18 | return version.getMajorVersion().equals("5.7"); 19 | } 20 | 21 | @Override 22 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException { 23 | File baseDir = files.baseDir(); 24 | FileUtils.deleteDirectory(new File(baseDir, "data")); 25 | 26 | Process p = Runtime.getRuntime().exec(new String[] { 27 | files.executable().getAbsolutePath(), 28 | "--no-defaults", 29 | "--initialize-insecure", 30 | "--ignore-db-dir", 31 | format("--basedir=%s", baseDir), 32 | format("--datadir=%s/data", baseDir) 33 | }); 34 | 35 | new ProcessRunner(files.executable().getAbsolutePath()).run(p, runtimeConfig, config.getTimeout(NANOSECONDS)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Mysql8Initializer.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | import org.apache.commons.io.FileUtils; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import static java.lang.String.format; 13 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 14 | 15 | public class Mysql8Initializer implements Initializer { 16 | @Override 17 | public boolean matches(Version version) { 18 | return version.getMajorVersion().equals("8.0"); 19 | } 20 | 21 | @Override 22 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException { 23 | File baseDir = files.baseDir(); 24 | FileUtils.deleteDirectory(new File(baseDir, "data")); 25 | 26 | Process p = Runtime.getRuntime().exec(new String[]{ 27 | files.executable().getAbsolutePath(), 28 | "--no-defaults", 29 | "--initialize-insecure", 30 | format("--basedir=%s", baseDir), 31 | format("--datadir=%s/data", baseDir)}); 32 | 33 | new ProcessRunner(files.executable().getAbsolutePath()).run(p, runtimeConfig, config.getTimeout(NANOSECONDS)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/NixBefore57Initializer.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | import com.wix.mysql.config.MysqldConfig; 4 | import com.wix.mysql.distribution.Version; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.distribution.Platform; 7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import static java.lang.String.format; 13 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 14 | 15 | public class NixBefore57Initializer implements Initializer { 16 | @Override 17 | public boolean matches(Version version) { 18 | return Platform.detect().isUnixLike() && 19 | (version.getMajorVersion().equals("5.6") || version.getMajorVersion().equals("5.5")); 20 | } 21 | 22 | @Override 23 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException { 24 | File baseDir = files.baseDir(); 25 | Process p = Runtime.getRuntime().exec(new String[]{ 26 | "scripts/mysql_install_db", 27 | "--force", 28 | "--no-defaults", 29 | format("--basedir=%s", baseDir), 30 | format("--datadir=%s/data", baseDir)}, 31 | null, 32 | baseDir); 33 | 34 | new ProcessRunner("scripts/mysql_install_db").run(p, runtimeConfig, config.getTimeout(NANOSECONDS)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/ProcessRunner.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.setup; 2 | 3 | import com.wix.mysql.exceptions.MissingDependencyException; 4 | import com.wix.mysql.io.TimingOutProcessExecutor; 5 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 6 | import de.flapdoodle.embed.process.io.IStreamProcessor; 7 | import de.flapdoodle.embed.process.io.Processors; 8 | import de.flapdoodle.embed.process.io.ReaderProcessor; 9 | import de.flapdoodle.embed.process.io.StreamToLineProcessor; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | 14 | import static java.lang.String.format; 15 | 16 | final class ProcessRunner { 17 | 18 | private final TimingOutProcessExecutor tope; 19 | 20 | ProcessRunner(String cmd) { 21 | this.tope = new TimingOutProcessExecutor(cmd); 22 | } 23 | 24 | void run(Process p, IRuntimeConfig runtimeConfig, long timeoutNanos) throws IOException { 25 | CollectingAndForwardingStreamProcessor wrapped = 26 | new CollectingAndForwardingStreamProcessor(runtimeConfig.getProcessOutput().getOutput()); 27 | IStreamProcessor loggingWatch = StreamToLineProcessor.wrap(wrapped); 28 | 29 | try { 30 | ReaderProcessor processorOne = Processors.connect(new InputStreamReader(p.getInputStream()), loggingWatch); 31 | ReaderProcessor processorTwo = Processors.connect(new InputStreamReader(p.getErrorStream()), loggingWatch); 32 | 33 | int retCode = tope.waitFor(p, timeoutNanos); 34 | 35 | if (retCode != 0) { 36 | processorOne.join(10000); 37 | processorTwo.join(10000); 38 | resolveException(retCode, wrapped.getOutput()); 39 | } 40 | 41 | } catch (InterruptedException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | private static void resolveException(int retCode, String output) { 47 | if (output.contains("error while loading shared libraries: libaio.so")) { 48 | throw new MissingDependencyException( 49 | "System library 'libaio.so.1' missing. " + 50 | "Please install it via system package manager, ex. 'sudo apt-get install libaio1'.\n" + 51 | "For details see: http://bugs.mysql.com/bug.php?id=60544"); 52 | } else { 53 | throw new RuntimeException(format("Command exited with error code: '%s' and output: '%s'", retCode, output)); 54 | } 55 | } 56 | 57 | public static class CollectingAndForwardingStreamProcessor implements IStreamProcessor { 58 | volatile String output = ""; 59 | final IStreamProcessor forwardTo; 60 | 61 | CollectingAndForwardingStreamProcessor(IStreamProcessor forwardTo) { 62 | this.forwardTo = forwardTo; 63 | } 64 | 65 | public void process(String block) { 66 | output = output + block; 67 | forwardTo.process(block); 68 | } 69 | 70 | public void onProcessed() { 71 | forwardTo.onProcessed(); 72 | } 73 | 74 | String getOutput() { 75 | return output; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/CommandFailedException.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.exceptions; 2 | 3 | public class CommandFailedException extends RuntimeException { 4 | public CommandFailedException(String cmd, String schema, int errorCode, String errorMessage) { 5 | super(String.format("Command '%s' on schema '%s' failed with errCode '%s' and output '%s'", 6 | cmd, schema, errorCode, errorMessage)); 7 | } 8 | 9 | public CommandFailedException(String cmd, String schema, String errorMessage, Throwable e) { 10 | super(String.format("Command '%s' on schema '%s' failed with message '%s'", cmd, schema, errorMessage), e); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/MissingDependencyException.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.exceptions; 2 | 3 | public class MissingDependencyException extends RuntimeException { 4 | public MissingDependencyException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/UnsupportedPlatformException.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.exceptions; 2 | 3 | public class UnsupportedPlatformException extends RuntimeException { 4 | public UnsupportedPlatformException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/io/ConsoleProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io; 2 | 3 | import de.flapdoodle.embed.process.io.progress.StandardConsoleProgressListener; 4 | 5 | import static java.lang.String.format; 6 | 7 | public class ConsoleProgressListener extends StandardConsoleProgressListener { 8 | 9 | private ProgressStepper progressStepper = new ProgressStepper(); 10 | 11 | @Override 12 | public void progress(String label, int percent) { 13 | if (progressStepper.hasNext(percent)) { 14 | int percentageToReport = progressStepper.setAndGet(percent); 15 | System.out.println(format("%s %d%%", label, percentageToReport)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/io/NotifyingStreamProcessor.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io; 2 | 3 | import de.flapdoodle.embed.process.io.IStreamProcessor; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class NotifyingStreamProcessor implements IStreamProcessor { 10 | 11 | private final List listeners = new ArrayList<>(); 12 | private final IStreamProcessor delegate; 13 | 14 | public NotifyingStreamProcessor(IStreamProcessor processor) { 15 | this.delegate = processor; 16 | } 17 | 18 | public ResultMatchingListener addListener(ResultMatchingListener listener) { 19 | listeners.add(listener); 20 | return listener; 21 | } 22 | 23 | @Override 24 | public void process(String block) { 25 | for (ResultMatchingListener listener : listeners) { 26 | listener.onMessage(block); 27 | } 28 | delegate.process(block); 29 | } 30 | 31 | @Override 32 | public void onProcessed() { 33 | 34 | } 35 | 36 | public static class ResultMatchingListener { 37 | 38 | private final String successPattern; 39 | private final String failurePattern = "[ERROR]"; 40 | private final StringBuilder output = new StringBuilder(); 41 | private boolean initWithSuccess = false; 42 | private String failureFound = null; 43 | private boolean completed = false; 44 | 45 | public ResultMatchingListener(String successPattern) { 46 | this.successPattern = successPattern; 47 | } 48 | 49 | public void onMessage(final String message) { 50 | output.append(message); 51 | if (containsPattern()) { 52 | gotResult(true, null); 53 | } else { 54 | int failureIndex = output.indexOf(failurePattern); 55 | if (failureIndex != -1) { 56 | gotResult(false, output.substring(failureIndex)); 57 | } 58 | } 59 | } 60 | 61 | private boolean containsPattern() { 62 | return output.indexOf(this.successPattern) != -1; 63 | } 64 | 65 | private synchronized void gotResult(boolean success, String message) { 66 | initWithSuccess = success; 67 | failureFound = message; 68 | completed = true; 69 | notify(); 70 | } 71 | 72 | public synchronized void waitForResult(long timeoutMs) { 73 | try { 74 | wait(timeoutMs); 75 | if (!completed) { 76 | throw new RuntimeException(String.format("Timeout of %s sec exceeded while waiting for process to complete", TimeUnit.MILLISECONDS.toSeconds(timeoutMs))); 77 | } 78 | } catch (InterruptedException e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | public boolean isInitWithSuccess() { 84 | return initWithSuccess; 85 | } 86 | 87 | public String getFailureFound() { 88 | return failureFound; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/io/ProgressStepper.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io; 2 | 3 | public class ProgressStepper { 4 | 5 | int lastReported = -1; 6 | 7 | public boolean hasNext(int value) { 8 | return (nextCandidate(value) != lastReported); 9 | } 10 | 11 | public int setAndGet(int value) { 12 | lastReported = nextCandidate(value); 13 | return lastReported; 14 | } 15 | 16 | private int nextCandidate(int value) { 17 | return (int)(5 * (Math.floor(Math.abs(value / 5)))); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/io/TimingOutProcessExecutor.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | 5 | import java.io.IOException; 6 | 7 | import static java.lang.String.format; 8 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 9 | 10 | public class TimingOutProcessExecutor { 11 | 12 | private final String cmd; 13 | 14 | public TimingOutProcessExecutor(String cmd) { 15 | this.cmd = cmd; 16 | } 17 | 18 | public int waitFor(Process p, long timeoutNanos) throws InterruptedException, IOException { 19 | long startTime = System.nanoTime(); 20 | long rem = timeoutNanos; 21 | 22 | do { 23 | try { 24 | return p.exitValue(); 25 | } catch (IllegalThreadStateException ex) { 26 | if (rem > 0) { 27 | Thread.sleep(Math.min(NANOSECONDS.toMillis(rem) + 1, 100)); 28 | } 29 | } 30 | rem = timeoutNanos - (System.nanoTime() - startTime); 31 | } while (rem > 0); 32 | String collectedOutput = IOUtils.toString(p.getInputStream()) + IOUtils.toString(p.getErrorStream()); 33 | p.destroy(); 34 | throw new InterruptedException(format("Timeout of %s sec exceeded while waiting for '%s' to complete. Collected output: %s", 35 | NANOSECONDS.toSeconds(timeoutNanos), 36 | this.cmd, 37 | collectedOutput)); 38 | } 39 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/store/SafeExtractedArtifactStore.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.store; 2 | 3 | import de.flapdoodle.embed.process.config.store.IDownloadConfig; 4 | import de.flapdoodle.embed.process.distribution.Distribution; 5 | import de.flapdoodle.embed.process.extract.DirectoryAndExecutableNaming; 6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet; 7 | import de.flapdoodle.embed.process.store.ExtractedArtifactStore; 8 | import de.flapdoodle.embed.process.store.IDownloader; 9 | import org.apache.commons.io.FileUtils; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | /** 15 | * This is a wrapper around `ExtractedArtifactStore` which deletes the temp directory BEFORE extracting 16 | * just in case we have left overs from last crashed run. 17 | */ 18 | class SafeExtractedArtifactStore extends ExtractedArtifactStore { 19 | private String directory; 20 | 21 | SafeExtractedArtifactStore(IDownloadConfig downloadConfig, IDownloader downloader, DirectoryAndExecutableNaming extraction, DirectoryAndExecutableNaming directory) { 22 | super(downloadConfig, downloader, extraction, directory); 23 | this.directory = directory.getDirectory().asFile().getAbsolutePath(); 24 | } 25 | 26 | @Override 27 | public IExtractedFileSet extractFileSet(Distribution distribution) throws IOException { 28 | FileUtils.deleteDirectory(new File(directory)); 29 | 30 | IExtractedFileSet extractedFiles = super.extractFileSet(distribution); 31 | 32 | // Files.createDirectory(new File(directory, "data").toPath()); 33 | 34 | return extractedFiles; 35 | } 36 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/store/SafeExtractedArtifactStoreBuilder.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.store; 2 | 3 | import com.wix.mysql.config.DownloadConfig; 4 | import com.wix.mysql.config.DownloadConfigBuilder; 5 | import com.wix.mysql.config.MysqldConfig; 6 | import com.wix.mysql.config.directories.TargetGeneratedFixedPath; 7 | import com.wix.mysql.config.extract.NoopNaming; 8 | import com.wix.mysql.config.extract.PathPrefixingNaming; 9 | import de.flapdoodle.embed.process.extract.DirectoryAndExecutableNaming; 10 | import de.flapdoodle.embed.process.io.directories.FixedPath; 11 | import de.flapdoodle.embed.process.io.directories.IDirectory; 12 | import de.flapdoodle.embed.process.store.Downloader; 13 | import de.flapdoodle.embed.process.store.IArtifactStore; 14 | 15 | import java.io.File; 16 | import java.util.UUID; 17 | 18 | public class SafeExtractedArtifactStoreBuilder extends de.flapdoodle.embed.process.store.ExtractedArtifactStoreBuilder { 19 | 20 | public SafeExtractedArtifactStoreBuilder defaults( 21 | final MysqldConfig mysqldConfig, 22 | final DownloadConfig downloadConfig) { 23 | 24 | String tempExtractDir = String.format("mysql-%s-%s", mysqldConfig.getVersion().getMajorVersion(), UUID.randomUUID()); 25 | String combinedPath = new File(mysqldConfig.getTempDir(), tempExtractDir).getPath(); 26 | IDirectory preExtractDir = new FixedPath(new File(downloadConfig.getCacheDir(), "extracted").getPath()); 27 | 28 | executableNaming().setDefault(new PathPrefixingNaming("bin/")); 29 | download().setDefault(new DownloadConfigBuilder().defaults(downloadConfig).build()); 30 | downloader().setDefault(new Downloader()); 31 | extractDir().setDefault(preExtractDir); 32 | extractExecutableNaming().setDefault(new NoopNaming()); 33 | 34 | tempDir().setDefault(new TargetGeneratedFixedPath(combinedPath)); 35 | 36 | return this; 37 | } 38 | 39 | @Override 40 | public IArtifactStore build() { 41 | DirectoryAndExecutableNaming extract = new DirectoryAndExecutableNaming(get(EXTRACT_DIR_FACTORY), get(EXTRACT_EXECUTABLE_NAMING)); 42 | DirectoryAndExecutableNaming temp = new DirectoryAndExecutableNaming(tempDir().get(), executableNaming().get()); 43 | 44 | return new SafeExtractedArtifactStore(get(DOWNLOAD_CONFIG), get(DOWNLOADER), extract, temp); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/main/java/com/wix/mysql/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.Reader; 5 | import java.nio.CharBuffer; 6 | import java.util.Calendar; 7 | import java.util.List; 8 | import java.util.TimeZone; 9 | 10 | import static java.lang.String.format; 11 | import static java.util.concurrent.TimeUnit.HOURS; 12 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 13 | 14 | public class Utils { 15 | public static void closeCloseables(Reader... readers) { 16 | 17 | for (Reader reader : readers) { 18 | try { 19 | if (reader != null) reader.close(); 20 | } catch (IOException ignored) { 21 | } 22 | } 23 | } 24 | 25 | public static String asHHmmOffset(TimeZone timeZone) { 26 | long offsetInMillis = timeZone.getOffset(Calendar.getInstance().getTimeInMillis()); 27 | return format("%s%02d:%02d", 28 | offsetInMillis >= 0 ? "+" : "-", 29 | Math.abs(MILLISECONDS.toHours(offsetInMillis)), 30 | MILLISECONDS.toMinutes(offsetInMillis) - HOURS.toMinutes(MILLISECONDS.toHours(offsetInMillis))); 31 | } 32 | 33 | public static T or(T arg1, T arg2) { 34 | return arg1 != null ? arg1 : arg2; 35 | } 36 | 37 | public static boolean isNullOrEmpty(String str) { 38 | return str == null || str.isEmpty(); 39 | } 40 | 41 | public static String join(List list, String delim) { 42 | if (list.isEmpty()) return ""; 43 | StringBuilder sb = new StringBuilder(list.get(0).toString()); 44 | for (int i = 1; i < list.size(); i++) { 45 | sb.append(delim).append(list.get(i).toString()); 46 | } 47 | return sb.toString(); 48 | } 49 | 50 | public static String readToString(Reader reader) throws IOException { 51 | StringBuilder sb = new StringBuilder(); 52 | CharBuffer buf = CharBuffer.allocate(1024); 53 | while (reader.read(buf) != -1) { 54 | buf.flip(); 55 | sb.append(buf); 56 | buf.clear(); 57 | } 58 | return sb.toString(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/_maven.repositories: -------------------------------------------------------------------------------- 1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice. 2 | #Tue Mar 22 18:10:48 EET 2016 3 | external-jar-with-schemas-1.0.1.jar>in-project= 4 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/_remote.repositories: -------------------------------------------------------------------------------- 1 | #NOTE: This is an Aether internal implementation file, its format can be changed without prior notice. 2 | #Sat May 14 14:58:31 EEST 2016 3 | external-jar-with-schemas-1.0.1.jar>= 4 | external-jar-with-schemas-1.0.1.pom>= 5 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-incubator/wix-embedded-mysql/2a86b59ecd66505a7f080d30c3578f84b3521afd/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.jar -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | external-jar-with-schemas 7 | 1.0.1 8 | POM was created from install:install-file 9 | 10 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | jar 4 | external-jar-with-schemas 5 | 6 | 1.0.1 7 | 8 | 1.0.1 9 | 10 | 20160516115831 11 | 12 | 13 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/data/arbitrary_script.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO t1 values(20) 2 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/data/arbitrary_script2.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM t1 -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/db/001_init.sql: -------------------------------------------------------------------------------- 1 | create table t1 (col1 INTEGER NOT NULL,col2 VARCHAR(10)); 2 | INSERT INTO t1 values(10, "zzz"); -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/db/002_update1.sql: -------------------------------------------------------------------------------- 1 | create table t2 (col1 INTEGER NOT NULL,col2 VARCHAR(10)); 2 | INSERT INTO t2 values(20, "zzz"); -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/db/003_update2.sql: -------------------------------------------------------------------------------- 1 | create table t3 (col1 INTEGER NOT NULL,col2 VARCHAR(10)); 2 | INSERT INTO t3 values(30, "zzz"); -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/db/004_update3.sql: -------------------------------------------------------------------------------- 1 | create table t1 (col1 INTEGER, col2 VARCHAR(10)); 2 | INSERT INTO t1 values(1, '你好!'); -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/EmbeddedMysqlTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.io.File 4 | import java.util.concurrent.TimeUnit 5 | 6 | import com.wix.mysql.EmbeddedMysql._ 7 | import com.wix.mysql.ScriptResolver.classPathScript 8 | import com.wix.mysql.config.Charset._ 9 | import com.wix.mysql.config.DownloadConfig.aDownloadConfig 10 | import com.wix.mysql.config.MysqldConfig.{SystemDefaults, aMysqldConfig} 11 | import com.wix.mysql.config.ProxyFactory.aHttpProxy 12 | import com.wix.mysql.config.SchemaConfig.aSchemaConfig 13 | import com.wix.mysql.distribution.Version 14 | import com.wix.mysql.exceptions.CommandFailedException 15 | import com.wix.mysql.support.IntegrationTest._ 16 | import com.wix.mysql.support.{HttpProxyServerSupport, IntegrationTest} 17 | 18 | import scala.collection.JavaConverters._ 19 | 20 | 21 | class EmbeddedMysqlTest extends IntegrationTest with HttpProxyServerSupport { 22 | 23 | "EmbeddedMysql instance" should { 24 | 25 | "start with default values" in { 26 | val config = testConfigBuilder().build 27 | 28 | val mysqld = start(anEmbeddedMysql(config)) 29 | 30 | mysqld must 31 | haveCharsetOf(UTF8MB4) and 32 | beAvailableOn(3310, "auser", "sa", SystemDefaults.SCHEMA) and 33 | haveServerTimezoneMatching("UTC") and 34 | haveSystemVariable("basedir", contain(pathFor("/target/", "/mysql-5.7-"))) 35 | } 36 | 37 | "use custom values provided via MysqldConfig" in { 38 | val tempDir = System.getProperty("java.io.tmpdir") 39 | val config = testConfigBuilder() 40 | .withCharset(LATIN1) 41 | .withUser("zeUser", "zePassword") 42 | .withPort(1112) 43 | .withTimeZone("US/Michigan") 44 | .withTempDir(tempDir) 45 | .build 46 | 47 | val mysqld = start(anEmbeddedMysql(config)) 48 | 49 | mysqld must 50 | haveCharsetOf(LATIN1) and 51 | beAvailableOn(1112, "zeUser", "zePassword", SystemDefaults.SCHEMA) and 52 | haveServerTimezoneMatching("US/Michigan") and 53 | haveSystemVariable("basedir", contain(pathFor(tempDir, "/mysql-5.7-"))) 54 | } 55 | 56 | 57 | "allow to provide network proxy" in { 58 | val targetVersion = Version.v5_6_latest 59 | anEmbeddedMysql(targetVersion).start().stop() 60 | 61 | withProxyOn(3210, 3220, targetVersion) { (proxy, proxyPort, targetPort, version) => 62 | val config = aMysqldConfig(version).build 63 | 64 | val mysqld = start(anEmbeddedMysql(config) 65 | .withDownloadConfig(aDownloadConfig() 66 | .withProxy(aHttpProxy("127.0.0.1", proxyPort)) 67 | .withBaseUrl(s"http://localhost:$targetPort") 68 | .build()) 69 | .addSchema("aschema")) 70 | 71 | mysqld must beAvailableOn(config, "aschema") 72 | proxy.wasDownloaded must beTrue 73 | } 74 | } 75 | 76 | 77 | "accept system variables" in { 78 | val config = testConfigBuilder() 79 | .withServerVariable("max_connect_errors", 666) 80 | .build 81 | 82 | val mysqld = start(anEmbeddedMysql(config)) 83 | 84 | mysqld must haveSystemVariable("max_connect_errors", ===("666")) 85 | } 86 | 87 | "not allow to override library-managed system variables" in { 88 | val config = testConfigBuilder() 89 | .withTimeZone("US/Michigan") 90 | .withServerVariable("default-time-zone", "US/Eastern") 91 | .build 92 | 93 | start(anEmbeddedMysql(config)) must throwA[RuntimeException] 94 | } 95 | 96 | "respect provided timeout" in { 97 | start(anEmbeddedMysql(aMysqldConfig(targetTestVersion).withTimeout(10, TimeUnit.MILLISECONDS).build)) must 98 | throwA[RuntimeException].like { case e => e.getMessage must contain("0 sec") } 99 | } 100 | } 101 | 102 | "EmbeddedMysql schema reload" should { 103 | "reset schema" in { 104 | val mysqldConfig = testConfigBuilder().build 105 | val schemaConfig = aSchemaConfig("aSchema") 106 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);")) 107 | .build 108 | 109 | val mysqld = start(anEmbeddedMysql(mysqldConfig).addSchema(schemaConfig)) 110 | 111 | anUpdate(mysqld, onSchema = "aSchema", sql = "insert into t1 values(10)") must beSuccessful 112 | aSelect[java.lang.Long](mysqld, onSchema = "aSchema", sql = "select col1 from t1 where col1 = 10;") mustEqual 10 113 | 114 | mysqld.reloadSchema(schemaConfig) 115 | 116 | aSelect[java.lang.Long](mysqld, onSchema = "aSchema", sql = "select col1 from t1 where col1 = 10;") must 117 | failWith("Incorrect result size: expected 1, actual 0") 118 | } 119 | } 120 | 121 | "EmbeddedMysql schema creation" should { 122 | "use defaults" in { 123 | val config = testConfigBuilder().build 124 | 125 | val mysqld = start(anEmbeddedMysql(config).addSchema("aSchema")) 126 | 127 | mysqld must 128 | haveSchemaCharsetOf(UTF8MB4, "aSchema") and 129 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema") 130 | } 131 | 132 | "use custom values" in { 133 | val config = testConfigBuilder().build 134 | val schema = aSchemaConfig("aSchema") 135 | .withCharset(LATIN1) 136 | .build 137 | 138 | val mysqld = start(anEmbeddedMysql(config).addSchema(schema)) 139 | 140 | mysqld must 141 | haveSchemaCharsetOf(LATIN1, "aSchema") and 142 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema") 143 | } 144 | 145 | "inherit schema charset from instance" in { 146 | val config = testConfigBuilder().withCharset(LATIN1).build 147 | val schema = aSchemaConfig("aSchema").build 148 | 149 | val mysqld = start(anEmbeddedMysql(config).addSchema(schema)) 150 | 151 | mysqld must 152 | haveSchemaCharsetOf(LATIN1, "aSchema") and 153 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema") 154 | } 155 | 156 | "apply migrations when providing single file" in { 157 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build) 158 | .addSchema("aSchema", aMigrationWith("create table t1 (col1 INTEGER);"))) 159 | 160 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful 161 | } 162 | 163 | "apply migrations from multiple files" in { 164 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build) 165 | .addSchema("aSchema", 166 | aMigrationWith("create table t1 (col1 INTEGER);"), 167 | aMigrationWith("create table t2 (col1 INTEGER);")) 168 | ) 169 | 170 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful 171 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t2;") must beSuccessful 172 | } 173 | 174 | "apply migrations via SchemaConfig" in { 175 | val config = aSchemaConfig("aSchema") 176 | .withScripts( 177 | aMigrationWith("create table t1 (col1 INTEGER);"), 178 | aMigrationWith("create table t2 (col1 INTEGER);")) 179 | .withCommands( 180 | "create table t3 (col1 INTEGER);\n" + 181 | "create table t4 (col2 INTEGER)") 182 | .build 183 | 184 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(config)) 185 | 186 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful 187 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t2;") must beSuccessful 188 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t3;") must beSuccessful 189 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col2) from t4;") must beSuccessful 190 | } 191 | 192 | "quote created table name" in { 193 | val config = aSchemaConfig("a-table") 194 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);")) 195 | .build 196 | 197 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(config)) 198 | 199 | aQuery(mysqld, onSchema = "a-table", sql = "select count(col1) from t1;") must beSuccessful 200 | } 201 | 202 | } 203 | 204 | "EmbeddedMysql schema modification" should { 205 | 206 | "drop existing schema" in { 207 | val schemaConfig = aSchemaConfig("aSchema").build 208 | 209 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(schemaConfig)) 210 | 211 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName) 212 | 213 | mysqld.dropSchema(schemaConfig) 214 | 215 | mysqld must notHaveSchema(schemaConfig.getName) 216 | } 217 | 218 | "fail on dropping of non existing schema" in { 219 | val schemaConfig = aSchemaConfig("aSchema").build 220 | 221 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build)) 222 | 223 | mysqld must notHaveSchema(schemaConfig.getName) 224 | 225 | mysqld.dropSchema(schemaConfig) must throwA[CommandFailedException] 226 | } 227 | 228 | "add schema after mysqld start" in { 229 | val schemaConfig = aSchemaConfig("aSchema").build 230 | 231 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build)) 232 | 233 | mysqld must notHaveSchema(schemaConfig.getName) 234 | 235 | mysqld.addSchema(schemaConfig) 236 | 237 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName) 238 | } 239 | 240 | "fail on adding existing schema" in { 241 | val schemaConfig = aSchemaConfig("aSchema").build 242 | 243 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(schemaConfig)) 244 | 245 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName) 246 | 247 | mysqld.addSchema(schemaConfig) must throwA[CommandFailedException] 248 | } 249 | } 250 | 251 | "EmbeddedMysql sql execution" should { 252 | 253 | "Execute arbitrary scripts without dropping existing data" in { 254 | val schemaName = "aSchema" 255 | 256 | val config = aSchemaConfig(schemaName) 257 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);")) 258 | .build 259 | 260 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build)) 261 | .addSchema(config) 262 | 263 | def rowCountInTable() = aSelect[java.lang.Long](mysqld, onSchema = schemaName, sql = "select count(*) from t1;") 264 | 265 | rowCountInTable() mustEqual 0 266 | 267 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script.sql")).asScala must contain(exactly("")) 268 | rowCountInTable() mustEqual 1 269 | 270 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script.sql")) 271 | rowCountInTable() mustEqual 2 272 | 273 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script2.sql")).asScala must haveSize(1) and contain(contain("col1")) 274 | 275 | rowCountInTable() mustEqual 2 276 | } 277 | } 278 | 279 | def pathFor(basedir: String, subdir: String): String = { 280 | new File(basedir, subdir).getPath 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/ExtendedCharsetTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import com.wix.mysql.EmbeddedMysql._ 4 | import com.wix.mysql.ScriptResolver.classPathScript 5 | import com.wix.mysql.config.Charset 6 | import com.wix.mysql.config.MysqldConfig.SystemDefaults 7 | import com.wix.mysql.support.IntegrationTest 8 | import com.wix.mysql.support.IntegrationTest._ 9 | 10 | class ExtendedCharsetTest extends IntegrationTest { 11 | 12 | "EmbeddedMysql instance" should { 13 | 14 | "support non-latin characters in login/password" in { 15 | skipped("not really supported for windows") 16 | // val config = testConfigBuilder 17 | // .withCharset(Charset.UTF8MB4) 18 | // .withUser("你", "好").build 19 | // 20 | // val mysqld = start(anEmbeddedMysql(config)) 21 | // 22 | // mysqld must beAvailableOn(3310, "你", "好", SystemDefaults.SCHEMA) 23 | } 24 | 25 | "support migration from file with extended charset" in { 26 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().withCharset(Charset.UTF8MB4).build) 27 | .addSchema("aSchema", classPathScript("/db/004_update3.sql"))) 28 | 29 | aSelect[java.lang.String](mysqld, onSchema = "aSchema", sql = "select col2 from t1 where col1 = 1;") mustEqual "你好!" 30 | } 31 | 32 | "support inline migration with extended charset" in { 33 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().withCharset(Charset.UTF8MB4).build) 34 | .addSchema( 35 | "aSchema", 36 | aMigrationWith("create table t1 (col1 INTEGER, col2 VARCHAR(10));\nINSERT INTO t1 values(1, '你好!');"))) 37 | 38 | aSelect[java.lang.String](mysqld, onSchema = "aSchema", sql = "select col2 from t1 where col1 = 1;") mustEqual "你好!" 39 | } 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/JavaUsageExamplesTest.java: -------------------------------------------------------------------------------- 1 | package com.wix.mysql; 2 | 3 | import com.wix.mysql.config.DownloadConfig; 4 | import com.wix.mysql.config.MysqldConfig; 5 | import com.wix.mysql.config.SchemaConfig; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql; 12 | import static com.wix.mysql.ScriptResolver.classPathScript; 13 | import static com.wix.mysql.ScriptResolver.classPathScripts; 14 | import static com.wix.mysql.config.Charset.LATIN1; 15 | import static com.wix.mysql.config.Charset.UTF8; 16 | import static com.wix.mysql.config.DownloadConfig.aDownloadConfig; 17 | import static com.wix.mysql.config.MysqldConfig.aMysqldConfig; 18 | import static com.wix.mysql.config.ProxyFactory.aHttpProxy; 19 | import static com.wix.mysql.config.SchemaConfig.aSchemaConfig; 20 | import static com.wix.mysql.distribution.Version.v5_6_latest; 21 | import static com.wix.mysql.distribution.Version.v5_7_latest; 22 | 23 | @Ignore 24 | public class JavaUsageExamplesTest { 25 | 26 | @Test 27 | public void defaultConfigurationAndASingleSchema() { 28 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 29 | .addSchema("aschema", classPathScript("db/001_init.sql")) 30 | .start(); 31 | 32 | //do work 33 | 34 | mysqld.stop(); //optional, as there is a shutdown hook 35 | } 36 | 37 | @Test 38 | public void defaultConfigurationAndASingleSchemaWithMultipleMigrations() { 39 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 40 | .addSchema("aschema", classPathScripts("db/*.sql")) 41 | .start(); 42 | 43 | //do work 44 | 45 | mysqld.stop(); //optional, as there is a shutdown hook 46 | } 47 | 48 | @Test 49 | public void mysqldConfigAndMultipleSchemas() { 50 | MysqldConfig config = aMysqldConfig(v5_7_latest) 51 | .withCharset(UTF8) 52 | .withPort(2215) 53 | .withUser("differentUser", "anotherPasword") 54 | .build(); 55 | 56 | EmbeddedMysql mysqld = anEmbeddedMysql(config) 57 | .addSchema("aschema", classPathScript("db/001_init.sql")) 58 | .addSchema("aschema2", classPathScripts("db/*.sql")) 59 | .start(); 60 | 61 | //do work 62 | 63 | mysqld.stop(); //optional, as there is a shutdown hook 64 | } 65 | 66 | @Test 67 | public void schemaConfigViaBuilder() { 68 | SchemaConfig schema = aSchemaConfig("aSchema") 69 | .withScripts(classPathScript("db/001_init.sql")) 70 | .withCharset(LATIN1) 71 | .build(); 72 | 73 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 74 | .addSchema(schema) 75 | .addSchema("aschema2", classPathScripts("db/*.sql")) 76 | .start(); 77 | 78 | //do work 79 | 80 | mysqld.stop(); //optional, as there is a shutdown hook 81 | } 82 | 83 | @Test 84 | public void schemaReset() { 85 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest) 86 | .addSchema("aschema", classPathScript("db/001_init.sql")) 87 | .start(); 88 | 89 | //do work 90 | 91 | SchemaConfig schema = aSchemaConfig("aschema") 92 | .withScripts(classPathScript("db/001_init.sql")) 93 | .build(); 94 | mysqld.reloadSchema(schema); 95 | 96 | //continue on doing work 97 | 98 | mysqld.stop(); //optional, as there is a shutdown hook 99 | } 100 | 101 | @Test 102 | public void customTimeout() { 103 | MysqldConfig config = aMysqldConfig(v5_6_latest) 104 | .withTimeout(2, TimeUnit.MINUTES) 105 | .build(); 106 | 107 | EmbeddedMysql mysqld = anEmbeddedMysql(config) 108 | .addSchema("aschema", classPathScript("db/001_init.sql")) 109 | .start(); 110 | 111 | //do work 112 | 113 | mysqld.stop(); //optional, as there is a shutdown hook 114 | } 115 | 116 | @Test 117 | public void customTempDir() { 118 | MysqldConfig config = aMysqldConfig(v5_6_latest) 119 | .withTempDir(System.getProperty("java.io.tmpdir")) 120 | .build(); 121 | 122 | EmbeddedMysql mysqld = anEmbeddedMysql(config) 123 | .addSchema("aschema", classPathScript("db/001_init.sql")) 124 | .start(); 125 | 126 | //do work 127 | 128 | mysqld.stop(); //optional, as there is a shutdown hook 129 | } 130 | 131 | @Test 132 | public void httpProxy() { 133 | DownloadConfig downloadConfig = aDownloadConfig() 134 | .withProxy(aHttpProxy("remote.host", 8080)) 135 | .build(); 136 | 137 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest, downloadConfig) 138 | .addSchema("aschema", classPathScript("db/001_init.sql")) 139 | .start(); 140 | 141 | //do work 142 | 143 | mysqld.stop(); //optional, as there is a shutdown hook 144 | } 145 | 146 | 147 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/MysqlTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql 4 | import com.wix.mysql.config.Charset 5 | import com.wix.mysql.exceptions.CommandFailedException 6 | import com.wix.mysql.support.IntegrationTest 7 | import com.wix.mysql.support.IntegrationTest.targetTestVersion 8 | 9 | class MysqlTest extends IntegrationTest { 10 | 11 | "mysql should emit exception info with message from 'mysql' command output'" in { 12 | val mysqld = start(anEmbeddedMysql(targetTestVersion)) 13 | val mysql = new MysqlClient(mysqld.getConfig, mysqld.executable, "information_schema", Charset.UTF8MB4) 14 | 15 | mysql.executeCommands("sele qwe from zz;") must throwA[CommandFailedException].like { 16 | case e: CommandFailedException => e.getMessage must contain( 17 | "ERROR 1064 (42000) at line 1: You have an error in your SQL syntax;") 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/ParallelEmbeddedMysqlTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.util.concurrent.TimeUnit 4 | 5 | import com.wix.mysql.EmbeddedMysql._ 6 | import com.wix.mysql.config.MysqldConfig.SystemDefaults 7 | import com.wix.mysql.support.IntegrationTest 8 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder 9 | 10 | import scala.concurrent.ExecutionContext.Implicits.global 11 | import scala.concurrent.duration._ 12 | import scala.concurrent.{Await, Future} 13 | 14 | class ParallelEmbeddedMysqlTest extends IntegrationTest { 15 | 16 | "EmbeddedMysql instance" should { 17 | 18 | "run 2 instances in parallel" in { 19 | withCleanRepo { 20 | Seq(runMysql(onPort = 3310), runMysql(onPort = 3311)).map(Await.result(_, 15 minutes)) must beSuccessful 21 | } 22 | } 23 | } 24 | 25 | def runMysql(onPort: Int) = Future { 26 | val config = testConfigBuilder().withPort(onPort).withTimeout(2, TimeUnit.MINUTES).build 27 | val mysqld = start(anEmbeddedMysql(config)) 28 | 29 | mysqld must beAvailableOn(onPort, "auser", "sa", SystemDefaults.SCHEMA) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/SchemaConfigTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.io.File 4 | 5 | import com.wix.mysql.config.Charset.aCharset 6 | import com.wix.mysql.config.SchemaConfig.aSchemaConfig 7 | import org.specs2.mutable.SpecWithJUnit 8 | 9 | import scala.collection.convert.wrapAll._ 10 | 11 | class SchemaConfigTest extends SpecWithJUnit { 12 | 13 | "SchemaConfig" should { 14 | "build with defaults" in { 15 | val schemaConfig = aSchemaConfig("aschema").build 16 | 17 | schemaConfig.getName mustEqual "aschema" 18 | schemaConfig.getCharset must beNull 19 | schemaConfig.getScripts must beEmpty 20 | } 21 | 22 | "build with custom charset" in { 23 | val charset = aCharset("charset", "collate") 24 | 25 | val schemaConfig = aSchemaConfig("aschema") 26 | .withCharset(charset) 27 | .build 28 | 29 | schemaConfig.getCharset mustEqual charset 30 | } 31 | 32 | "build with sources" in { 33 | val sources = Seq(Sources.fromFile(new File("/some")), Sources.fromFile(new File("/some/other"))) 34 | 35 | val schemaConfig = aSchemaConfig("aschema") 36 | .withScripts(sources) 37 | .build 38 | 39 | schemaConfig.getScripts.toSeq mustEqual sources 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/ScriptResolverTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.util 4 | 5 | import com.wix.mysql.ScriptResolver._ 6 | import org.specs2.matcher.{FileMatchers, Matcher} 7 | import org.specs2.mutable.SpecificationWithJUnit 8 | 9 | import scala.collection.convert.decorateAsScala._ 10 | 11 | class ScriptResolverTest extends SpecificationWithJUnit with FileMatchers { 12 | val contentsOf001Init = "create table t1 " 13 | val contentsOf002Update = "create table t2 " 14 | val contentsOf003Update = "create table t3 " 15 | val contentsOf004Update = "create table t1 " 16 | val contentsOf001InitInJar = "create table t1_jar" 17 | val contentsOf002UpdateInJar = "create table t2_jar" 18 | 19 | "classPathFile" should { 20 | 21 | "resolve a single classPath file" in { 22 | classPathFile("/db/001_init.sql") must beAScriptWith(contentsOf001Init) 23 | } 24 | 25 | "resolve a single classPath file without preceding '/'" in { 26 | classPathFile("db/001_init.sql") must beAScriptWith(contentsOf001Init) 27 | } 28 | 29 | "throw a ScriptResolutionException for a non-existent script" in { 30 | classPathFile("db/not-exists.sql") must throwA[ScriptResolutionException] 31 | } 32 | } 33 | 34 | "classPathFiles" should { 35 | 36 | "resolve multiple classPath files" in { 37 | classPathFiles("/db/*.sql") must containScripts( 38 | contentsOf001Init, 39 | contentsOf002Update, 40 | contentsOf003Update, 41 | contentsOf004Update) 42 | } 43 | 44 | "resolve multiple classPath files without preceding '/'" in { 45 | classPathFiles("db/*.sql") must containScripts( 46 | contentsOf001Init, 47 | contentsOf002Update, 48 | contentsOf003Update, 49 | contentsOf004Update) 50 | } 51 | 52 | "throw a ScriptResolutionException if no classPathFiles are found" in { 53 | classPathFiles("does-not-exist/*.sql") must throwA[ScriptResolutionException] 54 | } 55 | } 56 | 57 | "classPathScript" should { 58 | 59 | "resolve a single classPath file" in { 60 | classPathScript("/db/001_init.sql") must beAScriptWith(contentsOf001Init) 61 | } 62 | 63 | "resolve a single classPath file without preceding '/'" in { 64 | classPathScript("db/001_init.sql") must beAScriptWith(contentsOf001Init) 65 | } 66 | 67 | "resolve a single classPath file within packaged jar" in { 68 | classPathScript("/db-jar/001_jar-init.sql") must beAScriptWith(contentsOf001InitInJar) 69 | } 70 | 71 | "resolve a single classPath file within packaged jar without preceding '/'" in { 72 | classPathScript("db-jar/001_jar-init.sql") must beAScriptWith(contentsOf001InitInJar) 73 | } 74 | 75 | "throw a ScriptResolutionException for a non-existent script" in { 76 | classPathScript("db/not-exists.sql") must throwA[ScriptResolutionException] 77 | } 78 | } 79 | 80 | "classPathScripts" should { 81 | 82 | "resolve multiple classPath files" in { 83 | classPathScripts("/db/*.sql") must containScripts( 84 | contentsOf001Init, 85 | contentsOf002Update, 86 | contentsOf003Update, 87 | contentsOf004Update) 88 | } 89 | 90 | "resolve multiple classPath files without preceding '/'" in { 91 | classPathScripts("db/*.sql") must containScripts( 92 | contentsOf001Init, 93 | contentsOf002Update, 94 | contentsOf003Update, 95 | contentsOf004Update) 96 | } 97 | 98 | "resolve multiple classPath scripts within jar in classpath" in { 99 | classPathScripts("/db-jar/*.sql") must containScripts(contentsOf001InitInJar, contentsOf002UpdateInJar) 100 | } 101 | 102 | "resolve multiple classPath scripts within jar in classpath without preceding '/'" in { 103 | classPathScripts("/db-jar/*.sql") must containScripts(contentsOf001InitInJar, contentsOf002UpdateInJar) 104 | } 105 | 106 | "throw a ScriptResolutionException if no classPathFiles are found" in { 107 | classPathScripts("does-not-exist/*.sql") must throwA[ScriptResolutionException] 108 | } 109 | } 110 | 111 | def aScriptWith(fragment: String): Matcher[SqlScriptSource] = 112 | beAScriptWith(fragment) 113 | 114 | def beAScriptWith(fragment: String): Matcher[SqlScriptSource] = 115 | startWith(fragment) ^^ { 116 | (_: SqlScriptSource).read aka "script fragment mismatch" 117 | } 118 | 119 | def containScripts(fragments: String*): Matcher[util.List[SqlScriptSource]] = 120 | contain(exactly(fragments.map(aScriptWith): _*)).inOrder ^^ { 121 | (_: java.util.List[SqlScriptSource]).asScala 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/SupportedVersionsTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql 4 | import com.wix.mysql.config.MysqldConfig 5 | import com.wix.mysql.distribution.Version 6 | import com.wix.mysql.support.IntegrationTest 7 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder 8 | import org.specs2.matcher.Scope 9 | import org.specs2.specification.core.Fragment 10 | 11 | class SupportedVersionsTest extends IntegrationTest { 12 | 13 | val versionsToTest: Seq[Version] = Seq( 14 | Version.v5_5_latest, 15 | Version.v5_6_latest, 16 | Version.v5_7_latest, 17 | Version.v8_0_11, 18 | Version.v8_latest) filter (_.supportsCurrentPlatform) 19 | 20 | trait Context extends Scope { 21 | val log: Iterable[String] = aLogFor("root") 22 | } 23 | 24 | Fragment.foreach(versionsToTest) { version => 25 | s"$version should work on ${System.getProperty("os.name")}" in new Context { 26 | val config: MysqldConfig = testConfigBuilder(version).build 27 | 28 | val mysqld: EmbeddedMysql = start(anEmbeddedMysql(config).addSchema("aschema")) 29 | 30 | mysqld must beAvailableOn(config, "aschema") 31 | 32 | log must not(contain("Something bad happened.")) 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/UtilsTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql 2 | 3 | import java.util.{Calendar, Date, SimpleTimeZone} 4 | 5 | import com.wix.mysql.utils.Utils 6 | import org.specs2.mutable.SpecWithJUnit 7 | 8 | class UtilsTest extends SpecWithJUnit { 9 | "Utils" should { 10 | "take daylight savings into account" in { 11 | val cal = Calendar.getInstance() 12 | val millisInHour = 60 * 60 * 1000 13 | // this fake tz should always be in daylight time: 14 | val tz = new SimpleTimeZone(millisInHour * -5, "EDT", Calendar.JANUARY, 1, 0, 0, Calendar.DECEMBER, 31, 0, millisInHour * 24 - 1, millisInHour) 15 | val offset = Utils.asHHmmOffset(tz) 16 | tz.observesDaylightTime() mustEqual true 17 | tz.inDaylightTime(new Date()) mustEqual true 18 | offset mustEqual "-04:00" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/config/DownloadConfigTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config 2 | 3 | import java.io.File 4 | import java.nio.file.Files 5 | 6 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql 7 | import com.wix.mysql.config.DownloadConfig.aDownloadConfig 8 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder 9 | import com.wix.mysql.support.{IntegrationTest, MysqlCacheServingHttpServer} 10 | import de.flapdoodle.embed.process.exceptions.DistributionException 11 | import org.apache.commons.io.FileUtils.deleteDirectory 12 | import org.specs2.matcher.FileMatchers 13 | import org.specs2.mutable.BeforeAfter 14 | 15 | class DownloadConfigTest extends IntegrationTest with FileMatchers { 16 | 17 | "EmbeddedMysql download config" should { 18 | 19 | "store download cache in custom location" in { 20 | withTempDir { tempDir => 21 | val defaultCachePath = aDownloadConfig().build().getCacheDir 22 | val downloadConfig = aDownloadConfig().withCacheDir(tempDir).build() 23 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build, downloadConfig)) 24 | 25 | tempDir must not(beEqualToIgnoringSep(defaultCachePath)) 26 | aPath(tempDir, "extracted") must beADirectory and exist 27 | } 28 | } 29 | 30 | "uses custom download base url" in { 31 | withTempDir { tempDir => 32 | val downloadConfig = aDownloadConfig() 33 | .withCacheDir(tempDir) 34 | .withBaseUrl(s"http://localhost:2222") 35 | .build() 36 | 37 | start(anEmbeddedMysql(testConfigBuilder().build, downloadConfig)) must throwA[DistributionException].like { 38 | case e => e.getMessage must contain("Could not open inputStream for http://localhost:2222/MySQL-5.7") 39 | } 40 | } 41 | } 42 | 43 | "downloads via custom download base url" in new context { 44 | val mysqldConfig = testConfigBuilder().build 45 | 46 | ensureVersionPresentInCache(mysqldConfig) 47 | 48 | withTempDir { tempDir => 49 | val downloadConfig = aDownloadConfig() 50 | .withDownloadCacheDir(tempDir) 51 | .withBaseUrl(s"http://localhost:${httpServer.port}") 52 | .build() 53 | 54 | start(anEmbeddedMysql(mysqldConfig, downloadConfig)) 55 | 56 | aPath(tempDir, "extracted") must beADirectory and exist 57 | } 58 | } 59 | 60 | } 61 | 62 | class context extends BeforeAfter { 63 | val httpServer = new MysqlCacheServingHttpServer 64 | 65 | override def before: Any = { 66 | if (httpServer != null) { 67 | httpServer.start() 68 | } 69 | } 70 | 71 | override def after: Any = { 72 | if (httpServer != null) { 73 | httpServer.stop() 74 | } 75 | } 76 | } 77 | 78 | def aPath(basedir: String, subdir: String): File = { 79 | new File(basedir, subdir) 80 | } 81 | 82 | def withTempDir[T](f: String => T): T = { 83 | val tempDir = Files.createTempDirectory("embed-mysql-test").toFile 84 | 85 | try { 86 | f(tempDir.getAbsolutePath) 87 | } finally { 88 | deleteDirectory(tempDir) 89 | } 90 | } 91 | 92 | def ensureVersionPresentInCache(config: MysqldConfig): Unit = { 93 | anEmbeddedMysql(config).start().stop() 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/config/MysqldConfigTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.config 2 | 3 | import java.util.TimeZone 4 | import java.util.concurrent.TimeUnit 5 | 6 | import com.wix.mysql.config.Charset.{LATIN1, defaults} 7 | import com.wix.mysql.config.MysqldConfig.aMysqldConfig 8 | import com.wix.mysql.distribution.Version._ 9 | import org.specs2.mutable.SpecWithJUnit 10 | 11 | import scala.collection.JavaConverters._ 12 | 13 | class MysqldConfigTest extends SpecWithJUnit { 14 | 15 | "MysqldConfig" should { 16 | 17 | "build with defaults" in { 18 | val mysqldConfig = aMysqldConfig(v5_6_latest).build() 19 | 20 | mysqldConfig.getPort mustEqual 3310 21 | mysqldConfig.getVersion mustEqual v5_6_latest 22 | mysqldConfig.getCharset mustEqual defaults() 23 | mysqldConfig.getUsername mustEqual "auser" 24 | mysqldConfig.getPassword mustEqual "sa" 25 | mysqldConfig.getTimeZone mustEqual TimeZone.getTimeZone("UTC") 26 | mysqldConfig.getTimeout(TimeUnit.SECONDS) mustEqual 30 27 | } 28 | 29 | "accept custom port, user, charset, timezone" in { 30 | val mysqldConfig = aMysqldConfig(v5_6_latest) 31 | .withPort(1111) 32 | .withCharset(LATIN1) 33 | .withUser("otheruser", "otherpassword") 34 | .withTimeZone("Europe/Vilnius") 35 | .withTimeout(20, TimeUnit.SECONDS) 36 | .build() 37 | 38 | mysqldConfig.getPort mustEqual 1111 39 | mysqldConfig.getCharset mustEqual LATIN1 40 | mysqldConfig.getUsername mustEqual "otheruser" 41 | mysqldConfig.getPassword mustEqual "otherpassword" 42 | mysqldConfig.getTimeZone mustEqual TimeZone.getTimeZone("Europe/Vilnius") 43 | mysqldConfig.getTimeout(TimeUnit.MILLISECONDS) mustEqual 20000 44 | } 45 | 46 | "accept custom system variables" in { 47 | val mysqldConfig = aMysqldConfig(v5_6_latest) 48 | .withServerVariable("some-int", 123) 49 | .withServerVariable("some-string", "one") 50 | .withServerVariable("some-boolean", false) 51 | .build 52 | 53 | mysqldConfig.getServerVariables.asScala.map(_.toCommandLineArgument) mustEqual 54 | Seq("--some-int=123", "--some-string=one", "--some-boolean=false") 55 | } 56 | 57 | "accept free port" in { 58 | val mysqldConfig = aMysqldConfig(v5_6_latest) 59 | .withFreePort() 60 | .build() 61 | 62 | mysqldConfig.getPort mustNotEqual 3310 63 | } 64 | 65 | "fail if building with user 'root'" in { 66 | aMysqldConfig(v5_6_latest) 67 | .withUser("root", "doesnotmatter") 68 | .build() must throwA[IllegalArgumentException](message = "Usage of username 'root' is forbidden") 69 | } 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/MacOsSierraTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution 2 | 3 | import java.lang.System.{getProperty, setProperty} 4 | 5 | import com.wix.mysql.exceptions.UnsupportedPlatformException 6 | import org.specs2.execute.{AsResult, Result} 7 | import org.specs2.mutable.{Around, SpecWithJUnit} 8 | import org.specs2.specification.AroundEach 9 | import org.specs2.specification.core.Fragment 10 | 11 | class MacOsSierraTest extends SpecWithJUnit with Around with AroundEach { 12 | sequential 13 | 14 | val unsupportedVersions: Array[Version] = Version.values filter (!_.supportsCurrentPlatform) 15 | 16 | Fragment.foreach(unsupportedVersions) { version => 17 | 18 | s"$version should fail on Sierra with helpful message" in { 19 | 20 | version.asInDownloadPath() must throwAn[UnsupportedPlatformException]( 21 | message = s"$version is not supported on Mac OS Sierra. Minimum supported version is 5.7.15" 22 | ) 23 | } 24 | } 25 | 26 | def around[R: AsResult](r: => R): Result = { 27 | val currentOsName = getProperty("os.name") 28 | val currentOsVersion = getProperty("os.version") 29 | 30 | setProperty("os.name", "Mac OS X") 31 | setProperty("os.version", "10.12") 32 | 33 | try AsResult(r) 34 | finally { 35 | setProperty("os.name", currentOsName) 36 | setProperty("os.version", currentOsVersion) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/VersionTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution 2 | 3 | import java.lang.System._ 4 | 5 | import com.wix.mysql.distribution.Version._ 6 | import com.wix.mysql.exceptions.UnsupportedPlatformException 7 | import de.flapdoodle.embed.process.distribution.Platform 8 | import de.flapdoodle.embed.process.distribution.Platform._ 9 | import org.specs2.execute.{AsResult, Result} 10 | import org.specs2.mutable.SpecWithJUnit 11 | import org.specs2.specification.AroundEach 12 | 13 | class VersionTest extends SpecWithJUnit with AroundEach { 14 | sequential 15 | 16 | "platform detection should detect" >> { 17 | "OS X" in { 18 | givenPlatformSetTo(OS_X) 19 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15-osx10.11" 20 | } 21 | 22 | "OS X 5.7.17+, 5.6.35+ and use different file scheme" in { 23 | givenPlatformSetTo(OS_X) 24 | 25 | v5_7_17.asInDownloadPath must contain( "macos" ) 26 | v5_6_35.asInDownloadPath must contain( "macos" ) 27 | } 28 | 29 | 30 | "Windows" in { 31 | givenPlatformSetTo(Windows) 32 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15" 33 | } 34 | 35 | "Linux" in { 36 | givenPlatformSetTo(Linux) 37 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15-linux-glibc2.5" 38 | v5_7_18.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.18-linux-glibc2.5" 39 | v5_7_19.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.19-linux-glibc2.12" 40 | } 41 | 42 | } 43 | 44 | "verify that" >> { 45 | "windows for 5.5.X is not supported" in { 46 | givenPlatformSetTo(Windows) 47 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException] 48 | } 49 | 50 | "solaris is not supported" in { 51 | givenPlatformSetTo(Solaris) 52 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException] 53 | } 54 | 55 | "freebsd is not supported" in { 56 | givenPlatformSetTo(FreeBSD) 57 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException] 58 | } 59 | } 60 | 61 | def givenPlatformSetTo(platform: Platform): String = platform match { 62 | case Windows => setProperty("os.name", "Windows") 63 | case OS_X => setProperty("os.name", "Mac OS X") 64 | case Linux => setProperty("os.name", "Linux") 65 | case Solaris => setProperty("os.name", "SunOS") 66 | case FreeBSD => setProperty("os.name", "FreeBSD") 67 | case _ => throw new UnsupportedPlatformException("Unrecognized platform, currently not supported") 68 | } 69 | 70 | def around[R: AsResult](r: => R): Result = { 71 | val current = getProperty("os.name") 72 | try AsResult(r) 73 | finally setProperty("os.name", current) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/service/ServiceCommandBuilderTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.distribution.service 2 | 3 | import org.specs2.mutable.SpecWithJUnit 4 | 5 | import scala.collection.JavaConverters._ 6 | import scala.collection.convert.wrapAll._ 7 | 8 | class ServiceCommandBuilderTest extends SpecWithJUnit { 9 | "ServiceCommandBuilder" should { 10 | 11 | "build a command" in { 12 | new ServiceCommandBuilder("v1") 13 | .addAll(Seq("one", "two")) 14 | .emit().asScala mustEqual Seq("one", "two") 15 | } 16 | 17 | "throw an exception if emitting empty command" in { 18 | new ServiceCommandBuilder("v1").emit() must 19 | throwA[RuntimeException].like { case e => e.getMessage must contain("was not populated for version: v1") } 20 | } 21 | 22 | "throw an exception if adding duplicate command" in { 23 | new ServiceCommandBuilder("v1") 24 | .addAll(Seq("--some", "--another=12")) 25 | .addAll(Seq("--some")) must 26 | throwA[RuntimeException].like { case e => e.getMessage must contain("argument with name '--some' is already present") } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/io/ProgressStepperTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io 2 | 3 | import org.specs2.mutable.SpecWithJUnit 4 | 5 | class ProgressStepperTest extends SpecWithJUnit { 6 | 7 | "Progress Stepper setAndGet" should { 8 | 9 | "return 0 initially" in { 10 | new ProgressStepper().setAndGet(0) mustEqual 0 11 | } 12 | 13 | "return 5 for 5" in { 14 | new ProgressStepper().setAndGet(5) mustEqual 5 15 | } 16 | 17 | "return 5 for 9" in { 18 | new ProgressStepper().setAndGet(9) mustEqual 5 19 | } 20 | 21 | "return 10 for 10" in { 22 | new ProgressStepper().setAndGet(10) mustEqual 10 23 | } 24 | } 25 | 26 | "Progress Stepper hasNext" should { 27 | 28 | "is true for initial 0" in { 29 | new ProgressStepper().hasNext(0) must beTrue 30 | } 31 | 32 | "is false for already set value" in { 33 | val stepper = new ProgressStepper() 34 | stepper.setAndGet(0) 35 | stepper.hasNext(0) must beFalse 36 | } 37 | 38 | "is false for less than +5" in { 39 | val stepper = new ProgressStepper() 40 | stepper.setAndGet(0) 41 | stepper.hasNext(4) must beFalse 42 | } 43 | 44 | "is true for +5" in { 45 | val stepper = new ProgressStepper() 46 | stepper.setAndGet(0) 47 | stepper.hasNext(5) must beTrue 48 | } 49 | 50 | } 51 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/io/ResultMatchingListenerTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io 2 | 3 | import com.wix.mysql.io.NotifyingStreamProcessor.ResultMatchingListener 4 | import org.specs2.mutable.SpecWithJUnit 5 | 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | import scala.concurrent.Future 8 | 9 | class ResultMatchingListenerTest extends SpecWithJUnit { 10 | 11 | "ResultMatchingListenerTest" should { 12 | 13 | "should return success given output matched provided pattern" in { 14 | val listener = new ResultMatchingListener("SUCCESS") 15 | 16 | Future { 17 | listener.waitForResult(4000) 18 | } 19 | 20 | listener.onMessage("SUCCESS: completed") 21 | 22 | listener.isInitWithSuccess must beTrue 23 | listener.getFailureFound must beNull 24 | } 25 | 26 | "throw a timeout exception if command does not complete within provided timeout" in { 27 | new ResultMatchingListener("SUCCESS").waitForResult(1000) must 28 | throwA[RuntimeException].like { case e => e.getMessage must contain("Timeout of 1 sec exceeded") } 29 | } 30 | 31 | "should return error output given error expression matched" in { 32 | val listener = new ResultMatchingListener("SUCCESS") 33 | 34 | Future { 35 | Thread.sleep(500) 36 | listener.onMessage("[ERROR] woops") 37 | } 38 | 39 | listener.waitForResult(5000) 40 | 41 | listener.isInitWithSuccess must beFalse 42 | listener.getFailureFound mustEqual "[ERROR] woops" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/io/TimingOutProcessExecutorTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.io 2 | 3 | import java.io.{InputStream, OutputStream, StringReader} 4 | import java.util.concurrent.TimeUnit 5 | import java.util.concurrent.atomic.AtomicInteger 6 | 7 | import org.specs2.mutable.SpecWithJUnit 8 | 9 | class TimingOutProcessExecutorTest extends SpecWithJUnit { 10 | 11 | "TimingOutProcessExecutor" should { 12 | 13 | "throw an exception if command does not complete within provided timeout" in { 14 | new TimingOutProcessExecutor("cmd").waitFor(new FakeProcess(Integer.MAX_VALUE), TimeUnit.MILLISECONDS.toNanos(1000)) must 15 | throwA[InterruptedException].like { case e => e.getMessage must contain("Timeout of 1 sec exceeded while waiting for 'cmd'")} 16 | } 17 | 18 | "return process exit code if command does complete within execution bounds" in { 19 | new TimingOutProcessExecutor("").waitFor(new FakeProcess(3), TimeUnit.MILLISECONDS.toNanos(2000)) mustEqual 0 20 | } 21 | } 22 | } 23 | 24 | class FakeProcess(val completeAfterNumberOfCalls: Int) extends Process { 25 | val exitValueInvoctionCounter = new AtomicInteger(completeAfterNumberOfCalls) 26 | 27 | override def exitValue(): Int = { 28 | exitValueInvoctionCounter.decrementAndGet() match { 29 | case 0 => 0 30 | case _ => throw new IllegalThreadStateException() 31 | } 32 | } 33 | 34 | override def destroy(): Unit = {} 35 | 36 | override def waitFor(): Int = ??? 37 | 38 | override def getOutputStream: OutputStream = ??? 39 | 40 | override def getErrorStream: InputStream = new FakeInputStream("err") 41 | 42 | override def getInputStream: InputStream = new FakeInputStream("err") 43 | } 44 | 45 | class FakeInputStream(collectedOutput: String) extends InputStream { 46 | val output = new StringReader(collectedOutput) 47 | override def read(): Int = output.read() 48 | } 49 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/support/HttpProxyServerSupport.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.support 2 | 3 | import java.io.{File, FileInputStream} 4 | import java.nio.file.Files 5 | import java.util.UUID 6 | 7 | import com.wix.mysql.PackagePaths 8 | import com.wix.mysql.distribution.Version 9 | import de.flapdoodle.embed.process.distribution.Distribution 10 | import de.flapdoodle.embed.process.io.directories.UserHome 11 | import fi.iki.elonen.NanoHTTPD 12 | import org.littleshoot.proxy.impl.DefaultHttpProxyServer 13 | import org.littleshoot.proxy.{ActivityTrackerAdapter, FlowContext, HttpProxyServer} 14 | 15 | trait HttpProxyServerSupport { 16 | 17 | def withProxyOn[T](proxyPort: Int, targetPort: Int, servedVersion: Version)(f: (ConnectedActivityTracker, Int, Int, Version) => T): T = { 18 | val tracker = new ConnectedActivityTracker() 19 | val proxyBootstrap = DefaultHttpProxyServer 20 | .bootstrap() 21 | .plusActivityTracker(tracker) 22 | .withPort(proxyPort) 23 | 24 | var proxyServer: Option[HttpProxyServer] = None 25 | var staticsServer: Option[StaticsServer] = None 26 | val (fileToProxy, restoreFiles) = ProxyFiles(servedVersion) 27 | try { 28 | staticsServer = new StaticsServer(targetPort, fileToProxy).doStart() 29 | proxyServer = Some(proxyBootstrap.start()) 30 | 31 | f(tracker, proxyPort, targetPort, servedVersion) 32 | } finally { 33 | proxyServer.foreach(_.stop()) 34 | staticsServer.foreach(_.stop()) 35 | restoreFiles() 36 | } 37 | } 38 | } 39 | 40 | class ConnectedActivityTracker extends ActivityTrackerAdapter { 41 | val fiftyMegabytesInBytes = 50000000 42 | var bytesSent: Int = 0 43 | 44 | override def bytesSentToClient(flowContext: FlowContext, numberOfBytes: Int): Unit = { 45 | bytesSent += numberOfBytes 46 | } 47 | 48 | def wasDownloaded: Boolean = bytesSent > fiftyMegabytesInBytes 49 | } 50 | 51 | class StaticsServer(port: Int, fileToServe: File) extends NanoHTTPD(port) { 52 | 53 | def doStart(): Option[StaticsServer] = { 54 | start() 55 | Some(this) 56 | } 57 | 58 | override def serve(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response = { 59 | NanoHTTPD.newChunkedResponse(NanoHTTPD.Response.Status.ACCEPTED, "application/tar+gzip", new FileInputStream(fileToServe)) 60 | } 61 | } 62 | 63 | object ProxyFiles { 64 | val tmpDir: File = new UserHome(".embedmysql").asFile() 65 | type Restore = (File, () => Unit) 66 | 67 | def apply(version: Version): Restore = { 68 | val source = new File(tmpDir, new PackagePaths().getPath(Distribution.detectFor(version))) 69 | val target = new File(tmpDir, UUID.randomUUID().toString) 70 | if (!target.exists()) { 71 | Files.copy(source.toPath, target.toPath) 72 | } 73 | Files.delete(source.toPath) 74 | 75 | (target, () => { 76 | if (!source.exists()) { 77 | Files.copy(target.toPath, source.toPath) 78 | } 79 | Files.delete(target.toPath) 80 | }) 81 | 82 | } 83 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/support/InstanceMatchers.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.support 2 | 3 | import java.util.TimeZone 4 | 5 | import com.wix.mysql.EmbeddedMysql 6 | import com.wix.mysql.config.Charset._ 7 | import com.wix.mysql.config.MysqldConfig.SystemDefaults 8 | import com.wix.mysql.config.{Charset, MysqldConfig} 9 | import com.wix.mysql.utils.Utils 10 | import org.specs2.matcher.{Matcher, Matchers} 11 | 12 | trait InstanceMatchers extends Matchers { 13 | self: IntegrationTest => 14 | 15 | def haveSystemVariable(name: String, value: Matcher[String]): Matcher[EmbeddedMysql] = 16 | value ^^ { mySql: EmbeddedMysql => 17 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA) 18 | aSelect[String](ds, sql = s"SELECT variable_value FROM information_schema.global_variables WHERE variable_name = '$name';") 19 | } 20 | 21 | def haveCharsetOf(charset: Charset): Matcher[EmbeddedMysql] = 22 | ===(charset) ^^ { mySql: EmbeddedMysql => 23 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA) 24 | aCharset( 25 | aSelect[String](ds, sql = "SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'character_set_server';"), 26 | aSelect[String](ds, sql = "SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'collation_server';")) 27 | } 28 | 29 | def haveSchemaCharsetOf(charset: Charset, onSchema: String): Matcher[EmbeddedMysql] = 30 | ===(charset) ^^ { mySql: EmbeddedMysql => 31 | val ds = aDataSource(mySql.getConfig, onSchema) 32 | aCharset( 33 | aSelect[String](ds, sql = s"SELECT default_character_set_name FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';"), 34 | aSelect[String](ds, sql = s"SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';")) 35 | } 36 | 37 | def notHaveSchema(onSchema: String): Matcher[EmbeddedMysql] = 38 | ===(0) ^^ { mySql: EmbeddedMysql => 39 | val ds = aDataSource(mySql.getConfig, "information_schema") 40 | aSelect[java.lang.Integer](ds, sql = s"SELECT COUNT(1) FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';").toInt 41 | } 42 | 43 | def haveServerTimezoneMatching(timeZoneId: String): Matcher[EmbeddedMysql] = 44 | ===(Utils.asHHmmOffset(TimeZone.getTimeZone(timeZoneId))) ^^ { mySql: EmbeddedMysql => 45 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA) 46 | aSelect[String](ds, sql = s"SELECT @@global.time_zone;") 47 | } 48 | 49 | def beAvailableOn(port: Int, withUser: String, password: String, andSchema: String): Matcher[EmbeddedMysql] = 50 | beTrue ^^ { mySql: EmbeddedMysql => 51 | aSelect[java.lang.Long](aDataSource(withUser, password, port, andSchema), "select 1;") == 1 52 | } 53 | 54 | def beAvailableOn(config: MysqldConfig, andSchema: String): Matcher[EmbeddedMysql] = 55 | beAvailableOn(config.getPort, config.getUsername, config.getPassword, andSchema) 56 | } 57 | -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/support/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.support 2 | 3 | import java.util.UUID 4 | import java.util.concurrent.TimeUnit 5 | 6 | import ch.qos.logback.classic.Level.INFO 7 | import ch.qos.logback.classic.spi.ILoggingEvent 8 | import ch.qos.logback.classic.{Logger, LoggerContext} 9 | import ch.qos.logback.core.read.ListAppender 10 | import com.wix.mysql.config.MysqldConfig 11 | import com.wix.mysql.distribution.Version 12 | import com.wix.mysql.{EmbeddedMysql, Sources, SqlScriptSource} 13 | import de.flapdoodle.embed.process.io.directories.UserHome 14 | import javax.sql.DataSource 15 | import org.apache.commons.dbcp2.BasicDataSource 16 | import org.apache.commons.io.FileUtils._ 17 | import org.slf4j 18 | import org.slf4j.LoggerFactory 19 | import org.slf4j.LoggerFactory.getLogger 20 | import org.specs2.mutable.SpecWithJUnit 21 | import org.specs2.specification.BeforeAfterEach 22 | import org.springframework.dao.DataAccessException 23 | import org.springframework.jdbc.core.JdbcTemplate 24 | 25 | import scala.collection.JavaConversions._ 26 | import scala.reflect._ 27 | 28 | abstract class IntegrationTest extends SpecWithJUnit with BeforeAfterEach 29 | with InstanceMatchers with TestResourceSupport with JdbcSupport { 30 | 31 | sequential 32 | 33 | var mysqldInstances: Seq[EmbeddedMysql] = Seq() 34 | val log: slf4j.Logger = getLogger(this.getClass) 35 | 36 | def before: Any = mysqldInstances = Seq() 37 | 38 | def after: Any = mysqldInstances.foreach(_.stop) 39 | 40 | def start(mysqld: EmbeddedMysql.Builder): EmbeddedMysql = { 41 | val instance = mysqld.start 42 | mysqldInstances = mysqldInstances :+ instance 43 | instance 44 | } 45 | 46 | 47 | def aLogFor(app: String): Iterable[String] = { 48 | val appender: ListAppender[ILoggingEvent] = new ListAppender[ILoggingEvent] 49 | val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] 50 | 51 | val logger: Logger = context.getLogger(app) 52 | 53 | logger.setAdditive(false) 54 | logger.setLevel(INFO) 55 | logger.detachAppender(appender.getName) 56 | logger.addAppender(appender) 57 | 58 | appender.start() 59 | 60 | new Iterable[String] { 61 | def iterator = appender.list map (_.getMessage) iterator 62 | } 63 | } 64 | 65 | def withCleanRepo[T](f: => T): T = { 66 | val repository = new UserHome(".embedmysql").asFile 67 | val backupFolder = new UserHome(s".embedmysql-${UUID.randomUUID()}").asFile 68 | 69 | if (!repository.exists()) { 70 | f 71 | } else { 72 | moveDirectory(repository, backupFolder) 73 | try { 74 | f 75 | } finally { 76 | deleteDirectory(repository) 77 | moveDirectory(backupFolder, repository) 78 | } 79 | } 80 | } 81 | } 82 | 83 | object IntegrationTest { 84 | val targetTestVersion: Version = Version.v5_7_latest 85 | 86 | def testConfigBuilder(version: Version = targetTestVersion): MysqldConfig.Builder = MysqldConfig 87 | .aMysqldConfig(version) 88 | .withTimeout(60, TimeUnit.SECONDS) 89 | } 90 | 91 | trait JdbcSupport { 92 | self: IntegrationTest => 93 | 94 | def aDataSource(config: MysqldConfig, schema: String): DataSource = 95 | aDataSource(config.getUsername, config.getPassword, config.getPort, schema) 96 | 97 | def aDataSource(user: String, password: String, port: Int, schema: String): DataSource = { 98 | val dataSource: BasicDataSource = new BasicDataSource 99 | dataSource.setDriverClassName("com.mysql.jdbc.Driver") 100 | dataSource.setUrl(s"jdbc:mysql://localhost:$port/$schema?useSSL=false") 101 | dataSource.setUsername(user) 102 | dataSource.setPassword(password) 103 | dataSource 104 | } 105 | 106 | def aJdbcTemplate(mysqld: EmbeddedMysql, forSchema: String): JdbcTemplate = 107 | new JdbcTemplate(aDataSource(mysqld.getConfig, forSchema)) 108 | 109 | def aSelect[T: ClassTag](ds: DataSource, sql: String): T = 110 | new JdbcTemplate(ds).queryForObject(sql, classTag[T].runtimeClass.asInstanceOf[Class[T]]) 111 | 112 | def aSelect[T: ClassTag](mysqld: EmbeddedMysql, onSchema: String, sql: String): T = 113 | aJdbcTemplate(mysqld, onSchema).queryForObject(sql, classTag[T].runtimeClass.asInstanceOf[Class[T]]) 114 | 115 | def aQuery(mysqld: EmbeddedMysql, onSchema: String, sql: String): Unit = 116 | aJdbcTemplate(mysqld, forSchema = onSchema).execute(sql) 117 | 118 | def anUpdate(mysqld: EmbeddedMysql, onSchema: String, sql: String): Unit = 119 | aJdbcTemplate(mysqld, forSchema = onSchema).execute(sql) 120 | 121 | def aMigrationWith(sql: String): SqlScriptSource = Sources.fromFile(createTempFile(sql)) 122 | 123 | def beSuccessful = not(throwAn[Exception]) 124 | 125 | def failWith(fragment: String) = throwA[DataAccessException].like { case e => 126 | e.getMessage must contain(fragment) 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/support/MysqlCacheServingHttpServer.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.support 2 | 3 | import java.io.{BufferedInputStream, File} 4 | 5 | import com.wix.mysql.support.MysqlCacheServingHttpServer.PORT 6 | import fi.iki.elonen.router.RouterNanoHTTPD 7 | 8 | class MysqlCacheServingHttpServer extends RouterNanoHTTPD(PORT) { 9 | val port = PORT 10 | 11 | val embeddedMysqlCacheDir: File = new File(System.getProperty("user.home"), ".embedmysql").getAbsoluteFile 12 | addRoute("/(.)+", classOf[StaticPageTestHandler], embeddedMysqlCacheDir) 13 | } 14 | 15 | class StaticPageTestHandler extends RouterNanoHTTPD.StaticPageHandler { 16 | protected override def fileToInputStream(fileOrdirectory: File): BufferedInputStream = { 17 | super.fileToInputStream(fileOrdirectory) 18 | } 19 | } 20 | 21 | object MysqlCacheServingHttpServer { 22 | val PORT = 3000 23 | } -------------------------------------------------------------------------------- /wix-embedded-mysql/src/test/scala/com/wix/mysql/support/TestResourceSupport.scala: -------------------------------------------------------------------------------- 1 | package com.wix.mysql.support 2 | 3 | import java.io.{File => JFile} 4 | import java.nio.charset.Charset 5 | import java.util.UUID 6 | 7 | import org.apache.commons.io.FileUtils 8 | 9 | import scala.reflect.io.File 10 | 11 | trait TestResourceSupport { 12 | 13 | def createTempFile(content: String): JFile = { 14 | testResourceDirExists() 15 | val file = new JFile(s"$targetDir/classes/${UUID.randomUUID}") 16 | FileUtils.writeStringToFile(file, content, Charset.forName("UTF-8")) 17 | file 18 | } 19 | 20 | private def testResourceDirExists() { 21 | val f = File(s"$targetDir/classes") 22 | if (!f.exists || f.isFile) 23 | throw new RuntimeException("Could not generate file in test resources") 24 | } 25 | 26 | private val targetDir = { 27 | val target = new JFile(getClass.getProtectionDomain.getCodeSource.getLocation.getFile).getParentFile 28 | if (!File(s"$target/classes").exists) new JFile("target") else target 29 | } 30 | } --------------------------------------------------------------------------------