]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
4137a8f93d45cfb431e516ef3b3ca87d7f1d3cb0
[packages/trusty/mysql-wsrep-5.6.git] /
1 /*
2    Copyright (c) 2010, 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 com.mysql.clusterj.jpatest;
19
20 import java.sql.Connection;
21 import java.sql.PreparedStatement;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.util.ArrayList;
25 import java.util.Calendar;
26 import java.util.List;
27 import java.util.TimeZone;
28
29 import javax.persistence.Query;
30
31 import org.apache.openjpa.persistence.OpenJPAEntityManager;
32
33 import com.mysql.clusterj.jpatest.model.Employee;
34 import com.mysql.clusterj.jpatest.model.IdBase;
35
36 /**
37  *
38  */
39 public abstract class AbstractJPABaseTest extends SingleEMTestCase {
40
41     /** The local system default time zone, which is reset by resetLocalSystemDefaultTimeZone */
42     protected static TimeZone localSystemTimeZone = TimeZone.getDefault();
43
44     /** The connection to the database */
45     protected Connection connection;
46
47     /** The column descriptors */
48     private ColumnDescriptor[] columnDescriptors = getColumnDescriptors();
49
50     /** The instances used in the tests, generated by generateInstances */
51     List<IdBase> instances = new ArrayList<IdBase>();
52
53     /** List of expected results, generated by generateInstances */
54     private List<Object[]> expected = null;
55
56     /** Debug flag */
57     protected static boolean debug;
58
59     public AbstractJPABaseTest() {
60         debug = getDebug();
61     }
62
63     protected void getConnection() {
64         connection = (Connection) ((OpenJPAEntityManager)em).getConnection();
65         setAutoCommit(connection, false);
66     }
67
68     protected void setAutoCommit(Connection connection, boolean b) {
69         try {
70             connection.setAutoCommit(false);
71         } catch (SQLException e) {
72             throw new RuntimeException("setAutoCommit failed", e);
73         }
74     }
75
76     public void deleteAll() {
77         // delete all instances without verifying
78         em = emf.createEntityManager();
79         begin();
80         for (int i = 0; i < getNumberOfEmployees(); ++i) {
81             Employee e = em.find(Employee.class, i);
82             if (e != null) {
83                 em.remove(e);
84             }
85         }
86         commit();
87         em.close();
88     }
89     public void verifyDeleteAll() {
90         em = emf.createEntityManager();
91         begin();
92         for (int i = 0; i < getNumberOfEmployees(); ++i) {
93             Employee e = em.find(Employee.class, i);
94             if (e != null) {
95                 error("Entity exists after being removed: " + i);
96             }
97         }
98         commit();
99         failOnError();
100         em.close();
101     }
102     public void createAll() {
103         em = emf.createEntityManager();
104         begin();
105         for (int i = 0; i < getNumberOfEmployees(); ++i) {
106             Employee e = new Employee();
107             e.setId(i);
108             e.setAge(i);
109             e.setMagic(i);
110             e.setName("Employee " + i);
111             em.persist(e);
112         }
113         commit();
114         em.close();
115     }
116
117     public void findAll() {
118         em = emf.createEntityManager();
119         begin();
120         for (int i = 0; i < getNumberOfEmployees(); ++i) {
121             Employee e = em.find(Employee.class, i);
122             verifyEmployee(e, 0);
123         }
124         commit();
125         em.close();
126     }
127
128     public void updateThenVerifyAll() {
129         em = emf.createEntityManager();
130         begin();
131         for (int i = 0; i < getNumberOfEmployees(); ++i) {
132             Employee e = em.find(Employee.class, i);
133             e.setAge(i + 1);
134             verifyEmployee(e, 1);
135         }
136         commit();
137         begin();
138         for (int i = 0; i < getNumberOfEmployees(); ++i) {
139             Employee e = em.find(Employee.class, i);
140             verifyEmployee(e, 1);
141         }
142         commit();
143         em.close();
144     }
145
146     public void deleteThenVerifyAll() {
147         em = emf.createEntityManager();
148         begin();
149         for (int i = 0; i < getNumberOfEmployees(); ++i) {
150             Employee e = em.find(Employee.class, i);
151             verifyEmployee(e, 1);
152             em.remove(e);
153         }
154         commit();
155         begin();
156         for (int i = 0; i < getNumberOfEmployees(); ++i) {
157             Employee e = em.find(Employee.class, i);
158             if (e != null) {
159                 error("Entity exists after being removed: " + i);
160             }
161         }
162         commit();
163         em.close();
164     }
165
166     protected void verifyEmployee(Employee e, int updateOffset) {
167         int i = e.getId();
168         errorIfNotEqual("Error in age", i + updateOffset, e.getAge().intValue());
169         errorIfNotEqual("Error in magic", i, e.getMagic());
170         errorIfNotEqual("Error in name", "Employee " + i, e.getName());
171     }
172     protected int getNumberOfEmployees() {
173         return 1;
174     }
175
176     /** Convert year, month, day, hour, minute, second into milliseconds after the Epoch, UCT.
177      * @param year the year
178      * @param month the month (0 for January)
179      * @param day the day of the month
180      * @param hour the hour of the day
181      * @param minute the minute
182      * @param second the second
183      * @return
184      */
185     protected static long getMillisFor(int year, int month, int day, int hour, int minute, int second) {
186         Calendar calendar = Calendar.getInstance();
187         calendar.clear();
188         calendar.set(Calendar.YEAR, year);
189         calendar.set(Calendar.MONTH, month);
190         calendar.set(Calendar.DATE, day);
191         calendar.set(Calendar.HOUR, hour);
192         calendar.set(Calendar.MINUTE, minute);
193         calendar.set(Calendar.SECOND, second);
194         calendar.set(Calendar.MILLISECOND, 0);
195         long result = calendar.getTimeInMillis();
196         return result;
197     }
198
199     /** Convert year, month, day into milliseconds after the Epoch, UCT.
200      * Set hours, minutes, seconds, and milliseconds to zero.
201      * @param year the year
202      * @param month the month (0 for January)
203      * @param day the day of the month
204      * @return
205      */
206     protected static long getMillisFor(int year, int month, int day) {
207         Calendar calendar = Calendar.getInstance();
208         calendar.clear();
209         calendar.set(Calendar.YEAR, year);
210         calendar.set(Calendar.MONTH, month);
211         calendar.set(Calendar.DATE, day);
212         calendar.set(Calendar.HOUR, 0);
213         calendar.set(Calendar.MINUTE, 0);
214         calendar.set(Calendar.SECOND, 0);
215         calendar.set(Calendar.MILLISECOND, 0);
216         long result = calendar.getTimeInMillis();
217         return result;
218     }
219
220     /** Convert days, hours, minutes, and seconds into milliseconds after the Epoch, UCT.
221      * Date is index origin 1 so add one to the number of days. Default year and month,
222      * as these are assumed by Calendar to be the Epoch.
223      * @param day the number of days
224      * @param hour the hour (or number of hours)
225      * @param minute the minute (or number of minutes)
226      * @param second the second (or number of seconds)
227      * @return millis past the Epoch UCT
228      */
229     protected static long getMillisFor(int days, int hour, int minute, int second) {
230         Calendar calendar = Calendar.getInstance();
231         calendar.clear();
232         calendar.set(Calendar.DATE, days + 1);
233         calendar.set(Calendar.HOUR, hour);
234         calendar.set(Calendar.MINUTE, minute);
235         calendar.set(Calendar.SECOND, second);
236         calendar.set(Calendar.MILLISECOND, 0);
237         long result = calendar.getTimeInMillis();
238         return result;
239     }
240
241     /** Reset the local system default time zone to the time zone used
242      * by the MySQL server. This guarantees that there is no time zone
243      * offset between the time zone in the client and the time zone
244      * in the server.
245      * @param connection2 
246      */
247     protected static void resetLocalSystemDefaultTimeZone(Connection connection) {
248         try {
249             PreparedStatement statement = connection.prepareStatement("select @@global.time_zone, @@global.system_time_zone, @@session.time_zone");
250             ResultSet rs = statement.executeQuery();
251             // there are two columns in the result
252             rs.next();
253             String globalTimeZone = rs.getString(1);
254             String globalSystemTimeZone = rs.getString(2);
255             String sessionTimeZone = rs.getString(3);
256             if (debug) System.out.println("Global time zone: " + globalTimeZone + 
257                     " Global system time zone: " + globalSystemTimeZone +" Session time zone: " + sessionTimeZone);
258             connection.commit();
259             if ("SYSTEM".equalsIgnoreCase(globalTimeZone)) {
260                 globalTimeZone = globalSystemTimeZone;
261             } else {
262                 globalTimeZone = "GMT" + globalTimeZone;
263             }
264             localSystemTimeZone = TimeZone.getTimeZone(globalTimeZone);
265             if (debug) System.out.println("Local system time zone set to: " + globalTimeZone + "(" + localSystemTimeZone + ")");
266             TimeZone.setDefault(localSystemTimeZone);
267             // get a new connection after setting local default time zone
268             // because a connection contains a session calendar used to create Timestamp instances
269             connection.close();
270         } catch (SQLException e) {
271             throw new RuntimeException("setServerTimeZone failed", e);
272         }
273     }
274
275     /** This class describes columns and fields for a table and model class. 
276      * A subclass will instantiate instances of this class and provide handlers to
277      * read and write fields and columns via methods defined in the instance handler.
278      */
279     protected static class ColumnDescriptor {
280
281         private String columnName;
282     
283         protected InstanceHandler instanceHandler;
284     
285         public String getColumnName() {
286             return columnName;
287         }
288     
289         public Object getResultSetValue(ResultSet rs, int j) throws SQLException {
290             return instanceHandler.getResultSetValue(rs, j);
291         }
292     
293         public Object getFieldValue(IdBase instance) {
294             return instanceHandler.getFieldValue(instance);
295         }
296     
297         public void setFieldValue(IdBase instance, Object value) {
298             this.instanceHandler.setFieldValue(instance, value);
299         }
300     
301         public void setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value)
302                 throws SQLException {
303             instanceHandler.setPreparedStatementValue(preparedStatement, j, value);
304         }
305     
306         protected ColumnDescriptor(String name, InstanceHandler instanceHandler) {
307             this.columnName = name;
308             this.instanceHandler = instanceHandler;
309         }
310     }
311
312     protected interface InstanceHandler {
313         void setFieldValue(IdBase instance, Object value);
314         Object getResultSetValue(ResultSet rs, int j)
315                 throws SQLException;
316         Object getFieldValue(IdBase instance);
317         public void setPreparedStatementValue(PreparedStatement preparedStatement, int j, Object value)
318                 throws SQLException;
319     }
320
321     /** Subclasses can override this method to get debugging info printed to System.out */
322     protected boolean getDebug() {
323         return false;
324     }
325
326     /** Subclasses usually should not override this method to provide the list of expected results */
327     protected List<Object[]> getExpected() {
328         return expected;
329     }
330
331     /** Subclasses must override this method to provide the name of the table for the test */
332     protected String getTableName() {
333         return null;
334     }
335
336     /** Subclasses must override this method to provide the number of instances to create */
337     protected int getNumberOfInstances() {
338         return 0;
339     }
340
341     /** Subclasses must override this method to provide the column descriptors for the test */
342     protected ColumnDescriptor[] getColumnDescriptors() {
343         return null;
344     }
345
346     /** Subclasses must override this method to implement the model factory for the test */
347     protected IdBase getNewInstance(Class<? extends IdBase> modelClass) {
348         return null;
349     }
350
351     /** Subclasses must override this method to provide the model class for the test */
352     protected Class<? extends IdBase> getModelClass() {
353         return null;
354     }
355
356     /** Subclasses must override this method to provide values for rows (i) and columns (j) */
357     protected Object getColumnValue(int i, int j) {
358         return null;
359     }
360
361     /** Generated instances to persist. When using JDBC, the data is obtained from the instance
362      * via the column descriptors. As a side effect (!) create the list of expected results from read.
363      * @param columnDescriptors the column descriptors
364      * @return the generated instances
365      */
366     protected void generateInstances(ColumnDescriptor[] columnDescriptors) {
367         Class<? extends IdBase> modelClass = getModelClass();
368         expected = new ArrayList<Object[]>();
369         instances = new ArrayList<IdBase>();
370         IdBase instance = null;
371         int numberOfInstances = getNumberOfInstances();
372         for (int i = 0; i < numberOfInstances; ++i) {
373             // create the instance
374             instance = getNewInstance(modelClass);
375             instance.setId(i);
376             // create the expected result row
377             int j = 0;
378             for (ColumnDescriptor columnDescriptor: columnDescriptors) {
379                 Object value = getColumnValue(i, j);
380                 // set the column value in the instance
381                 columnDescriptor.setFieldValue(instance, value);
382                 // set the column value in the expected result
383                 if (debug) System.out.println("generateInstances set field " + columnDescriptor.getColumnName() + " to value "  + value);
384                 ++j;
385             }
386             instances.add(instance);
387             Object[] expectedRow = createRow(columnDescriptors, instance);
388             expected.add(expectedRow);
389         }
390         if (debug) System.out.println("Created " + instances.size() + " instances.");
391     }
392
393     /** Verify that the actual results match the expected results. If not, use the multiple error
394      * reporting method errorIfNotEqual defined in the superclass.
395      * @param where the location of the verification of results, normally the name of the test method
396      * @param expecteds the expected results
397      * @param actuals the actual results
398      */
399     protected void verify(String where, List<Object[]> expecteds, List<Object[]> actuals) {
400         for (int i = 0; i < expecteds.size(); ++i) {
401             Object[] expected = expecteds.get(i);
402             Object[] actual = actuals.get(i);
403             errorIfNotEqual(where + " got failure on id for row " + i, i, actual[0]);
404             for (int j = 1; j < expected.length; ++j) {
405                 errorIfNotEqual(where + " got failure to match column data for row "
406                         + i + " column " + j,
407                         expected[j], actual[j]);
408             }
409         }
410     }
411
412     protected void removeAll(Class<? extends IdBase> modelClass) {
413         Query query = em.createQuery("DELETE FROM " + modelClass.getSimpleName());
414         em.getTransaction().begin();
415         query.executeUpdate();
416         em.getTransaction().commit();
417     }
418
419     private void removeAll(String tableName) {
420         getConnection();
421         try {
422             PreparedStatement statement = connection.prepareStatement("DELETE FROM " + tableName);
423             statement.execute();
424             connection.commit();
425         } catch (SQLException e) {
426             // TODO Auto-generated catch block
427             e.printStackTrace();
428         }
429     }
430
431     /** Write data via JDBC and read back the data via JPA */
432     protected void writeJDBCreadJPA() {
433         generateInstances(columnDescriptors);
434         removeAll(getTableName());
435         List<Object[]> result = null;
436         writeToJDBC(columnDescriptors, instances);
437         result = readFromJPA(columnDescriptors);
438         verify("writeJDBCreadJPA", getExpected(), result);
439     }
440
441     /** Write data via JDBC and read back the data via JDBC */
442     protected void writeJDBCreadJDBC() {
443         generateInstances(columnDescriptors);
444         removeAll(getTableName());
445         List<Object[]> result = null;
446         writeToJDBC(columnDescriptors, instances);
447         result = readFromJDBC(columnDescriptors);
448         verify("writeJDBCreadJDBC", getExpected(), result);
449     }
450
451     /** Write data via JPA and read back the data via JPA */
452     protected void writeJPAreadJPA() {
453         generateInstances(columnDescriptors);
454         removeAll(getModelClass());
455         List<Object[]> result = null;
456         writeToJPA(columnDescriptors, instances);
457         result = readFromJPA(columnDescriptors);
458         verify("writeJPAreadJPA", getExpected(), result);
459     }
460
461     /** Write data via JPA and read back the data via JDBC */
462     protected void writeJPAreadJDBC() {
463         generateInstances(columnDescriptors);
464         removeAll(getTableName());
465         List<Object[]> result = null;
466         writeToJPA(columnDescriptors, instances);
467         result = readFromJDBC(columnDescriptors);
468         verify("writeJPAreadJDBC", getExpected(), result);
469     }
470
471     /** Write data via JPA */
472     protected void writeToJPA(ColumnDescriptor[] columnDescriptors, List<IdBase> instances) {
473         em.getTransaction().begin();
474         for (IdBase instance: instances) {
475             em.persist(instance);
476         }
477         em.getTransaction().commit();
478     }
479
480     /** Write data to JDBC. */
481     protected void writeToJDBC(ColumnDescriptor[] columnDescriptors, List<IdBase> instances) {
482         String tableName = getTableName();
483         StringBuffer buffer = new StringBuffer("INSERT INTO ");
484         buffer.append(tableName);
485         buffer.append(" (id");
486         for (ColumnDescriptor columnDescriptor: columnDescriptors) {
487             buffer.append(", ");
488             buffer.append(columnDescriptor.getColumnName());
489         }
490         buffer.append(") VALUES (?");
491         for (ColumnDescriptor columnDescriptor: columnDescriptors) {
492             buffer.append(", ?");
493         }
494         buffer.append(")");
495         String statement = buffer.toString();
496         if (debug) System.out.println(statement);
497     
498         PreparedStatement preparedStatement = null;
499         int i = 0;
500         try {
501             preparedStatement = connection.prepareStatement(statement);
502             if (debug) System.out.println(preparedStatement.toString());
503             for (i = 0; i < instances.size(); ++i) {
504                 IdBase instance = instances.get(i);
505                 preparedStatement.setInt(1, instance.getId());
506                 int j = 2;
507                 for (ColumnDescriptor columnDescriptor: columnDescriptors) {
508                     Object value = columnDescriptor.getFieldValue(instance);
509                     columnDescriptor.setPreparedStatementValue(preparedStatement, j++, value);
510                     if (debug) System.out.println("writeToJDBC set column: " + columnDescriptor.getColumnName() + " to value: " + value);
511                 }
512                 preparedStatement.execute();
513             }
514             connection.commit();
515         } catch (SQLException e) {
516             throw new RuntimeException("Failed to insert " + tableName + " at instance " + i, e);
517         }
518     }
519
520     /** Read data via JPA */
521     protected List<Object[]> readFromJPA(ColumnDescriptor[] columnDescriptors) {
522         Class<? extends IdBase> modelClass = getModelClass();
523         List<Object[]> result = new ArrayList<Object[]>();
524         em.getTransaction().begin();
525         for (int i = 0; i < getNumberOfInstances() ; ++i) {
526             IdBase instance = em.find(modelClass, i);
527             if (instance != null) {
528                 Object[] row = createRow(columnDescriptors, instance);
529                 result.add(row);
530             }
531         }
532         em.getTransaction().commit();
533         if (debug) System.out.println("readFromJPA: " + dump(result));
534         return result;
535     }
536
537     /** Read data via JDBC */
538     protected List<Object[]> readFromJDBC(ColumnDescriptor[] columnDescriptors) {
539         String tableName = getTableName();
540         List<Object[]> result = new ArrayList<Object[]>();
541         StringBuffer buffer = new StringBuffer("SELECT id");
542         for (ColumnDescriptor columnDescriptor: columnDescriptors) {
543             buffer.append(", ");
544             buffer.append(columnDescriptor.getColumnName());
545         }
546         buffer.append(" FROM ");
547         buffer.append(tableName);
548         buffer.append(" ORDER BY ID");
549         String statement = buffer.toString();
550         if (debug) System.out.println(statement);
551         PreparedStatement preparedStatement = null;
552         int i = 0;
553         try {
554             preparedStatement = connection.prepareStatement(statement);
555             ResultSet rs = preparedStatement.executeQuery();
556             while (rs.next()) {
557                 Object[] row = new Object[columnDescriptors.length + 1];
558                 int j = 1;
559                 row[0] = rs.getInt(1);
560                 for (ColumnDescriptor columnDescriptor: columnDescriptors) {
561                     row[j] = columnDescriptor.getResultSetValue(rs, j + 1);
562                     ++j;
563                 }
564                 ++i;
565                 result.add(row);
566             }
567             connection.commit();
568         } catch (SQLException e) {
569             throw new RuntimeException("Failed to read " + tableName + " at instance " + i, e);
570         }
571         if (debug) System.out.println("readFromJDBC: " + dump(result));
572         return result;
573     }
574
575     /** Create row data from an instance.
576      * @param columnDescriptors the column descriptors describing the data
577      * @param instance the instance to extract data from
578      * @return the row data representing the instance
579      */
580     private Object[] createRow(ColumnDescriptor[] columnDescriptors,
581             IdBase instance) {
582         Object[] row = new Object[columnDescriptors.length + 1];
583         row[0] = instance.getId();
584         int j = 1;
585         for (ColumnDescriptor columnDescriptor: columnDescriptors) {
586             row[j++] = columnDescriptor.getFieldValue(instance);
587         }
588         return row;
589     }
590
591     /** Dump the contents of the expected or actual results of the operation */
592     private String dump(List<Object[]> results) {
593         StringBuffer result = new StringBuffer(results.size() + " rows\n");
594         for (Object[] row: results) {
595             result.append("Id: ");
596             for (Object column: row) {
597                 result.append(column);
598                 result.append(' ');
599             }
600             result.append('\n');
601         }
602         return result.toString();
603     }
604
605 }