diff --git a/.travis.yml b/.travis.yml index da98449..1dde844 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,3 +36,13 @@ env: global: - secure: "t/DJwSV2TUQ2OxZBqDhWl1noCYqJkWGngSvcWMct5z4gcuNScg5Pw5ylBzNCl43lF045X9nOJdY40uH2UyNd1nwNW1XNehuJCgijU3rFX1xl/7rAq52B4FpECQhrwwzInpa5um/84Uwl61GghKqyFbxyGuxq7jzFSQl1vPmBuRdbKkkufr5APcIu5Ger9b1E7T9Crkcpv/u624T2Zz8Hxb8ezemAOK8fW9rTGLx044bjhCpubXoeYXdIuzKd0LxK5pop4ozoGZ5/L3DVB5JhXMrXi99Gjf+BPdiLTCChenU6H6kaqP//lOYRseRsn7kMT1i9zt5lfmn3udFgTekRjSDcz9YFwls/OjAo7rT0i8g7Rq68DiNJKojcKTB7Pp5j+PNVXru+QljvY/oFAfkWBUukKLemNALli2taStaXlfvRHeb+d5AnCPalwwOfNm6nkQJzkDxTxD6UlGj4IzTYQ1oxoAxkw7AIxKu9yp6zTgYX0OXHaioAdnpVYFUq9nIMUWVts957DVNHMTAbjXchyaG9Gsv8gE0iQN7hURhWnmrS+bQnWVlhrUYuIyavbDXVvHSzycEU59PNfGUFK+x7XujhWosmE6kHbSgJxZAF4SGxzCJpxOqrJTWjgYKgPwZ4yg2OFiQ04u8umqsFxHjvh02e9iG3OAA2l9F0L5A6jA8=" - secure: "CdB/iOvwr1UmoFn762u2vgru4J7Hyn+YO0KjWooe6k0VD6TNm7HIWYXyGSH/J3chO5BdzYv2TxnWFHaeTMq+84+MIBp/TfV3UfSX8pMuspvfqFtNcj7l2HCt9WJ95t4zvwyJjSR5lN6C2LIGrkztkx65uPi6SXqJUJnswMwjeftFfChTh05JprdZ5V8T/ArJzGLmy+EgqaNYR2lwtYUNf8MudTZgri9vVkECeO2c+V0/ELW5/+L/Qb1XnLcubPvMVfrxhuer8+YjLTDusjnIj9iwuLU5+kZM7uoAJDqLKHSSvruEtz2lMLNooEnINkJanNiRvpkfHLojJz0dAebdiuU47yz4Qpg1XFPGHwjDJvO0uL+C+DwGmKZ3kuWug+d6/5HtAcsSntdHxQhG0CoN48k7GkITTAdiTLmnzG+FBmUHswFP8bFZr24srMOuzWzCRr+xsKp93XWDHY+I93FnsBIEwWkqWjuwQUuEDv/6CGxRrdTk1Q8z/b6Ex/973cKFXRUnhwjQ2cBo+bScfRmUaMJBYSbY1yVGMDCyRtcpTClnhlzuQbXNVHxRMvREidRZkqM0C+LcERfHVzMaQ2QatYw1F0WCFSl8J5gjDNMwtHz0GU9t5gvLr7hogk2Jl8baBEI3LvkbANNz/mLP2oid4bZQmkmw9krQFZc/esLWFMI=" + +# to be able to see where it goes wrong (see also https://github.com/travis-ci/travis-ci/issues/6018) +after_failure: + - echo == Begin of test log(s) == + - for f in $(find . -name test.log); do echo "test log: $f"; cat $f; sleep 1; done + - echo == End of test log(s) == + +# give some time to spool the output +after_script: + - sleep 1 diff --git a/bootique-flyway/pom.xml b/bootique-flyway/pom.xml index 341f09f..2706daa 100644 --- a/bootique-flyway/pom.xml +++ b/bootique-flyway/pom.xml @@ -35,6 +35,10 @@ Provides Flyway integration with Bootique. + + 3.2.0 + + @@ -119,4 +123,22 @@ + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + test-jar + + + + + + + diff --git a/bootique-flyway/src/main/java/io/bootique/flyway/FlywayRunner.java b/bootique-flyway/src/main/java/io/bootique/flyway/FlywayRunner.java index 62a4d01..d529400 100644 --- a/bootique-flyway/src/main/java/io/bootique/flyway/FlywayRunner.java +++ b/bootique-flyway/src/main/java/io/bootique/flyway/FlywayRunner.java @@ -20,16 +20,19 @@ package io.bootique.flyway; import org.flywaydb.core.Flyway; -import org.flywaydb.core.api.MigrationInfoService; import org.flywaydb.core.api.MigrationInfo; +import org.flywaydb.core.api.MigrationInfoService; import org.flywaydb.core.api.MigrationVersion; +import org.flywaydb.core.internal.info.MigrationInfoDumper; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.flywaydb.core.internal.info.MigrationInfoDumper; import java.util.function.Consumer; public class FlywayRunner { + private static Logger logger = LoggerFactory.getLogger(FlywayRunner.class); + private final FlywaySettings settings; public FlywayRunner(FlywaySettings settings) { @@ -66,8 +69,6 @@ public void info() { MigrationVersion schemaVersionToOutput = currentSchemaVersion == null ? MigrationVersion.EMPTY : currentSchemaVersion; - final Logger logger = LoggerFactory.getLogger(FlywayRunner.class); - if(logger.isInfoEnabled()) { logger.info("Schema version: " + schemaVersionToOutput); logger.info(""); @@ -90,6 +91,7 @@ private void forEach(Consumer flywayConsumer) { .dataSource(ds) .configuration(settings.getProperties()) // takes precedence over location settings (do not use jdbc connection details though in a Flyway configuration file) ); + flywayConsumer.accept(flyway); }); } diff --git a/bootique-flyway/src/main/java/io/bootique/flyway/FlywaySettings.java b/bootique-flyway/src/main/java/io/bootique/flyway/FlywaySettings.java index d481bff..b21e076 100644 --- a/bootique-flyway/src/main/java/io/bootique/flyway/FlywaySettings.java +++ b/bootique-flyway/src/main/java/io/bootique/flyway/FlywaySettings.java @@ -21,17 +21,25 @@ import io.bootique.resource.ResourceFactory; -import javax.sql.DataSource; +import java.io.*; +import java.net.URL; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; -import java.io.*; -import java.net.URL; +import java.util.TreeMap; +import javax.sql.DataSource; +import org.flywaydb.core.api.FlywayException; import org.flywaydb.core.internal.configuration.ConfigUtils; +import org.flywaydb.core.internal.util.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FlywaySettings { + private static Logger logger = LoggerFactory.getLogger(FlywayRunner.class); + private final List dataSources; private final String[] locations; private final String[] configFiles; // list of config files to use @@ -55,19 +63,39 @@ public String[] getLocations() { public java.util.Map getProperties() { Map config = new HashMap(); - try { - for (String file : this.configFiles) { - URL url = new ResourceFactory(file).getUrl(); // file may have classpath: as a prefix - - Reader reader = new InputStreamReader(url.openStream()); - config.putAll(ConfigUtils.loadConfigurationFromReader(reader)); + for (String file : this.configFiles) { + final String errorMessage = "Unable to load config file: " + file; + + try { + final URL url = new ResourceFactory(file).getUrl(); // file may have classpath: as a prefix + final Reader reader = new InputStreamReader(url.openStream()); + + final Map fileConfig = ConfigUtils.loadConfigurationFromReader(reader); + + config.putAll(fileConfig); + dumpConfiguration(fileConfig, file); + } catch(IOException e) { + throw new FlywayException(errorMessage, e); } - } catch(IOException e) { - throw new RuntimeException(e); } - ConfigUtils.dumpConfiguration(config); + dumpConfiguration(config, "ALL"); return config; } + + /** + * Dumps the configuration to the console when debug output is activated. + * + * @param config The configured properties. + * @param resource The config file resource. + */ + private static void dumpConfiguration(Map config, String resource) { + if (logger.isDebugEnabled()) { + logger.debug("Using configuration resource " + resource); + for (Map.Entry entry : new TreeMap<>(config).entrySet()) { + logger.debug(entry.getKey() + " -> " + entry.getValue()); + } + } + } } diff --git a/bootique-flyway5/pom.xml b/bootique-flyway5/pom.xml new file mode 100644 index 0000000..a452b8f --- /dev/null +++ b/bootique-flyway5/pom.xml @@ -0,0 +1,139 @@ + + + + + 4.0.0 + + + io.bootique.flyway + bootique-flyway-parent + 2.0.B1-SNAPSHOT + + + bootique-flyway5 + jar + + bootique-flyway: Flyway5 Integration with Bootique + + Provides Flyway5 integration with Bootique. + + + + 5.1.4 + + + + + + io.bootique + bootique + compile + + + io.bootique.jdbc + bootique-jdbc + compile + + + org.flywaydb + flyway-core + compile + + + org.slf4j + slf4j-api + compile + + + io.bootique.flyway + bootique-flyway + ${project.parent.version} + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + io.bootique + bootique-test + test + + + com.h2database + h2 + test + + + io.bootique.jdbc + bootique-jdbc-test + test + + + io.bootique.jdbc + bootique-jdbc-tomcat + test + + + io.bootique.logback + bootique-logback + test + + + io.bootique.flyway + bootique-flyway + ${project.parent.version} + tests + test-jar + test + + + + + + + gpg + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + + diff --git a/bootique-flyway5/src/main/java/io/bootique/flyway/FlywayRunner.java b/bootique-flyway5/src/main/java/io/bootique/flyway/FlywayRunner.java new file mode 100644 index 0000000..dbec38a --- /dev/null +++ b/bootique-flyway5/src/main/java/io/bootique/flyway/FlywayRunner.java @@ -0,0 +1,103 @@ +/* + * Licensed to ObjectStyle LLC under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ObjectStyle LLC licenses + * this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.bootique.flyway; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationInfo; +import org.flywaydb.core.api.MigrationInfoService; +import org.flywaydb.core.api.MigrationVersion; +import org.flywaydb.core.api.configuration.FluentConfiguration; +import org.flywaydb.core.internal.info.MigrationInfoDumper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.Consumer; + +public class FlywayRunner { + private static Logger logger = LoggerFactory.getLogger(FlywayRunner.class); + + private final FlywaySettings settings; + + public FlywayRunner(FlywaySettings settings) { + this.settings = settings; + } + + public void migrate() { + forEach(Flyway::migrate); + } + + public void validate() { + forEach(Flyway::validate); + } + + public void baseline() { + forEach(Flyway::baseline); + } + + public void repair() { + forEach(Flyway::repair); + } + + public void info() { + settings.getDataSources().forEach(ds -> { + FluentConfiguration configuration = new FluentConfiguration(); + + Flyway flyway = new Flyway(configuration + .locations(settings.getLocations()) + .dataSource(ds) + .configure(settings.getProperties()) // takes precedence over location settings (do not use jdbc connection details though in a Flyway configuration file) + ); + + MigrationInfoService info = flyway.info(); + MigrationInfo current = info.current(); + MigrationVersion currentSchemaVersion = current == null ? MigrationVersion.EMPTY : current.getVersion(); + + MigrationVersion schemaVersionToOutput = currentSchemaVersion == null ? MigrationVersion.EMPTY : currentSchemaVersion; + + if(logger.isInfoEnabled()) { + logger.info("Schema version: " + schemaVersionToOutput); + logger.info(""); + + for (String line : MigrationInfoDumper.dumpToAsciiTable(info.all()).split("\\r?\\n")) { + logger.info(line); + } + } + }); + } + + public void clean() { + forEach(Flyway::clean); + } + + private void forEach(Consumer flywayConsumer) { + settings.getDataSources().forEach(ds -> { + FluentConfiguration configuration = new FluentConfiguration(); + + Flyway flyway = new Flyway(configuration + .locations(settings.getLocations()) + .dataSource(ds) + .configure(settings.getProperties()) // takes precedence over location settings (do not use jdbc connection details though in a Flyway configuration file) + ); + + flywayConsumer.accept(flyway); + }); + } +} diff --git a/bootique-flyway5/src/main/java/io/bootique/flyway/FlywaySettings.java b/bootique-flyway5/src/main/java/io/bootique/flyway/FlywaySettings.java new file mode 100644 index 0000000..cb509cd --- /dev/null +++ b/bootique-flyway5/src/main/java/io/bootique/flyway/FlywaySettings.java @@ -0,0 +1,111 @@ +/* + * Licensed to ObjectStyle LLC under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ObjectStyle LLC licenses + * this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.bootique.flyway; + +import io.bootique.resource.ResourceFactory; + +import java.io.*; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; +import javax.sql.DataSource; + +import org.flywaydb.core.api.FlywayException; +import org.flywaydb.core.internal.configuration.ConfigUtils; +import org.flywaydb.core.internal.util.FileCopyUtils; +import org.flywaydb.core.internal.util.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlywaySettings { + private static Logger logger = LoggerFactory.getLogger(FlywayRunner.class); + + private final List dataSources; + private final String[] locations; + private final String[] configFiles; // list of config files to use + + public FlywaySettings(List dataSources, + List locations, + List configFiles) { + this.dataSources = Collections.unmodifiableList(dataSources); + this.locations = locations.toArray(new String[0]); + this.configFiles = configFiles.toArray(new String[0]); + } + + public List getDataSources() { + return dataSources; + } + + public String[] getLocations() { + return locations; + } + + public java.util.Map getProperties() { + Map config = new HashMap(); + + for (String file : this.configFiles) { + final String errorMessage = "Unable to load config file: " + file; + + try { + final URL url = new ResourceFactory(file).getUrl(); // file may have classpath: as a prefix + final Reader reader = new InputStreamReader(url.openStream()); + + // loadConfigurationFromReader() only available for Flyway 6 + // config.putAll(ConfigUtils.loadConfigurationFromReader(reader)); + + final String contents = FileCopyUtils.copyToString(reader); + final Properties properties = new Properties(); + + properties.load(new StringReader(contents.replace("\\", "\\\\"))); + + final Map fileConfig = ConfigUtils.propertiesToMap(properties); + + config.putAll(fileConfig); + dumpConfiguration(fileConfig, file); + } catch(IOException e) { + throw new FlywayException(errorMessage, e); + } + } + + dumpConfiguration(config, "ALL"); + + return config; + } + + /** + * Dumps the configuration to the console when debug output is activated. + * + * @param config The configured properties. + * @param resource The config file resource. + */ + private static void dumpConfiguration(Map config, String resource) { + if (logger.isDebugEnabled()) { + logger.debug("Using configuration resource " + resource); + for (Map.Entry entry : new TreeMap<>(config).entrySet()) { + logger.debug(entry.getKey() + " -> " + entry.getValue()); + } + } + } +} diff --git a/bootique-flyway5/src/test/java/db/migration/V2__Update_table.java b/bootique-flyway5/src/test/java/db/migration/V2__Update_table.java new file mode 100644 index 0000000..c5e3967 --- /dev/null +++ b/bootique-flyway5/src/test/java/db/migration/V2__Update_table.java @@ -0,0 +1,44 @@ +/* + * Licensed to ObjectStyle LLC under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ObjectStyle LLC licenses + * this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package db.migration; + +import org.flywaydb.core.api.migration.jdbc.JdbcMigration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class V2__Update_table implements JdbcMigration { + private static Logger LOGGER = LoggerFactory.getLogger(V2__Update_table.class); + + @Override + public void migrate(Connection connection) { + String insertSQL = "INSERT INTO TEST (id, name) VALUES (2, 'Test 2')"; + + try (PreparedStatement statement = connection.prepareStatement(insertSQL)) { + statement.execute(); + } catch (SQLException e) { + LOGGER.error("Migration failed", e); + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/bootique-flyway5/src/test/java/path/migration/V2__Update_table_nonDefault.java b/bootique-flyway5/src/test/java/path/migration/V2__Update_table_nonDefault.java new file mode 100644 index 0000000..70693ce --- /dev/null +++ b/bootique-flyway5/src/test/java/path/migration/V2__Update_table_nonDefault.java @@ -0,0 +1,33 @@ +/* + * Licensed to ObjectStyle LLC under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ObjectStyle LLC licenses + * this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package path.migration; + +import db.migration.V2__Update_table; +import org.flywaydb.core.api.migration.jdbc.JdbcMigration; + +import java.sql.Connection; + +public class V2__Update_table_nonDefault implements JdbcMigration { + + @Override + public void migrate(Connection connection) { + new V2__Update_table().migrate(connection); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 31e83b0..b45c75f 100644 --- a/pom.xml +++ b/pom.xml @@ -39,11 +39,12 @@ bootique-flyway + bootique-flyway5 ${project.version} - 6.5.0 + 6.5.5 1.4.200