/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* * DOMVersionChecker.java * * Created on 29. April 2003, 11:13 */ package Sirius.util.DBVersionChecker; import org.jdom.*; import org.jdom.input.*; import java.io.*; import java.sql.*; import java.util.*; /** * Format des configfiles: Pfad der XML Beschreibung Datenbank Treiber Datenbank Name Benutzer Passwort * * @version $Revision$, $Date$ */ public class DOMVersionChecker implements VersionChecker { //~ Instance fields -------------------------------------------------------- private Connection conn; private DatabaseMetaData meta; private String xmlFile; private SAXBuilder parser; private ArrayList differences; /** * Datenstruktur: Versionsname => map { Tabelle => map { Spalte => map { nullable => "yes" / "no" / "unknown", size * => Zahlenwert type => Name des Typ } } } * * <p>Die Elemente der letzten map (Spalte) sind alle optional, ein Wert von "" wird im Vergleich automatisch als * \u00FCbereinstimmend mit der DB angenommen.</p> */ private TreeMap versionMap; /** Datenstruktur analog zu einer Version der versionMap, d.h. map { Tabelle => map ... */ private TreeMap dbStructure; //~ Constructors ----------------------------------------------------------- /** * Erstellt eine Instanz anhand eines configfiles der Form: Pfad der XML Beschreibung Datenbank Treiber Datenbank * Name Benutzer Passwort * * @param configfile DOCUMENT ME! * * @throws DBVersionException DOCUMENT ME! */ public DOMVersionChecker(final String configfile) throws DBVersionException { versionMap = new TreeMap(); dbStructure = new TreeMap(); differences = new ArrayList(); parser = new SAXBuilder(); try { final BufferedReader cfg = new BufferedReader(new FileReader(configfile)); xmlFile = cfg.readLine(); readVersionsXML(); setDB(cfg.readLine(), cfg.readLine(), cfg.readLine(), cfg.readLine()); } catch (IOException e) { System.err.println(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.DOMVersionChecker(String).error") + e); // NOI18N System.exit(1); } } /** * Creates a new instance of DOMVersionChecker. * * @param xmlFile DOCUMENT ME! * @param con DOCUMENT ME! */ public DOMVersionChecker(final String xmlFile, final Connection con) { this.xmlFile = xmlFile; versionMap = new TreeMap(); dbStructure = new TreeMap(); differences = new ArrayList(); parser = new SAXBuilder(); setDB(con); readVersionsXML(); } /** * Creates a new instance of DOMVersionChecker. * * @param xmlFile DOCUMENT ME! * @param driver DOCUMENT ME! * @param database DOCUMENT ME! * @param username DOCUMENT ME! * @param password DOCUMENT ME! */ public DOMVersionChecker(final String xmlFile, final String driver, final String database, final String username, final String password) { this.xmlFile = xmlFile; versionMap = new TreeMap(); dbStructure = new TreeMap(); differences = new ArrayList(); parser = new SAXBuilder(); readVersionsXML(); setDB(driver, database, username, password); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param args DOCUMENT ME! */ public static void main(final String[] args) { try { final String nL = System.getProperty("line.separator"); // NOI18N if (args.length < 2) { System.err.println( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.main(String[]).helpText", new Object[] { nL })); // NOI18N System.exit(0); } final DOMVersionChecker instance = new DOMVersionChecker(args[0]); if (args[1].equals("version")) { // NOI18N final String version = instance.checkVersion(); if (version != null) { System.out.println(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.main(String[]).foundVersion", new Object[] { version })); // NOI18N } else { System.out.println( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.main(String[])..noProperVersion")); // NOI18N } } else if (args[1].equals("generiere")) { // NOI18N instance.writeVersionXML(instance.xmlFile, args[2]); } else if (args[1].equals("vergleiche")) { // NOI18N if (instance.compareWithVersion(args[2])) { System.out.println(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.main(String[]).accordanceFound")); // NOI18N } else { System.out.println(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.main(String[]).noAccordanceFound")); // NOI18N final ArrayList diff = instance.getDifferences(); final Iterator it = diff.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } } catch (Throwable e) { System.err.println(e); } } /** * Setzt die zu benutzende Datenbank und liest ihre Struktur ein. * * @param driver DOCUMENT ME! * @param database DOCUMENT ME! * @param username DOCUMENT ME! * @param password DOCUMENT ME! */ public void setDB(final String driver, final String database, final String username, final String password) { try { Class.forName(driver).newInstance(); setDB(DriverManager.getConnection(database, username, password)); } catch (Exception e) { System.err.println(e); } } /** * Setzt die zu benutzende Datenbank und liest ihre Struktur ein. * * @param con DOCUMENT ME! */ public void setDB(final Connection con) { conn = con; try { meta = conn.getMetaData(); readDBStructure(); } catch (Exception e) { System.err.println(e); } } /** * Vergleicht nacheinander die vorhandenen Datenmodel-Versionen mit der Struktur der Datenbank, deren Version * festgestellt werden soll. Wird eine passende Version gefunden, so wird die Suche abgebrochen und der * zugeh\u00F6rige Name der Datenmodel-Version wird geliefert. * * @return wenn Versionsckeck erfolgreich: der Name der Datenmodell-Version, sonst null. * * @throws DBVersionException wenn beim checken zu einem Fehler kommt. */ @Override public String checkVersion() throws DBVersionException { final Set versions = versionMap.keySet(); final Iterator it = versions.iterator(); while (it.hasNext()) { final String version = (String)it.next(); if (compareWithVersion(version)) { return version; } } return null; } /** * Vergleicht ob das Datenmodell einer bestimten Version entspricht. * * @param version der Versionsname. * * @return true wenn das Datenmodel die Struktur der \u00FCbergebene Version besitzt. * * @throws DBVersionException wenn beim checken zu einem Fehler kommt. */ @Override public boolean compareWithVersion(final String version) throws DBVersionException { final TreeMap tables = (TreeMap)versionMap.get(version); if (tables == null) { throw new DBVersionException(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).DBVersionException")); // NOI18N } differences.clear(); final Set dbKeys = dbStructure.keySet(); final Set xmlKeys = tables.keySet(); final Iterator dbIter = dbKeys.iterator(); final Iterator xmlIter = xmlKeys.iterator(); while (dbIter.hasNext()) { // iterieren \u00FCber alle Tabellen der DB String dbTable = (String)dbIter.next(); String xmlTable; if (xmlIter.hasNext()) { // pr\u00FCfen ob es \u00FCberhaupt noch Eintr\u00E4ge im XML gibt xmlTable = (String)xmlIter.next(); } else { differences.add(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.tableOnlyInDB", new Object[] { dbTable })); // NOI18N continue; } while (dbTable.compareTo(xmlTable) < 0) { // da beide gleich sortiert sind fehlen Eintr\u00E4ge im XML differences.add(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.tableOnlyInDB", new Object[] { dbTable })); // NOI18N if (dbIter.hasNext()) { dbTable = (String)dbIter.next(); } else { break; } } while (dbTable.compareTo(xmlTable) > 0) { // im XML sind Eintr\u00E4ge die nicht in der DB sind differences.add(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.TableOnlyInXML", new Object[] { dbTable })); // NOI18N if (xmlIter.hasNext()) { xmlTable = (String)xmlIter.next(); } else { break; } } if (dbTable.compareTo(xmlTable) == 0) { // identische Tabelle in XML und DB, d.h. Spalten vergleichen final TreeMap dbColumnMap = (TreeMap)dbStructure.get(dbTable); final TreeMap xmlColumnMap = (TreeMap)tables.get(xmlTable); final Set dbColumns = dbColumnMap.keySet(); final Set xmlColumns = xmlColumnMap.keySet(); final Iterator dbColumnsIter = dbColumns.iterator(); final Iterator xmlColumnsIter = xmlColumns.iterator(); while (dbColumnsIter.hasNext()) { // iterieren \u00FCber die Spalten der Tabelle String dbColumn = (String)dbColumnsIter.next(); String xmlColumn; if (xmlColumnsIter.hasNext()) { xmlColumn = (String)xmlColumnsIter.next(); } else { differences.add( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.ColumnOnlyInDB", new Object[] { dbTable, dbColumn })); // NOI18N continue; } while (dbColumn.compareTo(xmlColumn) < 0) { differences.add( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.ColumnOnlyInDB", new Object[] { dbTable, dbColumn })); // NOI18N if (dbColumnsIter.hasNext()) { dbColumn = (String)dbColumnsIter.next(); } else { break; } } while (dbColumn.compareTo(xmlColumn) > 0) { differences.add( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).differences.ColumnOnlyInXML", new Object[] { dbTable, dbColumn })); // NOI18N if (xmlColumnsIter.hasNext()) { xmlColumn = (String)xmlColumnsIter.next(); } else { break; } } if (dbColumn.compareTo(xmlColumn) == 0) { // \u00FCbereinstimmender Spaltenname in XML und DB final TreeMap dbColumnData = (TreeMap)dbColumnMap.get(dbColumn); final TreeMap xmlColumnData = (TreeMap)xmlColumnMap.get(xmlColumn); if (xmlColumnData.get("nullable") != null) { // NOI18N if ( ((String)dbColumnData.get("nullable")).compareTo( // NOI18N xmlColumnData.get("nullable").toString()) // NOI18N != 0) { differences.add(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).nullableDiffers", new Object[] { dbTable, dbColumn, dbColumnData.get("nullable"), xmlColumnData.get("nullable") })); // NOI18N } } if (xmlColumnData.get("size") != null) { // NOI18N if ( ((String)dbColumnData.get("size")).compareTo(xmlColumnData.get("size").toString()) // NOI18N != 0) { differences.add(org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).sizeDiffers", new Object[] { dbTable, dbColumn, dbColumnData.get("size"), xmlColumnData.get("size") })); // NOI18N } } if (xmlColumnData.get("type") != null) { // NOI18N if ( ((String)dbColumnData.get("type")).compareTo(xmlColumnData.get("type").toString()) // NOI18N != 0) { differences.add( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.compareWithVersion(String).typeDiffers", new Object[] { dbTable, dbColumn, dbColumnData.get("type"), xmlColumnData.get("type") })); // NOI18N } } } } } } return differences.size() == 0; } /** * Liefert alle Datenmodel-Versionen die in dem Datenmodell-Speichermodul enthalten sind. * * @return Bezeichnungen der Datenmodel-Versionenen. * * @throws DBVersionException wenn es zu einem Fehler kommt. */ @Override public String[] getAllVersions() throws DBVersionException { return (String[])versionMap.keySet().toArray(new String[0]); } /** * Pr\u00FCft ob eine bestimmte Version in dem Datenmodell-Speichermodul enthalten ist. * * @param version der Versionsname. * * @return true wenn die gesuchte Version in dem Datenmodell-Speichermodul enthalten ist, sonst false. * * @throws DBVersionException wenn es zu einem Fehler kommt. */ @Override public boolean versionAvailable(final String version) throws DBVersionException { final Set versions = versionMap.keySet(); final Iterator it = versions.iterator(); while (it.hasNext()) { if (version.compareTo(it.next().toString()) == 0) { return true; } } return false; } /** * Speichert die derzeitig ausgew\u00E4hlte Datenbank in eine XML Datei. * * @param fileName Name der Datei in die gespeichert wird * @param versionName name attribut des version tag, das den Rest umschliesst */ public void writeVersionXML(final String fileName, final String versionName) { try { final FileWriter xml = new FileWriter(fileName); final ResultSet tables = meta.getTables(null, null, "%", null); // NOI18N xml.write("\t<Version name=\"" + versionName + "\">\n"); // NOI18N while (tables.next()) { if (tables.getString("TABLE_TYPE").compareTo("TABLE") != 0) { // Spezielle Tabellentypen wie // views//NOI18N \u00FCberspringen continue; } xml.write("\t\t<InternTable name=\"" + tables.getString("TABLE_NAME") + "\">\n"); // NOI18N final ResultSet columns = meta.getColumns(null, null, tables.getString("TABLE_NAME"), "%"); // NOI18N while (columns.next()) { xml.write("\t\t\t<Column nullable=\""); // NOI18N if (columns.getString("IS_NULLABLE").equals("YES")) { // NOI18N xml.write("true"); // NOI18N } else if (columns.getString("IS_NULLABLE").equals("NO")) { // NOI18N xml.write("false"); // NOI18N } else { // DB MetaInfo API garantiert keine // eindeutige Aussagen xml.write("unknown"); // NOI18N } xml.write("\" type=\"" + columns.getString("TYPE_NAME")); // NOI18N xml.write("\" size=\"" + columns.getString("COLUMN_SIZE")); // NOI18N xml.write("\">" + columns.getString("COLUMN_NAME") + "</Column>\n"); // NOI18N } xml.write("\t\t</InternTable>\n"); // NOI18N } xml.write("\t</Version>\n"); // NOI18N xml.close(); } catch (Exception e) { System.err.println(e); } } /** * Gibt die Unterschiede zwischen Datenbank und XML Beschreibung des zuletzt durchgef\u00FChrten Vergleiches * zur\u00FCck. * * @return Liste von strings mit den Unterschieden */ public ArrayList getDifferences() { return differences; } /** * Liest die XML Datei mit den Datenbankbeschreibungen ein und speichert die gefundenen Daten in versionMap. */ private void readVersionsXML() { try { final File test = new File(xmlFile); if (!test.exists()) { System.out.println( org.openide.util.NbBundle.getMessage( DOMVersionChecker.class, "DOMVersionChecker.readVersionsXML().noXMLFound")); // NOI18N return; } final Document xml = parser.build(test); final Element datamodel = xml.getRootElement(); final List versions = datamodel.getChildren("Version"); // NOI18N final Iterator versionsIter = versions.iterator(); while (versionsIter.hasNext()) { final Element version = (Element)versionsIter.next(); final List tables = version.getChildren("InternTable"); // NOI18N final Iterator tablesIter = tables.iterator(); final TreeMap tableMap = new TreeMap(); while (tablesIter.hasNext()) { final Element table = (Element)tablesIter.next(); final List columns = table.getChildren("Column"); // NOI18N final Iterator columnsIter = columns.iterator(); final TreeMap columnMap = new TreeMap(); while (columnsIter.hasNext()) { final Element column = (Element)columnsIter.next(); final TreeMap columnData = new TreeMap(); columnData.put("nullable", column.getAttributeValue("nullable")); // NOI18N columnData.put("size", column.getAttributeValue("size")); // NOI18N columnData.put("type", column.getAttributeValue("type")); // NOI18N columnMap.put(column.getText(), columnData); } tableMap.put(table.getAttributeValue("name"), columnMap); // NOI18N } versionMap.put(version.getAttributeValue("name"), tableMap); // NOI18N } } catch (Exception e) { System.err.println(e); } } /** * Liest die Struktur der derzeitig ausgew\u00E4hlten Datenbank in dbStructure ein. */ private void readDBStructure() { try { final ResultSet tables = meta.getTables(null, null, "%", null); // NOI18N while (tables.next()) { if (tables.getString("TABLE_TYPE").compareTo("TABLE") != 0) { // NOI18N continue; } final ResultSet columns = meta.getColumns(null, null, tables.getString("TABLE_NAME"), "%"); // NOI18N final TreeMap columnsMap = new TreeMap(); while (columns.next()) { final TreeMap columnData = new TreeMap(); if (columns.getString("IS_NULLABLE").equals("YES")) { // NOI18N columnData.put("nullable", "true"); // NOI18N } else if (columns.getString("IS_NULLABLE").equals("NO")) { // NOI18N columnData.put("nullable", "false"); // NOI18N } else { columnData.put("nullable", "unknown"); // NOI18N } columnData.put("size", columns.getString("COLUMN_SIZE")); // NOI18N columnData.put("type", columns.getString("TYPE_NAME")); // NOI18N columnsMap.put(columns.getString("COLUMN_NAME"), columnData); // NOI18N } dbStructure.put(tables.getString("TABLE_NAME"), columnsMap); // NOI18N } } catch (Exception e) { System.err.println(e); } } }