package fr.mch.mdo.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hsqldb.cmdline.SqlFile; import org.hsqldb.cmdline.SqlToolError; import fr.mch.mdo.test.resources.ITestResources; /** * This class is abstract because we do not want JUnit to launch this class. * * @author Mathieu * */ public abstract class MdoLoadingDatabaseTestCase extends MdoTestCase { private static boolean alreadyLoaded = false; private enum SqlDialect { HSQLDIALECT() { Map<String, String> sqlVarMap(Reader reader) { Map<String, String> sqlVarMap = new HashMap<String, String>(); // This is used to replace the "*{bytea}" by "BINARY" in the sql // file. sqlVarMap.put("bytea", "BINARY"); Map<Pattern, String> patternSubstitutes = new HashMap<Pattern, String>(); patternSubstitutes.put(Pattern.compile("\\*\\{(DEFAULT NEXTVAL.*)\\}"), "GENERATED BY DEFAULT AS IDENTITY"); patternSubstitutes.put(Pattern.compile("\\*\\{(OWNED.*)\\}"), "RESTART WITH 1"); sqlVarMap.putAll(this.sqlVarMap(reader, patternSubstitutes)); return sqlVarMap; } }, POSTGRESQLDIALECT() { Map<String, String> sqlVarMap(Reader reader) { Map<String, String> sqlVarMap = new HashMap<String, String>(); Map<Pattern, String> patternSubstitutes = new HashMap<Pattern, String>(); patternSubstitutes.put(Pattern.compile("\\*\\{(.*)\\}"), null); sqlVarMap.putAll(this.sqlVarMap(reader, patternSubstitutes)); return sqlVarMap; } }; Map<String, String> sqlVarMap(Reader reader) { return new HashMap<String, String>(); } Map<String, String> sqlVarMap(Reader reader, Map<Pattern, String> patternSubstitutes) { HashMap<String, String> result = new HashMap<String, String>(); BufferedReader br = null; try { String substituteX = ""; String line = null; br = new BufferedReader(reader); // For each line in the file while ((line = br.readLine()) != null) { for (Entry<Pattern, String> entry : patternSubstitutes.entrySet()) { Pattern pattern = entry.getKey(); String substitute = entry.getValue(); // Looking for *{DEFAULT NEXTVAL(xxx)} and replace by // substitute Matcher matcher = pattern.matcher(line); while (matcher.find()) { // This is used to replace the "*{xxx}" by // substitute in the sql file. if (substitute == null) { substituteX = matcher.group(1); } else { substituteX = substitute; } result.put(matcher.group(1), substituteX); } } } } catch (IOException e) { fail("SQL file error: " + e.getMessage()); } finally { try { br.close(); } catch (Exception e) { fail("Could not close the opened file: " + e.getMessage()); } } return result; } } // Files to be loaded protected URL[] fileURLs = { ITestResources.class.getResource("montagnesdorStructure.sql"), ITestResources.class.getResource("montagnesdorDatas.sql") }; protected MdoLoadingDatabaseTestCase(String testName) { super(testName); // Check if we have already loaded the database files. if (!alreadyLoaded) { Connection connection = this.getConnection(); String sqlDialectName = this.getSqlDialectName(); loadFiles(connection, sqlDialectName, fileURLs); alreadyLoaded = true; } } /** * This method is used to load the structure and data file in Database. * @param connection the connection to database. * @param sqlDialectName the SQL dialect. * @param fileURLs Files to be loaded. */ protected void loadFiles(Connection connection, String sqlDialectName, URL[] fileURLs) { // Prepare Connection with Hibernate prepareConnection(connection); // Prepare SQL Dialect SqlDialect sqlDialect = prepareSqlDialect(sqlDialectName); // Replace *{xxx} in the sql file by "yyy" in the loaded database // For PostGresql, the standard GENERATED BY DEFAULT AS IDENTITY must be // replace by DEFAULT NEXTVAL('dedicated_sequence_name'). // So, we have to map all PostGresql default generated sequences by // GENERATED BY DEFAULT AS IDENTITY. Map<String, String> sqlVarMap = new HashMap<String, String>(); SqlFile sqlFile = null; try { for (URL fileURL : fileURLs) { Reader reader = null; try { // Open reader reader = new InputStreamReader(fileURL.openStream(), "UTF8"); // Read and close reader sqlVarMap = sqlDialect.sqlVarMap(reader); // Open reader again because already read the reader reader = new InputStreamReader(fileURL.openStream(), "UTF8"); // Don't use URL.getFile or URL.getPath instead convert to URI first // Because when using URL and the path contains space then the URL.getFile or URL.getPath will convert space to "%20" String fileName = fileURL.getPath(); sqlFile = new SqlFile(reader, fileName, System.out, "UTF8", false); sqlFile.addUserVars(sqlVarMap); sqlFile.setConnection(connection); // Execute the SQL sqlFile.execute(); // The only reason for the following two statements is so // that changes made by one .sql file will effect the future SQL files. // Has no effect if you only execute one SQL file. connection = sqlFile.getConnection(); sqlVarMap = sqlFile.getUserVars(); } catch (IOException e) { fail("SQL file IOException: " + e.getMessage()); } catch (SqlToolError e) { fail("SQL file SqlToolError: " + e.getMessage()); } catch (SQLException e) { fail("SQL file SQLException: " + e.getMessage()); } finally { try { reader.close(); } catch (Exception se) { // Purposefully ignoring. // We have done what we want and are now going to exit, so // who cares. } } } } finally { try { connection.setAutoCommit(false); connection.close(); } catch (SQLException se) { // Purposefully ignoring. // We have done what we want and are now going to exit, so // who cares. } } } /** * Prepare the connection before using. * @param connection the connection. */ private void prepareConnection(Connection connection) { // Prepare Connection with Hibernate assertNotNull("Check not null SQL Connection", connection); //To be sure that the HSQLDB SQLFile.java will load the file into database try { connection.setAutoCommit(true); } catch (Exception e) { fail("Could not set autocommit to true: " + e.getMessage()); } } /** * Prepare the SQL Dialect before using. * @param sqlDialectName the SQL Dialect Name. * @return the SqlDialect enum. */ private SqlDialect prepareSqlDialect(String sqlDialectName) { // Prepare SQL Dialect SqlDialect result = null; assertNotNull("Check not null SQL Dialect name", sqlDialectName); try { result = SqlDialect.valueOf(sqlDialectName); } catch (Exception e) { fail("Could not parse the SQL Dialect: " + e.getMessage()); } return result; } protected abstract Connection getConnection(); protected abstract String getSqlDialectName(); }