--- /dev/null
+/*\r
+ * ============LICENSE_START====================================================\r
+ * org.onap.music.mdbc\r
+ * =============================================================================\r
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.\r
+ * =============================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END======================================================\r
+ */\r
+package org.onap.music.mdbc.examples;\r
+\r
+import java.sql.*;\r
+import java.text.SimpleDateFormat;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Random;\r
+\r
+public class MdbcTestMultiClient implements Runnable {\r
+ private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");\r
+ \r
+ private String connectionString = null;\r
+ private int threadId = -1;\r
+ \r
+ private List<String> connectionStrings = new ArrayList<String>();\r
+ private static final String defaultConnection = "jdbc:avatica:remote:url=http://localhost:30000/test;serialization=protobuf";\r
+ private String lastName = "Lastname";\r
+ private int baseId = 700;\r
+ private int baseIdRange = 50;\r
+ private int maxCalls = 50; \r
+ private int maxTimeMs = 60000;\r
+ private int minDelayBetweenTestsMs = 1000;\r
+ private int additionalDelayBetweenTestsMs = 1000;\r
+ private boolean doDelete = true;\r
+ private boolean doUpdate = true;\r
+ private int connectionCloseChancePct = 50;\r
+ private int skipInitialSelectPct = 25;\r
+ private int selectInsteadOfUpdatePct = 25;\r
+ \r
+ private boolean explainConnection = true;\r
+ \r
+ public static class Employee {\r
+ public final int empid;\r
+ public String lastname;\r
+ public String firstname;\r
+ public String address;\r
+ public String city;\r
+ \r
+ public Employee(int empid, String lastname, String firstname, String address, String city) {\r
+ super();\r
+ this.empid = empid;\r
+ this.lastname = lastname;\r
+ this.firstname = firstname;\r
+ this.address = address;\r
+ this.city = city;\r
+ }\r
+\r
+ public String getLastname() {\r
+ return lastname;\r
+ }\r
+\r
+ public void setLastname(String lastname) {\r
+ this.lastname = lastname;\r
+ }\r
+\r
+ public String getFirstname() {\r
+ return firstname;\r
+ }\r
+\r
+ public void setFirstname(String firstname) {\r
+ this.firstname = firstname;\r
+ }\r
+\r
+ public String getAddress() {\r
+ return address;\r
+ }\r
+\r
+ public void setAddress(String address) {\r
+ this.address = address;\r
+ }\r
+\r
+ public String getCity() {\r
+ return city;\r
+ }\r
+\r
+ public void setCity(String city) {\r
+ this.city = city;\r
+ }\r
+\r
+ public int getEmpid() {\r
+ return empid;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "Employee: " + empid + ", " + lastname + ", " + firstname + ", " + address + ", " + city;\r
+ }\r
+\r
+ \r
+ }\r
+\r
+ public MdbcTestMultiClient(String[] args) {\r
+ char currState = (char)0, noState = (char)0;\r
+ for (String arg : args) {\r
+ if (currState==noState) {\r
+ switch (arg) {\r
+ case "-?":\r
+ case "--help":\r
+ showHelp();\r
+ break;\r
+ case "-c":\r
+ case "--connection": \r
+ currState = 'c';\r
+ break;\r
+ case "-n":\r
+ case "--name": \r
+ currState = 'n';\r
+ break;\r
+ case "-b":\r
+ case "--baseId": \r
+ currState = 'b';\r
+ break;\r
+ case "-r":\r
+ case "--baseRange": \r
+ currState = 'r';\r
+ break;\r
+ case "-m":\r
+ case "--maxCalls": \r
+ currState = 'm';\r
+ break;\r
+ case "-t":\r
+ case "--maxTime": \r
+ currState = 't';\r
+ break;\r
+ case "-d":\r
+ case "--minDelay": \r
+ currState = 'd';\r
+ break;\r
+ case "-a":\r
+ case "--addDelay": \r
+ currState = 'a';\r
+ break;\r
+ case "-u":\r
+ case "--update": \r
+ currState = 'u';\r
+ break;\r
+ case "-x":\r
+ case "--delete": \r
+ currState = 'x';\r
+ break;\r
+ case "-l":\r
+ case "--closeChance": \r
+ currState = 'l';\r
+ break;\r
+ case "-s":\r
+ case "--skipInitialSelect":\r
+ currState = 's';\r
+ break;\r
+ case "-i":\r
+ case "--selectNotUpdate":\r
+ currState = 'i';\r
+ break;\r
+ default:\r
+ System.out.println("Didn't understand switch " + arg);\r
+ }\r
+ } else {\r
+ try {\r
+ switch (currState) {\r
+ case 'c':\r
+ connectionStrings.add(arg);\r
+ break;\r
+ case 'n':\r
+ lastName = arg;\r
+ break;\r
+ case 'b':\r
+ baseId = Integer.parseInt(arg);\r
+ break;\r
+ case 'r':\r
+ baseIdRange = Integer.parseInt(arg);\r
+ break;\r
+ case 'm':\r
+ maxCalls = Integer.parseInt(arg);\r
+ break;\r
+ case 't':\r
+ maxTimeMs = Integer.parseInt(arg);\r
+ break;\r
+ case 'd':\r
+ minDelayBetweenTestsMs = Integer.parseInt(arg);\r
+ break;\r
+ case 'a':\r
+ additionalDelayBetweenTestsMs = Integer.parseInt(arg);\r
+ break;\r
+ case 'u':\r
+ doUpdate = arg.toUpperCase().startsWith("Y");\r
+ break;\r
+ case 'x':\r
+ doDelete = arg.toUpperCase().startsWith("Y");\r
+ break;\r
+ case 'l':\r
+ connectionCloseChancePct = Integer.parseInt(arg);\r
+ break;\r
+ case 's':\r
+ skipInitialSelectPct = Integer.parseInt(arg);\r
+ break;\r
+ case 'i': \r
+ selectInsteadOfUpdatePct = Integer.parseInt(arg);\r
+ break;\r
+ default:\r
+ System.out.println("Bad state " + currState + "????");\r
+ }\r
+ } catch (NumberFormatException e) {\r
+ System.out.println("Bad integer " + arg + " for switch " + currState);\r
+ }\r
+ currState = noState;\r
+ }\r
+ }\r
+ if (connectionStrings.isEmpty()) connectionStrings.add(defaultConnection);\r
+ }\r
+\r
+ private void showHelp() {\r
+ System.out.println(\r
+ "-?; --help: Show help\n" + \r
+ "-c; --connection: MDBC connection string, may appear multiple times\n" + \r
+ "-n; --name: Last name in persons table, default \"Lastname\"\n" + \r
+ "-b; --baseId: Base ID, default 700\n" + \r
+ "-r; --baseRange: Range of ID, default 50\n" + \r
+ "-m; --maxCalls: Max number of commits (each may be 1+ updates), default 50\n" + \r
+ "-t; --maxTime: Max time in ms test will run, default 60000\n" + \r
+ "-d; --minDelay: Min delay between tests in ms, default 1000\n" + \r
+ "-a; --addDelay: Max randomized additional delay between tests in ms, default 1000\n" + \r
+ "-u; --update: Generate update statements; default Y\n" + \r
+ "-x; --delete: Generate delete statements; default Y\n" + \r
+ "-l; --closeChance: Percent chance of closing connection after each commit, default 50\n" +\r
+ "-s; --skipInitialSelect: Percent chance of skipping each initial select in a transaction, default 25\n" +\r
+ "-i; --selectNotUpdate: Percent chance of each action in a transaction being a select instead of an update, default 25"\r
+ );\r
+ \r
+ }\r
+\r
+ public MdbcTestMultiClient(MdbcTestMultiClient that, int i) {\r
+ this.connectionString = that.connectionStrings.get(i);\r
+ this.threadId = i;\r
+ \r
+ this.lastName = that.lastName;\r
+ this.lastName = that.lastName;\r
+ this.baseId = that.baseId;\r
+ this.baseIdRange = that.baseIdRange;\r
+ this.maxCalls = that.maxCalls;\r
+ this.maxTimeMs = that.maxTimeMs;\r
+ this.minDelayBetweenTestsMs = that.minDelayBetweenTestsMs;\r
+ this.additionalDelayBetweenTestsMs = that.additionalDelayBetweenTestsMs;\r
+ this.doDelete = that.doDelete;\r
+ this.doUpdate = that.doUpdate;\r
+ this.connectionCloseChancePct = that.connectionCloseChancePct;\r
+ this.skipInitialSelectPct = that.skipInitialSelectPct;\r
+ this.selectInsteadOfUpdatePct = that.selectInsteadOfUpdatePct;\r
+ }\r
+ \r
+ private static String[] tableNames = {"persons", "persons2"};\r
+\r
+ private void doTest(Connection connection, Random r) throws SQLException {\r
+ doLog("skipInitialSelectPct = " + skipInitialSelectPct + ", selectInsteadOfUpdatePct = " + selectInsteadOfUpdatePct);\r
+ HashMap<String, HashMap<Integer, Employee>> employeeMaps = new HashMap<String, HashMap<Integer, Employee>> ();\r
+// HashMap<Integer, Employee> employeeMap = new HashMap<Integer, Employee>();\r
+\r
+ for (String tableName : tableNames) {\r
+ if (r.nextInt(100)<skipInitialSelectPct) {\r
+ doLog("Skipping select");\r
+// employeeMap = null;\r
+ } else {\r
+ Statement querySt = connection.createStatement();\r
+ if (explainConnection) {\r
+ doLog("querySt is a: ");\r
+ Class<?> qsClass = querySt.getClass();\r
+ while (qsClass!=null) {\r
+ doLog(">>> " + qsClass.getName());\r
+ qsClass = qsClass.getSuperclass();\r
+ }\r
+ doLog("connection is a ");\r
+ qsClass = connection.getClass();\r
+ while (qsClass!=null) {\r
+ doLog(">>> " + qsClass.getName());\r
+ qsClass = qsClass.getSuperclass();\r
+ }\r
+ explainConnection = false;\r
+ }\r
+\r
+ HashMap<Integer, Employee> employeeMap = new HashMap<Integer, Employee>();\r
+ employeeMaps.put(tableName, employeeMap);\r
+ \r
+ ResultSet rs = executeQueryTimed("select * from " + tableName, querySt, false);\r
+ while (rs.next()) {\r
+ // doLog("PersonId = " + rs.getInt("personId") + ", lastname = " + rs.getString("lastname") + ", firstname = " + rs.getString("firstname"));\r
+ Employee emp = new Employee(rs.getInt("personId"), rs.getString("lastname"), rs.getString("firstname"), rs.getString("address"), rs.getString("city"));\r
+ employeeMap.put(rs.getInt("personId"), emp);\r
+ doLog("Found: " + emp);\r
+ }\r
+ querySt.close();\r
+ }\r
+ }\r
+\r
+ Statement insertStmt = connection.createStatement();\r
+ boolean firstTry = true;\r
+// String tableName = tableNames[r.nextInt(tableNames.length)];\r
+// HashMap<Integer, Employee> employeeMap = employeeMaps.get(tableName);\r
+// executeUpdateTimed(generateStatement(employeeMap, r, tableName), insertStmt, true);\r
+\r
+ while (firstTry || r.nextBoolean()) {\r
+ firstTry = false;\r
+ String tableName = tableNames[r.nextInt(tableNames.length)];\r
+ HashMap<Integer, Employee> employeeMap = employeeMaps.get(tableName);\r
+ if (r.nextInt(100)<selectInsteadOfUpdatePct) {\r
+ Statement querySt = connection.createStatement();\r
+ ResultSet rs = executeQueryTimed("select count(*) x from " + tableName, querySt, false);\r
+ if (rs.next()) doLog("Select count returned " + rs.getInt("x")); else doLog("Didn't get response from select count!");\r
+ rs.close();\r
+ querySt.close();\r
+ } else {\r
+ executeUpdateTimed(generateStatement(employeeMap, r, tableName), insertStmt, true);\r
+ }\r
+ }\r
+\r
+ connection.commit();\r
+\r
+ insertStmt.close();\r
+ }\r
+ \r
+ private void executeUpdateTimed(String sql, Statement insertStmt, boolean swallowException) throws SQLException {\r
+ long beforeTime, afterTime;\r
+\r
+ beforeTime = new java.util.Date().getTime();\r
+ try {\r
+ insertStmt.execute(sql);\r
+ } catch (org.apache.calcite.avatica.AvaticaSqlException e) {\r
+ if (swallowException) {\r
+ doLog("Caught exception: " + e);\r
+ } else {\r
+ throw e;\r
+ }\r
+ }\r
+ afterTime = new java.util.Date().getTime();\r
+ doLog("After update, execution time = " + (afterTime-beforeTime) + " ms");\r
+ }\r
+\r
+ private ResultSet executeQueryTimed(String sql, Statement querySt, boolean swallowException) throws SQLException {\r
+ long beforeTime, afterTime;\r
+ \r
+ doLog("Before select: " + sql);\r
+ beforeTime = new java.util.Date().getTime();\r
+ ResultSet rs = null;\r
+ try {\r
+ rs = querySt.executeQuery(sql);\r
+ } catch (SQLException e) {\r
+ if (swallowException) {\r
+ doLog("Caught exception: " + e);\r
+ } else {\r
+ throw e;\r
+ }\r
+ }\r
+ afterTime = new java.util.Date().getTime();\r
+ doLog("After select, execution time = " + (afterTime-beforeTime) + " ms");\r
+ return rs;\r
+ }\r
+\r
+ private String generateStatement(HashMap<Integer, Employee> employeeMap, Random r, String tableName) {\r
+ String toRet = null;\r
+ \r
+ boolean pickInsert = ((employeeMap==null) || (r.nextInt(8) >= employeeMap.size()));\r
+ if (!pickInsert) {\r
+ if (doDelete && doUpdate) {\r
+ if (r.nextBoolean()) toRet = generateDelete(employeeMap, r, tableName); else toRet = generateUpdate(employeeMap, r, tableName);\r
+ } else if (doDelete) {\r
+ toRet = generateDelete(employeeMap, r, tableName);\r
+ } else if (doUpdate) {\r
+ toRet = generateUpdate(employeeMap, r, tableName);\r
+ }\r
+ }\r
+ if (toRet==null) {\r
+ toRet = generateInsert(employeeMap, r, tableName);\r
+ }\r
+ \r
+ doLog("Generated statement: " + toRet);\r
+ \r
+ return toRet;\r
+ }\r
+\r
+ private String generateInsert(HashMap<Integer, Employee> employeeMap, Random r, String tableName) {\r
+ String toRet = null;\r
+ \r
+ Integer id = null;\r
+ int range = baseIdRange;\r
+ while (id==null) {\r
+ id = baseId + r.nextInt(range);\r
+ if (employeeMap!=null && employeeMap.containsKey(id)) id = null;\r
+ if (employeeMap==null) id+=baseIdRange;\r
+ range+=(baseIdRange/5);\r
+ }\r
+ Employee newEmp = new Employee(id, lastName, Character.toUpperCase(randomLetter(r)) + generateLetters(r, 4+r.nextInt(4)), generateLetters(r, 4).toUpperCase(), generateLetters(r, 4).toUpperCase());\r
+ toRet = "insert into " + tableName + " values (" + id + ", '" + newEmp.getLastname() + "', '" + newEmp.getFirstname() + "', '" + newEmp.getAddress() + "', '" + newEmp.getCity() + "')";\r
+ if (employeeMap!=null) employeeMap.put(id, newEmp);\r
+ \r
+ return toRet;\r
+ }\r
+\r
+ private String generateUpdate(HashMap<Integer, Employee> employeeMap, Random r, String tableName) {\r
+ String toRet = null;\r
+ \r
+ Employee toUpd = chooseTarget(employeeMap, r);\r
+ if (toUpd!=null) {\r
+ String newFirst = null;\r
+ if (toUpd.getFirstname().length()<=3 || r.nextBoolean()) {\r
+ newFirst = toUpd.getFirstname() + randomLetter(r);\r
+ } else {\r
+ newFirst = toUpd.getFirstname().substring(0, toUpd.getFirstname().length()-1);\r
+ }\r
+// toRet = "update " + tableName + " set firstname = '" + newFirst + "' where personid = " + toUpd.getEmpid();\r
+ toRet = "update " + tableName + " set firstname = '" + newFirst + "' where personid = " + toUpd.getEmpid() + " and lastname = '" + toUpd.getLastname() + "'";\r
+ toUpd.setFirstname(newFirst);\r
+ }\r
+ \r
+ return toRet;\r
+ }\r
+\r
+ private String generateLetters(Random r, int count) {\r
+ StringBuffer toRet = new StringBuffer();\r
+ for (int i=0; i<count; i++) {\r
+ Character c = null;\r
+ while (c==null) {\r
+ c = randomLetter(r);\r
+ char cc = c.charValue();\r
+ if ( (cc=='a' || cc=='e' || cc=='i' || cc=='o' || cc=='u') ^ (i%2==0) ) c = null;\r
+ }\r
+ toRet.append(c);\r
+ }\r
+ return toRet.toString();\r
+ }\r
+\r
+ private char randomLetter(Random r) {\r
+ int a = (int)'a';\r
+ return (char)(a+r.nextInt(26));\r
+ }\r
+\r
+ private String generateDelete(HashMap<Integer, Employee> employeeMap, Random r, String tableName) {\r
+ String toRet = null;\r
+ \r
+ Employee toDel = chooseTarget(employeeMap, r);\r
+ if (toDel!=null) {\r
+ toRet = "delete from " + tableName + " where personid = " + toDel.getEmpid() + " and lastname = '" + toDel.getLastname() + "'";\r
+ employeeMap.remove(toDel.getEmpid());\r
+ }\r
+ \r
+ return toRet;\r
+ }\r
+\r
+ \r
+ \r
+ private Employee chooseTarget(HashMap<Integer, Employee> employeeMap, Random r) {\r
+ Employee toPick = null;\r
+ int count = 0;\r
+ for (int id : employeeMap.keySet()) {\r
+ Employee emp = employeeMap.get(id);\r
+ if (!emp.getLastname().equals(lastName)) continue;\r
+ count++;\r
+ if (r.nextInt(count)==0) toPick = emp;\r
+ }\r
+ return toPick;\r
+ }\r
+\r
+ public void run() {\r
+ try {\r
+ Class.forName("org.apache.calcite.avatica.remote.Driver");\r
+ } catch (ClassNotFoundException e) {\r
+ e.printStackTrace();\r
+ System.exit(1);\r
+ }\r
+ \r
+ Connection connection = null;\r
+\r
+ int calls = 0;\r
+ long startTime = new java.util.Date().getTime();\r
+ Random r = new Random();\r
+ boolean done = false;\r
+\r
+ while (!done) {\r
+ if (connection==null) {\r
+ try {\r
+ doLog("Opening new connection");\r
+ connection = DriverManager.getConnection(connectionString);\r
+ connection.setAutoCommit(false);\r
+ } catch (SQLException e) {\r
+ e.printStackTrace();\r
+ return;\r
+ }\r
+ } else {\r
+ doLog("Keeping open connection");\r
+ }\r
+\r
+ if (calls==0) {\r
+ long initialDelay = 1 + (1000*threadId);\r
+ synchronized(Thread.currentThread()) {\r
+ doLog("Delaying for " + initialDelay);\r
+ try {\r
+ Thread.currentThread().wait(initialDelay);\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ done = true;\r
+ }\r
+ doLog("Done");\r
+ }\r
+ }\r
+ \r
+ try {\r
+ doLog("Running test");\r
+ doTest(connection, r);\r
+ doLog("Test complete");\r
+ } catch (SQLException e1) {\r
+ e1.printStackTrace();\r
+ done = true;\r
+ if (connection!=null) {\r
+ try {\r
+ doLog("Closing connection in catch block");\r
+ connection.close();\r
+ } catch (SQLException e) {\r
+ e.printStackTrace();\r
+ done = true;\r
+ } finally {\r
+ connection = null;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (!done && connection!=null && r.nextInt(100)<connectionCloseChancePct) {\r
+ try {\r
+ doLog("Closing connection");\r
+ connection.close();\r
+ connection = null;\r
+ doLog("Connection closed.");\r
+ } catch (SQLException e) {\r
+ e.printStackTrace();\r
+ done = true;\r
+ }\r
+ } else {\r
+ doLog("Not closing connection");\r
+ }\r
+ \r
+ calls++;\r
+ long msElapsed = (new java.util.Date().getTime()) - startTime;\r
+ if (calls>maxCalls || msElapsed > maxTimeMs) done = true;\r
+ \r
+ if (!done) {\r
+ long delay = r.nextInt(minDelayBetweenTestsMs);\r
+ while (r.nextBoolean()) delay += r.nextInt(additionalDelayBetweenTestsMs);\r
+ if (delay>0) {\r
+ doLog("Delaying for " + delay + " ms");\r
+ synchronized(Thread.currentThread()) {\r
+ try {\r
+ Thread.currentThread().wait(delay);\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ done = true;\r
+ }\r
+ }\r
+ doLog("Delaying done");\r
+ }\r
+ }\r
+ \r
+ doLog("");\r
+ }\r
+\r
+ if (connection!=null) {\r
+ try {\r
+ doLog("Closing connection at end");\r
+ connection.close();\r
+ } catch (SQLException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ doLog("All done.");\r
+ }\r
+\r
+ private void doLog(String string) {\r
+ System.out.println(">> Thread " + threadId + " " + sdf.format(new java.util.Date()) + " >> " + string);\r
+ }\r
+ \r
+ public static void main(String[] args) {\r
+ MdbcTestMultiClient mtc = new MdbcTestMultiClient(args);\r
+ mtc.runTests(); \r
+ }\r
+\r
+ private void runTests() {\r
+ for (int i=0; i<connectionStrings.size(); i++) {\r
+ MdbcTestMultiClient mt = new MdbcTestMultiClient(this, i);\r
+ Thread t = new Thread(mt);\r
+ t.start();\r
+ }\r
+ }\r
+ \r
+}\r