/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.tools; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import org.zoodb.internal.util.Util; import org.zoodb.jdo.ZooJdoHelper; import org.zoodb.jdo.ZooJdoProperties; import org.zoodb.schema.ZooClass; import org.zoodb.schema.ZooField; import org.zoodb.schema.ZooHandle; public class ZooCompareDb { private static boolean logToConsole = false; private static StringBuilder out; public static void main(String[] args) { if (args.length != 2) { System.out.println("Error: Please provide two databases."); System.out.println("Usage: "); System.out.println(" ZooCompareDb <db1> <db2>"); } String db1 = args[0]; String db2 = args[1]; if (!ZooHelper.getDataStoreManager().dbExists(db1)) { System.err.println("ERROR Database not found: " + db1); return; } if (!ZooHelper.getDataStoreManager().dbExists(db2)) { System.err.println("ERROR Database not found: " + db2); return; } System.out.println("Checking databases: " + db1 + " <--> " + db2); run(db1, db2); System.out.println("Comparing databases finished."); } public static String run(String db1, String db2) { PersistenceManager pm1 = null; PersistenceManager pm2 = null; try { out = new StringBuilder(); //do this only here for repeated calls from tests ZooJdoProperties props1 = new ZooJdoProperties(db1); PersistenceManagerFactory pmf1 = JDOHelper.getPersistenceManagerFactory(props1); pm1 = pmf1.getPersistenceManager(); pm1.currentTransaction().begin(); ZooJdoProperties props2 = new ZooJdoProperties(db2); PersistenceManagerFactory pmf2 = JDOHelper.getPersistenceManagerFactory(props2); pm2 = pmf2.getPersistenceManager(); pm2.currentTransaction().begin(); List<ZooClass> commonClasses = compareClasses(pm1, pm2); compareInstances(pm1, pm2, commonClasses); return out.toString(); } finally { if (pm1 != null) { pm1.currentTransaction().rollback(); pm1.close(); } if (pm2 != null) { pm2.currentTransaction().rollback(); pm2.close(); } } } private static List<ZooClass> compareClasses(PersistenceManager pm1, PersistenceManager pm2) { //List of classes that are identical in both databases List<ZooClass> commonClasses = new ArrayList<ZooClass>(); for (ZooClass cls1: ZooJdoHelper.schema(pm1).getAllClasses()) { boolean isValid = true; if (cls1.getName().contains("ZooClass")) { isValid = false; continue; } ZooClass cls2 = ZooJdoHelper.schema(pm2).getClass(cls1.getName()); if (cls2 == null) { log("Class not found in db2: " + cls1); isValid = false; continue; } for (ZooField f1: cls1.getAllFields()) { ZooField f2 = cls2.getField(f1.getName()); if (f2 == null) { log("Class field not found in db2: " + cls1.getName() + "." + f1.getName()); isValid = false; continue; } if (!f1.getTypeName().equals(f2.getTypeName())) { log("Class field type differ for: " + cls1.getName() + "." + f1.getName() + " " + f1.getTypeName() + " vs " + f2.getTypeName()); isValid = false; continue; } } for (ZooField f2: cls2.getAllFields()) { ZooField f1 = cls1.getField(f2.getName()); if (f1 == null) { log("Class field not found in db1: " + cls1.getName() + "." + f2.getName()); isValid = false; continue; } } if (isValid) { commonClasses.add(cls1); } } for (ZooClass cls2: ZooJdoHelper.schema(pm2).getAllClasses()) { if (cls2.getName().contains("ZooClass")) { continue; } ZooClass cls1 = ZooJdoHelper.schema(pm1).getClass(cls2.getName()); if (cls1 == null) { log("Class not found in db1: " + cls2); continue; } } return commonClasses; } private static void compareInstances(PersistenceManager pm1, PersistenceManager pm2, List<ZooClass> commonClasses) { for (ZooClass cls1: commonClasses) { ZooClass cls2 = ZooJdoHelper.schema(pm2).getClass(cls1.getName()); Iterator<ZooHandle> i1 = cls1.getHandleIterator(false); while (i1.hasNext()) { ZooHandle hdl1 = i1.next(); ZooHandle hdl2 = ZooJdoHelper.schema(pm2).getHandle(hdl1.getOid()); if (hdl2 == null) { log("Object not found in db2: " + Util.oidToString(hdl1.getOid()) + " " + cls1); continue; } //compare object type if (!hdl1.getType().getName().equals(hdl2.getType().getName())) { log("Object has different classes: " + Util.oidToString(hdl1.getOid()) + " " + cls1 + " vs " + hdl2.getType()); continue; } //compare object data for (ZooField f1: cls1.getAllFields()) { ZooField f2 = cls2.getField(f1.getName()); Object v1 = f1.getValue(hdl1); Object v2 = f2.getValue(hdl2); if (v1 == v2) { //e.g. null continue; } if (v1.getClass() != v2.getClass()) { //For arrays this also covers dimensions and inner type log("Field value has different type: " + Util.oidToString(hdl1.getOid()) + " " + cls1 + "." + f1.getName() + ": " + v1.getClass() + " vs " + v1.getClass()); continue; } if (v1.getClass().isArray() && arrayEquals(v1, v2)) { continue; } if ((v1 == null && v2 != null) || !v1.equals(v2)) { log("Field has different values: " + Util.oidToString(hdl1.getOid()) + " " + cls1 + "." + f1.getName() + ": " + f1.getValue(hdl1) + " vs " + f2.getValue(hdl2)); continue; } } } } //reverse check for (ZooClass cls1: commonClasses) { ZooClass cls2 = ZooJdoHelper.schema(pm2).getClass(cls1.getName()); Iterator<ZooHandle> i2 = cls2.getHandleIterator(false); while (i2.hasNext()) { ZooHandle hdl2 = i2.next(); ZooHandle hdl1 = ZooJdoHelper.schema(pm1).getHandle(hdl2.getOid()); if (hdl1 == null) { log("Object not found in db1: " + Util.oidToString(hdl2.getOid()) + " " + cls2); continue; } } } } private static boolean arrayEquals(Object v1, Object v2) { if (v1.getClass().getComponentType() == Boolean.TYPE) { return Arrays.equals((boolean[])v1, (boolean[])v2); } if (v1.getClass().getComponentType() == Byte.TYPE) { return Arrays.equals((byte[])v1, (byte[])v2); } if (v1.getClass().getComponentType() == Character.TYPE) { return Arrays.equals((char[])v1, (char[])v2); } if (v1.getClass().getComponentType() == Double.TYPE) { return Arrays.equals((double[])v1, (double[])v2); } if (v1.getClass().getComponentType() == Float.TYPE) { return Arrays.equals((float[])v1, (float[])v2); } if (v1.getClass().getComponentType() == Integer.TYPE) { return Arrays.equals((int[])v1, (int[])v2); } if (v1.getClass().getComponentType() == Long.TYPE) { return Arrays.equals((long[])v1, (long[])v2); } if (v1.getClass().getComponentType() == Short.TYPE) { return Arrays.equals((short[])v1, (short[])v2); } return Arrays.deepEquals((Object[])v1, (Object[])v2); } private static void log(String s) { if (logToConsole) { System.out.println(s); } else { out.append(s + '\n'); } } }