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