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