package com.rjconsultores.ventaboletos; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.flywaydb.core.Flyway; import org.flywaydb.core.api.callback.FlywayCallback; import com.rjconsultores.ventaboletos.vo.FlyWayCustomDetail; public class FlyWay { private static final Logger log = LogManager.getLogger(FlyWay.class); private static final FlyWay INSTANCE = new FlyWay(); final private static String TABELA_FLYWAY = "schema_version_cst"; final private static String AMBIENTE_DEV = "dev"; final private static String AMBIENTE_CONSULTA = "consulta"; final File JBOSS_DATA = getJBossDataDirectory(); private DataSource dataSource = null; private String url = null; private String user = null; private String password = null; private String location = "db.migration"; private String oldies = "db.backup.oracle"; private FlyWay() { } public static FlyWay getInstance() { return INSTANCE; } private File getJBossDataDirectory() { try { return new File(System.getProperty("jboss.server.data.dir")); } catch (Exception e) { return null; } } public void defineLocation(final String location) { this.location = location; } public void defineDataSource(final DataSource dataSource) { this.dataSource = dataSource; fixFiles(dataSource); } public void defineProperties(final String url, final String user, final String password) { this.url = url; this.user = user; this.password = password; } private void fixFiles(final DataSource ds) { try { Connection c = dataSource.getConnection(); Statement s = c.createStatement(); fixVersion(s, "20161710.1833", "20161017.1833", "V20161017_1833__mantis8112.sql"); fixVersion(s, "20161910.0934", "20161019.0934", "V20161019_0934__mantis7907.sql"); fixVersion(s, "20162410.1119", "20161024.1119", "V20161024_1119__mantis7904.sql"); s.close(); c.close(); } catch (SQLException e) { log.error(e.getMessage(), e); } } private void fixVersion(final Statement s, final String oldVersion, final String newVersion, final String newScript) throws SQLException { ResultSet rs = s.executeQuery("select count(*) as total from \"schema_version\" where \"version\" like '" + oldVersion + "'"); int total = 0; while (rs.next()) { total = rs.getInt("total"); } rs.close(); if (total > 0) { log.info("Fix version " + oldVersion + " to new version " + newVersion + " and new script " + newScript); StringBuilder sql = new StringBuilder(""); sql.append(" update \"schema_version\""); sql.append(" set \"version\" = '" + newVersion + "', \"script\" = '" + newScript + "'"); sql.append(" where \"version\" like '" + oldVersion + "'"); s.executeUpdate(sql.toString()); } } public boolean start() { return start(null, null, false); } public boolean start(Boolean isReloadFyway) { return start(null, null, isReloadFyway); } public boolean startCustom() { String location = "db.performance"; if (JBOSS_DATA != null) { File data = new File(JBOSS_DATA, "flyway"); if (!data.exists()) { data.mkdirs(); } if (!validarScripts(data)) { return true; } location = "filesystem:" + data.getPath(); } return start(location, TABELA_FLYWAY, false); } public boolean start(final String customLocation, final String customTable, Boolean isReloadFlyway) { log.info("Executando Flyway..."); try{ String ambiente = System.getProperty("ambiente") ==null || System.getProperty("ambiente").isEmpty() ? "" : System.getProperty("ambiente"); if( (AMBIENTE_DEV.equalsIgnoreCase(ambiente) && AMBIENTE_CONSULTA.equalsIgnoreCase(ambiente)) && Boolean.FALSE.equals(isReloadFlyway) ) { log.warn("Flyway não executado. Ambiente = dev ou consulta"); return true; } final Flyway flyway = new Flyway(); if (customTable != null) { flyway.setTable(customTable); } if (customLocation != null) { flyway.setLocations(customLocation); } else if (this.location != null && !this.location.equals("db.migration")) { flyway.setLocations(this.location); }else { flyway.setLocations(this.oldies, this.location); } if (dataSource != null) { flyway.setDataSource(dataSource); } else { flyway.setDataSource(url, user, password); dataSource = flyway.getDataSource(); } execute(flyway); log.info("Flyway executado."); }catch(Throwable t){ log.error("Erro ao executar o Flyway",t); return false; } return true; } public void execute(final Flyway flyway) { flyway.setValidateOnMigrate(false); flyway.setIgnoreFutureMigrations(true); flyway.setOutOfOrder(true); flyway.setBaselineOnMigrate(true); if (flyway.getTable().equals(TABELA_FLYWAY)) { FlyWayCallback errorCallback = new FlyWayCallback(); FlywayCallback[] callbacks = new FlywayCallback[]{ errorCallback }; flyway.setCallbacks(callbacks); boolean reexecute = false; do { reexecute = false; try { flyway.migrate(); } catch (final Exception e) { if (errorCallback.getScriptError() != null && JBOSS_DATA != null) { reexecute = true; log.error("[Flyway] Erro ao executar o script: " + errorCallback.getScriptError() + ". Este script sera desabilitado na tabela FLYWAY_SCRIPTS."); File scriptFile = new File(JBOSS_DATA, "flyway"); scriptFile = new File(scriptFile, errorCallback.getScriptError()); if (scriptFile.exists()) { boolean success = scriptFile.delete(); log.info("[Flyway] Excluindo fisicamente o script: " + errorCallback.getScriptError() + ". " + (success ? "OK" : "ERROR")); if (!success) { reexecute = false; } } Connection c = null; PreparedStatement ps = null; try { c = flyway.getDataSource().getConnection(); ps = c.prepareStatement("update flyway_scripts set activo = 0 where nome like ?"); ps.setString(1, errorCallback.getScriptError().replace(".sql", "")); ps.executeUpdate(); } catch (SQLException e1) { } finally { if (ps != null) { try { ps.close(); } catch (SQLException e1) {} } if (c != null) { try { c.close(); } catch (SQLException e1) {} } } } flyway.repair(); } } while (reexecute); } else { flyway.migrate(); } } private boolean validarScripts(File data) { Map scripts = new HashMap(0); try { StringBuilder sb = new StringBuilder(); boolean existe = existeSchema(dataSource.getConnection()); if (existe) { sb.append(" select nome as nome_arquivo, sql as sql_arquivo, sql_erro as erro, datahora_execucao as datahora"); sb.append(" from flyway_scripts"); sb.append(" where activo = 1 and nome not in (select replace(\"script\", '.sql', '') from \"schema_version_cst\")"); sb.append(" order by nome_arquivo"); } else { sb.append(" select nome as nome_arquivo, sql as sql_arquivo, sql_erro as erro, datahora_execucao as datahora"); sb.append(" from flyway_scripts"); sb.append(" where activo = 1"); sb.append(" order by nome_arquivo"); } Connection c = dataSource.getConnection(); Statement s = c.createStatement(); ResultSet rs = s.executeQuery(sb.toString()); while (rs.next()) { if (!isRegistroValido(rs.getString("nome_arquivo"), rs.getString("sql_arquivo"))) { continue; } Timestamp timestamp = rs.getTimestamp("datahora"); FlyWayCustomDetail detail = new FlyWayCustomDetail(); detail.setVersion(rs.getString("nome_arquivo")); detail.setSql(rs.getString("sql_arquivo")); detail.setErrorCode(rs.getString("erro")); if (timestamp != null) { detail.setDatetimeExecute(new java.util.Date(timestamp.getTime())); } scripts.put(rs.getString("nome_arquivo"), detail); } rs.close(); s.close(); c.close(); } catch (SQLException e) { log.error(e.getMessage(), e); } if (scripts.isEmpty()) { return false; } boolean retorno = false; Writer writer = null; for (Map.Entry entry : scripts.entrySet()) { if (entry.getValue().getDatetimeExecute() != null) { Calendar atual = Calendar.getInstance(); if (atual.getTime().before(entry.getValue().getDatetimeExecute())) { continue; } } File scriptFile = new File(data, entry.getKey() + ".sql"); if (scriptFile.exists()) { scriptFile.delete(); } if (!scriptFile.exists()) { StringBuilder sb = new StringBuilder(); if (entry.getValue().getErrorCode() != null) { sb.append("declare").append("\n"); sb.append(" object_exists exception;").append("\n").append("\n"); sb.append(" pragma exception_init (object_exists , ").append(entry.getValue().getErrorCode()).append(");").append("\n"); sb.append("begin").append("\n"); sb.append(" execute immediate '").append(entry.getValue().getSql()).append("';").append("\n"); sb.append(" exception when object_exists then null;").append("\n"); sb.append("end;"); } else { sb.append(entry.getValue().getSql()); } try { writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(scriptFile), "utf-8")); writer.write(sb.toString()); log.info("[Flyway] Script gerado: " + scriptFile.toString()); retorno = true; } catch (IOException ex) { } finally { try {writer.close();} catch (Exception ex) {log.info(ex.getMessage(), ex);} } } } return retorno; } private boolean isRegistroValido(String versao, String sql) { boolean valido = true; String v[] = versao.split("_"); if (!versao.startsWith("V")) { valido = false; } else { if (!(v.length == 4)) { valido = false; } } if (!valido) { Connection c = null; PreparedStatement ps = null; try { c = dataSource.getConnection(); ps = c.prepareStatement("update flyway_scripts set activo = 0 where nome like ?"); ps.setString(1, versao); ps.executeUpdate(); } catch (SQLException e1) { } finally { if (ps != null) { try { ps.close(); } catch (SQLException e1) {} } if (c != null) { try { c.close(); } catch (SQLException e1) {} } } } return valido; } private boolean existeSchema(Connection c) { StringBuilder sb = new StringBuilder(); sb.append("SELECT count(table_name) as existe FROM user_tables WHERE table_name = 'schema_version_cst'"); try { Statement s = c.createStatement(); ResultSet rs = s.executeQuery(sb.toString()); int existe = 0; while (rs.next()) { existe = rs.getInt("existe"); } rs.close(); s.close(); c.close(); if (existe > 0) { return true; } } catch (SQLException e) { log.error(e.getMessage(), e); } return false; } }