From 2bee08b6eae0a077006b824a1ce671ce5c9f45b7 Mon Sep 17 00:00:00 2001 From: Alejandro Revilla Date: Mon, 30 Sep 2019 16:42:06 -0300 Subject: [PATCH] Add flyWay support (CLI commands) --- libraries.gradle | 5 +- modules/db-flyway/build.gradle | 11 ++++ .../java/org/jpos/flyway/FlywaySupport.java | 63 +++++++++++++++++++ .../src/main/java/org/jpos/q2/cli/FLYWAY.java | 55 ++++++++++++++++ .../java/org/jpos/q2/cli/flyway/BASELINE.java | 13 ++++ .../java/org/jpos/q2/cli/flyway/CLEAN.java | 22 +++++++ .../java/org/jpos/q2/cli/flyway/INFO.java | 26 ++++++++ .../java/org/jpos/q2/cli/flyway/MIGRATE.java | 15 +++++ .../java/org/jpos/q2/cli/flyway/REPAIR.java | 13 ++++ .../java/org/jpos/q2/cli/flyway/VALIDATE.java | 13 ++++ .../src/main/java/org/jpos/ee/DB.java | 21 +++++-- settings.gradle | 3 +- 12 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 modules/db-flyway/build.gradle create mode 100644 modules/db-flyway/src/main/java/org/jpos/flyway/FlywaySupport.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/FLYWAY.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/BASELINE.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/CLEAN.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/INFO.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/MIGRATE.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/REPAIR.java create mode 100644 modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/VALIDATE.java diff --git a/libraries.gradle b/libraries.gradle index f58eb9b37e..a05a25521b 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -19,6 +19,7 @@ ext { httpAsyncClientVersion = '4.1.4' mysqlJDBCVersion = '8.0.15' c3p0Version = '0.9.5.3' + flywaydbVersion = '6.0.4' libraries = [ //jUnit (Tests) @@ -111,10 +112,10 @@ ext { jsonSchemaValidator: "com.github.java-json-tools:json-schema-validator:${jsonSchemaVersion}", guava: "com.google.guava:guava:${guavaVersion}", - httpAsyncClient: "org.apache.httpcomponents:httpasyncclient:${httpAsyncClientVersion}" + httpAsyncClient: "org.apache.httpcomponents:httpasyncclient:${httpAsyncClientVersion}", + flywaydb: "org.flywaydb:flyway-core:${flywaydbVersion}" ] - jsonSchemaValidatorLibs = [ libraries.jsonSchemaValidator, libraries.guava diff --git a/modules/db-flyway/build.gradle b/modules/db-flyway/build.gradle new file mode 100644 index 0000000000..335cefa3b7 --- /dev/null +++ b/modules/db-flyway/build.gradle @@ -0,0 +1,11 @@ +description = 'jPOS-EE :: flyWay support' + +dependencies { + compile project(':modules:dbsupport') + compile project(':modules:db-postgresql') + compile project(':modules:logback') + compile libraries.flywaydb +} + +apply from: "${rootProject.projectDir}/jpos-app.gradle" + diff --git a/modules/db-flyway/src/main/java/org/jpos/flyway/FlywaySupport.java b/modules/db-flyway/src/main/java/org/jpos/flyway/FlywaySupport.java new file mode 100644 index 0000000000..80c7f4a56c --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/flyway/FlywaySupport.java @@ -0,0 +1,63 @@ +package org.jpos.flyway; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.configuration.FluentConfiguration; +import org.flywaydb.core.api.logging.Log; +import org.flywaydb.core.api.logging.LogCreator; +import org.flywaydb.core.api.logging.LogFactory; +import org.jpos.ee.DB; + +import java.util.Arrays; +import java.util.Properties; + +public class FlywaySupport implements LogCreator, Log { + protected Flyway getFlyway(String configModifier, String args[]) { + LogFactory.setFallbackLogCreator(this); + Properties p = new DB(configModifier).getProperties(); + FluentConfiguration config = Flyway.configure().dataSource( + p.getProperty("hibernate.connection.url"), + p.getProperty("hibernate.connection.username"), + p.getProperty("hibernate.connection.password")) + .outOfOrder(has(args, "--out-of-order")); + return config.load(); + } + + public Log createLogger(Class clazz) { + return this; + } + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public void debug(String message) { + System.out.println("DEBUG: " + message); + } + + @Override + public void info(String message) { + System.out.println(message); + } + + @Override + public void warn(String message) { + System.err.println("WARNING: " + message); + } + + @Override + public void error(String message) { + System.err.println("ERROR: " + message); + } + + @Override + public void error(String message, Exception e) { + System.err.println("ERROR: " + message); + e.printStackTrace(System.err); + } + + private boolean has (String[] args, String arg) { + return Arrays.stream(args).anyMatch(s -> s.equals(arg)); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/FLYWAY.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/FLYWAY.java new file mode 100644 index 0000000000..51c4ba52b7 --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/FLYWAY.java @@ -0,0 +1,55 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2019 jPOS Software SRL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.jpos.q2.cli; + +import org.flywaydb.core.Flyway; +import org.jpos.ee.DB; +import org.jpos.q2.CLIContext; +import org.jpos.q2.CLISubSystem; + +import java.util.Properties; + +public class FLYWAY implements CLISubSystem { + public static final String PREFIX = "flyway.dbmodifier"; + + @Override + public String getPrompt(CLIContext ctx, String[] args) { + String prefix = null; + if (args.length > 1) { + prefix = args[1]; + ctx.getUserData().put(PREFIX, prefix); + } else { + ctx.getUserData().remove(PREFIX); + } + return "flyway" + (prefix != null ? "[" + args[1] + "]" : "") + "> "; + } + + @Override + public String[] getCompletionPrefixes(CLIContext ctx, String[] args) { + return new String[] { "org.jpos.q2.cli.flyway." }; + } + + private Flyway getFlyWay() { + Properties p = new DB().getProperties(); + return Flyway.configure().dataSource( + p.getProperty("hibernate.connection.url"), + p.getProperty("hibernate.connection.username"), + p.getProperty("hibernate.connection.password")).load(); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/BASELINE.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/BASELINE.java new file mode 100644 index 0000000000..38dca76ee6 --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/BASELINE.java @@ -0,0 +1,13 @@ +package org.jpos.q2.cli.flyway; + +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class BASELINE extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) { + getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args).baseline(); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/CLEAN.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/CLEAN.java new file mode 100644 index 0000000000..4c1f23dc49 --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/CLEAN.java @@ -0,0 +1,22 @@ +package org.jpos.q2.cli.flyway; + +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class CLEAN extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) { + boolean superSure = false; + boolean sure = cli.confirm("Are you sure you want to Clean your database (Yes/No) ? "); + if (sure) + superSure = cli.confirm("This action can not be reversed - still want to proceed (Yes/No) ? "); + + if (superSure) + getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args).clean(); + else { + cli.println ("No action taken"); + } + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/INFO.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/INFO.java new file mode 100644 index 0000000000..ece44a726b --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/INFO.java @@ -0,0 +1,26 @@ +package org.jpos.q2.cli.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.internal.info.MigrationInfoDumper; +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class INFO extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) throws Exception { + Flyway flyway = getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args); + MigrationInfoService info = flyway.info(); + MigrationInfo current = info.current(); + MigrationVersion currentSchemaVersion = current == null ? MigrationVersion.EMPTY : current.getVersion(); + MigrationVersion schemaVersionToOutput = currentSchemaVersion == null ? MigrationVersion.EMPTY : currentSchemaVersion; + + cli.println ("Schema version: " + schemaVersionToOutput); + cli.println (""); + cli.println(MigrationInfoDumper.dumpToAsciiTable(info.all())); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/MIGRATE.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/MIGRATE.java new file mode 100644 index 0000000000..58feec04fd --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/MIGRATE.java @@ -0,0 +1,15 @@ +package org.jpos.q2.cli.flyway; + +import org.flywaydb.core.Flyway; +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class MIGRATE extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) { + Flyway flyway = getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args); + flyway.migrate(); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/REPAIR.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/REPAIR.java new file mode 100644 index 0000000000..cb4c307c5f --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/REPAIR.java @@ -0,0 +1,13 @@ +package org.jpos.q2.cli.flyway; + +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class REPAIR extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) { + getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args).repair(); + } +} diff --git a/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/VALIDATE.java b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/VALIDATE.java new file mode 100644 index 0000000000..788ea101f1 --- /dev/null +++ b/modules/db-flyway/src/main/java/org/jpos/q2/cli/flyway/VALIDATE.java @@ -0,0 +1,13 @@ +package org.jpos.q2.cli.flyway; + +import org.jpos.flyway.FlywaySupport; +import org.jpos.q2.CLICommand; +import org.jpos.q2.CLIContext; +import org.jpos.q2.cli.FLYWAY; + +public class VALIDATE extends FlywaySupport implements CLICommand{ + @Override + public void exec(CLIContext cli, String[] args) { + getFlyway((String) cli.getUserData().get(FLYWAY.PREFIX), args).validate(); + } +} diff --git a/modules/dbsupport/src/main/java/org/jpos/ee/DB.java b/modules/dbsupport/src/main/java/org/jpos/ee/DB.java index 0335b1b396..0cde891362 100644 --- a/modules/dbsupport/src/main/java/org/jpos/ee/DB.java +++ b/modules/dbsupport/src/main/java/org/jpos/ee/DB.java @@ -77,6 +77,7 @@ public class DB implements Closeable { private static final String MODULES_CONFIG_PATH = "META-INF/org/jpos/ee/modules/"; private static Map sessionFactories = Collections.synchronizedMap(new HashMap<>()); private static Map metadatas = Collections.synchronizedMap(new HashMap<>()); + private static Map properties = Collections.synchronizedMap(new HashMap<>()); /** * Creates DB Object using default Hibernate instance @@ -110,6 +111,7 @@ public DB (String configModifier) { this.configModifier = configModifier; sfSems.putIfAbsent(configModifier, new Semaphore(1)); mdSems.putIfAbsent(configModifier, new Semaphore(1)); + getSessionFactory(); } /** @@ -118,7 +120,7 @@ public DB (String configModifier) { * @param log Log object */ public DB(Log log) { - super(); + this(); setLog(log); } @@ -158,6 +160,11 @@ public SessionFactory getSessionFactory() { return sf; } + public Properties getProperties() { + String cm = configModifier != null ? configModifier : ""; + return properties.get(cm); + } + public Dialect getDialect() { return dialect; } @@ -494,6 +501,7 @@ public void printStats() } } + @Override public String toString() { return "DB{" + (configModifier != null ? configModifier : "") + '}'; @@ -533,10 +541,11 @@ private Metadata getMetadata() throws IOException, ConfigurationException, Docum ssrb.configure(hibCfg); propFile = System.getProperty(dbPropertiesPrefix + "DB_PROPERTIES", "cfg/" + dbPropertiesPrefix + "db.properties"); Properties dbProps = loadProperties(propFile); - if (dbProps != null) { - for (Map.Entry entry : dbProps.entrySet()) { - ssrb.applySetting((String) entry.getKey(), Environment.get((String) entry.getValue())); - } + for (Map.Entry entry : dbProps.entrySet()) { + String k = (String) entry.getKey(); + String v = Environment.get((String) entry.getValue()); + ssrb.applySetting(k,v); + dbProps.setProperty(k,v); } // if DBInstantiator has put db user name and/or password in Space, set Hibernate config accordingly @@ -548,6 +557,7 @@ private Metadata getMetadata() throws IOException, ConfigurationException, Docum if (pass != null) ssrb.applySetting("hibernate.connection.password", pass); + MetadataSources mds = new MetadataSources(ssrb.build()); List moduleConfigs = ModuleUtils.getModuleEntries(MODULES_CONFIG_PATH); for (String moduleConfig : moduleConfigs) { @@ -560,6 +570,7 @@ private Metadata getMetadata() throws IOException, ConfigurationException, Docum } md = mds.buildMetadata(); metadatas.put(cm, md); + properties.put(cm, dbProps); } } finally { mdSem.release(); diff --git a/settings.gradle b/settings.gradle index eb627fcb4b..9b1a6e3192 100644 --- a/settings.gradle +++ b/settings.gradle @@ -42,7 +42,8 @@ include ':modules:core', ':modules:iso-http-server', ':modules:iso-http-servlet', ':modules:http-client', - ':modules:seqno' + ':modules:seqno', + ':modules:db-flyway' rootProject.name = 'jposee'