From 1826f40874d95cabd02b8830882f43ba0bf4a011 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 8 May 2023 23:30:44 +0200 Subject: [PATCH] [DEV] Migration base 1 OK --- src/org/kar/archidata/SqlWrapper.java | 158 +++++++++------- .../catcher/FailException404API.java | 2 +- .../archidata/migration/MigrationEngine.java | 173 ++++++++++++------ .../migration/MigrationInterface.java | 9 +- .../archidata/migration/MigrationModel.java | 15 +- .../archidata/migration/MigrationSqlStep.java | 49 +++-- 6 files changed, 270 insertions(+), 136 deletions(-) diff --git a/src/org/kar/archidata/SqlWrapper.java b/src/org/kar/archidata/SqlWrapper.java index 30542e5..b83815d 100644 --- a/src/org/kar/archidata/SqlWrapper.java +++ b/src/org/kar/archidata/SqlWrapper.java @@ -71,13 +71,14 @@ public class SqlWrapper { // TODO : Maybe connect with a temporary not specified connection interface to a db ... PreparedStatement ps = entry.connection.prepareStatement("show databases"); ResultSet rs = ps.executeQuery(); + //LOGGER.info("List all tables: equals? '{}'", name); while (rs.next()) { String data = rs.getString(1); + //LOGGER.info(" - '{}'", data); if (name.equals(data)) { return true; } } - //int count = ret.getInt("total"); return false; } catch (SQLException ex) { LOGGER.error("Can not check if the DB exist SQL-error !!! {}", ex.getMessage()); @@ -92,64 +93,56 @@ public class SqlWrapper { } throw new InternalServerErrorException("Can Not manage the DB-access"); } - public static boolean createDB(String name) throws InternalServerErrorException { + public static boolean createDB(String name) { if (ConfigBaseVariable.getDBType().equals("sqlite")) { // no base manage in sqLite ... // TODO: check if the file exist or not ... return true; } - DBEntry entry; try { - entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, true); - } catch (IOException ex) { - // TODO Auto-generated catch block + return 1 == SqlWrapper.executeSimpleQuerry("CREATE DATABASE `" + name + "`;", true); + } catch (SQLException ex) { ex.printStackTrace(); - LOGGER.error("Can not Create the DB {}", ex.getMessage()); + LOGGER.error("Can not check if the DB exist!!! {}", ex.getMessage()); + return false; + } catch (IOException ex) { + ex.printStackTrace(); + LOGGER.error("Can not check if the DB exist!!! {}", ex.getMessage()); return false; } - try { - PreparedStatement ps = entry.connection.prepareStatement("CREATE DATABASE ?"); - ps.setString(1, name); - int ret = ps.executeUpdate(); - return ret == 1; - } catch (SQLException ex) { - LOGGER.error("Can not Create the DB SQL-error !!! {}", ex.getMessage()); - } finally { - try { - entry.close(); - } catch (IOException e) { - e.printStackTrace(); - } - entry = null; - } - throw new InternalServerErrorException("Can Not manage the DB-access"); } public static boolean isTableExist(String name) throws InternalServerErrorException { try { String request = ""; - if (!ConfigBaseVariable.getDBType().equals("sqlite")) { - request = """ - SELECT count(*) AS total - FROM information_schema.tables - WHERE table_name = ?; - LIMIT 1; - """; - } else { + if (ConfigBaseVariable.getDBType().equals("sqlite")) { request = """ SELECT COUNT(*) AS total FROM sqlite_master WHERE type = 'table' AND name = ?; """; + // PreparedStatement ps = entry.connection.prepareStatement("show tables"); + DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); + PreparedStatement ps = entry.connection.prepareStatement(request); + ps.setString(1, name); + ResultSet ret = ps.executeQuery(); + int count = ret.getInt("total"); + return count == 1; + } else { + DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); + // TODO : Maybe connect with a temporary not specified connection interface to a db ... + PreparedStatement ps = entry.connection.prepareStatement("show tables"); + ResultSet rs = ps.executeQuery(); + //LOGGER.info("List all tables: equals? '{}'", name); + while (rs.next()) { + String data = rs.getString(1); + //LOGGER.info(" - '{}'", data); + if (name.equals(data)) { + return true; + } + } + return false; } - - // PreparedStatement ps = entry.connection.prepareStatement("show tables"); - DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); - PreparedStatement ps = entry.connection.prepareStatement(request); - ps.setString(1, name); - ResultSet ret = ps.executeQuery(); - int count = ret.getInt("total"); - return count == 1; } catch (SQLException ex) { LOGGER.error("Can not check if the table exist SQL-error !!! {}", ex.getMessage()); } catch (IOException ex) { @@ -793,11 +786,23 @@ public class SqlWrapper { addElement(ps, elem.Value(), iii++); } } - - public static void executeSimpleQuerry(String querry) throws SQLException, IOException { - DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); + public static int executeSimpleQuerry(String querry, boolean root) throws SQLException, IOException { + DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, root); Statement stmt = entry.connection.createStatement(); - stmt.executeUpdate(querry); + return stmt.executeUpdate(querry); + } + + public static int executeSimpleQuerry(String querry) throws SQLException, IOException { + return executeSimpleQuerry(querry, false); + } + public static boolean executeQuerry(String querry, boolean root) throws SQLException, IOException { + DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, root); + Statement stmt = entry.connection.createStatement(); + return stmt.execute(querry); + } + + public static boolean executeQuerry(String querry) throws SQLException, IOException { + return executeQuerry(querry, false); } @SuppressWarnings("unchecked") @@ -939,9 +944,6 @@ public class SqlWrapper { //boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0; StringBuilder query = new StringBuilder(); query.append("SELECT "); - //query.append(tableName); - //query.append(" SET "); - boolean firstField = true; int count = 0; StateLoad[] autoClasify = new StateLoad[clazz.getFields().length]; @@ -1228,17 +1230,22 @@ public class SqlWrapper { entry = null; } } - - public static String createTable(Class clazz) throws Exception { + + public static List createTable(Class clazz) throws Exception { + return createTable(clazz, true); + } + public static List createTable(Class clazz, boolean createDrop) throws Exception { String tableName = getTableName(clazz); boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0; + List outList = new ArrayList<>(); StringBuilder out = new StringBuilder(); - StringBuilder otherTable = new StringBuilder(); // Drop Table - if (createIfNotExist) { - out.append("DROP TABLE IF EXISTS `"); - out.append(tableName); - out.append("`;\n"); + if (createIfNotExist && createDrop) { + StringBuilder tableTmp = new StringBuilder(); + tableTmp.append("DROP TABLE IF EXISTS `"); + tableTmp.append(tableName); + tableTmp.append("`;"); + outList.add(tableTmp.toString()); } // create Table: out.append("CREATE TABLE `"); @@ -1273,13 +1280,16 @@ public class SqlWrapper { if (name.endsWith("s")) { localName = name.substring(0, name.length()-1); } - if (createIfNotExist) { - otherTable.append("DROP TABLE IF EXISTS `"); - otherTable.append(tableName); - otherTable.append("_link_"); - otherTable.append(localName); - otherTable.append("`;\n"); + if (createIfNotExist && createDrop) { + StringBuilder tableTmp = new StringBuilder(); + tableTmp.append("DROP TABLE IF EXISTS `"); + tableTmp.append(tableName); + tableTmp.append("_link_"); + tableTmp.append(localName); + tableTmp.append("`;"); + outList.add(tableTmp.toString()); } + StringBuilder otherTable = new StringBuilder(); otherTable.append("CREATE TABLE `"); otherTable.append(tableName); otherTable.append("_link_"); @@ -1317,7 +1327,8 @@ public class SqlWrapper { if (!ConfigBaseVariable.getDBType().equals("sqlite")) { otherTable.append(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n\n"); } - otherTable.append(";\n\n"); + otherTable.append(";"); + outList.add(otherTable.toString()); } else { if (firstField) { out.append("\n\t\t`"); @@ -1355,11 +1366,18 @@ public class SqlWrapper { } if (defaultValue == null) { if (updateTime || createTime) { + out.append("DEFAULT CURRENT_TIMESTAMP"); if (!ConfigBaseVariable.getDBType().equals("sqlite")) { - out.append("DEFAULT CURRENT_TIMESTAMP "); - } else { - out.append("DEFAULT CURRENT_TIMESTAMP(3) "); - } + out.append("(3)"); + } + out.append(" "); + } + if (updateTime) { + out.append("ON UPDATE CURRENT_TIMESTAMP"); + if (!ConfigBaseVariable.getDBType().equals("sqlite")) { + out.append("(3)"); + } + out.append(" "); } } else { out.append("DEFAULT "); @@ -1369,6 +1387,13 @@ public class SqlWrapper { out.append(defaultValue); } out.append(" "); + if (updateTime) { + out.append("ON UPDATE CURRENT_TIMESTAMP"); + if (!ConfigBaseVariable.getDBType().equals("sqlite")) { + out.append("(3)"); + } + out.append(" "); + } } } else if (defaultValue == null) { if (updateTime || createTime) { @@ -1414,8 +1439,9 @@ public class SqlWrapper { if (!ConfigBaseVariable.getDBType().equals("sqlite")) { out.append(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci"); } - out.append(";\n"); - return out.toString() + otherTable.toString(); + out.append(";"); + outList.add( out.toString()); + return outList; } diff --git a/src/org/kar/archidata/catcher/FailException404API.java b/src/org/kar/archidata/catcher/FailException404API.java index 1ac0be0..69e6b71 100644 --- a/src/org/kar/archidata/catcher/FailException404API.java +++ b/src/org/kar/archidata/catcher/FailException404API.java @@ -16,7 +16,7 @@ implements ExceptionMapper { public Response toResponse(ClientErrorException exception) { RestErrorResponse ret = build(exception); logger.error("Error UUID={}", ret.uuid); - return Response.status(Response.Status.NOT_FOUND) + return Response.status(exception.getResponse().getStatusInfo().toEnum()) .entity(ret) .type(MediaType.APPLICATION_JSON) .build(); diff --git a/src/org/kar/archidata/migration/MigrationEngine.java b/src/org/kar/archidata/migration/MigrationEngine.java index 036eb15..6a8ea43 100644 --- a/src/org/kar/archidata/migration/MigrationEngine.java +++ b/src/org/kar/archidata/migration/MigrationEngine.java @@ -1,9 +1,14 @@ package org.kar.archidata.migration; +import java.io.IOException; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.kar.archidata.SqlWrapper; +import org.kar.archidata.annotation.SQLComment; +import org.kar.archidata.annotation.SQLLimitSize; +import org.kar.archidata.db.DBConfig; import org.kar.archidata.db.DBEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,15 +39,10 @@ public class MigrationEngine { * Get the current version/migration name * @return String represent the last migration. If null then no migration has been done. */ - public String getCurrentVersion() { - // TODO: check if the DB exist : - if (SqlWrapper.isTableExist("migration")) { - + public MigrationModel getCurrentVersion() { + if (!SqlWrapper.isTableExist("KAR_migration")) { + return null; } - - // check if migration table exist: - - // get the current migration try { List data = SqlWrapper.gets(MigrationModel.class, false); if (data == null) { @@ -53,30 +53,11 @@ public class MigrationEngine { LOGGER.error("Fail to Request migration table in the DB: empty size"); return null; } - return data.get(data.size()-1).name; - } catch (Exception ex) { - LOGGER.error("Fail to Request migration table in the DB:{}", ex.getMessage()); - ex.printStackTrace(); - } - return null; - } - - /** - * Get the current migration log generated - * @return String represent migration log (separate with \\n) - */ - public String getLastLog() { - try { - List data = SqlWrapper.gets(MigrationModel.class, false); - if (data == null) { - LOGGER.error("Can not collect the migration table in the DB:{}"); - return null; + LOGGER.debug("List of migrations:"); + for (MigrationModel elem : data) { + LOGGER.debug(" - date={} name={} end{}", elem.modify_date, elem.name, elem.terminated); } - if (data.size() == 0) { - LOGGER.error("Fail to Request migration table in the DB: empty size"); - return null; - } - return data.get(data.size()-1).log; + return data.get(data.size()-1); } catch (Exception ex) { LOGGER.error("Fail to Request migration table in the DB:{}", ex.getMessage()); ex.printStackTrace(); @@ -84,53 +65,141 @@ public class MigrationEngine { return null; } - public void migrate(DBEntry entry) { - String currentVersion = getCurrentVersion(); - List toApply = new ArrayList<>(); - boolean find = false; - for (int iii=0; iii create one", config.getDbName()); + // create the local DB: + SqlWrapper.createDB(config.getDbName()); + } + exist = SqlWrapper.isDBExist(config.getDbName()); + while (!exist) { + LOGGER.error("DB: '{}' DOES NOT EXIST after trying to create one ", config.getDbName()); + LOGGER.error("Waiting administrator create a new one, we check after 30 seconds..."); + Thread.sleep(30000); + exist = SqlWrapper.isDBExist(config.getDbName()); + } + LOGGER.info("DB '{}' exist.", config.getDbName()); + // STEP 2: Check migration table exist: + LOGGER.info("Verify existance of migration table '{}'", "KAR_migration"); + exist = SqlWrapper.isTableExist("KAR_migration"); + if (!exist) { + // create the table: + List sqlQuery; + try { + sqlQuery = SqlWrapper.createTable(MigrationModel.class, false); + } catch (Exception ex) { + ex.printStackTrace(); + while (true) { + LOGGER.error("Fail to create the local DB SQL model for migaration ==> wait administrator interventions"); + Thread.sleep(60*60*1000); + } + } + LOGGER.info("Create Table with : {}", sqlQuery.get(0)); + try { + SqlWrapper.executeQuerry(sqlQuery.get(0));//.replace("`", "").replace("\t", "")); + } catch (SQLException | IOException ex) { + // TODO Auto-generated catch block + ex.printStackTrace(); + while (true) { + LOGGER.error("Fail to create the local DB model for migaration ==> wait administrator interventions"); + Thread.sleep(60*60*1000); } - continue; } - toApply.add(this.datas.get(iii)); } + MigrationModel currentVersion = getCurrentVersion(); + List toApply = new ArrayList<>(); + if (currentVersion == null) { + //This is a first migration + LOGGER.info("First installation of the system ==> Create the DB"); + if (this.init == null) { + toApply = this.datas; + } else { + toApply.add(this.init); + } + } else { + if (currentVersion.terminated == false) { + while(true) { + LOGGER.error("An error occured in the last migration: '{}' defect @{}/{} ==> wait administrator interventions", currentVersion.name , currentVersion.stepId, currentVersion.count); + Thread.sleep(60*60*1000); + } + } + LOGGER.info("Upgrade the system Current version: {}", currentVersion); + boolean find = false; + for (int iii=0; iii toApply = new ArrayList<>(); boolean find = false; for (int iii=this.datas.size()-1; iii>=0; iii--) { if ( ! find) { - if (this.datas.get(iii).getName() == currentVersion) { + if (this.datas.get(iii).getName() == currentVersion.name) { find = true; } continue; } - if (this.datas.get(iii).getName() == currentVersion) { + if (this.datas.get(iii).getName() == currentVersion.name) { break; } toApply.add(this.datas.get(iii)); } + int id = 0; + int count = toApply.size(); for (MigrationInterface elem : toApply) { - revertSingle(entry, elem); + revertSingle(entry, elem, id, count); } } - public void revertSingle(DBEntry entry, MigrationInterface elem) { + public void revertSingle(DBEntry entry, MigrationInterface elem, int id, int count) { LOGGER.info("Revert migration: {} [BEGIN]", elem.getName()); LOGGER.info("Revert migration: {} [ END ]", elem.getName()); diff --git a/src/org/kar/archidata/migration/MigrationInterface.java b/src/org/kar/archidata/migration/MigrationInterface.java index 2a5a923..cf2a561 100644 --- a/src/org/kar/archidata/migration/MigrationInterface.java +++ b/src/org/kar/archidata/migration/MigrationInterface.java @@ -12,9 +12,10 @@ public interface MigrationInterface { * Migrate the system to a new version. * @param entry DB interface for the migration. * @param log Stored data in the BDD for the migration progression. + * @param migration Migration post data on each step... * @return true if migration is finished. */ - boolean applyMigration(DBEntry entry, StringBuilder log); + boolean applyMigration(DBEntry entry, StringBuilder log, MigrationModel model); /** * Remove a migration the system to the previous version. * @param entry DB interface for the migration. @@ -22,4 +23,10 @@ public interface MigrationInterface { * @return true if migration is finished. */ boolean revertMigration(DBEntry entry, StringBuilder log); + + /** + * Get the number of step in the migration process. + * @return count of SQL access. + */ + int getNumberOfStep(); } diff --git a/src/org/kar/archidata/migration/MigrationModel.java b/src/org/kar/archidata/migration/MigrationModel.java index 4f9c7d9..bb2fbaa 100644 --- a/src/org/kar/archidata/migration/MigrationModel.java +++ b/src/org/kar/archidata/migration/MigrationModel.java @@ -1,24 +1,33 @@ package org.kar.archidata.migration; import org.kar.archidata.annotation.SQLComment; +import org.kar.archidata.annotation.SQLDefault; import org.kar.archidata.annotation.SQLIfNotExists; import org.kar.archidata.annotation.SQLLimitSize; +import org.kar.archidata.annotation.SQLNotNull; import org.kar.archidata.annotation.SQLTableName; import org.kar.archidata.model.GenericTable; import com.fasterxml.jackson.annotation.JsonInclude; +// For logs only +//public static final String TABLE_NAME = "KAR_migration"; + @SQLTableName ("KAR_migration") @SQLIfNotExists @JsonInclude(JsonInclude.Include.NON_NULL) public class MigrationModel extends GenericTable{ - @SQLComment("Name of the migration") + @SQLComment("Name of the migration") @SQLLimitSize(256) public String name; + @SQLNotNull + @SQLDefault("'0'") + @SQLComment("if the migration is well terminated or not") + public Boolean terminated = false; @SQLComment("index in the migration progression") - public Integer index; + public Integer stepId = 0; @SQLComment("number of element in the migration") public Integer count; @SQLComment("Log generate by the migration") - public String log; + public String log = ""; } diff --git a/src/org/kar/archidata/migration/MigrationSqlStep.java b/src/org/kar/archidata/migration/MigrationSqlStep.java index 23b9afa..df3d995 100644 --- a/src/org/kar/archidata/migration/MigrationSqlStep.java +++ b/src/org/kar/archidata/migration/MigrationSqlStep.java @@ -20,28 +20,45 @@ public class MigrationSqlStep implements MigrationInterface{ } @Override - public boolean applyMigration(DBEntry entry, StringBuilder log) { + public boolean applyMigration(DBEntry entry, StringBuilder log, MigrationModel model) { for (int iii=0; iii>>> SQL ACTION : {}/{}", iii, actions.size()); + LOGGER.info(" >>>> SQL ACTION : {}/{}", iii, actions.size()); String action = actions.get(iii); - LOGGER.debug("SQL request: ```{}```", action); + LOGGER.info("SQL request: ```{}```", action); log.append("SQL: " + action + "\n"); try { - SqlWrapper.executeSimpleQuerry(action); - } catch (SQLException ex) { + SqlWrapper.executeQuerry(action); + } catch (SQLException | IOException ex) { ex.printStackTrace(); - LOGGER.debug("SQL request ERROR: ", ex.getMessage()); + LOGGER.info("SQL request ERROR: ", ex.getMessage()); log.append("SQL request ERROR: " + ex.getMessage() + "\n"); - return false; - } catch (IOException ex) { - ex.printStackTrace(); - LOGGER.debug("IO request ERROR: ", ex.getMessage()); - log.append("IO request ERROR: " + ex.getMessage() + "\n"); + model.stepId = iii+1; + model.log = log.toString(); + try { + SqlWrapper.update(model, model.id, List.of("stepId", "log")); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } return false; } log.append("action [" + iii + "/" + actions.size() + "] ==> DONE\n"); - LOGGER.debug(" >>>> SQL ACTION : {}/{} ==> DONE", iii, actions.size()); + LOGGER.info(" >>>> SQL ACTION : {}/{} ==> DONE", iii, actions.size()); + model.stepId = iii+1; + model.log = log.toString(); + try { + SqlWrapper.update(model, model.id, List.of("stepId", "log")); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } return true; } @@ -55,7 +72,13 @@ public class MigrationSqlStep implements MigrationInterface{ actions.add(action); } public void addClass(Class clazz) throws Exception { - actions.add(SqlWrapper.createTable(clazz)); + List tmp = SqlWrapper.createTable(clazz, false); + actions.addAll(tmp); + } + + @Override + public int getNumberOfStep() { + return actions.size(); } }