diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java index 80c7b644a..b1589fcd3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java @@ -16,6 +16,9 @@ import org.springframework.context.annotation.PropertySource; /** * hawkBit Flyway autoconfiguration loading the flyway defaults properties. + *

+ * Used when this module is packed in a spring boot application in order to migrate the hawkbit jpa database schema at runtime. + * Another option will be to use hawkbit-repository-jpa-init module to migrate the database schema before hawkbit is started. */ @Configuration @PropertySource("classpath:/hawkbit-jpa-flyway-defaults.properties") diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java index 5c225bef5..24e5e8666 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java +++ b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java @@ -9,6 +9,8 @@ */ package org.eclipse.hawkbit.repository.jpa.init; +import java.util.Arrays; +import java.util.ServiceLoader; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,6 +19,8 @@ import org.flywaydb.core.Flyway; import org.flywaydb.core.api.CoreErrorCode; import org.flywaydb.core.api.ErrorCode; import org.flywaydb.core.api.FlywayException; +import org.flywaydb.core.api.callback.Callback; +import org.flywaydb.core.api.configuration.FluentConfiguration; import org.flywaydb.core.api.exception.FlywayValidateException; /** @@ -69,25 +73,44 @@ public class HawkbitFlywayDbInit { private static final String USER = prop("username", "sa"); private static final String PASSWORD = prop("password", ""); private static final String TABLE = prop("table", "schema_version"); + // comma separated, fully qualified class names of callbacks + private static final String[] CALLBACKS = Arrays + .stream(prop("callbacks", "").split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .toArray(String[]::new); private static final String[] LOCATIONS = prop("locations", "db/migration").split(","); private static final String SQL_MIGRATION_SUFFIXES = prop("sql-migration-suffixes", suffix(URL)) + ".sql"; - private static final String EXCEPTION_MESSAGE = "Exception message: {}"; - + // java:S3776 - better be at one place. not so complex + // java:S1181 - we want to catch Throwable to log it and exit with proper exit code + @SuppressWarnings({ "java:S3776", "java:S1181" }) public static void main(final String[] args) { - final Flyway flyway = Flyway.configure() + log.info("Start ({}): {}@{}, table: {}, locations: {}, sql-migration-suffixes: {}", + MODE, USER, URL, TABLE, LOCATIONS, SQL_MIGRATION_SUFFIXES); + + // configured via system properties callbacks are with prority. If not confiured - try to load via service loader + final Callback[] callbackViaServiceLoader = CALLBACKS.length == 0 ? ServiceLoader.load(Callback.class).stream() + .map(ServiceLoader.Provider::get) + .toArray(Callback[]::new) : new Callback[0]; + FluentConfiguration fluentConfiguration = Flyway.configure() .dataSource(URL, USER, PASSWORD) .table(TABLE) .locations(LOCATIONS) .sqlMigrationSuffixes(SQL_MIGRATION_SUFFIXES) .cleanDisabled(true) .validateOnMigrate(true) - .envVars() - .load(); - log.info( - "Start ({}): {}@{}, table: {}, locations: {}, sql-migration-suffixes: {}", - MODE, USER, URL, TABLE, LOCATIONS, SQL_MIGRATION_SUFFIXES); + .envVars(); + if (CALLBACKS.length == 0) { + if (callbackViaServiceLoader.length != 0) { + fluentConfiguration = fluentConfiguration.callbacks(callbackViaServiceLoader); + } + } else { + fluentConfiguration = fluentConfiguration.callbacks(CALLBACKS); + } + + final Flyway flyway = fluentConfiguration.load(); if (MODE.equals(MIGRATE)) { try { @@ -96,7 +119,7 @@ public class HawkbitFlywayDbInit { final ErrorCode errorCode = e.getErrorCode(); final int errorCodeOrdinal = errorCode instanceof CoreErrorCode coreErrorCode ? coreErrorCode.ordinal() : -1; log.error("Flyway migrate failed: error code = {}, error code ordinal = {}", errorCode, errorCodeOrdinal); - log.debug(EXCEPTION_MESSAGE, e.getMessage()); + logException(e); System.exit(EXIT_CODE_FAILED); // migration failed } catch (final Throwable e) { log.error("Flyway validate failed (undeclared exception): ", e); @@ -108,14 +131,15 @@ public class HawkbitFlywayDbInit { } catch (final FlywayValidateException e) { final ErrorCode errorCode = e.getErrorCode(); final int errorCodeOrdinal = errorCode instanceof CoreErrorCode coreErrorCode ? coreErrorCode.ordinal() : -1; - log.error("Flyway validate failed (FlywayValidateException): error code = {}, error code ordinal = {}", errorCode, errorCodeOrdinal); - log.info(EXCEPTION_MESSAGE, e.getMessage()); + log.error("Flyway validate failed (FlywayValidateException): error code = {}, error code ordinal = {}", errorCode, + errorCodeOrdinal); + logException(e); System.exit(EXIT_CODE_VALIDATE_FAILED); // validation failed, not real error but tables are not valid } catch (final FlywayException e) { final ErrorCode errorCode = e.getErrorCode(); final int errorCodeOrdinal = errorCode instanceof CoreErrorCode coreErrorCode ? coreErrorCode.ordinal() : -1; log.error("Flyway validate failed: error code = {}, error code ordinal = {}", errorCode, errorCodeOrdinal); - log.info(EXCEPTION_MESSAGE, e.getMessage()); + logException(e); System.exit(EXIT_CODE_FAILED); // validation failed } catch (final Throwable e) { log.error("Flyway validate failed (undeclared exception): ", e); @@ -126,6 +150,14 @@ public class HawkbitFlywayDbInit { System.exit(EXIT_CODE_SUCCESS); } + private static void logException(final FlywayException e) { + if (log.isDebugEnabled()) { + log.debug("Exception message: {}", e.getMessage(), e); + } else { + log.info("Exception: ", e); + } + } + private static String prop(final String key, final String defaultValue) { String value = env(key); if (value == null) { diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml index 87af64287..bd72d5379 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml +++ b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml @@ -24,8 +24,9 @@ + - + \ No newline at end of file