1 /* 2 Copyright (c) 2010, 2021, Oracle and/or its affiliates. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License, version 2.0, for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 package testsuite.clusterj; 26 27 import java.lang.reflect.InvocationHandler; 28 import java.lang.reflect.Method; 29 import java.lang.reflect.Proxy; 30 import java.sql.Connection; 31 import java.sql.PreparedStatement; 32 import java.sql.ResultSet; 33 import java.sql.SQLException; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Calendar; 37 import java.util.Comparator; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.TimeZone; 43 import java.util.TreeSet; 44 45 import com.mysql.clusterj.Session; 46 47 import testsuite.clusterj.model.AllPrimitives; 48 import testsuite.clusterj.model.Dn2id; 49 import testsuite.clusterj.model.Employee; 50 import testsuite.clusterj.model.IdBase; 51 52 public abstract class AbstractClusterJModelTest extends AbstractClusterJTest { 53 54 /** The local system default time zone, which is reset by resetLocalSystemDefaultTimeZone */ 55 protected static TimeZone localSystemTimeZone = TimeZone.getDefault(); 56 57 /** ONE_SECOND is the number of milliseconds in one second. */ 58 protected static final long ONE_SECOND = 1000L; 59 60 /** ONE_MINUTE is the number of milliseconds in one minute. */ 61 protected static final long ONE_MINUTE = 1000L * 60L; 62 63 /** ONE_HOUR is the number of milliseconds in one hour. */ 64 protected static final long ONE_HOUR = 1000L * 60L * 60L; 65 66 /** TEN_HOURS is the number of milliseconds in ten hours. */ 67 protected static final long TEN_HOURS = 1000L * 60L * 60L * 10L; 68 69 /** ONE_DAY is the number of milliseconds in one day. */ 70 protected static final long ONE_DAY = 1000L * 60L * 60L * 24L; 71 72 /** Convert year, month, day, hour, minute, second into milliseconds after the Epoch, UCT. 73 * @param year the year 74 * @param month the month (0 for January) 75 * @param day the day of the month 76 * @param hour the hour of the day 77 * @param minute the minute 78 * @param second the second 79 * @return 80 */ getMillisFor(int year, int month, int day, int hour, int minute, int second)81 protected static long getMillisFor(int year, int month, int day, int hour, int minute, int second) { 82 Calendar calendar = Calendar.getInstance(); 83 calendar.clear(); 84 calendar.set(Calendar.YEAR, year); 85 calendar.set(Calendar.MONTH, month); 86 calendar.set(Calendar.DATE, day); 87 calendar.set(Calendar.HOUR, hour); 88 calendar.set(Calendar.MINUTE, minute); 89 calendar.set(Calendar.SECOND, second); 90 calendar.set(Calendar.MILLISECOND, 0); 91 long result = calendar.getTimeInMillis(); 92 return result; 93 } 94 95 /** Convert year, month, day into milliseconds after the Epoch, UCT. 96 * Set hours, minutes, seconds, and milliseconds to zero. 97 * @param year the year 98 * @param month the month (0 for January) 99 * @param day the day of the month 100 * @return 101 */ getMillisFor(int year, int month, int day)102 protected static long getMillisFor(int year, int month, int day) { 103 Calendar calendar = Calendar.getInstance(); 104 calendar.clear(); 105 calendar.set(Calendar.YEAR, year); 106 calendar.set(Calendar.MONTH, month); 107 calendar.set(Calendar.DATE, day); 108 calendar.set(Calendar.HOUR, 0); 109 calendar.set(Calendar.MINUTE, 0); 110 calendar.set(Calendar.SECOND, 0); 111 calendar.set(Calendar.MILLISECOND, 0); 112 long result = calendar.getTimeInMillis(); 113 return result; 114 } 115 116 /** Convert hours, minutes, and seconds into milliseconds after the Epoch, UCT. 117 * Default year, month, and day, 118 * as these are assumed by Calendar to be the Epoch. 119 * @param day the number of days: ignored 120 * @param hour the hour (or number of hours) 121 * @param minute the minute (or number of minutes) 122 * @param second the second (or number of seconds) 123 * @return millis past the Epoch UCT 124 */ getMillisFor(int days, int hour, int minute, int second)125 protected static long getMillisFor(int days, int hour, int minute, int second) { 126 Calendar calendar = Calendar.getInstance(); 127 calendar.clear(); 128 calendar.set(Calendar.HOUR, hour); 129 calendar.set(Calendar.MINUTE, minute); 130 calendar.set(Calendar.SECOND, second); 131 calendar.set(Calendar.MILLISECOND, 0); 132 long result = calendar.getTimeInMillis(); 133 return result; 134 } 135 136 /** A1 values. */ 137 String[] a1values = new String[]{"dc=abc", "dc=prs", "dc=xyz"}; 138 139 protected List<Employee> employees; 140 141 protected List<Dn2id> dn2ids; 142 143 protected static Object[] dn2idPK = setupDn2idPK(); 144 145 /** The instances used in the tests, generated by generateInstances */ 146 protected List<IdBase> instances = new ArrayList<IdBase>(); 147 148 /** List of expected results, generated by generateInstances */ 149 private List<Object[]> expected = null; 150 151 /** The column descriptors as provided by subclasses */ 152 ColumnDescriptor[] columnDescriptors = null; 153 154 /** The class loader for the domain object type */ 155 protected ClassLoader loader; 156 AbstractClusterJModelTest()157 public AbstractClusterJModelTest() { 158 columnDescriptors = getColumnDescriptors(); 159 } 160 getCleanupAfterTest()161 protected boolean getCleanupAfterTest() { 162 return true; 163 } 164 165 /** Set this to false to avoid running test (e.g. if no model class) */ 166 protected boolean runTest = true; 167 168 @Override localSetUp()169 public void localSetUp() { 170 createSessionFactory(); 171 session = sessionFactory.getSession(); 172 setAutoCommit(connection, false); 173 try { 174 session.newInstance(getModelClass()); 175 runTest = true; 176 } catch (Exception e) { 177 System.out.println("Ignoring test; no model class " + getModelClass().getName()); 178 runTest = false; 179 } 180 181 if (getModelClass() != null && getCleanupAfterTest()) { 182 addTearDownClasses(getModelClass()); 183 } 184 } 185 186 /** Reset the local system default time zone to the time zone used 187 * by the MySQL server. This guarantees that there is no time zone 188 * offset between the time zone in the client and the time zone 189 * in the server. 190 * @param connection 191 */ resetLocalSystemDefaultTimeZone(Connection connection)192 protected static void resetLocalSystemDefaultTimeZone(Connection connection) { 193 try { 194 PreparedStatement statement = connection.prepareStatement("select @@global.time_zone, @@global.system_time_zone, @@session.time_zone"); 195 ResultSet rs = statement.executeQuery(); 196 // there are two columns in the result 197 rs.next(); 198 String globalTimeZone = rs.getString(1); 199 String globalSystemTimeZone = rs.getString(2); 200 String sessionTimeZone = rs.getString(3); 201 // if (debug) System.out.println("Global time zone: " + globalTimeZone + 202 // " Global system time zone: " + globalSystemTimeZone +" Session time zone: " + sessionTimeZone); 203 connection.commit(); 204 if ("SYSTEM".equalsIgnoreCase(globalTimeZone)) { 205 globalTimeZone = globalSystemTimeZone; 206 } else { 207 globalTimeZone = "GMT" + globalTimeZone; 208 } 209 localSystemTimeZone = TimeZone.getTimeZone(globalTimeZone); 210 // if (debug) System.out.println("Local system time zone set to: " + globalTimeZone + "(" + localSystemTimeZone + ")"); 211 // TimeZone.setDefault(localSystemTimeZone); 212 // get a new connection after setting local default time zone 213 // because a connection contains a session calendar used to create Timestamp instances 214 connection.close(); 215 } catch (SQLException e) { 216 throw new RuntimeException("setServerTimeZone failed", e); 217 } 218 } 219 setAutoCommit(Connection connection, boolean b)220 protected void setAutoCommit(Connection connection, boolean b) { 221 try { 222 connection.setAutoCommit(false); 223 } catch (SQLException e) { 224 throw new RuntimeException("setAutoCommit failed", e); 225 } 226 } 227 createEmployeeInstances(int count)228 protected void createEmployeeInstances(int count) { 229 employees = new ArrayList<Employee>(count); 230 for (int i = 0; i < count; ++i) { 231 Employee emp = session.newInstance(Employee.class); 232 emp.setId(i); 233 emp.setName("Employee number " + i); 234 emp.setAge(i); 235 emp.setMagic(i); 236 employees.add(emp); 237 } 238 } 239 consistencyCheck(Employee emp)240 protected void consistencyCheck(Employee emp) { 241 int id = emp.getId(); 242 String expectedName = "Employee number " + id; 243 String actualName = emp.getName(); 244 if (!expectedName.equals(actualName)) { 245 // System.out.println("expected " + dump(expectedName)); 246 // System.out.println("actual " + dump(actualName)); 247 error("Employee " + id 248 + " name mismatch; expected length: " + expectedName.length() + "'" + expectedName 249 + "'; actual length: " + actualName.length() + "'" + actualName + "'"); 250 } 251 int actualAge = emp.getAge(); 252 if (!(actualAge == id)) { 253 error("Employee " + id 254 + " age mismatch; expected " + id 255 + "'; actual '" + actualAge); 256 } 257 int actualMagic = emp.getMagic(); 258 if (!(actualMagic == id)) { 259 error("Employee " + id 260 + " magic mismatch; expected " + id 261 + "'; actual '" + actualMagic); 262 } 263 } 264 consistencyCheck(Iterable<T> instances)265 protected <T> void consistencyCheck(Iterable<T> instances) { 266 for (T instance: instances) { 267 if (instance instanceof Employee) { 268 consistencyCheck((Employee)instance); 269 } else if (instance instanceof Dn2id) { 270 consistencyCheck((Dn2id)instance); 271 } 272 } 273 } 274 createDn2idInstances(int number)275 protected void createDn2idInstances(int number) { 276 dn2ids = new ArrayList<Dn2id>(); 277 for (int i = 0; i < number; ++i) { 278 Dn2id d = session.newInstance(Dn2id.class); 279 d.setEid(i); 280 d.setObjectClasses("testObject"); 281 // XObjectClasses has a NullValue=DEFAULT so don't need to set it 282 d.setA0("dc=com"); 283 // a1 should pick all of the a1values equally 284 d.setA1(getA1for(number, i)); 285 d.setA2("ou=people"); 286 d.setA3(getA3for(i)); 287 d.setA4(""); 288 d.setA5(""); 289 d.setA6(""); 290 d.setA7(""); 291 d.setA8(""); 292 d.setA9(""); 293 d.setA10(""); 294 d.setA11(""); 295 d.setA12(""); 296 d.setA13(""); 297 d.setA14(""); 298 d.setA15(""); 299 dn2ids.add(d); 300 } 301 } 302 consistencyCheck(Dn2id dn2id)303 protected void consistencyCheck(Dn2id dn2id) { 304 long eid = dn2id.getEid(); 305 String expected = getA3for(eid); 306 String actual = dn2id.getA3(); 307 if (!expected.equals(actual)) { 308 error("Dn2id " + eid 309 + " a3 mismatch; expected '" + expected 310 + "'; actual '" + actual + "'"); 311 } 312 } 313 314 /** Subclasses usually should not override this method to provide the list of expected results */ getExpected()315 protected List<Object[]> getExpected() { 316 return expected; 317 } 318 319 /** Subclasses must override this method to provide the name of the table for the test */ getTableName()320 protected String getTableName() { 321 return null; 322 } 323 324 /** Subclasses must override this method to provide the number of instances to create */ getNumberOfInstances()325 protected int getNumberOfInstances() { 326 return 0; 327 } 328 329 /** Subclasses must override this method to provide the column descriptors for the test */ getColumnDescriptors()330 protected ColumnDescriptor[] getColumnDescriptors() { 331 return null; 332 } 333 334 /** Subclasses must override this method to provide the model class for the test */ getModelClass()335 Class<? extends IdBase> getModelClass() { 336 return null; 337 } 338 339 /** Subclasses must override this method to provide values for rows (i) and columns (j) */ getColumnValue(int i, int j)340 protected Object getColumnValue(int i, int j) { 341 return null; 342 } 343 344 /** Subclasses may override this method to convert an int into a key value */ convertToKey(int i)345 protected Object convertToKey(int i) { 346 return Integer.valueOf(i); 347 } 348 349 /** Write data via JDBC and read back the data via NDB */ writeJDBCreadNDB()350 protected void writeJDBCreadNDB() { 351 if (!runTest) return; 352 generateInstances(getColumnDescriptors()); 353 removeAll(getModelClass()); 354 List<Object[]> result = null; 355 writeToJDBC(columnDescriptors, instances); 356 result = readFromNDB(columnDescriptors); 357 verify("writeJDBCreadNDB", getExpected(), result); 358 } 359 360 /** Write data via JDBC and read back the data via JDBC */ writeJDBCreadJDBC()361 protected void writeJDBCreadJDBC() { 362 if (!runTest) return; 363 generateInstances(getColumnDescriptors()); 364 removeAll(getModelClass()); 365 List<Object[]> result = null; 366 writeToJDBC(columnDescriptors, instances); 367 result = readFromJDBC(columnDescriptors); 368 verify("writeJDBCreadJDBC", getExpected(), result); 369 } 370 371 /** Write data via NDB and read back the data via NDB */ writeNDBreadNDB()372 protected void writeNDBreadNDB() { 373 if (!runTest) return; 374 generateInstances(getColumnDescriptors()); 375 removeAll(getModelClass()); 376 List<Object[]> result = null; 377 writeToNDB(columnDescriptors, instances); 378 result = readFromNDB(columnDescriptors); 379 verify("writeNDBreadNDB", getExpected(), result); 380 } 381 382 /** Write data via NDB and read back the data via JDBC */ writeNDBreadJDBC()383 protected void writeNDBreadJDBC() { 384 if (!runTest) return; 385 generateInstances(getColumnDescriptors()); 386 removeAll(getModelClass()); 387 List<Object[]> result = null; 388 writeToNDB(columnDescriptors, instances); 389 result = readFromJDBC(columnDescriptors); 390 verify("writeNDBreadJDBC", getExpected(), result); 391 } 392 393 /** Dump the contents of the expected or actual results of the operation */ dumpListOfObjectArray(List<Object[]> results)394 private String dumpListOfObjectArray(List<Object[]> results) { 395 StringBuffer result = new StringBuffer(results.size() + " rows\n"); 396 for (Object[] row: results) { 397 result.append("Id: "); 398 for (Object column: row) { 399 result.append(column); 400 result.append(' '); 401 } 402 result.append('\n'); 403 } 404 return result.toString(); 405 } 406 queryAndVerifyResults(String where, ColumnDescriptor[] columnDescriptors, String conditions, Object[] parameters, int... objectIds)407 protected void queryAndVerifyResults(String where, ColumnDescriptor[] columnDescriptors, 408 String conditions, Object[] parameters, int... objectIds) { 409 List<Object[]> results = queryJDBC(columnDescriptors, conditions, parameters); 410 verifyQueryResults(where, results, objectIds); 411 } 412 413 /** Read data via JDBC */ queryJDBC(ColumnDescriptor[] columnDescriptors, String conditions, Object[] parameters)414 protected List<Object[]> queryJDBC(ColumnDescriptor[] columnDescriptors, 415 String conditions, Object[] parameters) { 416 getConnection(); 417 String tableName = getTableName(); 418 List<Object[]> result = new ArrayList<Object[]>(); 419 StringBuffer buffer = new StringBuffer("SELECT id"); 420 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 421 buffer.append(", "); 422 buffer.append(columnDescriptor.getColumnName()); 423 } 424 buffer.append(" FROM "); 425 buffer.append(tableName); 426 buffer.append(" WHERE "); 427 buffer.append(conditions); 428 String statement = buffer.toString(); 429 if (debug) System.out.println(statement); 430 PreparedStatement preparedStatement = null; 431 try { 432 int p = 1; 433 preparedStatement = connection.prepareStatement(statement); 434 for (Object parameter: parameters) { 435 preparedStatement.setObject(p++, parameter); 436 } 437 ResultSet rs = preparedStatement.executeQuery(); 438 while (rs.next()) { 439 Object[] row = new Object[columnDescriptors.length + 1]; 440 int j = 1; 441 row[0] = rs.getInt(1); 442 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 443 row[j] = columnDescriptor.getResultSetValue(rs, j + 1); 444 ++j; 445 } 446 result.add(row); 447 } 448 if (!connection.getAutoCommit()) { 449 connection.commit(); 450 } 451 } catch (SQLException e) { 452 throw new RuntimeException("Failed to read " + tableName, e); 453 } 454 if (debug) System.out.println("readFromJDBC: " + dumpObjectArray(result)); 455 return result; 456 } 457 458 /** Dump the contents of the expected or actual results of the operation */ dumpObjectArray(List<Object[]> results)459 private String dumpObjectArray(List<Object[]> results) { 460 StringBuffer result = new StringBuffer(results.size() + " rows\n"); 461 for (Object[] row: results) { 462 result.append("Id: "); 463 for (Object column: row) { 464 result.append(column); 465 result.append(' '); 466 } 467 result.append('\n'); 468 } 469 return result.toString(); 470 } 471 verifyQueryResults(String where, List<Object[]> results, int... objectIds)472 protected void verifyQueryResults(String where, List<Object[]> results, int... objectIds) { 473 errorIfNotEqual(where + " mismatch in number of results.", objectIds.length, results.size()); 474 for (Object[] result: results) { 475 int id = (Integer)result[0]; 476 if (Arrays.binarySearch(objectIds, id) < 0) { 477 // couldn't find it 478 error(where + " result " + id + " not expected."); 479 } 480 } 481 } 482 483 /** Verify that the actual results match the expected results. If not, use the multiple error 484 * reporting method errorIfNotEqual defined in the superclass. 485 * @param where the location of the verification of results, normally the name of the test method 486 * @param expecteds the expected results 487 * @param actuals the actual results 488 */ verify(String where, List<Object[]> expecteds, List<Object[]> actuals)489 protected void verify(String where, List<Object[]> expecteds, List<Object[]> actuals) { 490 if (expecteds.size() != actuals.size()) { 491 error(where + " failure on size of results: expected: " + expecteds.size() + " actual: " + actuals.size()); 492 return; 493 } 494 for (int i = 0; i < expecteds.size(); ++i) { 495 Object[] expected = expecteds.get(i); 496 Object[] actual = actuals.get(i); 497 errorIfNotEqual(where + " got failure on id for row " + i, i, actual[0]); 498 for (int j = 1; j < expected.length; ++j) { 499 errorIfNotEqual(where + " got failure to match column data for row " 500 + i + " column " + j, 501 expected[j], actual[j]); 502 } 503 } 504 } 505 506 /** Generated instances to persist. When using JDBC, the data is obtained from the instance 507 * via the column descriptors. As a side effect (!) create the list of expected results from read. 508 * @param columnDescriptors the column descriptors 509 * @return the generated instances 510 */ generateInstances(ColumnDescriptor[] columnDescriptors)511 protected void generateInstances(ColumnDescriptor[] columnDescriptors) { 512 Class<? extends IdBase> modelClass = getModelClass(); 513 expected = new ArrayList<Object[]>(); 514 instances = new ArrayList<IdBase>(); 515 IdBase instance = null; 516 int numberOfInstances = getNumberOfInstances(); 517 for (int i = 0; i < numberOfInstances; ++i) { 518 // create the instance 519 instance = getNewInstance(modelClass); 520 instance.setId(i); 521 // create the expected result row 522 int j = 0; 523 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 524 Object value = getColumnValue(i, j); 525 if (debug) System.out.println("generateInstances set field " + columnDescriptor.getColumnName() 526 + " to value " + value); 527 // set the column value in the instance 528 columnDescriptor.setFieldValue(instance, value); 529 // check that the value was set correctly 530 Object actual = columnDescriptor.getFieldValue(instance); 531 errorIfNotEqual("generateInstances value mismatch for " + columnDescriptor.getColumnName(), 532 dump(value), dump(actual)); 533 ++j; 534 } 535 instances.add(instance); 536 // set the column values in the expected result 537 Object[] expectedRow = createRow(columnDescriptors, instance); 538 expected.add(expectedRow); 539 } 540 if (debug) System.out.println("Created " + instances.size() + " instances of " + modelClass.getName()); 541 } 542 543 /** Create a new instance of the parameter interface 544 * @param modelClass the interface to instantiate 545 * @return an instance of the class 546 */ getNewInstance(Class<? extends IdBase> modelClass)547 protected IdBase getNewInstance(Class<? extends IdBase> modelClass) { 548 IdBase instance; 549 instance = session.newInstance(modelClass); 550 return instance; 551 } 552 553 /** Write data to JDBC. */ writeToJDBC(ColumnDescriptor[] columnDescriptors, List<IdBase> instances)554 protected void writeToJDBC(ColumnDescriptor[] columnDescriptors, List<IdBase> instances) { 555 String tableName = getTableName(); 556 StringBuffer buffer = new StringBuffer("INSERT INTO "); 557 buffer.append(tableName); 558 buffer.append(" (id"); 559 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 560 buffer.append(", "); 561 buffer.append(columnDescriptor.getColumnName()); 562 } 563 buffer.append(") VALUES (?"); 564 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 565 buffer.append(", ?"); 566 } 567 buffer.append(")"); 568 String statement = buffer.toString(); 569 if (debug) System.out.println(statement); 570 571 PreparedStatement preparedStatement = null; 572 int i = 0; 573 try { 574 preparedStatement = connection.prepareStatement(statement); 575 if (debug) System.out.println(preparedStatement.toString()); 576 for (i = 0; i < instances.size(); ++i) { 577 IdBase instance = instances.get(i); 578 preparedStatement.setInt(1, instance.getId()); 579 int j = 2; 580 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 581 Object value = columnDescriptor.getFieldValue(instance); 582 columnDescriptor.setPreparedStatementValue(preparedStatement, j++, value); 583 if (debug) System.out.println("writeToJDBC set column: " + columnDescriptor.getColumnName() + " to value: " + value); 584 } 585 preparedStatement.execute(); 586 } 587 if (!connection.getAutoCommit()) { 588 connection.commit(); 589 } 590 } catch (SQLException e) { 591 throw new RuntimeException("Failed to insert " + tableName + " at instance " + i, e); 592 } 593 } 594 595 /** Write data via NDB */ writeToNDB(ColumnDescriptor[] columnDescriptors, List<IdBase> instances)596 protected void writeToNDB(ColumnDescriptor[] columnDescriptors, List<IdBase> instances) { 597 session.currentTransaction().begin(); 598 session.makePersistentAll(instances); 599 session.currentTransaction().commit(); 600 } 601 602 /** Read data via NDB */ readFromNDB(ColumnDescriptor[] columnDescriptors)603 protected List<Object[]> readFromNDB(ColumnDescriptor[] columnDescriptors) { 604 Class<? extends IdBase> modelClass = getModelClass(); 605 List<Object[]> result = new ArrayList<Object[]>(); 606 session.currentTransaction().begin(); 607 for (int i = 0; i < getNumberOfInstances() ; ++i) { 608 IdBase instance = session.find(modelClass, convertToKey(i)); 609 if (instance != null) { 610 Object[] row = createRow(columnDescriptors, instance); 611 result.add(row); 612 } 613 } 614 session.currentTransaction().commit(); 615 if (debug) System.out.println("readFromNDB: " + dumpListOfObjectArray(result)); 616 return result; 617 } 618 619 /** Create row data from an instance. 620 * @param columnDescriptors the column descriptors describing the data 621 * @param instance the instance to extract data from 622 * @return the row data representing the instance 623 */ createRow(ColumnDescriptor[] columnDescriptors, IdBase instance)624 private Object[] createRow(ColumnDescriptor[] columnDescriptors, 625 IdBase instance) { 626 Object[] row = new Object[columnDescriptors.length + 1]; 627 row[0] = instance.getId(); 628 int j = 1; 629 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 630 row[j++] = columnDescriptor.getFieldValue(instance); 631 } 632 return row; 633 } 634 635 /** Read data via JDBC ordered by id */ readFromJDBC(ColumnDescriptor[] columnDescriptors)636 protected List<Object[]> readFromJDBC(ColumnDescriptor[] columnDescriptors) { 637 String tableName = getTableName(); 638 List<Object[]> result = new ArrayList<Object[]>(); 639 Set<Object[]> rows = new TreeSet<Object[]>(new Comparator<Object[]>(){ 640 public int compare(Object[] me, Object[] other) { 641 return ((Integer)me[0]) - ((Integer)other[0]); 642 } 643 }); 644 StringBuffer buffer = new StringBuffer("SELECT id"); 645 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 646 buffer.append(", "); 647 buffer.append(columnDescriptor.getColumnName()); 648 } 649 buffer.append(" FROM "); 650 buffer.append(tableName); 651 String statement = buffer.toString(); 652 if (debug) System.out.println(statement); 653 PreparedStatement preparedStatement = null; 654 int i = 0; 655 try { 656 preparedStatement = connection.prepareStatement(statement); 657 ResultSet rs = preparedStatement.executeQuery(); 658 while (rs.next()) { 659 Object[] row = new Object[columnDescriptors.length + 1]; 660 int j = 1; 661 row[0] = rs.getInt(1); 662 for (ColumnDescriptor columnDescriptor: columnDescriptors) { 663 row[j] = columnDescriptor.getResultSetValue(rs, j + 1); 664 ++j; 665 } 666 ++i; 667 rows.add(row); 668 } 669 if (!connection.getAutoCommit()) { 670 connection.commit(); 671 } 672 } catch (SQLException e) { 673 throw new RuntimeException("Failed to read " + tableName + " at instance " + i, e); 674 } 675 result = new ArrayList<Object[]>(rows); 676 if (debug) System.out.println("readFromJDBC: " + dumpListOfObjectArray(result)); 677 return result; 678 } 679 680 @SuppressWarnings("unchecked") // cast proxy to T proxyFor(final Class<T> cls)681 protected <T> T proxyFor (final Class<T> cls) { 682 InvocationHandler handler = new InvocationHandler() { 683 private Map<String, Object> values = new HashMap<String, Object>(); 684 public Object invoke(Object instance, Method method, Object[] args) 685 throws Throwable { 686 String methodName = method.getName(); 687 String propertyName = methodName.substring(3); 688 String methodPrefix = methodName.substring(0, 3); 689 if ("get".equals(methodPrefix)) { 690 return values.get(propertyName); 691 } else if ("set".equals(methodPrefix)) { 692 values.put(propertyName, args[0]); 693 return null; 694 } 695 // error 696 throw new RuntimeException("Not a get/set method: " + methodName); 697 } 698 699 }; 700 Object proxy = Proxy.newProxyInstance(loader, new Class[] {cls}, handler); 701 return (T)proxy; 702 } 703 704 /** This class describes columns and fields for a table and model class. 705 * A subclass will instantiate instances of this class and provide handlers to 706 * read and write fields and columns via methods defined in the instance handler. 707 */ 708 protected static class ColumnDescriptor { 709 710 private String columnName; 711 712 protected InstanceHandler instanceHandler; 713 getColumnName()714 public String getColumnName() { 715 return columnName; 716 } 717 getResultSetValue(ResultSet rs, int j)718 public Object getResultSetValue(ResultSet rs, int j) throws SQLException { 719 return instanceHandler.getResultSetValue(rs, j); 720 } 721 getFieldValue(IdBase instance)722 public Object getFieldValue(IdBase instance) { 723 return instanceHandler.getFieldValue(instance); 724 } 725 setFieldValue(IdBase instance, Object value)726 public void setFieldValue(IdBase instance, Object value) { 727 this.instanceHandler.setFieldValue(instance, value); 728 } 729 setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value)730 public void setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value) 731 throws SQLException { 732 instanceHandler.setPreparedStatementValue(preparedStatement, j, value); 733 } 734 ColumnDescriptor(String name, InstanceHandler instanceHandler)735 public ColumnDescriptor(String name, InstanceHandler instanceHandler) { 736 this.columnName = name; 737 this.instanceHandler = instanceHandler; 738 } 739 } 740 741 protected interface InstanceHandler { setFieldValue(IdBase instance, Object value)742 void setFieldValue(IdBase instance, Object value); getResultSetValue(ResultSet rs, int j)743 Object getResultSetValue(ResultSet rs, int j) 744 throws SQLException; getFieldValue(IdBase instance)745 Object getFieldValue(IdBase instance); setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value)746 public void setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value) 747 throws SQLException; 748 } 749 getA1for(int number, int index)750 protected String getA1for(int number, int index) { 751 int a1factor = 1 + number/a1values.length; 752 return a1values[index/a1factor]; 753 } 754 getA3for(long i)755 protected String getA3for(long i) { 756 return "employeenumber=100000" + i; 757 } 758 createAllPrimitivesInstances(int number)759 protected void createAllPrimitivesInstances(int number) { 760 createAllPrimitivesInstances(session, number); 761 } 762 createAllPrimitivesInstances(Session session, int number)763 protected void createAllPrimitivesInstances(Session session, int number) { 764 for (int i = 0; i < number; ++i) { 765 AllPrimitives instance = createAllPrimitiveInstance(session, i); 766 instances.add(instance); 767 } 768 } 769 createAllPrimitiveInstance(Session session, int i)770 protected AllPrimitives createAllPrimitiveInstance(Session session, int i) { 771 AllPrimitives instance = session.newInstance(AllPrimitives.class, i); 772 initialize(instance, i); 773 return instance; 774 } 775 initialize(AllPrimitives instance, int i)776 protected void initialize(AllPrimitives instance, int i) { 777 instance.setInt_not_null_hash(i); 778 instance.setInt_not_null_btree(i); 779 instance.setInt_not_null_both(i); 780 instance.setInt_not_null_none(i); 781 instance.setInt_null_hash(i); 782 instance.setInt_null_btree(i); 783 instance.setInt_null_both(i); 784 instance.setInt_null_none(i); 785 786 instance.setLong_not_null_hash((long)i); 787 instance.setLong_not_null_btree((long)i); 788 instance.setLong_not_null_both((long)i); 789 instance.setLong_not_null_none((long)i); 790 instance.setLong_null_hash((long)i); 791 instance.setLong_null_btree((long)i); 792 instance.setLong_null_both((long)i); 793 instance.setLong_null_none((long)i); 794 795 instance.setByte_not_null_hash((byte)i); 796 instance.setByte_not_null_btree((byte)i); 797 instance.setByte_not_null_both((byte)i); 798 instance.setByte_not_null_none((byte)i); 799 instance.setByte_null_hash((byte)i); 800 instance.setByte_null_btree((byte)i); 801 instance.setByte_null_both((byte)i); 802 instance.setByte_null_none((byte)i); 803 804 instance.setShort_not_null_hash((short)i); 805 instance.setShort_not_null_btree((short)i); 806 instance.setShort_not_null_both((short)i); 807 instance.setShort_not_null_none((short)i); 808 instance.setShort_null_hash((short)i); 809 instance.setShort_null_btree((short)i); 810 instance.setShort_null_both((short)i); 811 instance.setShort_null_none((short)i); 812 } 813 setupDn2idPK()814 protected static Object[] setupDn2idPK() { 815 Object[] result = new Object[16]; 816 result[0] = "dc=com"; 817 // pk[1] changes and is set inside loop 818 result[1] = "dc=example"; 819 result[2] = "ou=people"; 820 // pk[3] changes and is set inside loop 821 result[4] = ""; 822 result[5] = ""; 823 result[6] = ""; 824 result[7] = ""; 825 result[8] = ""; 826 result[9] = ""; 827 result[10] = ""; 828 result[11] = ""; 829 result[12] = ""; 830 result[13] = ""; 831 result[14] = ""; 832 result[15] = ""; 833 return result; 834 } 835 836 } 837