001: /*
002: * HA-JDBC: High-Availability JDBC
003: * Copyright (c) 2004-2007 Paul Ferraro
004: *
005: * This library is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU Lesser General Public License as published by the
007: * Free Software Foundation; either version 2.1 of the License, or (at your
008: * option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
013: * for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public License
016: * along with this library; if not, write to the Free Software Foundation,
017: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: ferraro@users.sourceforge.net
020: */
021: package net.sf.hajdbc.sync;
022:
023: import java.sql.Connection;
024: import java.sql.PreparedStatement;
025: import java.sql.ResultSet;
026: import java.sql.SQLException;
027: import java.sql.Statement;
028: import java.sql.Types;
029: import java.util.ArrayList;
030: import java.util.Arrays;
031: import java.util.Collections;
032: import java.util.concurrent.ExecutorService;
033: import java.util.concurrent.Executors;
034:
035: import net.sf.hajdbc.ColumnProperties;
036: import net.sf.hajdbc.Database;
037: import net.sf.hajdbc.DatabaseMetaDataCache;
038: import net.sf.hajdbc.DatabaseProperties;
039: import net.sf.hajdbc.Dialect;
040: import net.sf.hajdbc.ForeignKeyConstraint;
041: import net.sf.hajdbc.SequenceProperties;
042: import net.sf.hajdbc.SynchronizationContext;
043: import net.sf.hajdbc.SynchronizationStrategy;
044: import net.sf.hajdbc.TableProperties;
045: import net.sf.hajdbc.UniqueConstraint;
046:
047: import org.easymock.EasyMock;
048: import org.testng.annotations.DataProvider;
049: import org.testng.annotations.Test;
050:
051: /**
052: * @author Paul Ferraro
053: */
054: @SuppressWarnings("nls")
055: public class TestDifferentialSynchronizationStrategy implements
056: SynchronizationStrategy {
057: private SynchronizationStrategy strategy = new DifferentialSynchronizationStrategy();
058:
059: @DataProvider(name="context")
060: Object[][] contextProvider() {
061: return new Object[][] { new Object[] { EasyMock
062: .createStrictMock(SynchronizationContext.class) } };
063: }
064:
065: @Override
066: @SuppressWarnings("unchecked")
067: @Test(dataProvider="context")
068: public <D> void synchronize(SynchronizationContext<D> context)
069: throws SQLException {
070: Database<D> sourceDatabase = EasyMock
071: .createStrictMock(Database.class);
072: Database<D> targetDatabase = EasyMock
073: .createStrictMock(Database.class);
074: Connection sourceConnection = EasyMock
075: .createStrictMock(Connection.class);
076: Connection targetConnection = EasyMock
077: .createStrictMock(Connection.class);
078: Statement statement = EasyMock
079: .createStrictMock(Statement.class);
080: DatabaseMetaDataCache metaData = EasyMock
081: .createStrictMock(DatabaseMetaDataCache.class);
082: DatabaseProperties database = EasyMock
083: .createStrictMock(DatabaseProperties.class);
084: TableProperties table = EasyMock
085: .createStrictMock(TableProperties.class);
086: Dialect dialect = EasyMock.createStrictMock(Dialect.class);
087: ForeignKeyConstraint foreignKey = EasyMock
088: .createStrictMock(ForeignKeyConstraint.class);
089: UniqueConstraint primaryKey = EasyMock
090: .createStrictMock(UniqueConstraint.class);
091: UniqueConstraint uniqueKey = EasyMock
092: .createStrictMock(UniqueConstraint.class);
093: Statement targetStatement = EasyMock
094: .createStrictMock(Statement.class);
095: ResultSet targetResultSet = EasyMock
096: .createStrictMock(ResultSet.class);
097: Statement sourceStatement = EasyMock
098: .createStrictMock(Statement.class);
099: ResultSet sourceResultSet = EasyMock
100: .createStrictMock(ResultSet.class);
101: PreparedStatement deleteStatement = EasyMock
102: .createStrictMock(PreparedStatement.class);
103: PreparedStatement insertStatement = EasyMock
104: .createStrictMock(PreparedStatement.class);
105: PreparedStatement updateStatement = EasyMock
106: .createStrictMock(PreparedStatement.class);
107: ColumnProperties column1 = EasyMock
108: .createStrictMock(ColumnProperties.class);
109: ColumnProperties column2 = EasyMock
110: .createStrictMock(ColumnProperties.class);
111: ColumnProperties column3 = EasyMock
112: .createStrictMock(ColumnProperties.class);
113: ColumnProperties column4 = EasyMock
114: .createStrictMock(ColumnProperties.class);
115: ExecutorService executor = Executors.newSingleThreadExecutor();
116: SequenceProperties sequence = EasyMock
117: .createStrictMock(SequenceProperties.class);
118:
119: EasyMock.expect(context.getSourceDatabase()).andReturn(
120: sourceDatabase);
121: EasyMock.expect(context.getConnection(sourceDatabase))
122: .andReturn(sourceConnection);
123:
124: EasyMock.expect(context.getTargetDatabase()).andReturn(
125: targetDatabase);
126: EasyMock.expect(context.getConnection(targetDatabase))
127: .andReturn(targetConnection);
128:
129: EasyMock.expect(context.getDialect()).andReturn(dialect);
130: EasyMock.expect(context.getExecutor()).andReturn(executor);
131:
132: targetConnection.setAutoCommit(true);
133:
134: EasyMock.expect(context.getDatabaseProperties()).andReturn(
135: database);
136: EasyMock.expect(database.getTables()).andReturn(
137: Collections.singleton(table));
138: EasyMock.expect(context.getDialect()).andReturn(dialect);
139:
140: EasyMock.expect(context.getTargetDatabase()).andReturn(
141: targetDatabase);
142: EasyMock.expect(context.getConnection(targetDatabase))
143: .andReturn(targetConnection);
144:
145: EasyMock.expect(targetConnection.createStatement()).andReturn(
146: targetStatement);
147:
148: EasyMock.expect(table.getForeignKeyConstraints()).andReturn(
149: Collections.singleton(foreignKey));
150: EasyMock.expect(
151: dialect.getDropForeignKeyConstraintSQL(foreignKey))
152: .andReturn("drop fk");
153:
154: targetStatement.addBatch("drop fk");
155:
156: EasyMock.expect(targetStatement.executeBatch()).andReturn(null);
157:
158: targetStatement.close();
159:
160: EasyMock.expect(context.getDatabaseProperties()).andReturn(
161: database);
162: EasyMock.expect(database.getTables()).andReturn(
163: Collections.singleton(table));
164:
165: EasyMock.expect(table.getUniqueConstraints()).andReturn(
166: new ArrayList<UniqueConstraint>(Arrays
167: .asList(new UniqueConstraint[] { primaryKey,
168: uniqueKey })));
169: EasyMock.expect(table.getPrimaryKey()).andReturn(primaryKey);
170: EasyMock.expect(context.getDialect()).andReturn(dialect);
171:
172: EasyMock.expect(context.getTargetDatabase()).andReturn(
173: targetDatabase);
174: EasyMock.expect(context.getConnection(targetDatabase))
175: .andReturn(targetConnection);
176: EasyMock.expect(targetConnection.createStatement()).andReturn(
177: targetStatement);
178:
179: EasyMock.expect(dialect.getDropUniqueConstraintSQL(uniqueKey))
180: .andReturn("drop uk");
181:
182: targetStatement.addBatch("drop uk");
183:
184: EasyMock.expect(targetStatement.executeBatch()).andReturn(null);
185:
186: targetStatement.close();
187:
188: targetConnection.setAutoCommit(false);
189:
190: EasyMock.expect(table.getName()).andReturn("table");
191: EasyMock.expect(table.getPrimaryKey()).andReturn(primaryKey);
192: EasyMock.expect(primaryKey.getColumnList()).andReturn(
193: Arrays.asList(new String[] { "column1", "column2" }));
194:
195: EasyMock.expect(table.getColumns()).andReturn(
196: Arrays.asList(new String[] { "column1", "column2",
197: "column3", "column4" }));
198:
199: EasyMock.expect(targetConnection.createStatement()).andReturn(
200: targetStatement);
201: targetStatement.setFetchSize(0);
202:
203: // Disable order checking, since statement is executed asynchronously
204: EasyMock.checkOrder(targetStatement, false);
205: EasyMock.checkOrder(sourceConnection, false);
206: EasyMock.checkOrder(sourceStatement, false);
207:
208: EasyMock
209: .expect(
210: targetStatement
211: .executeQuery("SELECT column1, column2, column3, column4 FROM table ORDER BY column1, column2"))
212: .andReturn(targetResultSet);
213:
214: EasyMock.expect(sourceConnection.createStatement()).andReturn(
215: sourceStatement);
216:
217: sourceStatement.setFetchSize(0);
218:
219: EasyMock
220: .expect(
221: sourceStatement
222: .executeQuery("SELECT column1, column2, column3, column4 FROM table ORDER BY column1, column2"))
223: .andReturn(sourceResultSet);
224:
225: EasyMock.checkOrder(targetStatement, true);
226: EasyMock.checkOrder(sourceConnection, true);
227: EasyMock.checkOrder(sourceStatement, true);
228:
229: EasyMock
230: .expect(
231: targetConnection
232: .prepareStatement("DELETE FROM table WHERE column1 = ? AND column2 = ?"))
233: .andReturn(deleteStatement);
234: EasyMock
235: .expect(
236: targetConnection
237: .prepareStatement("INSERT INTO table (column1, column2, column3, column4) VALUES (?, ?, ?, ?)"))
238: .andReturn(insertStatement);
239: EasyMock
240: .expect(
241: targetConnection
242: .prepareStatement("UPDATE table SET column3 = ?, column4 = ? WHERE column1 = ? AND column2 = ?"))
243: .andReturn(updateStatement);
244:
245: EasyMock.expect(sourceResultSet.next()).andReturn(true);
246: EasyMock.expect(targetResultSet.next()).andReturn(true);
247:
248: // Trigger insert
249: EasyMock.expect(sourceResultSet.getObject(1)).andReturn(1);
250: EasyMock.expect(targetResultSet.getObject(1)).andReturn(1);
251: EasyMock.expect(sourceResultSet.getObject(2)).andReturn(1);
252: EasyMock.expect(targetResultSet.getObject(2)).andReturn(2);
253:
254: insertStatement.clearParameters();
255:
256: EasyMock.expect(table.getColumnProperties("column1"))
257: .andReturn(column1);
258: EasyMock.expect(dialect.getColumnType(column1)).andReturn(
259: Types.INTEGER);
260: EasyMock.expect(sourceResultSet.getObject(1)).andReturn(1);
261: EasyMock.expect(sourceResultSet.wasNull()).andReturn(false);
262: insertStatement.setObject(1, 1, Types.INTEGER);
263:
264: EasyMock.expect(table.getColumnProperties("column2"))
265: .andReturn(column2);
266: EasyMock.expect(dialect.getColumnType(column2)).andReturn(
267: Types.INTEGER);
268: EasyMock.expect(sourceResultSet.getObject(2)).andReturn(1);
269: EasyMock.expect(sourceResultSet.wasNull()).andReturn(false);
270: insertStatement.setObject(2, 1, Types.INTEGER);
271:
272: EasyMock.expect(table.getColumnProperties("column3"))
273: .andReturn(column3);
274: EasyMock.expect(dialect.getColumnType(column3)).andReturn(
275: Types.BLOB);
276: EasyMock.expect(sourceResultSet.getBlob(3)).andReturn(null);
277: EasyMock.expect(sourceResultSet.wasNull()).andReturn(true);
278: insertStatement.setNull(3, Types.BLOB);
279:
280: EasyMock.expect(table.getColumnProperties("column4"))
281: .andReturn(column4);
282: EasyMock.expect(dialect.getColumnType(column4)).andReturn(
283: Types.CLOB);
284: EasyMock.expect(sourceResultSet.getClob(4)).andReturn(null);
285: EasyMock.expect(sourceResultSet.wasNull()).andReturn(true);
286: insertStatement.setNull(4, Types.CLOB);
287:
288: insertStatement.addBatch();
289:
290: EasyMock.expect(sourceResultSet.next()).andReturn(true);
291:
292: // Trigger update
293: EasyMock.expect(sourceResultSet.getObject(1)).andReturn(1);
294: EasyMock.expect(targetResultSet.getObject(1)).andReturn(1);
295: EasyMock.expect(sourceResultSet.getObject(2)).andReturn(2);
296: EasyMock.expect(targetResultSet.getObject(2)).andReturn(2);
297:
298: updateStatement.clearParameters();
299:
300: // Nothing to update
301: EasyMock.expect(table.getColumnProperties("column3"))
302: .andReturn(column3);
303: EasyMock.expect(dialect.getColumnType(column3)).andReturn(
304: Types.VARCHAR);
305: EasyMock.expect(sourceResultSet.getObject(3)).andReturn("");
306: EasyMock.expect(targetResultSet.getObject(3)).andReturn("");
307: EasyMock.expect(sourceResultSet.wasNull()).andReturn(false);
308: updateStatement.setObject(1, "", Types.VARCHAR);
309: EasyMock.expect(targetResultSet.wasNull()).andReturn(false);
310:
311: // Nothing to update
312: EasyMock.expect(table.getColumnProperties("column4"))
313: .andReturn(column4);
314: EasyMock.expect(dialect.getColumnType(column4)).andReturn(
315: Types.VARCHAR);
316: EasyMock.expect(sourceResultSet.getObject(4)).andReturn(null);
317: EasyMock.expect(targetResultSet.getObject(4)).andReturn(null);
318: EasyMock.expect(sourceResultSet.wasNull()).andReturn(true);
319: updateStatement.setNull(2, Types.VARCHAR);
320: EasyMock.expect(targetResultSet.wasNull()).andReturn(true);
321:
322: EasyMock.expect(sourceResultSet.next()).andReturn(true);
323: EasyMock.expect(targetResultSet.next()).andReturn(true);
324:
325: // Trigger update
326: EasyMock.expect(sourceResultSet.getObject(1)).andReturn(1);
327: EasyMock.expect(targetResultSet.getObject(1)).andReturn(1);
328: EasyMock.expect(sourceResultSet.getObject(2)).andReturn(3);
329: EasyMock.expect(targetResultSet.getObject(2)).andReturn(3);
330:
331: updateStatement.clearParameters();
332:
333: EasyMock.expect(table.getColumnProperties("column3"))
334: .andReturn(column3);
335: EasyMock.expect(dialect.getColumnType(column3)).andReturn(
336: Types.VARCHAR);
337: EasyMock.expect(sourceResultSet.getObject(3)).andReturn("");
338: EasyMock.expect(targetResultSet.getObject(3)).andReturn(null);
339: EasyMock.expect(sourceResultSet.wasNull()).andReturn(false);
340: updateStatement.setObject(1, "", Types.VARCHAR);
341: EasyMock.expect(targetResultSet.wasNull()).andReturn(true);
342:
343: EasyMock.expect(table.getColumnProperties("column4"))
344: .andReturn(column4);
345: EasyMock.expect(dialect.getColumnType(column4)).andReturn(
346: Types.VARCHAR);
347: EasyMock.expect(sourceResultSet.getObject(4)).andReturn(null);
348: EasyMock.expect(targetResultSet.getObject(4)).andReturn("");
349: EasyMock.expect(sourceResultSet.wasNull()).andReturn(true);
350: updateStatement.setNull(2, Types.VARCHAR);
351: EasyMock.expect(targetResultSet.wasNull()).andReturn(false);
352:
353: EasyMock.expect(table.getColumnProperties("column1"))
354: .andReturn(column1);
355: EasyMock.expect(dialect.getColumnType(column1)).andReturn(
356: Types.INTEGER);
357: EasyMock.expect(targetResultSet.getObject(1)).andReturn(1);
358: updateStatement.setObject(3, 1, Types.INTEGER);
359:
360: EasyMock.expect(table.getColumnProperties("column2"))
361: .andReturn(column2);
362: EasyMock.expect(dialect.getColumnType(column2)).andReturn(
363: Types.INTEGER);
364: EasyMock.expect(targetResultSet.getObject(2)).andReturn(3);
365: updateStatement.setObject(4, 3, Types.INTEGER);
366:
367: updateStatement.addBatch();
368:
369: EasyMock.expect(sourceResultSet.next()).andReturn(false);
370: EasyMock.expect(targetResultSet.next()).andReturn(true);
371:
372: deleteStatement.clearParameters();
373:
374: EasyMock.expect(table.getColumnProperties("column1"))
375: .andReturn(column1);
376: EasyMock.expect(dialect.getColumnType(column1)).andReturn(
377: Types.INTEGER);
378: EasyMock.expect(targetResultSet.getObject(1)).andReturn(2);
379: deleteStatement.setObject(1, 2, Types.INTEGER);
380:
381: EasyMock.expect(table.getColumnProperties("column2"))
382: .andReturn(column2);
383: EasyMock.expect(dialect.getColumnType(column2)).andReturn(
384: Types.INTEGER);
385: EasyMock.expect(targetResultSet.getObject(2)).andReturn(1);
386: deleteStatement.setObject(2, 1, Types.INTEGER);
387:
388: deleteStatement.addBatch();
389:
390: EasyMock.expect(targetResultSet.next()).andReturn(false);
391:
392: EasyMock.expect(deleteStatement.executeBatch()).andReturn(null);
393: deleteStatement.close();
394:
395: EasyMock.expect(insertStatement.executeBatch()).andReturn(null);
396: insertStatement.close();
397:
398: EasyMock.expect(updateStatement.executeBatch()).andReturn(null);
399: updateStatement.close();
400:
401: targetStatement.close();
402: sourceStatement.close();
403:
404: targetConnection.commit();
405:
406: targetConnection.setAutoCommit(true);
407:
408: EasyMock.expect(table.getUniqueConstraints()).andReturn(
409: new ArrayList<UniqueConstraint>(Arrays
410: .asList(new UniqueConstraint[] { primaryKey,
411: uniqueKey })));
412: EasyMock.expect(table.getPrimaryKey()).andReturn(primaryKey);
413: EasyMock.expect(context.getDialect()).andReturn(dialect);
414:
415: EasyMock.expect(context.getTargetDatabase()).andReturn(
416: targetDatabase);
417: EasyMock.expect(context.getConnection(targetDatabase))
418: .andReturn(targetConnection);
419: EasyMock.expect(targetConnection.createStatement()).andReturn(
420: targetStatement);
421:
422: EasyMock
423: .expect(dialect.getCreateUniqueConstraintSQL(uniqueKey))
424: .andReturn("create uk");
425:
426: targetStatement.addBatch("create uk");
427: EasyMock.expect(targetStatement.executeBatch()).andReturn(null);
428:
429: targetStatement.close();
430:
431: EasyMock.expect(context.getDatabaseProperties()).andReturn(
432: database);
433: EasyMock.expect(database.getTables()).andReturn(
434: Collections.singleton(table));
435: EasyMock.expect(context.getDialect()).andReturn(dialect);
436:
437: EasyMock.expect(context.getTargetDatabase()).andReturn(
438: targetDatabase);
439: EasyMock.expect(context.getConnection(targetDatabase))
440: .andReturn(targetConnection);
441:
442: EasyMock.expect(targetConnection.createStatement()).andReturn(
443: targetStatement);
444:
445: EasyMock.expect(table.getForeignKeyConstraints()).andReturn(
446: Collections.singleton(foreignKey));
447: EasyMock.expect(
448: dialect.getCreateForeignKeyConstraintSQL(foreignKey))
449: .andReturn("create fk");
450:
451: targetStatement.addBatch("create fk");
452:
453: EasyMock.expect(targetStatement.executeBatch()).andReturn(null);
454:
455: targetStatement.close();
456:
457: {
458: EasyMock.expect(context.getSourceDatabase()).andReturn(
459: sourceDatabase);
460: EasyMock.expect(context.getConnection(sourceDatabase))
461: .andReturn(sourceConnection);
462: EasyMock.expect(sourceConnection.createStatement())
463: .andReturn(sourceStatement);
464:
465: EasyMock.expect(context.getTargetDatabase()).andReturn(
466: targetDatabase);
467: EasyMock.expect(context.getConnection(targetDatabase))
468: .andReturn(targetConnection);
469: EasyMock.expect(targetConnection.createStatement())
470: .andReturn(targetStatement);
471:
472: EasyMock.expect(context.getDialect()).andReturn(dialect);
473:
474: EasyMock.expect(context.getDatabaseProperties()).andReturn(
475: database);
476: EasyMock.expect(database.getTables()).andReturn(
477: Collections.singleton(table));
478:
479: EasyMock.expect(table.getIdentityColumns()).andReturn(
480: Collections.singleton("column"));
481: EasyMock.expect(table.getName()).andReturn("table");
482: EasyMock
483: .expect(
484: sourceStatement
485: .executeQuery("SELECT max(column) FROM table"))
486: .andReturn(sourceResultSet);
487: EasyMock.expect(sourceResultSet.next()).andReturn(true);
488: EasyMock.expect(sourceResultSet.getLong(1)).andReturn(1L);
489:
490: sourceResultSet.close();
491:
492: EasyMock.expect(table.getColumnProperties("column"))
493: .andReturn(column1);
494: EasyMock.expect(
495: dialect.getAlterIdentityColumnSQL(table, column1,
496: 2L)).andReturn("column = 1");
497:
498: targetStatement.addBatch("column = 1");
499: EasyMock.expect(targetStatement.executeBatch()).andReturn(
500: null);
501:
502: sourceStatement.close();
503: targetStatement.close();
504: }
505: {
506: EasyMock.expect(context.getDatabaseProperties()).andReturn(
507: database);
508: EasyMock.expect(database.getSequences()).andReturn(
509: Collections.singleton(sequence));
510:
511: EasyMock.expect(context.getSourceDatabase()).andReturn(
512: sourceDatabase);
513: EasyMock.expect(context.getActiveDatabaseSet()).andReturn(
514: Collections.singleton(sourceDatabase));
515: EasyMock.expect(context.getExecutor()).andReturn(executor);
516: EasyMock.expect(context.getDialect()).andReturn(dialect);
517:
518: EasyMock.expect(dialect.getNextSequenceValueSQL(sequence))
519: .andReturn("sequence next value");
520:
521: EasyMock.expect(context.getConnection(sourceDatabase))
522: .andReturn(sourceConnection);
523: EasyMock.expect(sourceConnection.createStatement())
524: .andReturn(sourceStatement);
525: EasyMock
526: .expect(
527: sourceStatement
528: .executeQuery("sequence next value"))
529: .andReturn(sourceResultSet);
530:
531: EasyMock.expect(sourceResultSet.next()).andReturn(true);
532:
533: EasyMock.expect(sourceResultSet.getLong(1)).andReturn(1L);
534:
535: sourceStatement.close();
536:
537: EasyMock.expect(context.getTargetDatabase()).andReturn(
538: targetDatabase);
539: EasyMock.expect(context.getConnection(targetDatabase))
540: .andReturn(targetConnection);
541: EasyMock.expect(targetConnection.createStatement())
542: .andReturn(targetStatement);
543:
544: EasyMock.expect(dialect.getAlterSequenceSQL(sequence, 2L))
545: .andReturn("alter sequence");
546:
547: targetStatement.addBatch("alter sequence");
548:
549: EasyMock.expect(targetStatement.executeBatch()).andReturn(
550: null);
551:
552: targetStatement.close();
553: }
554:
555: EasyMock.replay(context, sourceDatabase, targetDatabase,
556: sourceConnection, targetConnection, statement,
557: metaData, database, table, dialect, foreignKey,
558: primaryKey, uniqueKey, targetStatement,
559: targetResultSet, sourceStatement, sourceResultSet,
560: deleteStatement, insertStatement, updateStatement,
561: column1, column2, column3, column4);
562:
563: this.strategy.synchronize(context);
564:
565: EasyMock.verify(context, sourceDatabase, targetDatabase,
566: sourceConnection, targetConnection, statement,
567: metaData, database, table, dialect, foreignKey,
568: primaryKey, uniqueKey, targetStatement,
569: targetResultSet, sourceStatement, sourceResultSet,
570: deleteStatement, insertStatement, updateStatement,
571: column1, column2, column3, column4);
572: }
573: }
|