001: package net.sourceforge.squirrel_sql.plugins.dbdiff;
002:
003: /*
004: * Copyright (C) 2007 Rob Manning
005: * manningr@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.sql.SQLException;
022: import java.util.ArrayList;
023: import java.util.HashMap;
024: import java.util.HashSet;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import javax.swing.JOptionPane;
030: import javax.swing.SwingUtilities;
031:
032: import net.sourceforge.squirrel_sql.client.gui.mainframe.MainFrame;
033: import net.sourceforge.squirrel_sql.client.session.ISession;
034: import net.sourceforge.squirrel_sql.fw.dialects.DialectFactory;
035: import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
036: import net.sourceforge.squirrel_sql.fw.sql.DatabaseObjectType;
037: import net.sourceforge.squirrel_sql.fw.sql.IDatabaseObjectInfo;
038: import net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData;
039: import net.sourceforge.squirrel_sql.fw.sql.ITableInfo;
040: import net.sourceforge.squirrel_sql.fw.util.StringManager;
041: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
042: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
043: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
044: import net.sourceforge.squirrel_sql.plugins.dbdiff.gui.ColumnDiffDialog;
045: import net.sourceforge.squirrel_sql.plugins.dbdiff.util.DBUtil;
046:
047: /**
048: * This is the class that performs the table copy using database connections
049: * to two different database schemas.
050: */
051: public class DiffExecutor extends I18NBaseObject {
052:
053: /** Logger for this class. */
054: private final static ILogger s_log = LoggerController
055: .createLogger(DiffExecutor.class);
056:
057: /** Internationalized strings for this class. */
058: private static final StringManager s_stringMgr = StringManagerFactory
059: .getStringManager(DiffExecutor.class);
060:
061: static interface i18n {
062: // i18n[DiffExecutor.noDiffsMessage=No differences were detected]
063: String NO_DIFFS_MESSAGE = s_stringMgr
064: .getString("DiffExecutor.noDiffsMessage");
065: }
066:
067: /** the class that provides out session information */
068: SessionInfoProvider prov = null;
069:
070: /** the source session. This comes from prov */
071: ISession sourceSession = null;
072:
073: /** the destination session. This comes from prov */
074: ISession destSession = null;
075:
076: /** the thread we do the work in */
077: private Thread execThread = null;
078:
079: /** whether or not the user cancelled the copy operation */
080: private volatile boolean cancelled = false;
081:
082: /** the start time in millis that the copy operation began */
083: private long start = 0;
084:
085: /** the finish time in millis that the copy operation began */
086: private long end = 0;
087:
088: private List<ColumnDifference> colDifferences = new ArrayList<ColumnDifference>();
089:
090: /**
091: * Constructor.
092: *
093: * @param p the provider of information regarding what to copy where.
094: */
095: public DiffExecutor(SessionInfoProvider p) {
096: prov = p;
097: sourceSession = prov.getDiffSourceSession();
098: destSession = prov.getDiffDestSession();
099: }
100:
101: /**
102: * Starts the thread that executes the copy operation.
103: */
104: public void execute() {
105: Runnable runnable = new Runnable() {
106: public void run() {
107: try {
108: _execute();
109: } catch (Exception e) {
110: e.printStackTrace();
111: }
112: }
113: };
114: execThread = new Thread(runnable);
115: execThread.setName("DBDiff Executor Thread");
116: execThread.start();
117: }
118:
119: /**
120: * Cancels the copy operation.
121: */
122: public void cancel() {
123: cancelled = true;
124: execThread.interrupt();
125: }
126:
127: /**
128: * Performs the table diff operation.
129: */
130: private void _execute() throws SQLException {
131: start = System.currentTimeMillis();
132: boolean encounteredException = false;
133: IDatabaseObjectInfo[] sourceObjs = prov
134: .getSourceSelectedDatabaseObjects();
135: IDatabaseObjectInfo[] destObjs = prov
136: .getDestSelectedDatabaseObjects();
137:
138: if (!sanityCheck(sourceObjs, destObjs)) {
139: return;
140: }
141:
142: ISQLDatabaseMetaData sourceMetaData = prov
143: .getDiffSourceSession().getMetaData();
144: ISQLDatabaseMetaData destMetaData = prov.getDiffDestSession()
145: .getMetaData();
146:
147: Map<String, ITableInfo> tableMap1 = getTableMap(sourceMetaData,
148: sourceObjs);
149: Map<String, ITableInfo> tableMap2 = getTableMap(destMetaData,
150: destObjs);
151:
152: Set<String> tableNames = getAllTableNames(tableMap1);
153: tableNames.addAll(getAllTableNames(tableMap2));
154:
155: try {
156: TableDiffExecutor diff = new TableDiffExecutor(
157: sourceMetaData, destMetaData);
158: for (String table : tableNames) {
159: if (tableMap1.containsKey(table)) {
160: if (tableMap2.containsKey(table)) {
161: ITableInfo t1 = tableMap1.get(table);
162: ITableInfo t2 = tableMap2.get(table);
163: diff.setTableInfos(t1, t2);
164: diff.execute();
165: List<ColumnDifference> columnDiffs = diff
166: .getColumnDifferences();
167: if (columnDiffs != null
168: && columnDiffs.size() > 0) {
169: colDifferences.addAll(columnDiffs);
170: for (ColumnDifference colDiff : columnDiffs) {
171: System.out.println(colDiff.toString());
172: }
173: }
174: } else {
175: // table exists in source db but not dest
176: }
177: } else {
178: // table doesn't exist in source db
179: }
180:
181: }
182: final MainFrame frame = sourceSession.getApplication()
183: .getMainFrame();
184: if (colDifferences != null && colDifferences.size() > 0) {
185: GUIUtils.processOnSwingEventThread(new Runnable() {
186: public void run() {
187: ColumnDiffDialog dialog = new ColumnDiffDialog(
188: frame, false);
189: dialog.setColumnDifferences(colDifferences);
190: dialog.setSession1Label(sourceSession
191: .getAlias().getName());
192: dialog.setSession2Label(destSession.getAlias()
193: .getName());
194: dialog.setVisible(true);
195: }
196: });
197: } else {
198: SwingUtilities.invokeLater(new Runnable() {
199: public void run() {
200: JOptionPane.showMessageDialog(frame,
201: i18n.NO_DIFFS_MESSAGE, "DBDiff",
202: JOptionPane.INFORMATION_MESSAGE);
203: }
204: });
205: }
206: } catch (SQLException e) {
207: s_log.error(
208: "Encountered unexpected exception while executing "
209: + "diff: " + e.getMessage(), e);
210: }
211:
212: if (encounteredException) {
213: return;
214: }
215: end = System.currentTimeMillis();
216: }
217:
218: private Set<String> getAllTableNames(Map<String, ITableInfo> tables) {
219: HashSet<String> result = new HashSet<String>();
220: result.addAll(tables.keySet());
221: return result;
222: }
223:
224: private Map<String, ITableInfo> getTableMap(
225: ISQLDatabaseMetaData md, IDatabaseObjectInfo[] objs)
226: throws SQLException {
227: HashMap<String, ITableInfo> result = new HashMap<String, ITableInfo>();
228: if (objs[0].getDatabaseObjectType() == DatabaseObjectType.TABLE) {
229: for (int i = 0; i < objs.length; i++) {
230: IDatabaseObjectInfo info = objs[i];
231: result.put(info.getSimpleName(), (ITableInfo) info);
232: }
233: } else {
234: // Assume objs[0] is a schema/catalog
235: String catalog = objs[0].getCatalogName();
236: String schema = objs[0].getSchemaName();
237: md.getTables(catalog, schema, null,
238: new String[] { "TABLE" }, null);
239: }
240: return result;
241: }
242:
243: /**
244: * Returns a list of column differences.
245: *
246: * @return Returns null if no diffs exist.
247: */
248: public List<ColumnDifference> getColumnDifferences() {
249: return colDifferences;
250: }
251:
252: /**
253: * Must have the same number of objects to compare in each set, and they
254: * must be the same type of Objects (Schemas or Tables)
255: *
256: * @param sourceObjs
257: * @param destObjs
258: * @return
259: */
260: private boolean sanityCheck(IDatabaseObjectInfo[] sourceObjs,
261: IDatabaseObjectInfo[] destObjs) {
262: boolean result = true;
263: if (sourceObjs.length != destObjs.length) {
264: result = false;
265: }
266: if (sourceObjs[0].getDatabaseObjectType() != destObjs[0]
267: .getDatabaseObjectType()) {
268: result = false;
269: }
270: return result;
271: }
272:
273: private int[] getTableCounts() {
274: int[] result = null;
275:
276: ISession sourceSession = prov.getDiffSourceSession();
277: IDatabaseObjectInfo[] dbObjs = prov
278: .getSourceSelectedDatabaseObjects();
279: if (dbObjs != null) {
280: result = new int[dbObjs.length];
281: for (int i = 0; i < dbObjs.length; i++) {
282: if (false == dbObjs[i] instanceof ITableInfo) {
283: continue;
284: }
285: try {
286: ITableInfo ti = (ITableInfo) dbObjs[i];
287: result[i] = DBUtil.getTableCount(sourceSession, ti
288: .getCatalogName(), ti.getSchemaName(), ti
289: .getSimpleName(),
290: DialectFactory.SOURCE_TYPE);
291: } catch (Exception e) {
292: s_log
293: .error(
294: "Unexpected exception while attempting to get table counts",
295: e);
296: result[i] = 0;
297: }
298: }
299: }
300: return result;
301: }
302:
303: /**
304: *
305: * @return
306: */
307: private long getElapsedSeconds() {
308: long result = 1;
309: double elapsed = end - start;
310: if (elapsed > 1000) {
311: result = Math.round(elapsed / 1000);
312: }
313: return result;
314: }
315:
316: }
|