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