/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * Licensed 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. * * Initial developer(s): Stephane Giron * Contributor(s): */ package com.continuent.tungsten.replicator.util; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.sql.Date; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.util.LinkedHashMap; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.continuent.tungsten.common.config.TungstenProperties; import com.continuent.tungsten.common.exec.ArgvIterator; import com.continuent.tungsten.replicator.ReplicatorException; import com.continuent.tungsten.replicator.database.Database; import com.continuent.tungsten.replicator.database.DatabaseFactory; /** * This class defines a THLManagerCtrl that implements a utility to access * THLManager methods. See the printHelp() command for a description of current * commands. * * @author <a href="mailto:stephane.giron@continuent.com">Stephane Giron</a> * @version 1.0 */ public class DsQueryCtrl { protected static ArgvIterator argvIterator = null; /** * Connect to the underlying database containing THL. * * @throws ReplicatorException */ public void prepare() throws ReplicatorException, InterruptedException { } /** * Disconnect from the THL database. */ public void release() { } /** * Main method to run utility. * * @param argv optional command string */ @SuppressWarnings("unchecked") public static void main(String argv[]) { try { // Command line parameters and options. String configFile = null; String fileName = null; String user = null, password = null, url = null; // Parse command line arguments. ArgvIterator argvIterator = new ArgvIterator(argv); if (!argvIterator.hasNext()) { printHelp(); System.exit(0); } String curArg = null; while (argvIterator.hasNext()) { curArg = argvIterator.next(); if ("-conf".equals(curArg)) { configFile = argvIterator.next(); } else if ("-file".equals(curArg)) { fileName = argvIterator.next(); } else if ("-user".equals(curArg)) { user = argvIterator.next(); } else if ("-password".equals(curArg)) { if (System.console() == null) fatal("Console not available. Unable to type password interactively.", null); System.out.print("Enter password: "); password = new String(System.console().readPassword()); } else if ("-url".equals(curArg)) { url = argvIterator.next(); } else if (curArg.startsWith("-")) fatal("Unrecognized option: " + curArg, null); } if (configFile != null) { File file = new File(configFile); if (!file.exists() || !file.canRead()) fatal("Unable to read config file (" + configFile + ")", null); TungstenProperties props = new TungstenProperties(); props.load(new FileInputStream(file)); if (user == null) user = props.getString("user", "", true); if (password == null) password = props.getString("password", "", true); if (url == null) url = props.getString("url", "", true); } if (url == null) fatal("URL must be provided (either using -url option or in configuration file)", null); BufferedReader br = null; boolean readingFromStdIn = false; if (fileName == null) { readingFromStdIn = true; br = new BufferedReader(new InputStreamReader(System.in)); } else { File file = new File(fileName); if (!file.exists() || !file.canRead()) fatal("Unable to read sql file (" + fileName + ")", null); br = new BufferedReader(new FileReader(file)); } Database database = DatabaseFactory.createDatabase(url, user, password); try { database.connect(); } catch (SQLException e) { System.out.println(e.getMessage()); // Send the stack trace to SYSERR e.printStackTrace(); // Adn return the error code System.exit(e.getErrorCode()); } String sql = null; SQLException sqlEx; JSONArray jsonArr = new JSONArray(); while ((sql = br.readLine()) != null) { sqlEx = null; sql = sql.trim(); if (readingFromStdIn && sql.length() == 0) break; else if (sql.startsWith("#") || sql.length() == 0) continue; LinkedHashMap<String, Object> jsonObj = new LinkedHashMap<String, Object>(); jsonArr.add(jsonObj); Statement stmt = null; int rc = 0; try { stmt = database.createStatement(); boolean isRS = false; jsonObj.put("statement", sql); try { isRS = stmt.execute(sql); rc = 0; } catch (SQLException e) { rc = e.getErrorCode(); sqlEx = e; } finally { jsonObj.put("rc", rc); } if (rc == 0) jsonObj.put("results", logResults(stmt, isRS)); else jsonObj.put("results", new JSONArray()); jsonObj.put("error", logError(sqlEx)); } catch (Exception e) { e.printStackTrace(); } finally { if (stmt != null) stmt.close(); } } DsQueryCtrl.println(jsonArr.toJSONString()); } catch (Exception e) { e.printStackTrace(); } } private static Object logError(SQLException sqlEx) { if (sqlEx == null) return null; return sqlEx.getMessage(); } @SuppressWarnings("unchecked") private static JSONArray logResults(Statement stmt, boolean isRS) throws SQLException { JSONArray json = new JSONArray(); int updateCount = -1; while (isRS || (updateCount = stmt.getUpdateCount()) > -1) { if (isRS) { ResultSet rs = null; try { rs = stmt.getResultSet(); json.add(logResultsetResult(rs)); } finally { if (rs != null) { rs.close(); rs = null; } } } else { json.add(logUpdateCount(updateCount)); } isRS = stmt.getMoreResults(); } return json; } @SuppressWarnings("unchecked") private static JSONArray logResultsetResult(ResultSet rs) throws SQLException { JSONArray json = new JSONArray(); if (rs != null) while (rs.next()) { json.add(logRow(rs)); } return json; } @SuppressWarnings("unchecked") private static JSONObject logUpdateCount(int updateCount) { JSONObject json = new JSONObject(); json.put("rowcount", updateCount); return json; } private static LinkedHashMap<String, Object> logRow(ResultSet rs) throws SQLException { LinkedHashMap<String, Object> json = new LinkedHashMap<String, Object>(); ResultSetMetaData metaData = rs.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { Object value = null; if (metaData.getColumnType(i) == java.sql.Types.TIMESTAMP) value = rs.getTimestamp(i); else value = rs.getObject(i); String columnName = metaData.getColumnLabel(i); if (value instanceof Timestamp) { String valueAsString = ((Timestamp) value).toString(); json.put(columnName, valueAsString); } else if (value instanceof Date) { String valueAsString = ((Date) value).toString(); json.put(columnName, valueAsString); } else if (value instanceof Time) { String valueAsString = ((Time) value).toString(); json.put(columnName, valueAsString); } else json.put(columnName, value); } return json; } protected static void printHelp() { println("Query Utility"); println("Syntax: query {-url <jdbc_url> [-user <jdbc_user>] [-password] | -conf <path_to_file>} [-file <path_to_sql_file>] "); println("Options:"); println(" -url <jdbc_url> - JDBC url of the database to connect to"); println(" -user <jdbc_user> - User used to connect to the database"); println(" -password - Prompt for password"); println(""); println(" -conf <path_to_file> - Configuration file that contains values for connection properties (url, user and password)"); println(" -file <path_to_sql_file> - File containing the SQL commands to run."); println(" If missing, read SQL commands from STDIN"); } /** * Print a message to stdout with trailing new line character. * * @param msg */ protected static void println(String msg) { System.out.println(msg); } /** * Print a message to stdout without trailing new line character. * * @param msg */ protected static void print(String msg) { System.out.print(msg); } /** * Abort following a fatal error. * * @param msg * @param t */ protected static void fatal(String msg, Throwable t) { System.out.println(msg); if (t != null) t.printStackTrace(); fail(); } /** * Exit with a process failure code. */ protected static void fail() { System.exit(1); } /** * Exit with a process success code. */ protected static void succeed() { System.exit(0); } /** * Reads a character from stdin, blocks until it is not received. * * @return true if use pressed `y`, false otherwise. */ protected static boolean readYes() throws IOException { return (System.in.read() == 'y'); } /** * Returns a value of a given Boolean object or false if the object is null. * * @param bool Boolean object to check and return. * @return the value of a given Boolean object or false if the object is * null. */ protected static boolean getBoolOrFalse(Boolean bool) { if (bool != null) return bool; else return false; } }