1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: ExportImportTest.java,v 1.2 2005/11/12 12:52:06 tanderson Exp $
44   */
45  package org.exolab.jms.tools.migration;
46  
47  import java.io.InputStream;
48  import java.sql.Connection;
49  import java.sql.DriverManager;
50  import java.sql.SQLException;
51  
52  import org.apache.derby.jdbc.EmbeddedDataSource;
53  
54  import junit.framework.TestCase;
55  import org.dbunit.Assertion;
56  import org.dbunit.database.DatabaseConnection;
57  import org.dbunit.database.IDatabaseConnection;
58  import org.dbunit.database.QueryDataSet;
59  import org.dbunit.dataset.IDataSet;
60  import org.dbunit.dataset.ITable;
61  import org.exolab.jms.config.Configuration;
62  import org.exolab.jms.config.ConfigurationReader;
63  import org.exolab.jms.config.DatabaseConfiguration;
64  import org.exolab.jms.config.RdbmsDatabaseConfiguration;
65  import org.exolab.jms.persistence.PersistenceException;
66  import org.exolab.jms.tools.db.Database;
67  import org.exolab.jms.tools.db.RDBMSTool;
68  import org.exolab.jms.tools.db.SchemaHelper;
69  
70  
71  /***
72   * Tests the {@link Exporter} and {@link Importer}.
73   *
74   * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
75   * @version $Revision: 1.2 $ $Date: 2005/11/12 12:52:06 $
76   */
77  public class ExportImportTest extends TestCase {
78  
79      /***
80       * Migration database name.
81       */
82      private static final String DB_NAME = "openjms_migdb";
83  
84      /***
85       * Destinations table query. Ignores destination identifiers as these
86       * may be different between databases.
87       */
88      private static final String DESTINATIONS_QUERY
89              = "select name, isQueue from destinations order by name";
90      /***
91       * Message handles table query. Translates destination and consumer
92       * identifiers to their corresponding names, as these may be different
93       * between databases.
94       */
95      private static final String MESSAGE_HANDLES_QUERY
96              = "select messageId, d.name destination, c.name consumer, "
97                + "     priority, acceptedTime, sequenceNumber, expiryTime, "
98                + "     delivered "
99                + "from message_handles m, destinations d, consumers c "
100               + "where m.destinationId = d.destinationId and "
101               + "      m.consumerId = c.consumerId "
102               + "order by messageId, destination, consumer";
103 
104     /***
105      * Consumers table query. Ignores the 'created' column which is no longer
106      * used.
107      */
108     private static final String CONSUMERS_QUERY
109             = "select c.name name, d.name destination "
110               + "from consumers c, destinations d "
111               + "where c.destinationId = d.destinationId "
112               + "order by name, destination";
113 
114     /***
115      * Verifies that the exporter creates the tables in the target proxy,
116      * if they don't exist.
117      *
118      * @throws Exception for any error
119      */
120     public void testExporterCreateTables() throws Exception {
121         // load the configuration for the master database
122         Configuration config = read("/openjmstest.xml");
123 
124         // ensure the tables don't exist
125         EmbeddedDataSource ds = MigrationHelper.getDataSource(DB_NAME);
126         RDBMSTool tool = new RDBMSTool(ds.getConnection());
127         Database schema = MigrationHelper.getSchema();
128         tool.drop(schema);
129         assertFalse(tool.hasTables(schema.getTable()));
130 
131         // Create the exporter, and verify it has created the tables.
132         new Exporter(config, DB_NAME, false);
133         assertTrue(tool.hasTables(schema.getTable()));
134     }
135 
136     /***
137      * Verifies that the exporter will only export data if the delete flag
138      * is true when the tables exists.
139      *
140      * @throws Exception for any error
141      */
142     public void testExporterDelete() throws Exception {
143         // load the configuration for the master database
144         Configuration config = read("/openjmstest.xml");
145 
146         // create the target database.
147         EmbeddedDataSource ds = MigrationHelper.getDataSource(DB_NAME);
148         RDBMSTool tool = new RDBMSTool(ds.getConnection());
149         Database schema = MigrationHelper.getSchema();
150         tool.drop(schema);
151         tool.create(schema);
152 
153         // Create the exporter
154         try {
155             new Exporter(config, DB_NAME, false);
156             fail("Expected Exporter construction to fail, as tables exist");
157         } catch (PersistenceException expected) {
158             // expected behaviour
159         }
160 
161         try {
162             new Exporter(config, DB_NAME, true);
163         } catch (PersistenceException unexpected) {
164             fail("Expected Exporter construction to succeed");
165         }
166     }
167 
168     /***
169      * Verifies that the importer creates the tables in the target master,
170      * if they don't exist.
171      *
172      * @throws Exception for any error
173      */
174     public void testImporterCreateTables() throws Exception {
175         // load the configuration for the target master database 'migrated'
176         Configuration config = read("/openjmstest_migrated.xml");
177 
178         // ensure the tables don't exist
179         RDBMSTool tool = new RDBMSTool(config);
180         Database schema = SchemaHelper.getSchema();
181         tool.drop(schema);
182         assertFalse(tool.hasTables(schema.getTable()));
183 
184         // Create the importer, and verify it has created the tables.
185         new Importer(config, DB_NAME, false);
186         assertTrue(tool.hasTables(schema.getTable()));
187     }
188 
189     /***
190      * Verifies that the importer will only import data if the delete flag
191      * is true when the tables exists.
192      *
193      * @throws Exception for any error
194      */
195     public void testImporterDelete() throws Exception {
196         // load the configuration for the target master database 'migrated'
197         Configuration config = read("/openjmstest_migrated.xml");
198 
199         // create the target database.
200         RDBMSTool tool = new RDBMSTool(config);
201         Database schema = SchemaHelper.getSchema();
202         tool.drop(schema);
203         tool.create(schema);
204         tool.close();
205 
206         // Create the importer
207         try {
208             new Importer(config, DB_NAME, false);
209             fail("Expected Importer construction to fail, as tables exist");
210         } catch (PersistenceException expected) {
211             // expected behaviour
212         }
213 
214         try {
215             new Importer(config, DB_NAME, true);
216         } catch (PersistenceException unexpected) {
217             fail("Expected Importer construction to succeed");
218         }
219     }
220 
221     /***
222      * Exports data from a database and imports it to another, verifying the
223      * contents are the same between each.
224      *
225      * @throws Exception for any error
226      */
227     public void testRoundtrip() throws Exception {
228         // load the configuration for the master database
229         Configuration master = read("/openjmstest.xml");
230 
231         // export the data to a proxy database
232         Exporter exporter = new Exporter(master, DB_NAME, true);
233         exporter.apply();
234 
235         // load the configuration for the target master database 'migrated'
236         Configuration migrated = read("/openjmstest_migrated.xml");
237 
238         // import the data from the proxy database into 'migrated'
239         Importer importer = new Importer(migrated, DB_NAME, true);
240         importer.apply();
241 
242         // verify data between the two master databases
243         IDatabaseConnection masterConn = getConnection(master);
244         IDatabaseConnection migratedConn = getConnection(migrated);
245         IDataSet expectedDataSet = masterConn.createDataSet();
246         IDataSet actualDataSet = migratedConn.createDataSet();
247 
248         // NOTE: don't care about the contents of the 'seeds' and 'system_data'
249         // tables as neither are explicitly migrated, and the contents of
250         // each may differ
251         checkQuery("destinations", DESTINATIONS_QUERY, masterConn,
252                    migratedConn);
253         checkTable("messages", expectedDataSet, actualDataSet);
254         checkQuery("message_handles", MESSAGE_HANDLES_QUERY, masterConn,
255                    migratedConn);
256         checkQuery("consumers", CONSUMERS_QUERY, masterConn, migratedConn);
257         checkTable("users", expectedDataSet, actualDataSet);
258     }
259 
260     /***
261      * Reads a configuration from a resource.
262      *
263      * @param path the path to the resource
264      * @throws Exception for any error
265      */
266     private Configuration read(String path) throws Exception {
267         InputStream stream = ExportImportTest.class.getResourceAsStream(path);
268         return ConfigurationReader.read(stream);
269     }
270 
271     /***
272      * Returns a dbunit connection for a database.
273      *
274      * @param config the configuration to use
275      * @return the connection
276      * @throws SQLException for any error
277      */
278     private IDatabaseConnection getConnection(Configuration config)
279             throws SQLException {
280         DatabaseConfiguration db = config.getDatabaseConfiguration();
281         Connection connection
282                 = getConnection(db.getRdbmsDatabaseConfiguration());
283         return new DatabaseConnection(connection);
284     }
285 
286     /***
287      * Returns a connection given the configuration.
288      *
289      * @param config the config to use
290      * @throws SQLException for any error
291      */
292     private Connection getConnection(RdbmsDatabaseConfiguration config)
293             throws SQLException {
294         return DriverManager.getConnection(config.getUrl(), config.getUser(),
295                                            config.getPassword());
296     }
297 
298     /***
299      * Verifies that the contents of the named table is the same in 2 datasets.
300      *
301      * @param table           the table name
302      * @param expectedDataSet the data set containing the expected results
303      * @param actualDataSet   the data st containing the actual results
304      * @throws Exception for any error
305      */
306     private void checkTable(String table, IDataSet expectedDataSet,
307                             IDataSet actualDataSet) throws Exception {
308         ITable expected = expectedDataSet.getTable(table);
309         ITable actual = actualDataSet.getTable(table);
310 
311         // make sure there is actually data to compare against
312         assertTrue(expected.getRowCount() != 0);
313 
314         Assertion.assertEquals(expected, actual);
315     }
316 
317     /***
318      * Verifies that the results returned by a query are the same from two
319      * different connections.
320      *
321      * @param table              the table name
322      * @param query              the query to execute
323      * @param expectedConnection the connection containing the expected results
324      * @param actualConnection   the connection containing the actual results
325      * @throws Exception for any error
326      */
327     private void checkQuery(String table, String query,
328                             IDatabaseConnection expectedConnection,
329                             IDatabaseConnection actualConnection)
330             throws Exception {
331         QueryDataSet expectedDataSet = new QueryDataSet(expectedConnection);
332         expectedDataSet.addTable(table, query);
333 
334         QueryDataSet actualDataSet = new QueryDataSet(actualConnection);
335         actualDataSet.addTable(table, query);
336 
337         // new FlatXmlWriter(System.out).write(expectedDataSet);
338         // new FlatXmlWriter(System.out).write(actualDataSet);
339 
340         // make sure there is actually data to compare against
341         assertTrue(expectedDataSet.getTable(table).getRowCount() != 0);
342 
343         Assertion.assertEquals(expectedDataSet, actualDataSet);
344     }
345 
346 }