/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.airavata.registry.tool; import org.apache.commons.cli.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.net.URI; import java.sql.*; import java.text.DecimalFormat; import java.util.*; import java.util.Date; public class DBMigrator { private static final Logger logger = LoggerFactory.getLogger(DBMigrator.class); private static final String delimiter = ";"; private static final String MIGRATE_SQL_DERBY = "migrate_derby.sql"; private static final String MIGRATE_SQL_MYSQL = "migrate_mysql.sql"; private static final String REGISTRY_VERSION = "registry.version"; private static final String AIRAVATA_VERSION = "0.5"; private static String currentAiravataVersion; private static String relativePath; private static String SELECT_QUERY; private static String INSERT_QUERY; private static String UPDATE_QUERY; private static String jdbcURL; private static String jdbcUser; private static String jdbcPwd; public static void main(String[] args) { parseArguments(args); generateConfigTableQueries(); updateDB(jdbcURL, jdbcUser, jdbcPwd); } public static void generateConfigTableQueries(){ SELECT_QUERY = "SELECT * FROM CONFIGURATION WHERE config_key='" + REGISTRY_VERSION + "' and category_id='SYSTEM'"; INSERT_QUERY = "INSERT INTO CONFIGURATION (config_key, config_val, expire_date, category_id) VALUES('" + REGISTRY_VERSION + "', '" + getIncrementedVersion(currentAiravataVersion) + "', '" + getCurrentDate() + "','SYSTEM')"; UPDATE_QUERY = "UPDATE CONFIGURATION SET config_val='" + getIncrementedVersion(currentAiravataVersion) + "', expire_date='" + getCurrentDate() + "' WHERE config_key='" + REGISTRY_VERSION + "' and category_id='SYSTEM'"; } //we assume given database is up and running public static void updateDB (String jdbcUrl, String jdbcUser, String jdbcPwd){ relativePath = "db-scripts/" + getIncrementedVersion(currentAiravataVersion) + "/"; InputStream sqlStream = null; Scanner in = new Scanner(System.in); if (jdbcUrl == null || jdbcUrl.equals("")){ System.out.println("Enter JDBC URL : "); jdbcUrl = in.next(); } if (jdbcUser == null || jdbcUser.equals("")){ System.out.println("Enter JDBC Username : "); jdbcUser = in.next(); } if (jdbcPwd == null || jdbcPwd.equals("")){ System.out.println("Enter JDBC password : "); jdbcPwd = in.next(); } String dbType = getDBType(jdbcUrl); String jdbcDriver = null; Connection connection; try { File file = null; if (dbType.contains("derby")){ jdbcDriver = "org.apache.derby.jdbc.ClientDriver"; sqlStream = DBMigrator.class.getClassLoader().getResourceAsStream(relativePath + MIGRATE_SQL_DERBY); } else if (dbType.contains("mysql")){ jdbcDriver = "com.mysql.jdbc.Driver"; sqlStream = DBMigrator.class.getClassLoader().getResourceAsStream(relativePath + MIGRATE_SQL_MYSQL); } Class.forName(jdbcDriver).newInstance(); connection = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPwd); if (canUpdated(connection)){ executeSQLScript(connection, sqlStream); //update configuration table with airavata version updateConfigTable(connection); } } catch (ClassNotFoundException e) { logger.error("Unable to find SQL scripts..." , e); } catch (InstantiationException e) { logger.error("Error while updating the database..." , e); } catch (IllegalAccessException e) { logger.error("Error while updating the database..." , e); } catch (SQLException e) { logger.error("Error while updating the database..." , e); } catch (Exception e) { logger.error("Error while updating the database..." , e); } } private static boolean canUpdated (Connection conn){ if (!currentAiravataVersion.equals(AIRAVATA_VERSION)){ String config = executeSelectQuery(conn); if (config != null){ if (config.equals(getIncrementedVersion(currentAiravataVersion))) { return false; } else { return true; } } } else if (currentAiravataVersion.equals(AIRAVATA_VERSION)){ return true; } return false; } private static void updateConfigTable (Connection connection){ // if existing need to update, otherwise insert if (executeSelectQuery(connection) != null){ executeQuery(connection, UPDATE_QUERY); } else { executeQuery(connection, INSERT_QUERY); } } private static Timestamp getCurrentDate (){ Calendar cal = Calendar.getInstance(); Date date = cal.getTime(); Timestamp d = new Timestamp(date.getTime()); return d; } private static String getIncrementedVersion (String currentVersion){ DecimalFormat decimalFormat = new DecimalFormat("#,##0.0"); Double currentVer = Double.parseDouble(currentVersion); double v = currentVer + .1; String formattedVal = decimalFormat.format(v); return formattedVal; } private static String executeSelectQuery (Connection conn){ try { Statement statement = conn.createStatement(); ResultSet rs = statement.executeQuery(SELECT_QUERY); if (rs != null){ while (rs.next()) { currentAiravataVersion = rs.getString(2); return currentAiravataVersion; } } } catch (SQLException e) { logger.error(e.getMessage() , e); } return null; } private static void executeQuery (Connection conn, String query){ try { Statement statement = conn.createStatement(); statement.execute(query) ; } catch (SQLException e) { logger.error(e.getMessage() , e); } } private static void executeSQLScript(Connection conn, InputStream inputStream) throws Exception { StringBuffer sql = new StringBuffer(); BufferedReader reader = null; try{ reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (line.startsWith("//")) { continue; } if (line.startsWith("--")) { continue; } StringTokenizer st = new StringTokenizer(line); if (st.hasMoreTokens()) { String token = st.nextToken(); if ("REM".equalsIgnoreCase(token)) { continue; } } sql.append(" ").append(line); // SQL defines "--" as a comment to EOL // and in Oracle it may contain a hint // so we cannot just remove it, instead we must end it if (line.indexOf("--") >= 0) { sql.append("\n"); } if ((checkStringBufferEndsWith(sql, delimiter))) { String sqlString = sql.substring(0, sql.length() - delimiter.length()); executeSQL(sqlString, conn); sql.replace(0, sql.length(), ""); } } System.out.println(sql.toString()); // Catch any statements not followed by ; if (sql.length() > 0) { executeSQL(sql.toString(), conn); } }catch (IOException e){ logger.error("Error occurred while executing SQL script for creating Airavata database", e); throw new Exception("Error occurred while executing SQL script for creating Airavata database", e); }finally { if (reader != null) { reader.close(); } } } private static String getDBType(String jdbcURL){ try{ String cleanURI = jdbcURL.substring(5); URI uri = URI.create(cleanURI); return uri.getScheme(); } catch (Exception e) { logger.error(e.getMessage(), e); return null; } } public static boolean checkStringBufferEndsWith(StringBuffer buffer, String suffix) { if (suffix.length() > buffer.length()) { return false; } // this loop is done on purpose to avoid memory allocation performance // problems on various JDKs // StringBuffer.lastIndexOf() was introduced in jdk 1.4 and // implementation is ok though does allocation/copying // StringBuffer.toString().endsWith() does massive memory // allocation/copying on JDK 1.5 // See http://issues.apache.org/bugzilla/show_bug.cgi?id=37169 int endIndex = suffix.length() - 1; int bufferIndex = buffer.length() - 1; while (endIndex >= 0) { if (buffer.charAt(bufferIndex) != suffix.charAt(endIndex)) { return false; } bufferIndex--; endIndex--; } return true; } private static void executeSQL(String sql, Connection conn) throws Exception { if ("".equals(sql.trim())) { return; } Statement statement = null; try { logger.debug("SQL : " + sql); boolean ret; int updateCount = 0, updateCountTotal = 0; statement = conn.createStatement(); ret = statement.execute(sql); updateCount = statement.getUpdateCount(); do { if (!ret) { if (updateCount != -1) { updateCountTotal += updateCount; } } ret = statement.getMoreResults(); if (ret) { updateCount = statement.getUpdateCount(); } } while (ret); logger.debug(sql + " : " + updateCountTotal + " rows affected"); SQLWarning warning = conn.getWarnings(); while (warning != null) { logger.warn(warning + " sql warning"); warning = warning.getNextWarning(); } conn.clearWarnings(); } catch (SQLException e) { if (e.getSQLState().equals("X0Y32")) { logger.info("Table Already Exists", e); } else { throw new Exception("Error occurred while executing : " + sql, e); } } finally { if (statement != null) { try { statement.close(); } catch (SQLException e) { logger.error("Error occurred while closing result set.", e); } } } } public static void parseArguments(String[] args){ try{ Options options = new Options(); options.addOption("url", true , "JDBC URL"); options.addOption("user", true, "JDBC Username"); options.addOption("pwd", true, "JDBC Password"); options.addOption("v", true, "Airavata Current Version"); CommandLineParser parser = new PosixParser(); CommandLine cmd = parser.parse( options, args); jdbcURL = cmd.getOptionValue("url"); if (jdbcURL == null){ logger.info("You should enter JDBC URL and JDBC Credentials as parameters..."); } jdbcUser = cmd.getOptionValue("user"); if (jdbcUser == null){ logger.info("You should enter JDBC URL and JDBC Credentials as parameters..."); } jdbcPwd = cmd.getOptionValue("pwd"); currentAiravataVersion = cmd.getOptionValue("v"); if (currentAiravataVersion == null){ logger.info("You should enter current Airavata version you are using..."); } } catch (ParseException e) { logger.error("Error while reading command line parameters" , e); } } protected static InputStream readFile(File file) { StringBuilder fileContentsBuilder = new StringBuilder(); BufferedReader bufferedReader = null; try { char[] buffer = new char[32767]; bufferedReader = new BufferedReader(new FileReader(file)); int read = 0; do { read = bufferedReader.read(buffer); if (read > 0) { fileContentsBuilder.append(buffer, 0, read); } } while (read > 0); } catch (Exception e) { logger.error("Failed to read file " + file.getPath(), e); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { logger.error("Unable to close BufferedReader for " + file.getPath(), e); } } } System.out.println(fileContentsBuilder.toString()); InputStream is = new ByteArrayInputStream(fileContentsBuilder.toString().getBytes()); return is; } }